Skip to content

Commit 6fc38e7

Browse files
committed
Clean up remaining opcodes for foreach([] as $x)
I'm not familiar with the data structures involved in the passes after dfa, so I'm not sure how to safely improve on this (removing NOPs). Two useless FE_RESET_R and FE_FREE would be left over whether the empty array was from a literal, a variable, or a class constant.
1 parent 36175b3 commit 6fc38e7

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,24 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op
273273
}
274274
}
275275

276+
/* Clean up FE_RESET_R and FE_FREE for foreach over empty arrays (TODO: Remove left over no-op) */
277+
for (i = 0; i < target; i++) {
278+
zend_op *cur_op = &op_array->opcodes[i];
279+
zend_op *next_op;
280+
zend_ssa_op *ssa_op;
281+
if ((cur_op->opcode != ZEND_FE_RESET_R && cur_op->opcode != ZEND_FE_RESET_RW) || cur_op->op1_type != IS_CONST) {
282+
continue;
283+
}
284+
next_op = cur_op + 1;
285+
if (next_op->opcode != ZEND_FE_FREE || next_op->op1_type != IS_VAR || cur_op->result_type != IS_VAR || cur_op->result.var != next_op->op1.var) {
286+
continue;
287+
}
288+
ssa_op = &ssa->ops[i];
289+
zend_ssa_remove_result_def(ssa, ssa_op);
290+
zend_ssa_remove_instr(ssa, next_op, ssa_op + 1);
291+
zend_ssa_remove_instr(ssa, cur_op, ssa_op);
292+
}
293+
276294
op_array->last = target;
277295
}
278296
free_alloca(shiftlist, use_heap);

ext/opcache/tests/opt/dfa_001.phpt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
--TEST--
2+
DFA 001: Foreach over empty array is a no-op
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=-1
7+
opcache.opt_debug_level=0x20000
8+
opcache.preload=
9+
--SKIPIF--
10+
<?php require_once('skipif.inc'); ?>
11+
--FILE--
12+
<?php
13+
class Loop {
14+
const VALUES = [];
15+
public static function test() {
16+
echo "Start\n";
17+
$y = [];
18+
foreach ($y as $x) {
19+
}
20+
echo "Done\n";
21+
}
22+
public static function test2() {
23+
foreach (self::VALUES as $x) {
24+
}
25+
}
26+
public static function test3() {
27+
foreach ([] as $k => &$v) {
28+
}
29+
}
30+
}
31+
// TODO: Clean up remaining ZEND_NOOP opcodes from previous passes in a final pass?
32+
Loop::test();
33+
Loop::test2();
34+
Loop::test3();
35+
--EXPECTF--
36+
$_main: ; (lines=7, args=0, vars=0, tmps=0)
37+
; (after optimizer)
38+
; %sdfa_001.php:1-24
39+
L0 (21): INIT_STATIC_METHOD_CALL 0 string("Loop") string("test")
40+
L1 (21): DO_UCALL
41+
L2 (22): INIT_STATIC_METHOD_CALL 0 string("Loop") string("test2")
42+
L3 (22): DO_UCALL
43+
L4 (23): INIT_STATIC_METHOD_CALL 0 string("Loop") string("test3")
44+
L5 (23): DO_UCALL
45+
L6 (24): RETURN int(1)
46+
47+
Loop::test: ; (lines=5, args=0, vars=0, tmps=0)
48+
; (after optimizer)
49+
; %sdfa_001.php:4-10
50+
L0 (5): ECHO string("Start
51+
")
52+
L1 (7): NOP
53+
L2 (7): NOP
54+
L3 (9): ECHO string("Done
55+
")
56+
L4 (10): RETURN null
57+
58+
Loop::test2: ; (lines=3, args=0, vars=0, tmps=0)
59+
; (after optimizer)
60+
; %sdfa_001.php:11-14
61+
L0 (12): NOP
62+
L1 (12): NOP
63+
L2 (14): RETURN null
64+
65+
Loop::test3: ; (lines=3, args=0, vars=0, tmps=0)
66+
; (after optimizer)
67+
; %sdfa_001.php:15-18
68+
L0 (16): NOP
69+
L1 (16): NOP
70+
L2 (18): RETURN null
71+
Start
72+
Done

0 commit comments

Comments
 (0)