diff --git a/Zend/tests/vm_kind_tailcall_clang_windows.phpt b/Zend/tests/vm_kind_tailcall_clang_windows.phpt new file mode 100644 index 000000000000..f66a17ae0daf --- /dev/null +++ b/Zend/tests/vm_kind_tailcall_clang_windows.phpt @@ -0,0 +1,25 @@ +--TEST-- +Tailcall VM is selected when compiled with Clang >= 19 on Windows x64 +--SKIPIF-- + clang version (\d+)/', $info, $m)) { + die('skip not compiled with clang'); +} + +if ((int)$m[1] < 19) { + die('skip requires clang >= 19'); +} +?> +--FILE-- + +--EXPECT-- +string(21) "ZEND_VM_KIND_TAILCALL" diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index cef44695be85..d26d6a166dda 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -2497,7 +2497,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(__aarch64__))\n"; + $str .= "#elif defined(HAVE_MUSTTAIL) && defined(HAVE_PRESERVE_NONE) && (defined(__x86_64__) || defined(_M_X64) || 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 b63177e2421e..92b46e6628f3 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -41,7 +41,7 @@ static const char *const zend_vm_kind_name[] = { /* HYBRID requires support for computed GOTO and global register variables*/ #elif (defined(__GNUC__) && defined(HAVE_GCC_GLOBAL_REGS)) # define ZEND_VM_KIND ZEND_VM_KIND_HYBRID -#elif defined(HAVE_MUSTTAIL) && defined(HAVE_PRESERVE_NONE) && (defined(__x86_64__) || defined(__aarch64__)) +#elif defined(HAVE_MUSTTAIL) && defined(HAVE_PRESERVE_NONE) && (defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__)) # define ZEND_VM_KIND ZEND_VM_KIND_TAILCALL #else # define ZEND_VM_KIND ZEND_VM_KIND_CALL diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index c4b88b74f64d..e802d213aff1 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3314,6 +3314,27 @@ static PRUNTIME_FUNCTION zend_jit_unwind_callback(DWORD64 pc, PVOID context) static void zend_jit_setup_unwinder(void) { +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* TAILCALL VM: fixed_save_regset=0, no registers pushed in prologue. + * fixed_stack_frame_size=40, fixed_call_stack_size=48 (16+IR_SHADOW_ARGS). + * Prologue is: sub rsp, 0x58 (88 bytes = 40+48). */ + static const unsigned char uw_data[] = { + 0x01, // Version=1, Flags=0 + 0x04, // Size of prolog (sub rsp,imm8 = 4 bytes: 48 83 ec 58) + 0x01, // Count of unwind codes + 0x00, // Frame Register=none + 0x04, 0xa2, // offset 4: UWOP_ALLOC_SMALL info=10, alloc=(10+1)*8=88 + 0x00, 0x00, // padding + }; + /* Exit call variant: base 88 + 304 (shadow+GP+FP+padding) = 392 (0x188) */ + static const unsigned char uw_data_exitcall[] = { + 0x01, // Version=1, Flags=0 + 0x07, // Size of prolog (sub rsp,imm32 = 7 bytes: 48 81 ec 88 01 00 00) + 0x02, // Count of unwind codes + 0x00, // Frame Register=none + 0x07, 0x01, 0x31, 0x00, // offset 7: UWOP_ALLOC_LARGE info=0, size/8=49, alloc=392 + }; +#else /* Hardcoded SEH unwind data for JIT-ed PHP functions with "fixed stack frame" */ static const unsigned char uw_data[] = { 0x01, // UBYTE: 3 Version , UBYTE: 5 Flags @@ -3348,6 +3369,7 @@ static void zend_jit_setup_unwinder(void) 0x02, 0x50, // 1: pushq %rbp 0x01, 0x30, // 0: pushq %rbx }; +#endif zend_jit_uw_func = (PRUNTIME_FUNCTION)*dasm_ptr; *dasm_ptr = (char*)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(sizeof(RUNTIME_FUNCTION) * 4 + diff --git a/win32/build/confutils.js b/win32/build/confutils.js index fd1e9ce0be10..e695a00c815c 100644 --- a/win32/build/confutils.js +++ b/win32/build/confutils.js @@ -3374,6 +3374,10 @@ function toolset_setup_common_cflags() var vc_ver = probe_binary(PATH_PROG('cl', null)); ADD_FLAG("CFLAGS"," -fms-compatibility -fms-compatibility-version=" + vc_ver + " -fms-extensions"); + + if (CLANGVERS >= 1900 && TARGET_ARCH === 'x64') { + AC_DEFINE('HAVE_PRESERVE_NONE', 1, 'Whether the compiler supports __attribute__((preserve_none))'); + } } if (!CLANG_TOOLSET) {