@@ -51,6 +51,7 @@ typedef struct {
51
51
zend_bitset phi_dead ;
52
52
zend_bitset instr_worklist ;
53
53
zend_bitset phi_worklist ;
54
+ zend_bitset phi_worklist_no_val ;
54
55
uint32_t instr_worklist_len ;
55
56
uint32_t phi_worklist_len ;
56
57
unsigned reorder_dtor_effects : 1 ;
@@ -213,43 +214,51 @@ static inline zend_bool may_have_side_effects(
213
214
}
214
215
}
215
216
216
- static inline void add_to_worklists (context * ctx , int var_num ) {
217
+ static zend_always_inline void add_to_worklists (context * ctx , int var_num , int check ) {
217
218
zend_ssa_var * var = & ctx -> ssa -> vars [var_num ];
218
219
if (var -> definition >= 0 ) {
219
- if (zend_bitset_in (ctx -> instr_dead , var -> definition )) {
220
+ if (! check || zend_bitset_in (ctx -> instr_dead , var -> definition )) {
220
221
zend_bitset_incl (ctx -> instr_worklist , var -> definition );
221
222
}
222
223
} else if (var -> definition_phi ) {
223
- if (zend_bitset_in (ctx -> phi_dead , var_num )) {
224
+ if (! check || zend_bitset_in (ctx -> phi_dead , var_num )) {
224
225
zend_bitset_incl (ctx -> phi_worklist , var_num );
225
226
}
226
227
}
227
228
}
228
229
229
- static inline void add_to_phi_worklist_only (context * ctx , int var_num ) {
230
+ static inline void add_to_phi_worklist_no_val (context * ctx , int var_num ) {
230
231
zend_ssa_var * var = & ctx -> ssa -> vars [var_num ];
231
232
if (var -> definition_phi && zend_bitset_in (ctx -> phi_dead , var_num )) {
232
- zend_bitset_incl (ctx -> phi_worklist , var_num );
233
+ zend_bitset_incl (ctx -> phi_worklist_no_val , var_num );
233
234
}
234
235
}
235
236
236
- static inline void add_operands_to_worklists (context * ctx , zend_op * opline , zend_ssa_op * ssa_op ) {
237
+ static zend_always_inline void add_operands_to_worklists (context * ctx , zend_op * opline , zend_ssa_op * ssa_op , int check ) {
237
238
if (ssa_op -> result_use >= 0 ) {
238
- add_to_worklists (ctx , ssa_op -> result_use );
239
+ add_to_worklists (ctx , ssa_op -> result_use , check );
239
240
}
240
- if (ssa_op -> op1_use >= 0 && !zend_ssa_is_no_val_use (opline , ssa_op , ssa_op -> op1_use )) {
241
- add_to_worklists (ctx , ssa_op -> op1_use );
241
+ if (ssa_op -> op1_use >= 0 ) {
242
+ if (!zend_ssa_is_no_val_use (opline , ssa_op , ssa_op -> op1_use )) {
243
+ add_to_worklists (ctx , ssa_op -> op1_use , check );
244
+ } else {
245
+ add_to_phi_worklist_no_val (ctx , ssa_op -> op1_use );
246
+ }
242
247
}
243
- if (ssa_op -> op2_use >= 0 && !zend_ssa_is_no_val_use (opline , ssa_op , ssa_op -> op2_use )) {
244
- add_to_worklists (ctx , ssa_op -> op2_use );
248
+ if (ssa_op -> op2_use >= 0 ) {
249
+ if (!zend_ssa_is_no_val_use (opline , ssa_op , ssa_op -> op2_use )) {
250
+ add_to_worklists (ctx , ssa_op -> op2_use , check );
251
+ } else {
252
+ add_to_phi_worklist_no_val (ctx , ssa_op -> op2_use );
253
+ }
245
254
}
246
255
}
247
256
248
- static inline void add_phi_sources_to_worklists (context * ctx , zend_ssa_phi * phi ) {
257
+ static zend_always_inline void add_phi_sources_to_worklists (context * ctx , zend_ssa_phi * phi , int check ) {
249
258
zend_ssa * ssa = ctx -> ssa ;
250
259
int source ;
251
260
FOREACH_PHI_SOURCE (phi , source ) {
252
- add_to_worklists (ctx , source );
261
+ add_to_worklists (ctx , source , check );
253
262
} FOREACH_PHI_SOURCE_END ();
254
263
}
255
264
@@ -303,6 +312,7 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
303
312
ssa_op -> op1_use = free_var ;
304
313
ssa_op -> op1_use_chain = ssa -> vars [free_var ].use_chain ;
305
314
ssa -> vars [free_var ].use_chain = ssa_op - ssa -> ops ;
315
+ return 0 ;
306
316
}
307
317
return 1 ;
308
318
}
@@ -468,64 +478,70 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor
468
478
ctx .phi_worklist_len = zend_bitset_len (ssa -> vars_count );
469
479
ctx .phi_worklist = alloca (sizeof (zend_ulong ) * ctx .phi_worklist_len );
470
480
memset (ctx .phi_worklist , 0 , sizeof (zend_ulong ) * ctx .phi_worklist_len );
481
+ ctx .phi_worklist_no_val = alloca (sizeof (zend_ulong ) * ctx .phi_worklist_len );
482
+ memset (ctx .phi_worklist_no_val , 0 , sizeof (zend_ulong ) * ctx .phi_worklist_len );
471
483
472
484
/* Optimistically assume all instructions and phis to be dead */
473
485
ctx .instr_dead = alloca (sizeof (zend_ulong ) * ctx .instr_worklist_len );
474
- memset (ctx .instr_dead , 0xff , sizeof (zend_ulong ) * ctx .instr_worklist_len );
486
+ memset (ctx .instr_dead , 0 , sizeof (zend_ulong ) * ctx .instr_worklist_len );
475
487
ctx .phi_dead = alloca (sizeof (zend_ulong ) * ctx .phi_worklist_len );
476
488
memset (ctx .phi_dead , 0xff , sizeof (zend_ulong ) * ctx .phi_worklist_len );
477
489
478
- /* Mark instruction with side effects as live */
479
- FOREACH_INSTR_NUM ( i ) {
480
- if ( may_have_side_effects ( op_array , ssa , & op_array -> opcodes [ i ], & ssa -> ops [ i ], ctx . reorder_dtor_effects )
481
- || zend_may_throw ( & op_array -> opcodes [ i ], op_array , ssa )
482
- || has_varargs ) {
483
- zend_bitset_excl ( ctx . instr_dead , i );
484
- add_operands_to_worklists ( & ctx , & op_array -> opcodes [ i ], & ssa -> ops [ i ]) ;
490
+ /* Mark reacable instruction without side effects as dead */
491
+ int b = ssa -> cfg . blocks_count ;
492
+ while ( b > 0 ) {
493
+ b -- ;
494
+ zend_basic_block * block = & ssa -> cfg . blocks [ b ];
495
+ if (!( block -> flags & ZEND_BB_REACHABLE )) {
496
+ continue ;
485
497
}
486
- } FOREACH_INSTR_NUM_END ();
498
+ i = block -> start + block -> len ;
499
+ while (i > block -> start ) {
500
+ i -- ;
501
+
502
+ if (zend_bitset_in (ctx .instr_worklist , i )) {
503
+ zend_bitset_excl (ctx .instr_worklist , i );
504
+ add_operands_to_worklists (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ], 0 );
505
+ } else if (may_have_side_effects (op_array , ssa , & op_array -> opcodes [i ], & ssa -> ops [i ], ctx .reorder_dtor_effects )
506
+ || zend_may_throw (& op_array -> opcodes [i ], op_array , ssa )
507
+ || has_varargs ) {
508
+ add_operands_to_worklists (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ], 0 );
509
+ } else {
510
+ zend_bitset_incl (ctx .instr_dead , i );
511
+ }
512
+
513
+ }
514
+ }
487
515
488
516
/* Propagate liveness backwards to all definitions of used vars */
489
517
while (!zend_bitset_empty (ctx .instr_worklist , ctx .instr_worklist_len )
490
518
|| !zend_bitset_empty (ctx .phi_worklist , ctx .phi_worklist_len )) {
491
519
while ((i = zend_bitset_pop_first (ctx .instr_worklist , ctx .instr_worklist_len )) >= 0 ) {
492
520
zend_bitset_excl (ctx .instr_dead , i );
493
- add_operands_to_worklists (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ]);
521
+ add_operands_to_worklists (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ], 1 );
494
522
}
495
523
while ((i = zend_bitset_pop_first (ctx .phi_worklist , ctx .phi_worklist_len )) >= 0 ) {
496
524
zend_bitset_excl (ctx .phi_dead , i );
497
- add_phi_sources_to_worklists (& ctx , ssa -> vars [i ].definition_phi );
525
+ zend_bitset_excl (ctx .phi_worklist_no_val , i );
526
+ add_phi_sources_to_worklists (& ctx , ssa -> vars [i ].definition_phi , 1 );
498
527
}
499
528
}
500
529
501
530
/* Eliminate dead instructions */
502
- FOREACH_INSTR_NUM (i ) {
503
- if (zend_bitset_in (ctx .instr_dead , i )) {
504
- if (dce_instr (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ])) {
505
- removed_ops ++ ;
506
- }
507
- }
508
- } FOREACH_INSTR_NUM_END ();
531
+ ZEND_BITSET_FOREACH (ctx .instr_dead , ctx .instr_worklist_len , i ) {
532
+ removed_ops += dce_instr (& ctx , & op_array -> opcodes [i ], & ssa -> ops [i ]);
533
+ } ZEND_BITSET_FOREACH_END ();
509
534
510
535
/* Improper uses don't count as "uses" for the purpose of instruction elimination,
511
- * but we have to retain phis defining them. Push those phis to the worklist. */
512
- FOREACH_INSTR_NUM (i ) {
513
- if (ssa -> ops [i ].op1_use >= 0 && zend_ssa_is_no_val_use (& op_array -> opcodes [i ], & ssa -> ops [i ], ssa -> ops [i ].op1_use )) {
514
- add_to_phi_worklist_only (& ctx , ssa -> ops [i ].op1_use );
515
- }
516
- if (ssa -> ops [i ].op2_use >= 0 && zend_ssa_is_no_val_use (& op_array -> opcodes [i ], & ssa -> ops [i ], ssa -> ops [i ].op2_use )) {
517
- add_to_phi_worklist_only (& ctx , ssa -> ops [i ].op2_use );
518
- }
519
- } FOREACH_INSTR_NUM_END ();
520
-
521
- /* Propagate this information backwards, marking any phi with an improperly used
536
+ * but we have to retain phis defining them.
537
+ * Propagate this information backwards, marking any phi with an improperly used
522
538
* target as non-dead. */
523
- while ((i = zend_bitset_pop_first (ctx .phi_worklist , ctx .phi_worklist_len )) >= 0 ) {
539
+ while ((i = zend_bitset_pop_first (ctx .phi_worklist_no_val , ctx .phi_worklist_len )) >= 0 ) {
524
540
zend_ssa_phi * phi = ssa -> vars [i ].definition_phi ;
525
541
int source ;
526
542
zend_bitset_excl (ctx .phi_dead , i );
527
543
FOREACH_PHI_SOURCE (phi , source ) {
528
- add_to_phi_worklist_only (& ctx , source );
544
+ add_to_phi_worklist_no_val (& ctx , source );
529
545
} FOREACH_PHI_SOURCE_END ();
530
546
}
531
547
@@ -534,14 +550,12 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor
534
550
if (zend_bitset_in (ctx .phi_dead , phi -> ssa_var )) {
535
551
zend_ssa_remove_uses_of_var (ssa , phi -> ssa_var );
536
552
zend_ssa_remove_phi (ssa , phi );
553
+ } else {
554
+ /* Remove trivial phis (phis with identical source operands) */
555
+ try_remove_trivial_phi (& ctx , phi );
537
556
}
538
557
} FOREACH_PHI_END ();
539
558
540
- /* Remove trivial phis (phis with identical source operands) */
541
- FOREACH_PHI (phi ) {
542
- try_remove_trivial_phi (& ctx , phi );
543
- } FOREACH_PHI_END ();
544
-
545
559
removed_ops += simplify_jumps (ssa , op_array );
546
560
547
561
return removed_ops ;
0 commit comments