mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Use original op_array when JIT compiling a Closure
zend_jit() assumes that Closure op_arrays have no scope, but this is not true when using the hot counters, first exec, or trace triggers as they use the executed op_array, which is in case of Closures is a copy, with a scope. In the tracing JIT this problem is avoided as we fetch the original op_array when compiling a Closure. Here I replicate this for the hot counters and first exec triggers. Fixes GH-16186 Closes GH-16200
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
38
ext/opcache/tests/gh16186_001.phpt
Normal file
38
ext/opcache/tests/gh16186_001.phpt
Normal file
@@ -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--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
private $x;
|
||||
public function getIncrementor() {
|
||||
return function() { $this->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)
|
||||
}
|
||||
38
ext/opcache/tests/gh16186_002.phpt
Normal file
38
ext/opcache/tests/gh16186_002.phpt
Normal file
@@ -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--
|
||||
<?php
|
||||
|
||||
class A {
|
||||
private $x;
|
||||
public function getIncrementor() {
|
||||
return function() { $this->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)
|
||||
}
|
||||
Reference in New Issue
Block a user