@@ -184,30 +184,66 @@ void scdf_solve(scdf_ctx *scdf, const char *name) {
184
184
/* If a live range starts in a reachable block and ends in an unreachable block, we should
185
185
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
186
186
* 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 ) {
189
205
const zend_op_array * op_array = scdf -> op_array ;
190
206
const zend_cfg * cfg = & scdf -> ssa -> cfg ;
191
- const zend_basic_block * block = & cfg -> blocks [block_idx ];
192
207
if (!(cfg -> flags & ZEND_FUNC_FREE_LOOP_VAR )) {
193
- return 0 ;
208
+ return false ;
194
209
}
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 ++ ) {
196
232
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 ;
208
236
}
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 );
209
242
}
210
- return 0 ;
243
+
244
+ /* This block has no predecessors anymore. */
245
+ block -> predecessors_count = 0 ;
246
+ return removed_ops ;
211
247
}
212
248
213
249
/* 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) {
218
254
int i ;
219
255
int removed_ops = 0 ;
220
256
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
+ }
226
265
}
227
266
}
228
267
return removed_ops ;
0 commit comments