Skip to content

Commit cdf1a4f

Browse files
committed
Fix zend_optimizer_replace_by_const() leaving stale OP2 uses behind
For opcodes that do not consume their OP1 operand, the constant is propagated into every use of the temporary rather than just the first one. That loop only ever looked at OP1, so a use of the same temporary as an OP2 operand was left referring to a temporary that no longer has a definition: function test($v) { switch ($v) { case [$x] = (int)1.5: break; } } The ZEND_CAST is folded and rewritten into the ZEND_FETCH_LIST_R of the list assignment, but the comparison emitted for the case expression uses the same temporary as its OP2 and keeps the dangling reference, which makes SSA construction fail its integrity check. An OP2 operand is always consumed (ZEND_FE_FETCH_R/RW, where OP2 is a def rather than a use, is the sole exception), so such a use is the last one and the loop can stop there. Assisted-By: Claude <noreply@anthropic.com>
1 parent 8723e66 commit cdf1a4f

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

‎Zend/Optimizer/zend_optimizer.c‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,17 @@ bool zend_optimizer_replace_by_const(zend_op_array *op_array,
666666
if (is_last) {
667667
break;
668668
}
669+
} else if (opline->op2_type == type && opline->op2.var == var
670+
&& opline->opcode != ZEND_FE_FETCH_R
671+
&& opline->opcode != ZEND_FE_FETCH_RW) {
672+
/* An OP2 operand is always consumed, so this is the last use.
673+
* OP2 of FE_FETCH is a def rather than a use, stop there. */
674+
Z_TRY_ADDREF_P(val);
675+
if (!zend_optimizer_update_op2_const(op_array, opline, val)) {
676+
zval_ptr_dtor(val);
677+
return false;
678+
}
679+
break;
669680
}
670681
opline++;
671682
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
zend_optimizer_replace_by_const(): OP2 uses of a temporary kept alive by FETCH_LIST_R
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
--FILE--
9+
<?php
10+
function test($v) {
11+
switch ($v) {
12+
case [$x] = (int)1.5:
13+
return "matched " . var_export($x, true);
14+
default:
15+
return "default";
16+
}
17+
}
18+
var_dump(test(1));
19+
var_dump(test(2));
20+
?>
21+
--EXPECT--
22+
string(12) "matched NULL"
23+
string(7) "default"

0 commit comments

Comments
 (0)