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