// SPDX-License-Identifier: MIT // // Various utilities for flowgraphs. // // Copyright (c) 2017 Luc Van Oostenryck. // #include "flowgraph.h" #include "linearize.h" #include "flow.h" // for bb_generation #include #include #include struct cfg_info { struct basic_block_list *list; unsigned long gen; unsigned int nr; }; static void label_postorder(struct basic_block *bb, struct cfg_info *info) { struct basic_block *child; if (bb->generation == info->gen) return; bb->generation = info->gen; FOR_EACH_PTR_REVERSE(bb->children, child) { label_postorder(child, info); } END_FOR_EACH_PTR_REVERSE(child); bb->postorder_nr = info->nr++; add_bb(&info->list, bb); } static void reverse_bbs(struct basic_block_list **dst, struct basic_block_list *src) { struct basic_block *bb; FOR_EACH_PTR_REVERSE(src, bb) { add_bb(dst, bb); } END_FOR_EACH_PTR_REVERSE(bb); } static void debug_postorder(struct entrypoint *ep) { struct basic_block *bb; printf("%s's reverse postorder:\n", show_ident(ep->name->ident)); FOR_EACH_PTR(ep->bbs, bb) { printf("\t.L%u: %u\n", bb->nr, bb->postorder_nr); } END_FOR_EACH_PTR(bb); } // // cfg_postorder - Set the BB's reverse postorder links // // Do a postorder DFS walk and set the links // (which will do the reverse part). // int cfg_postorder(struct entrypoint *ep) { struct cfg_info info = { .gen = ++bb_generation, }; label_postorder(ep->entry->bb, &info); // OK, now info.list contains the node in postorder // Reuse ep->bbs for the reverse postorder. free_ptr_list(&ep->bbs); ep->bbs = NULL; reverse_bbs(&ep->bbs, info.list); free_ptr_list(&info.list); if (dbg_postorder) debug_postorder(ep); return info.nr; } // // Calculate the dominance tree following: // "A simple, fast dominance algorithm" // by K. D. Cooper, T. J. Harvey, and K. Kennedy. // cfr. http://www.cs.rice.edu/∼keith/EMBED/dom.pdf // static struct basic_block *intersect_dom(struct basic_block *doms[], struct basic_block *b1, struct basic_block *b2) { int f1 = b1->postorder_nr, f2 = b2->postorder_nr; while (f1 != f2) { while (f1 < f2) { b1 = doms[f1]; f1 = b1->postorder_nr; } while (f2 < f1) { b2 = doms[f2]; f2 = b2->postorder_nr; } } return b1; } static void debug_domtree(struct entrypoint *ep) { struct basic_block *bb = ep->entry->bb; printf("%s's idoms:\n", show_ident(ep->name->ident)); FOR_EACH_PTR(ep->bbs, bb) { if (bb == ep->entry->bb) continue; // entry node has no idom printf("\t%s <- %s\n", show_label(bb), show_label(bb->idom)); } END_FOR_EACH_PTR(bb); } void domtree_build(struct entrypoint *ep) { struct basic_block *entry = ep->entry->bb; struct basic_block **doms; struct basic_block *bb; unsigned int size; int max_level = 0; int changed; // First calculate the (reverse) postorder. // This will give use us: // - the links to do a reverse postorder traversal // - the order number for each block size = cfg_postorder(ep); // initialize the dominators array doms = calloc(size, sizeof(*doms)); assert(entry->postorder_nr == size-1); doms[size-1] = entry; do { struct basic_block *b; changed = 0; FOR_EACH_PTR(ep->bbs, b) { struct basic_block *p; int bnr = b->postorder_nr; struct basic_block *new_idom = NULL; if (b == entry) continue; // ignore entry node FOR_EACH_PTR(b->parents, p) { unsigned int pnr = p->postorder_nr; if (!doms[pnr]) continue; if (!new_idom) { new_idom = p; continue; } new_idom = intersect_dom(doms, p, new_idom); } END_FOR_EACH_PTR(p); assert(new_idom); if (doms[bnr] != new_idom) { doms[bnr] = new_idom; changed = 1; } } END_FOR_EACH_PTR(b); } while (changed); // set the idom links FOR_EACH_PTR(ep->bbs, bb) { struct basic_block *idom = doms[bb->postorder_nr]; if (bb == entry) continue; // ignore entry node bb->idom = idom; add_bb(&idom->doms, bb); } END_FOR_EACH_PTR(bb); entry->idom = NULL; // set the dominance levels FOR_EACH_PTR(ep->bbs, bb) { struct basic_block *idom = bb->idom; int level = idom ? idom->dom_level + 1 : 0; bb->dom_level = level; if (max_level < level) max_level = level; } END_FOR_EACH_PTR(bb); ep->dom_levels = max_level + 1; free(doms); if (dbg_domtree) debug_domtree(ep); } // dt_dominates - does BB a dominates BB b? bool domtree_dominates(struct basic_block *a, struct basic_block *b) { if (a == b) // dominance is reflexive return true; if (a == b->idom) return true; if (b == a->idom) return false; // can't dominate if deeper in the DT if (a->dom_level >= b->dom_level) return false; // FIXME: can be faster if we have the DFS in-out numbers // walk up the dominator tree for (b = b->idom; b; b = b->idom) { if (b == a) return true; } return false; }