@@ -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 ;
0 commit comments