From bc05bfe7c58a0df531e92cb43f3ea2e6332c3c68 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 18 Aug 2025 15:14:35 +0200 Subject: [PATCH] Fit JIT variable not stored before YIELD JIT doesn't recognize that variables may be used after returning from a trace due to YIELD, so some effects may never be stored to memory. YIELD ops terminate trace recordings with ZEND_JIT_TRACE_STOP_RETURN, and are handled mostly like RETURN. Here I change zend_jit_trace_execute() so that YIELD terminates recordings with ZEND_JIT_TRACE_STOP_INTERPRETER instead, to ensure that we recognize that variables may be used after returning from the trace due to YIELD. Fixes GH-19493 Closes GH-19515 --- NEWS | 3 +++ ext/opcache/jit/zend_jit_vm_helpers.c | 15 +++++++++++++-- ext/opcache/tests/jit/gh19493-001.phpt | 24 ++++++++++++++++++++++++ ext/opcache/tests/jit/gh19493-002.phpt | 24 ++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 ext/opcache/tests/jit/gh19493-001.phpt create mode 100644 ext/opcache/tests/jit/gh19493-002.phpt diff --git a/NEWS b/NEWS index 3fe788a7ead..c082f336225 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() triggers "Constant already defined" warning). (ilutov) +- Opcache: + . Fixed bug GH-19493 (JIT variable not stored before YIELD). (Arnaud) + - OpenSSL: . Fixed bug GH-19245 (Success error message on TLS stream accept failure). (Jakub Zelenka) diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index f4d261cd753..8b85324ef89 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -937,11 +937,18 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, break; } +#ifdef HAVE_GCC_GLOBAL_REGS + const zend_op *prev_opline = opline; +#endif handler = (zend_vm_opcode_handler_t)ZEND_OP_TRACE_INFO(opline, offset)->call_handler; #ifdef HAVE_GCC_GLOBAL_REGS handler(); if (UNEXPECTED(opline == zend_jit_halt_op)) { - stop = ZEND_JIT_TRACE_STOP_RETURN; + if (prev_opline->opcode == ZEND_YIELD || prev_opline->opcode == ZEND_YIELD_FROM) { + stop = ZEND_JIT_TRACE_STOP_INTERPRETER; + } else { + stop = ZEND_JIT_TRACE_STOP_RETURN; + } opline = NULL; halt = ZEND_JIT_TRACE_HALT; break; @@ -951,7 +958,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, rc = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); if (rc != 0) { if (rc < 0) { - stop = ZEND_JIT_TRACE_STOP_RETURN; + if (opline->opcode == ZEND_YIELD || opline->opcode == ZEND_YIELD_FROM) { + stop = ZEND_JIT_TRACE_STOP_INTERPRETER; + } else { + stop = ZEND_JIT_TRACE_STOP_RETURN; + } opline = NULL; halt = ZEND_JIT_TRACE_HALT; break; diff --git a/ext/opcache/tests/jit/gh19493-001.phpt b/ext/opcache/tests/jit/gh19493-001.phpt new file mode 100644 index 00000000000..68e5d6e2401 --- /dev/null +++ b/ext/opcache/tests/jit/gh19493-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19493 001: Var not stored before YIELD +--FILE-- +getReturn()); + +?> +--EXPECT-- +int(99) diff --git a/ext/opcache/tests/jit/gh19493-002.phpt b/ext/opcache/tests/jit/gh19493-002.phpt new file mode 100644 index 00000000000..57f2a982673 --- /dev/null +++ b/ext/opcache/tests/jit/gh19493-002.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19493 002: Var not stored before YIELD_FROM +--FILE-- +getReturn()); + +?> +--EXPECT-- +int(99)