From e27e79f5f3545dcaaf26bd16adad163513b09628 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Thu, 9 Jan 2025 20:14:13 +0100
Subject: [PATCH] Backport fix GH-17307
This is a backport of GH-17319 to fix GH-17307 on lower branches.
---
ext/opcache/jit/zend_jit_arm64.dasc | 15 ++++++--------
ext/opcache/jit/zend_jit_x86.dasc | 11 ++++------
ext/opcache/tests/jit/gh17307.phpt | 32 +++++++++++++++++++++++++++++
3 files changed, 42 insertions(+), 16 deletions(-)
create mode 100644 ext/opcache/tests/jit/gh17307.phpt
diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc
index c351c482d51cb..2cbf68643086a 100644
--- a/ext/opcache/jit/zend_jit_arm64.dasc
+++ b/ext/opcache/jit/zend_jit_arm64.dasc
@@ -8471,6 +8471,7 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
{
uint32_t used_stack;
bool stack_check = 1;
+ const size_t func_type_offset = is_closure ? offsetof(zend_closure, func.type) : offsetof(zend_function, type);
// REG0 -> zend_function
// FCARG1 -> used_stack
@@ -8484,15 +8485,11 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value + ZEND_OBSERVER_ENABLED) * sizeof(zval);
| // if (EXPECTED(ZEND_USER_CODE(func->type))) {
- if (!is_closure) {
- | LOAD_32BIT_VAL FCARG1w, used_stack
- | // Check whether REG0 is an internal function.
- | ldrb TMP1w, [REG0, #offsetof(zend_function, type)]
- | TST_32_WITH_CONST TMP1w, 1, TMP2w
- | bne >1
- } else {
- | LOAD_32BIT_VAL FCARG1w, used_stack
- }
+ | LOAD_32BIT_VAL FCARG1w, used_stack
+ | // Check whether REG0 is an internal function.
+ | ldrb TMP1w, [REG0, #func_type_offset]
+ | TST_32_WITH_CONST TMP1w, 1, TMP2w
+ | bne >1
| // used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
| LOAD_32BIT_VAL REG2w, opline->extended_value
if (!is_closure) {
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index f1c52bcc976e3..9cf0c6cd8e881 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -9072,6 +9072,7 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
{
uint32_t used_stack;
bool stack_check = 1;
+ const size_t func_type_offset = is_closure ? offsetof(zend_closure, func.type) : offsetof(zend_function, type);
if (func) {
used_stack = zend_vm_calc_used_stack(opline->extended_value, func);
@@ -9082,13 +9083,9 @@ static int zend_jit_push_call_frame(dasm_State **Dst, const zend_op *opline, con
used_stack = (ZEND_CALL_FRAME_SLOT + opline->extended_value + ZEND_OBSERVER_ENABLED) * sizeof(zval);
| // if (EXPECTED(ZEND_USER_CODE(func->type))) {
- if (!is_closure) {
- | test byte [r0 + offsetof(zend_function, type)], 1
- | mov FCARG1a, used_stack
- | jnz >1
- } else {
- | mov FCARG1a, used_stack
- }
+ | test byte [r0 + func_type_offset], 1
+ | mov FCARG1a, used_stack
+ | jnz >1
| // used_stack += (func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args)) * sizeof(zval);
| mov edx, opline->extended_value
if (!is_closure) {
diff --git a/ext/opcache/tests/jit/gh17307.phpt b/ext/opcache/tests/jit/gh17307.phpt
new file mode 100644
index 0000000000000..292d695963c2e
--- /dev/null
+++ b/ext/opcache/tests/jit/gh17307.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-17307 (Internal closure causes JIT failure)
+--EXTENSIONS--
+opcache
+simplexml
+bcmath
+--INI--
+opcache.jit=1254
+opcache.jit_hot_func=1
+opcache.jit_buffer_size=32M
+--FILE--
+");
+
+function run_loop($firstTerms, $closure) {
+ foreach ($firstTerms as $firstTerm) {
+ \debug_zval_dump($firstTerm);
+ $closure($firstTerm, "10");
+ }
+}
+
+run_loop($simple, bcadd(...));
+echo "Done\n";
+
+?>
+--EXPECTF--
+object(SimpleXMLElement)#%d (0) refcount(3){
+}
+object(SimpleXMLElement)#%d (0) refcount(3){
+}
+Done