1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix deoptimization after exit during inc/dec

When the assumption that (PRE|POST)_(INC|DEC) overflows turns out to be
false and we exit, effects are lost if op1 or result were in regs.

Fix by updating the stack map before creating the exit point.

Fixes GH-19669
Closes GH-19680
This commit is contained in:
Arnaud Le Blanc
2025-09-03 11:48:39 +02:00
parent 98aba6c165
commit 4e0e88a140
4 changed files with 64 additions and 15 deletions

4
NEWS
View File

@@ -6,6 +6,10 @@ PHP NEWS
. Fixed bug GH-19765 (object_properties_load() bypasses readonly property
checks). (timwolla)
- Opcache:
. Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex).
(Arnaud)
- Standard:
. Fixed bug GH-12265 (Cloning an object breaks serialization recursion).
(nielsdos)

View File

@@ -4902,33 +4902,30 @@ static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t o
int32_t exit_point;
const void *exit_addr;
zend_jit_trace_stack *stack;
uint32_t old_res_info = 0;
uint32_t old_res_info = 0, old_op1_info = 0;
stack = JIT_G(current_frame)->stack;
if (opline->result_type != IS_UNUSED) {
old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) {
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), ref);
} else {
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), op1_lval_ref);
}
}
old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_LONG, 0);
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->op1.var), ref);
exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
opline->result_type != IS_UNUSED) {
if_overflow = ir_IF(ir_OVERFLOW(ref));
ir_IF_FALSE_cold(if_overflow);
jit_set_Z_LVAL(jit, res_addr, ref);
if (Z_MODE(res_addr) != IS_REG) {
jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG);
}
jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr));
ir_IF_TRUE(if_overflow);
} else {
ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr));
}
ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr));
if (opline->result_type != IS_UNUSED) {
SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
}
SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
} else {
if_overflow = ir_IF(ir_OVERFLOW(ref));
ir_IF_FALSE(if_overflow);

View File

@@ -0,0 +1,24 @@
--TEST--
GH-19669: assertion failure zend_jit_trace_type_to_info_ex
--CREDITS--
YuanchengJiang
--SKIPIF--
<?php
if (PHP_INT_SIZE !== 8) {
die('skip output depends PHP_INT_SIZE=8');
}
?>
--FILE--
<?php
function test() {
$a = PHP_INT_MIN;
$b = 0;
while ($b++ < 2) {
$a = (int) ($a-- + $a - $b);
}
return $a;
}
var_dump(test());
?>
--EXPECT--
int(-3)

View File

@@ -0,0 +1,24 @@
--TEST--
GH-19669 002: assertion failure zend_jit_trace_type_to_info_ex
--CREDITS--
YuanchengJiang
--SKIPIF--
<?php
if (PHP_INT_SIZE !== 8) {
die('skip output depends PHP_INT_SIZE=8');
}
?>
--FILE--
<?php
function test() {
$a = PHP_INT_MIN;
$b = -1;
while ($b++ < 2) {
$a = (int) (--$a + $a - $b);
}
return $a;
}
var_dump(test());
?>
--EXPECT--
int(-10)