Skip to content

Commit e7a83ec

Browse files
committed
Fix bug #78271
When cleaning nops in the dfa pass, we were always keeping the smart branch inhibiting nop that occurs directly before the jump instruction. However, as we skip unreachable blocks entirely, it may happen that we need to keep a nop that occurs further back, prior to the unreachable blocks. Account for that case now. We should really do something about the smart branch situation, this is very fragile...
1 parent 3fa9f9c commit e7a83ec

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ PHP NEWS
2424
. Fixed bug #78189 (file cache strips last character of uname hash). (cmb)
2525
. Fixed bug #78202 (Opcache stats for cache hits are capped at 32bit NUM).
2626
(cmb)
27+
. Fixed bug #78271 (Invalid result of if-else). (Nikita)
2728

2829
- PCRE:
2930
. Fixed bug #78197 (PCRE2 version check in configure fails for "##.##-xxx"

Zend/tests/bug78271.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #78271: Invalid result of if-else
3+
--FILE--
4+
<?
5+
function test($a, $b){
6+
if ($a==10) {
7+
$w="x";
8+
} else {
9+
$w="y";
10+
}
11+
12+
if ($b) {
13+
$d1="none";
14+
$d2="block";
15+
} else {
16+
$d1="block";
17+
$d2="none";
18+
}
19+
20+
echo $d2.$b."\n";
21+
22+
}
23+
24+
test(1, 1);
25+
?>
26+
--EXPECT--
27+
block1

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,37 @@ int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx,
125125
return SUCCESS;
126126
}
127127

128+
static zend_bool is_smart_branch_inhibiting_nop(
129+
zend_op_array *op_array, uint32_t target, uint32_t current,
130+
zend_basic_block *b, zend_basic_block *blocks_end)
131+
{
132+
uint32_t next;
133+
/* Target points one past the last non-nop instruction. Make sure there is one. */
134+
if (target == 0) {
135+
return 0;
136+
}
137+
138+
/* Find the next instruction, skipping unreachable or empty blocks. */
139+
next = current + 1;
140+
if (next >= b->start + b->len) {
141+
do {
142+
b++;
143+
if (b == blocks_end) {
144+
return 0;
145+
}
146+
} while (!(b->flags & ZEND_BB_REACHABLE) || b->len == 0);
147+
next = b->start;
148+
}
149+
150+
return (op_array->opcodes[next].opcode == ZEND_JMPZ ||
151+
op_array->opcodes[next].opcode == ZEND_JMPNZ) &&
152+
zend_is_smart_branch(op_array->opcodes + target - 1);
153+
}
154+
128155
static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_optimizer_ctx *ctx)
129156
{
130157
zend_basic_block *blocks = ssa->cfg.blocks;
131-
zend_basic_block *end = blocks + ssa->cfg.blocks_count;
158+
zend_basic_block *blocks_end = blocks + ssa->cfg.blocks_count;
132159
zend_basic_block *b;
133160
zend_func_info *func_info;
134161
int j;
@@ -152,7 +179,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
152179
}
153180
}
154181

155-
for (b = blocks; b < end; b++) {
182+
for (b = blocks; b < blocks_end; b++) {
156183
if (b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE)) {
157184
uint32_t end;
158185

@@ -174,13 +201,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
174201
while (i < end) {
175202
shiftlist[i] = i - target;
176203
if (EXPECTED(op_array->opcodes[i].opcode != ZEND_NOP) ||
177-
/* Keep NOP to support ZEND_VM_SMART_BRANCH. Using "target-1" instead of
178-
* "i-1" here to check the last non-NOP instruction. */
179-
(target > 0 &&
180-
i + 1 < op_array->last &&
181-
(op_array->opcodes[i+1].opcode == ZEND_JMPZ ||
182-
op_array->opcodes[i+1].opcode == ZEND_JMPNZ) &&
183-
zend_is_smart_branch(op_array->opcodes + target - 1))) {
204+
is_smart_branch_inhibiting_nop(op_array, target, i, b, blocks_end)) {
184205
if (i != target) {
185206
op_array->opcodes[target] = op_array->opcodes[i];
186207
ssa->ops[target] = ssa->ops[i];
@@ -240,7 +261,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
240261
}
241262

242263
/* update branch targets */
243-
for (b = blocks; b < end; b++) {
264+
for (b = blocks; b < blocks_end; b++) {
244265
if ((b->flags & ZEND_BB_REACHABLE) && b->len != 0) {
245266
zend_op *opline = op_array->opcodes + b->start + b->len - 1;
246267
zend_optimizer_shift_jump(op_array, opline, shiftlist);

0 commit comments

Comments
 (0)