Skip to content

Commit f1f68b6

Browse files
committed
Optimize DJ spanning tree back-edge check
Instead of walking the DJ spanning tree upwards, record entry and exit times during the DFS and use these to determine whether CJ edges are sp-back edges.
1 parent 6633e84 commit f1f68b6

File tree

1 file changed

+19
-21
lines changed

1 file changed

+19
-21
lines changed

ext/opcache/Optimizer/zend_cfg.c

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -765,9 +765,9 @@ static void swap_blocks(block_info *a, block_info *b) {
765765
int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32_t *flags) /* {{{ */
766766
{
767767
int i, j, k, n;
768-
int depth;
768+
int depth, time;
769769
zend_basic_block *blocks = cfg->blocks;
770-
int *dj_spanning_tree;
770+
int *entry_times, *exit_times;
771771
zend_worklist work;
772772
int flag = ZEND_FUNC_NO_LOOPS;
773773
block_info *sorted_blocks;
@@ -776,19 +776,24 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32
776776
ALLOCA_FLAG(sorted_blocks_use_heap)
777777

778778
ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
779-
dj_spanning_tree = do_alloca(sizeof(int) * cfg->blocks_count, tree_use_heap);
780779

781-
for (i = 0; i < cfg->blocks_count; i++) {
782-
dj_spanning_tree[i] = -1;
783-
}
780+
/* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
781+
* querties. These are implemented by checking entry/exit times of the DFS search. */
782+
entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
783+
exit_times = entry_times + cfg->blocks_count;
784+
memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
785+
784786
zend_worklist_push(&work, 0);
787+
time = 0;
785788
while (zend_worklist_len(&work)) {
786789
next:
787790
i = zend_worklist_peek(&work);
791+
if (entry_times[i] == -1) {
792+
entry_times[i] = time++;
793+
}
788794
/* Visit blocks immediately dominated by i. */
789795
for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
790796
if (zend_worklist_push(&work, j)) {
791-
dj_spanning_tree[j] = i;
792797
goto next;
793798
}
794799
}
@@ -800,10 +805,10 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32
800805
} else if (blocks[succ].idom == i) {
801806
continue;
802807
} else if (zend_worklist_push(&work, succ)) {
803-
dj_spanning_tree[succ] = i;
804808
goto next;
805809
}
806810
}
811+
exit_times[i] = time++;
807812
zend_worklist_pop(&work);
808813
}
809814

@@ -840,18 +845,11 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32
840845
zend_worklist_push(&work, pred);
841846
} else {
842847
/* Otherwise it's a cross-join edge. See if it's a branch
843-
to an ancestor on the dominator spanning tree. */
844-
int dj_parent = pred;
845-
while (dj_parent >= 0) {
846-
if (dj_parent == i) {
847-
/* An sp-back edge: mark as irreducible. */
848-
blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
849-
flag |= ZEND_FUNC_IRREDUCIBLE;
850-
flag &= ~ZEND_FUNC_NO_LOOPS;
851-
break;
852-
} else {
853-
dj_parent = dj_spanning_tree[dj_parent];
854-
}
848+
to an ancestor on the DJ spanning tree. */
849+
if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
850+
blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
851+
flag |= ZEND_FUNC_IRREDUCIBLE;
852+
flag &= ~ZEND_FUNC_NO_LOOPS;
855853
}
856854
}
857855
}
@@ -867,7 +865,7 @@ int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg, uint32
867865
}
868866

869867
free_alloca(sorted_blocks, sorted_blocks_use_heap);
870-
free_alloca(dj_spanning_tree, tree_use_heap);
868+
free_alloca(entry_times, tree_use_heap);
871869
ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
872870
*flags |= flag;
873871

0 commit comments

Comments
 (0)