Skip to content

Commit 4e0e88a

Browse files
committed
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
1 parent 98aba6c commit 4e0e88a

File tree

4 files changed

+64
-15
lines changed

4 files changed

+64
-15
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug GH-19765 (object_properties_load() bypasses readonly property
77
checks). (timwolla)
88

9+
- Opcache:
10+
. Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex).
11+
(Arnaud)
12+
913
- Standard:
1014
. Fixed bug GH-12265 (Cloning an object breaks serialization recursion).
1115
(nielsdos)

ext/opcache/jit/zend_jit_ir.c

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4902,33 +4902,30 @@ static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t o
49024902
int32_t exit_point;
49034903
const void *exit_addr;
49044904
zend_jit_trace_stack *stack;
4905-
uint32_t old_res_info = 0;
4905+
uint32_t old_res_info = 0, old_op1_info = 0;
49064906

49074907
stack = JIT_G(current_frame)->stack;
49084908
if (opline->result_type != IS_UNUSED) {
49094909
old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var));
4910+
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
49104911
if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) {
4911-
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0);
4912+
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), ref);
4913+
} else {
4914+
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), op1_lval_ref);
49124915
}
49134916
}
4917+
old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var));
4918+
SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_LONG, 0);
4919+
SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->op1.var), ref);
4920+
49144921
exit_point = zend_jit_trace_get_exit_point(opline + 1, 0);
49154922
exit_addr = zend_jit_trace_get_exit_addr(exit_point);
4916-
if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) &&
4917-
opline->result_type != IS_UNUSED) {
4918-
if_overflow = ir_IF(ir_OVERFLOW(ref));
4919-
ir_IF_FALSE_cold(if_overflow);
4920-
jit_set_Z_LVAL(jit, res_addr, ref);
4921-
if (Z_MODE(res_addr) != IS_REG) {
4922-
jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG);
4923-
}
4924-
jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr));
4925-
ir_IF_TRUE(if_overflow);
4926-
} else {
4927-
ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr));
4928-
}
4923+
ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr));
4924+
49294925
if (opline->result_type != IS_UNUSED) {
49304926
SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info);
49314927
}
4928+
SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info);
49324929
} else {
49334930
if_overflow = ir_IF(ir_OVERFLOW(ref));
49344931
ir_IF_FALSE(if_overflow);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-19669: assertion failure zend_jit_trace_type_to_info_ex
3+
--CREDITS--
4+
YuanchengJiang
5+
--SKIPIF--
6+
<?php
7+
if (PHP_INT_SIZE !== 8) {
8+
die('skip output depends PHP_INT_SIZE=8');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
function test() {
14+
$a = PHP_INT_MIN;
15+
$b = 0;
16+
while ($b++ < 2) {
17+
$a = (int) ($a-- + $a - $b);
18+
}
19+
return $a;
20+
}
21+
var_dump(test());
22+
?>
23+
--EXPECT--
24+
int(-3)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
GH-19669 002: assertion failure zend_jit_trace_type_to_info_ex
3+
--CREDITS--
4+
YuanchengJiang
5+
--SKIPIF--
6+
<?php
7+
if (PHP_INT_SIZE !== 8) {
8+
die('skip output depends PHP_INT_SIZE=8');
9+
}
10+
?>
11+
--FILE--
12+
<?php
13+
function test() {
14+
$a = PHP_INT_MIN;
15+
$b = -1;
16+
while ($b++ < 2) {
17+
$a = (int) (--$a + $a - $b);
18+
}
19+
return $a;
20+
}
21+
var_dump(test());
22+
?>
23+
--EXPECT--
24+
int(-10)

0 commit comments

Comments
 (0)