@@ -672,6 +672,11 @@ ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, ze
672
672
int blocks_count = cfg -> blocks_count ;
673
673
int j , k , changed ;
674
674
675
+ if (cfg -> blocks_count == 1 ) {
676
+ blocks [0 ].level = 0 ;
677
+ return ;
678
+ }
679
+
675
680
ALLOCA_FLAG (use_heap )
676
681
int * postnum = do_alloca (sizeof (int ) * cfg -> blocks_count , use_heap );
677
682
memset (postnum , -1 , sizeof (int ) * cfg -> blocks_count );
@@ -692,16 +697,14 @@ ZEND_API void zend_cfg_compute_dominators_tree(const zend_op_array *op_array, ze
692
697
for (k = 0 ; k < blocks [j ].predecessors_count ; k ++ ) {
693
698
int pred = cfg -> predecessors [blocks [j ].predecessor_offset + k ];
694
699
695
- if (idom < 0 ) {
696
- if (blocks [pred ].idom >= 0 )
697
- idom = pred ;
698
- continue ;
699
- }
700
-
701
700
if (blocks [pred ].idom >= 0 ) {
702
- while (idom != pred ) {
703
- while (postnum [pred ] < postnum [idom ]) pred = blocks [pred ].idom ;
704
- while (postnum [idom ] < postnum [pred ]) idom = blocks [idom ].idom ;
701
+ if (idom < 0 ) {
702
+ idom = pred ;
703
+ } else {
704
+ while (idom != pred ) {
705
+ while (postnum [pred ] < postnum [idom ]) pred = blocks [pred ].idom ;
706
+ while (postnum [idom ] < postnum [pred ]) idom = blocks [idom ].idom ;
707
+ }
705
708
}
706
709
}
707
710
}
@@ -765,19 +768,6 @@ static bool dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
765
768
}
766
769
/* }}} */
767
770
768
- typedef struct {
769
- int id ;
770
- int level ;
771
- } block_info ;
772
- static int compare_block_level (const block_info * a , const block_info * b ) {
773
- return b -> level - a -> level ;
774
- }
775
- static void swap_blocks (block_info * a , block_info * b ) {
776
- block_info tmp = * a ;
777
- * a = * b ;
778
- * b = tmp ;
779
- }
780
-
781
771
ZEND_API void zend_cfg_identify_loops (const zend_op_array * op_array , zend_cfg * cfg ) /* {{{ */
782
772
{
783
773
int i , j , k , n ;
@@ -786,17 +776,22 @@ ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *c
786
776
int * entry_times , * exit_times ;
787
777
zend_worklist work ;
788
778
int flag = ZEND_FUNC_NO_LOOPS ;
789
- block_info * sorted_blocks ;
779
+ int * sorted_blocks ;
790
780
ALLOCA_FLAG (list_use_heap )
791
781
ALLOCA_FLAG (tree_use_heap )
792
- ALLOCA_FLAG (sorted_blocks_use_heap )
782
+
783
+ if (cfg - > blocks_count == 1 ) {
784
+ cfg -> flags |= flag ;
785
+ return ;
786
+ }
793
787
794
788
ZEND_WORKLIST_ALLOCA (& work , cfg -> blocks_count , list_use_heap );
795
789
796
790
/* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
797
791
* queries. These are implemented by checking entry/exit times of the DFS search. */
798
- entry_times = do_alloca (2 * sizeof (int ) * cfg -> blocks_count , tree_use_heap );
792
+ entry_times = do_alloca (3 * sizeof (int ) * cfg -> blocks_count , tree_use_heap );
799
793
exit_times = entry_times + cfg -> blocks_count ;
794
+ sorted_blocks = exit_times + cfg -> blocks_count ;
800
795
memset (entry_times , -1 , 2 * sizeof (int ) * cfg -> blocks_count );
801
796
802
797
zend_worklist_push (& work , 0 );
@@ -826,27 +821,35 @@ ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *c
826
821
zend_worklist_pop (& work );
827
822
}
828
823
829
- /* Sort blocks by decreasing level, which is the order in which we want to process them */
830
- sorted_blocks = do_alloca (sizeof (block_info ) * cfg -> blocks_count , sorted_blocks_use_heap );
831
- for (i = 0 ; i < cfg -> blocks_count ; i ++ ) {
832
- sorted_blocks [i ].id = i ;
833
- sorted_blocks [i ].level = blocks [i ].level ;
824
+ /* Sort blocks by level, which is the opposite order in which we want to process them */
825
+ sorted_blocks [0 ] = 0 ;
826
+ j = 0 ;
827
+ n = 1 ;
828
+ while (j != n ) {
829
+ i = j ;
830
+ j = n ;
831
+ for (; i < j ; i ++ ) {
832
+ int child ;
833
+ for (child = blocks [sorted_blocks [i ]].children ; child >= 0 ; child = blocks [child ].next_child ) {
834
+ sorted_blocks [n ++ ] = child ;
835
+ }
836
+ }
834
837
}
835
- zend_sort (sorted_blocks , cfg -> blocks_count , sizeof (block_info ),
836
- (compare_func_t ) compare_block_level , (swap_func_t ) swap_blocks );
837
838
838
- /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
839
- Graphs". */
839
+ /* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ Graphs". */
840
+ while (n > 0 ) {
841
+ i = sorted_blocks [-- n ];
840
842
841
- for (n = 0 ; n < cfg -> blocks_count ; n ++ ) {
842
- i = sorted_blocks [n ].id ;
843
+ if (blocks [i ].predecessors_count < 2 ) {
844
+ /* loop header has at least two input edges */
845
+ continue ;
846
+ }
843
847
844
- zend_bitset_clear (work .visited , zend_bitset_len (cfg -> blocks_count ));
845
848
for (j = 0 ; j < blocks [i ].predecessors_count ; j ++ ) {
846
849
int pred = cfg -> predecessors [blocks [i ].predecessor_offset + j ];
847
850
848
851
/* A join edge is one for which the predecessor does not
849
- immediately dominate the successor. */
852
+ immediately dominate the successor. */
850
853
if (blocks [i ].idom == pred ) {
851
854
continue ;
852
855
}
@@ -856,6 +859,9 @@ ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *c
856
859
if (dominates (blocks , i , pred )) {
857
860
blocks [i ].flags |= ZEND_BB_LOOP_HEADER ;
858
861
flag &= ~ZEND_FUNC_NO_LOOPS ;
862
+ if (!zend_worklist_len (& work )) {
863
+ zend_bitset_clear (work .visited , zend_bitset_len (cfg -> blocks_count ));
864
+ }
859
865
zend_worklist_push (& work , pred );
860
866
} else {
861
867
/* Otherwise it's a cross-join edge. See if it's a branch
@@ -885,7 +891,6 @@ ZEND_API void zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *c
885
891
}
886
892
}
887
893
888
- free_alloca (sorted_blocks , sorted_blocks_use_heap );
889
894
free_alloca (entry_times , tree_use_heap );
890
895
ZEND_WORKLIST_FREE_ALLOCA (& work , list_use_heap );
891
896
0 commit comments