From ed0f2db2d64e31b34e7ff70d8c7ca339f3f8c34e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Oct 2024 17:42:32 +0200 Subject: [PATCH] Ignore scope when compiling closure in non-tracing jit --- ext/opcache/jit/zend_jit.c | 17 +++++++++++++ ext/opcache/jit/zend_jit_internal.h | 2 ++ ext/opcache/tests/gh16186_001.phpt | 38 +++++++++++++++++++++++++++++ ext/opcache/tests/gh16186_002.phpt | 38 +++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 ext/opcache/tests/gh16186_001.phpt create mode 100644 ext/opcache/tests/gh16186_002.phpt diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 565776492670..a80126cda21b 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -1286,6 +1286,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op bool ce_is_instanceof; bool on_this; + ZEND_ASSERT(!(op_array->fn_flags & ZEND_ACC_CLOSURE) || !(op_array->scope)); + if (JIT_G(bisect_limit)) { jit_bisect_pos++; if (jit_bisect_pos >= JIT_G(bisect_limit)) { @@ -2818,6 +2820,18 @@ static int zend_real_jit_func(zend_op_array *op_array, zend_script *script, cons /* Build SSA */ memset(&ssa, 0, sizeof(zend_ssa)); + if (op_array->fn_flags & ZEND_ACC_CLOSURE) { + if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC) { + zend_jit_op_array_extension *jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); + op_array = (zend_op_array*) jit_extension->op_array; + } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { + zend_jit_op_array_hot_extension *jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); + op_array = (zend_op_array*) jit_extension->op_array; + } else { + ZEND_ASSERT(!op_array->scope); + } + } + if (zend_jit_op_array_analyze1(op_array, script, &ssa) != SUCCESS) { goto jit_failure; } @@ -3035,6 +3049,7 @@ static int zend_jit_setup_hot_counters(zend_op_array *op_array) } memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_HOT_COUNTERS; + jit_extension->op_array = op_array; jit_extension->counter = &zend_jit_hot_counters[zend_jit_op_array_hash(op_array) & (ZEND_HOT_COUNTERS_COUNT - 1)]; for (i = 0; i < op_array->last; i++) { jit_extension->orig_handlers[i] = op_array->opcodes[i].handler; @@ -3079,6 +3094,7 @@ int zend_jit_op_array(zend_op_array *op_array, zend_script *script) } memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_FIRST_EXEC; + jit_extension->op_array = op_array; jit_extension->orig_handler = (void*)opline->handler; ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension); opline->handler = (const void*)zend_jit_runtime_jit_handler; @@ -3108,6 +3124,7 @@ int zend_jit_op_array(zend_op_array *op_array, zend_script *script) } memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_PROF_REQUEST; + jit_extension->op_array = op_array; jit_extension->orig_handler = (void*)opline->handler; ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension); opline->handler = (const void*)zend_jit_profile_jit_handler; diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index a303197d1c5c..4fe07222d8f7 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -123,6 +123,7 @@ static zend_always_inline bool zend_jit_same_addr(zend_jit_addr addr1, zend_jit_ typedef struct _zend_jit_op_array_extension { zend_func_info func_info; + const zend_op_array *op_array; const void *orig_handler; } zend_jit_op_array_extension; @@ -160,6 +161,7 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend typedef struct _zend_jit_op_array_hot_extension { zend_func_info func_info; + const zend_op_array *op_array; int16_t *counter; const void *orig_handlers[1]; } zend_jit_op_array_hot_extension; diff --git a/ext/opcache/tests/gh16186_001.phpt b/ext/opcache/tests/gh16186_001.phpt new file mode 100644 index 000000000000..2547b8f3b7e5 --- /dev/null +++ b/ext/opcache/tests/gh16186_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-16186 001 (Non-tracing JIT uses Closure scope) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=0234 +opcache.file_update_protection=0 +opcache.revalidate_freq=0 +opcache.protect_memory=1 +--FILE-- +x++; }; + } +} +$a = new A(); +$f = $a->getIncrementor(); +$c = new stdClass(); +$f(); +$f2 = Closure::bind($f, $c); +$f2(); +$f2(); + +var_dump($c); + +?> +--EXPECTF-- +Warning: Undefined property: stdClass::$x in %s on line %d +object(stdClass)#%d (1) { + ["x"]=> + int(2) +} diff --git a/ext/opcache/tests/gh16186_002.phpt b/ext/opcache/tests/gh16186_002.phpt new file mode 100644 index 000000000000..72978146680a --- /dev/null +++ b/ext/opcache/tests/gh16186_002.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-16186 002 (Non-tracing JIT uses Closure scope) +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=0214 +opcache.file_update_protection=0 +opcache.revalidate_freq=0 +opcache.protect_memory=1 +--FILE-- +x++; }; + } +} +$a = new A(); +$f = $a->getIncrementor(); +$c = new stdClass(); +$f(); +$f2 = Closure::bind($f, $c); +$f2(); +$f2(); + +var_dump($c); + +?> +--EXPECTF-- +Warning: Undefined property: stdClass::$x in %s on line %d +object(stdClass)#%d (1) { + ["x"]=> + int(2) +}