Commit 5efde93f089 for php.net
commit 5efde93f089a5d5d610ca7640064f851bf17a8f0
Author: Marc <m@pyc.ac>
Date: Tue Sep 22 13:06:25 2026 +0200
Enable the TAILCALL VM when building with --disable-gcc-global-regs on GCC >= 16 (#23603)
diff --git a/NEWS b/NEWS
index c91c7ce877a..02da37e8108 100644
--- a/NEWS
+++ b/NEWS
@@ -114,6 +114,8 @@ PHP NEWS
when $allow_string is false is now deprecated. (Daniel Scherzer)
. Fixed bug GH-23232 (lone namespace separator asks the autoloader for an
empty class name). (spawnia)
+ . Enabled the TAILCALL VM (--disable-gcc-global-regs) when building with GCC >= 16.
+ (henderkes)
- CLI:
. Fixed bug GH-23242 (PHP development server does not support Expect
diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php
index e00aff17a92..674d1ed5b67 100755
--- a/Zend/zend_vm_gen.php
+++ b/Zend/zend_vm_gen.php
@@ -2522,7 +2522,7 @@ function gen_vm_opcodes_header(
$str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_HYBRID\n";
}
if ($GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] === "ZEND_VM_KIND_HYBRID" || $GLOBALS["vm_kind_name"][ZEND_VM_GEN_KIND] === "ZEND_VM_KIND_CALL") {
- $str .= "#elif defined(HAVE_MUSTTAIL) && defined(HAVE_PRESERVE_NONE) && (defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__)) && defined(__clang__)\n";
+ $str .= "#elif defined(HAVE_MUSTTAIL) && defined(HAVE_PRESERVE_NONE) && (defined(__x86_64__) || defined(__aarch64__))\n";
$str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_TAILCALL\n";
$str .= "#else\n";
$str .= "# define ZEND_VM_KIND\t\tZEND_VM_KIND_CALL\n";
diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h
index 4e0d3ec43d9..1d204b9281f 100644
Binary files a/Zend/zend_vm_opcodes.h and b/Zend/zend_vm_opcodes.h differ
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index e91da6aeb8d..85109c7d0e0 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -3100,23 +3100,10 @@ static int zend_real_jit_func(zend_op_array *op_array, zend_script *script, cons
return FAILURE;
}
-/* Run-time JIT handler */
-#if ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL
-static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS)
-#else
-static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS)
-#endif
+/* GCC cannot tail-call from a function that uses setjmp. */
+static zend_never_inline void zend_runtime_jit_compile(zend_op_array *op_array)
{
-#if GCC_GLOBAL_REGS
- zend_execute_data *execute_data;
- zend_op *opline;
-#else
- const zend_op *orig_opline = opline;
-#endif
-
- execute_data = EG(current_execute_data);
- zend_op_array *op_array = &EX(func)->op_array;
- opline = op_array->opcodes;
+ const zend_op *opline = op_array->opcodes;
zend_jit_op_array_extension *jit_extension;
bool do_bailout = 0;
@@ -3154,6 +3141,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(Z
if (do_bailout) {
zend_bailout();
}
+}
+
+/* Run-time JIT handler */
+#if ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL
+static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS)
+#else
+static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS)
+#endif
+{
+#if GCC_GLOBAL_REGS
+ zend_execute_data *execute_data;
+#else
+ const zend_op *orig_opline = opline;
+#endif
+
+ execute_data = EG(current_execute_data);
+ zend_runtime_jit_compile(&EX(func)->op_array);
/* JIT-ed code is going to be called by VM */
#if GCC_GLOBAL_REGS