Skip to content

Commit 455ae28

Browse files
committed
Fix function JIT JMPNZ smart branch
When a smart branch and jump live in separate basic blocks, the JIT can't skip the jitting of the jump, as it may be reachable through another predecessor. When the smart branch is executed using zend_jit_handler(), we're manually writing the result of the branch to the given temporary so that the jump will work as expected. That happens in zend_jit_set_cond(). However, this was only correctly handled for JMPZ branches. The current opline was compared to opline following the jump, which would set the var to 1 if equal, i.e. the branch was not taken, meaning var was not zero. For JMPNZ we need to do the opposite. Fixes GH-21593
1 parent 3748255 commit 455ae28

File tree

4 files changed

+54
-3
lines changed

4 files changed

+54
-3
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ PHP NEWS
1616
- Opcache:
1717
. Fixed bug GH-21158 (JIT: Assertion jit->ra[var].flags & (1<<0) failed in
1818
zend_jit_use_reg). (Arnaud)
19+
. Fixed bug GH-21593 (Borked function JIT JMPNZ smart branch). (ilutov)
1920

2021
- SPL:
2122
. Fixed bug GH-21499 (RecursiveArrayIterator getChildren UAF after parent

ext/opcache/jit/zend_jit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2748,7 +2748,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
27482748
if (i == end
27492749
&& (opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
27502750
/* smart branch split across basic blocks */
2751-
if (!zend_jit_set_cond(&ctx, opline + 2, opline->result.var)) {
2751+
if (!zend_jit_set_cond(&ctx, opline, opline + 2, opline->result.var)) {
27522752
goto jit_failure;
27532753
}
27542754
}

ext/opcache/jit/zend_jit_ir.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4105,11 +4105,12 @@ static int zend_jit_cond_jmp(zend_jit_ctx *jit, const zend_op *next_opline, int
41054105
return 1;
41064106
}
41074107

4108-
static int zend_jit_set_cond(zend_jit_ctx *jit, const zend_op *next_opline, uint32_t var)
4108+
static int zend_jit_set_cond(zend_jit_ctx *jit, const zend_op *opline, const zend_op *next_opline, uint32_t var)
41094109
{
41104110
ir_ref ref;
41114111

4112-
ref = ir_ADD_U32(ir_ZEXT_U32(jit_CMP_IP(jit, IR_EQ, next_opline)), ir_CONST_U32(IS_FALSE));
4112+
ir_op op = (opline->result_type & IS_SMART_BRANCH_JMPZ) ? IR_EQ : IR_NE;
4113+
ref = ir_ADD_U32(ir_ZEXT_U32(jit_CMP_IP(jit, op, next_opline)), ir_CONST_U32(IS_FALSE));
41134114

41144115
// EX_VAR(var) = ...
41154116
ir_STORE(ir_ADD_OFFSET(jit_FP(jit), var + offsetof(zval, u1.type_info)), ref);

ext/opcache/tests/jit/gh21593.phpt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-21593: Function JIT JMPNZ smart branch
3+
--CREDITS--
4+
paulmhh
5+
--EXTENSIONS--
6+
opcache
7+
--INI--
8+
opcache.enable=1
9+
opcache.enable_cli=1
10+
opcache.jit=function
11+
--FILE--
12+
<?php
13+
14+
function test1($a) {
15+
if (isset($a?->a)) {
16+
echo "1\n";
17+
}
18+
}
19+
20+
function test2($a) {
21+
if (!isset($a?->a)) {
22+
echo "2\n";
23+
}
24+
}
25+
26+
function test3($a) {
27+
if (empty($a?->a)) {
28+
echo "3\n";
29+
}
30+
}
31+
32+
function test4($a) {
33+
if (!empty($a?->a)) {
34+
echo "4\n";
35+
}
36+
}
37+
38+
$a = new stdClass;
39+
$a->a = 'a';
40+
41+
test1($a);
42+
test2($a);
43+
test3($a);
44+
test4($a);
45+
46+
?>
47+
--EXPECT--
48+
1
49+
4

0 commit comments

Comments
 (0)