mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge branch 'PHP-8.4'
* PHP-8.4: JIT: Check exception on exit
This commit is contained in:
@@ -321,17 +321,18 @@ typedef enum _zend_jit_trace_stop {
|
||||
|
||||
#define ZEND_JIT_TRACE_SUPPORTED 0
|
||||
|
||||
#define ZEND_JIT_EXIT_JITED (1<<0)
|
||||
#define ZEND_JIT_EXIT_BLACKLISTED (1<<1)
|
||||
#define ZEND_JIT_EXIT_TO_VM (1<<2) /* exit to VM without attempt to create a side trace */
|
||||
#define ZEND_JIT_EXIT_RESTORE_CALL (1<<3) /* deoptimizer should restore EX(call) chain */
|
||||
#define ZEND_JIT_EXIT_POLYMORPHISM (1<<4) /* exit because of polymorphic call */
|
||||
#define ZEND_JIT_EXIT_FREE_OP1 (1<<5)
|
||||
#define ZEND_JIT_EXIT_FREE_OP2 (1<<6)
|
||||
#define ZEND_JIT_EXIT_PACKED_GUARD (1<<7)
|
||||
#define ZEND_JIT_EXIT_CLOSURE_CALL (1<<8) /* exit because of polymorphic INIT_DYNAMIC_CALL call */
|
||||
#define ZEND_JIT_EXIT_METHOD_CALL (1<<9) /* exit because of polymorphic INIT_METHOD_CALL call */
|
||||
#define ZEND_JIT_EXIT_INVALIDATE (1<<10) /* invalidate current trace */
|
||||
#define ZEND_JIT_EXIT_JITED (1<<0)
|
||||
#define ZEND_JIT_EXIT_BLACKLISTED (1<<1)
|
||||
#define ZEND_JIT_EXIT_TO_VM (1<<2) /* exit to VM without attempt to create a side trace */
|
||||
#define ZEND_JIT_EXIT_RESTORE_CALL (1<<3) /* deoptimizer should restore EX(call) chain */
|
||||
#define ZEND_JIT_EXIT_POLYMORPHISM (1<<4) /* exit because of polymorphic call */
|
||||
#define ZEND_JIT_EXIT_FREE_OP1 (1<<5)
|
||||
#define ZEND_JIT_EXIT_FREE_OP2 (1<<6)
|
||||
#define ZEND_JIT_EXIT_PACKED_GUARD (1<<7)
|
||||
#define ZEND_JIT_EXIT_CLOSURE_CALL (1<<8) /* exit because of polymorphic INIT_DYNAMIC_CALL call */
|
||||
#define ZEND_JIT_EXIT_METHOD_CALL (1<<9) /* exit because of polymorphic INIT_METHOD_CALL call */
|
||||
#define ZEND_JIT_EXIT_INVALIDATE (1<<10) /* invalidate current trace */
|
||||
#define ZEND_JIT_EXIT_CHECK_EXCEPTION (1<<11)
|
||||
|
||||
#define ZEND_JIT_EXIT_FIXED (1U<<31) /* the exit_info can't be changed by zend_jit_snapshot_handler() */
|
||||
|
||||
|
||||
@@ -14646,12 +14646,12 @@ result_fetched:
|
||||
ZEND_ASSERT(end_inputs == IR_UNUSED);
|
||||
if ((res_info & MAY_BE_GUARD) && JIT_G(current_frame)) {
|
||||
uint8_t type = concrete_type(res_info);
|
||||
uint32_t flags = 0;
|
||||
uint32_t flags = ZEND_JIT_EXIT_CHECK_EXCEPTION;
|
||||
|
||||
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))
|
||||
&& !delayed_fetch_this
|
||||
&& !op1_avoid_refcounting) {
|
||||
flags = ZEND_JIT_EXIT_FREE_OP1;
|
||||
flags |= ZEND_JIT_EXIT_FREE_OP1;
|
||||
}
|
||||
|
||||
if ((opline->result_type & (IS_VAR|IS_TMP_VAR))
|
||||
|
||||
@@ -3503,7 +3503,7 @@ static int zend_jit_trace_exit_needs_deoptimization(uint32_t trace_num, uint32_t
|
||||
uint32_t stack_size;
|
||||
zend_jit_trace_stack *stack;
|
||||
|
||||
if (opline || (flags & (ZEND_JIT_EXIT_RESTORE_CALL|ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2))) {
|
||||
if (opline || (flags & (ZEND_JIT_EXIT_RESTORE_CALL|ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2|ZEND_JIT_EXIT_CHECK_EXCEPTION))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3663,7 +3663,7 @@ static int zend_jit_trace_deoptimization(
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
|
||||
if (flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2|ZEND_JIT_EXIT_CHECK_EXCEPTION)) {
|
||||
zend_jit_check_exception(jit);
|
||||
}
|
||||
|
||||
@@ -8013,6 +8013,9 @@ static void zend_jit_dump_exit_info(zend_jit_trace_info *t)
|
||||
if (t->exit_info[i].flags & ZEND_JIT_EXIT_FREE_OP2) {
|
||||
fprintf(stderr, "/FREE_OP2");
|
||||
}
|
||||
if (t->exit_info[i].flags & ZEND_JIT_EXIT_CHECK_EXCEPTION) {
|
||||
fprintf(stderr, "/CHK_EXC");
|
||||
}
|
||||
for (j = 0; j < stack_size; j++) {
|
||||
uint8_t type = STACK_TYPE(stack, j);
|
||||
if (type != IS_UNKNOWN) {
|
||||
@@ -8724,9 +8727,14 @@ int ZEND_FASTCALL zend_jit_trace_exit(uint32_t exit_num, zend_jit_registers_buf
|
||||
EX(opline) = opline-1;
|
||||
zval_ptr_dtor_nogc(EX_VAR((opline-1)->op1.var));
|
||||
}
|
||||
if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2)) {
|
||||
if (t->exit_info[exit_num].flags & (ZEND_JIT_EXIT_FREE_OP1|ZEND_JIT_EXIT_FREE_OP2|ZEND_JIT_EXIT_CHECK_EXCEPTION)) {
|
||||
if (EG(exception)) {
|
||||
return 1;
|
||||
/* EX(opline) was overridden in zend_jit_trace_exit_stub(),
|
||||
* and may be wrong when IP is reused. */
|
||||
if (GCC_GLOBAL_REGS) {
|
||||
EX(opline) = EG(exception_op);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (t->exit_info[exit_num].flags & ZEND_JIT_EXIT_METHOD_CALL) {
|
||||
|
||||
37
ext/opcache/tests/jit/gh18262-001.phpt
Normal file
37
ext/opcache/tests/jit/gh18262-001.phpt
Normal file
@@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
GH-18262 001 (Assertion failure Zend/zend_vm_execute.h JIT)
|
||||
--CREDITS--
|
||||
YuanchengJiang
|
||||
--FILE--
|
||||
<?php
|
||||
#[AllowDynamicProperties]
|
||||
class B {
|
||||
public int $fusion;
|
||||
}
|
||||
class C extends B {
|
||||
}
|
||||
class D extends C {
|
||||
public function __destruct() {
|
||||
}
|
||||
}
|
||||
$tests = [
|
||||
[C::class, new C()],
|
||||
[C::class, new B()],
|
||||
[D::class, new B()],
|
||||
];
|
||||
foreach ($tests as [$class, $instance]) {
|
||||
$obj = (new ReflectionClass($class))->newLazyProxy(function ($obj) use ($instance) {
|
||||
$instance->b = 1;
|
||||
return $instance;
|
||||
});
|
||||
var_dump($obj->b);
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(1)
|
||||
int(1)
|
||||
|
||||
Fatal error: Uncaught TypeError: %s in %s:%d
|
||||
Stack trace:
|
||||
#0 {main}
|
||||
thrown in %s on line %d
|
||||
34
ext/opcache/tests/jit/gh18262-002.phpt
Normal file
34
ext/opcache/tests/jit/gh18262-002.phpt
Normal file
@@ -0,0 +1,34 @@
|
||||
--TEST--
|
||||
GH-18262 002 (Assertion failure Zend/zend_vm_execute.h JIT)
|
||||
--FILE--
|
||||
<?php
|
||||
#[AllowDynamicProperties]
|
||||
class B {
|
||||
public function __construct($init) {
|
||||
if ($init) {
|
||||
$this->b = $init;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tests = [
|
||||
new B(1),
|
||||
new B(0),
|
||||
];
|
||||
|
||||
set_error_handler(function ($_, $errstr) {
|
||||
throw new \Exception($errstr);
|
||||
});
|
||||
|
||||
foreach ($tests as $obj) {
|
||||
var_dump($obj->b);
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(1)
|
||||
|
||||
Fatal error: Uncaught Exception: Undefined property: B::$b in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): {closure:%s:%d}(2, 'Undefined prope...', '%s', %d)
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
||||
35
ext/opcache/tests/jit/gh18262-003.phpt
Normal file
35
ext/opcache/tests/jit/gh18262-003.phpt
Normal file
@@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
GH-18262 003 (Assertion failure Zend/zend_vm_execute.h JIT)
|
||||
--FILE--
|
||||
<?php
|
||||
#[AllowDynamicProperties]
|
||||
class B {
|
||||
public function __construct($init) {
|
||||
if ($init) {
|
||||
$this->b = $init;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tests = [
|
||||
new B(1),
|
||||
new B('str'), // slow deoptimization, create linked side trace
|
||||
new B(0), // jump to side trace with fast deoptimization
|
||||
];
|
||||
|
||||
set_error_handler(function ($_, $errstr) {
|
||||
throw new \Exception($errstr);
|
||||
});
|
||||
|
||||
foreach ($tests as $obj) {
|
||||
try {
|
||||
var_dump($obj->b);
|
||||
} catch (Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1)
|
||||
string(3) "str"
|
||||
Exception: Undefined property: B::$b
|
||||
36
ext/opcache/tests/jit/gh18262-004.phpt
Normal file
36
ext/opcache/tests/jit/gh18262-004.phpt
Normal file
@@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
GH-18262 004 (Assertion failure Zend/zend_vm_execute.h JIT)
|
||||
--FILE--
|
||||
<?php
|
||||
class B {
|
||||
public function __construct(
|
||||
public $throw,
|
||||
) { }
|
||||
public function __get($name) {
|
||||
return $this->throw === '1' ? 'str' : 1;
|
||||
}
|
||||
public function __destruct() {
|
||||
if ($this->throw === '1') {
|
||||
throw new Exception(__METHOD__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$tests = [
|
||||
'0',
|
||||
'1',
|
||||
];
|
||||
|
||||
foreach ($tests as $test) {
|
||||
// Second iteration exits, and free op1 throws
|
||||
var_dump((new B($test))->b);
|
||||
}
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(1)
|
||||
|
||||
Fatal error: Uncaught Exception: B::__destruct in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): B->__destruct()
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
||||
Reference in New Issue
Block a user