1
0
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:
Arnaud Le Blanc
2024-10-03 17:42:32 +02:00
parent 07e418abfb
commit 82f70dba7d
4 changed files with 95 additions and 0 deletions

View File

@@ -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;

View File

@@ -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;

View 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)
}

View 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)
}