Skip to content

Commit e0e5b59

Browse files
committed
Try harder to clean up unreachable loop free block
While we can't drop the loop free, we can drop other instructions in the same block. We should also indicate that it no longer has predecessors.
1 parent fc0cfd1 commit e0e5b59

File tree

4 files changed

+84
-23
lines changed

4 files changed

+84
-23
lines changed

Zend/Optimizer/scdf.c

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -184,30 +184,66 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
184184
/* If a live range starts in a reachable block and ends in an unreachable block, we should
185185
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
186186
* is necessary for the correctness of temporary compaction. */
187-
static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) {
188-
uint32_t i;
187+
static bool is_live_loop_var_free(
188+
scdf_ctx *scdf, const zend_op *opline, const zend_ssa_op *ssa_op) {
189+
if (!zend_optimizer_is_loop_var_free(opline)) {
190+
return false;
191+
}
192+
193+
int ssa_var = ssa_op->op1_use;
194+
if (ssa_var < 0) {
195+
return false;
196+
}
197+
198+
int op_num = scdf->ssa->vars[ssa_var].definition;
199+
ZEND_ASSERT(op_num >= 0);
200+
uint32_t def_block = scdf->ssa->cfg.map[op_num];
201+
return zend_bitset_in(scdf->executable_blocks, def_block);
202+
}
203+
204+
static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, const zend_basic_block *block) {
189205
const zend_op_array *op_array = scdf->op_array;
190206
const zend_cfg *cfg = &scdf->ssa->cfg;
191-
const zend_basic_block *block = &cfg->blocks[block_idx];
192207
if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) {
193-
return 0;
208+
return false;
194209
}
195-
for (i = block->start; i < block->start + block->len; i++) {
210+
211+
for (uint32_t i = block->start; i < block->start + block->len; i++) {
212+
if (is_live_loop_var_free(scdf, &op_array->opcodes[i], &scdf->ssa->ops[i])) {
213+
return true;
214+
}
215+
}
216+
return false;
217+
}
218+
219+
static uint32_t cleanup_loop_var_free_block(scdf_ctx *scdf, zend_basic_block *block) {
220+
zend_ssa *ssa = scdf->ssa;
221+
const zend_op_array *op_array = scdf->op_array;
222+
const zend_cfg *cfg = &ssa->cfg;
223+
uint32_t removed_ops = 0;
224+
225+
/* Removes phi nodes */
226+
for (zend_ssa_phi *phi = ssa->blocks[block - cfg->blocks].phis; phi; phi = phi->next) {
227+
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
228+
zend_ssa_remove_phi(ssa, phi);
229+
}
230+
231+
for (uint32_t i = block->start; i < block->start + block->len; i++) {
196232
zend_op *opline = &op_array->opcodes[i];
197-
if (zend_optimizer_is_loop_var_free(opline)) {
198-
int ssa_var = scdf->ssa->ops[i].op1_use;
199-
if (ssa_var >= 0) {
200-
int op_num = scdf->ssa->vars[ssa_var].definition;
201-
uint32_t def_block;
202-
ZEND_ASSERT(op_num >= 0);
203-
def_block = cfg->map[op_num];
204-
if (zend_bitset_in(scdf->executable_blocks, def_block)) {
205-
return 1;
206-
}
207-
}
233+
zend_ssa_op *ssa_op = &scdf->ssa->ops[i];
234+
if (is_live_loop_var_free(scdf, opline, ssa_op)) {
235+
continue;
208236
}
237+
238+
/* While we have to preserve the loop var free, we can still remove other instructions
239+
* in the block. */
240+
zend_ssa_remove_defs_of_instr(ssa, ssa_op);
241+
zend_ssa_remove_instr(ssa, opline, ssa_op);
209242
}
210-
return 0;
243+
244+
/* This block has no predecessors anymore. */
245+
block->predecessors_count = 0;
246+
return removed_ops;
211247
}
212248

213249
/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
@@ -218,11 +254,14 @@ int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
218254
int i;
219255
int removed_ops = 0;
220256
for (i = 0; i < ssa->cfg.blocks_count; i++) {
221-
if (!zend_bitset_in(scdf->executable_blocks, i)
222-
&& (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
223-
&& !kept_alive_by_loop_var_free(scdf, i)) {
224-
removed_ops += ssa->cfg.blocks[i].len;
225-
zend_ssa_remove_block(scdf->op_array, ssa, i);
257+
zend_basic_block *block = &ssa->cfg.blocks[i];
258+
if (!zend_bitset_in(scdf->executable_blocks, i) && (block->flags & ZEND_BB_REACHABLE)) {
259+
if (!kept_alive_by_loop_var_free(scdf, block)) {
260+
removed_ops += block->len;
261+
zend_ssa_remove_block(scdf->op_array, ssa, i);
262+
} else {
263+
removed_ops += cleanup_loop_var_free_block(scdf, block);
264+
}
226265
}
227266
}
228267
return removed_ops;

Zend/Optimizer/zend_ssa.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1290,7 +1290,7 @@ static void zend_ssa_remove_phi_from_block(zend_ssa *ssa, zend_ssa_phi *phi) /*
12901290
}
12911291
/* }}} */
12921292

1293-
static inline void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */
1293+
void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) /* {{{ */
12941294
{
12951295
if (ssa_op->op1_def >= 0) {
12961296
zend_ssa_remove_uses_of_var(ssa, ssa_op->op1_def);

Zend/Optimizer/zend_ssa.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *op
151151
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
152152

153153
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
154+
void zend_ssa_remove_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op);
154155
void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
155156
void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
156157
void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
match expression always errors
3+
--FILE--
4+
<?php
5+
function get_value() {
6+
return 0;
7+
}
8+
function test() {
9+
match(get_value()) {
10+
false => $a,
11+
true => $b,
12+
};
13+
}
14+
try {
15+
test();
16+
} catch (UnhandledMatchError $e) {
17+
echo $e->getMessage(), "\n";
18+
}
19+
?>
20+
--EXPECT--
21+
Unhandled match case 0

0 commit comments

Comments
 (0)