In a directed graph with a fixed entry , vertex dominates if every path from to passes through . Each has a unique immediate dominator — its closest strict dominator — and these form a tree.
Basic facts
- dominates everything reachable from it.
- Dominance is a partial order; the dominators of form a chain from to .
- is the parent of in the dominator tree.
- The set of vertices dominated by is exactly ‘s subtree in the dominator tree.
- Unreachable vertices are simply excluded.
Algorithms
| Method | Time | Difficulty |
|---|---|---|
| Iterative dataflow (Cooper-Harvey-Kennedy) | worst, near-linear in practice | easy — ~30 lines |
| Lengauer-Tarjan simple | hard | |
| Lengauer-Tarjan sophisticated | very hard | |
| Alstrup et al. | theoretical |
The easy algorithm
Process vertices in reverse postorder, repeatedly intersecting dominator sets until a fixed point:
vector<int> idom, order, rpoNum; // reverse postorder numbering
int intersect(int a, int b) { // "closest common dominator"
while (a != b) {
while (rpoNum[a] > rpoNum[b]) a = idom[a];
while (rpoNum[b] > rpoNum[a]) b = idom[b];
}
return a;
}
void computeIdom(int root) {
idom.assign(n, -1);
idom[root] = root;
bool changed = true;
while (changed) {
changed = false;
for (int u : order) { // reverse postorder, skip root
if (u == root) continue;
int newIdom = -1;
for (int p : rpred[u]) {
if (idom[p] == -1) continue;
newIdom = (newIdom == -1) ? p : intersect(p, newIdom);
}
if (newIdom != -1 && idom[u] != newIdom) { idom[u] = newIdom; changed = true; }
}
}
}This converges in very few iterations on real graphs and is what to write unless is enormous.
Special cases that are much easier
| Graph | Dominator tree |
|---|---|
| DAG | process in topological order; of all predecessors in the partially built tree |
| Tree | the tree itself |
| Undirected connected | every vertex’s idom is its parent in any BFS/DFS tree only if… (in general, use the block-cut tree instead) |
| Reducible flow graph (structured code) | the iterative algorithm converges in passes where is the loop nesting depth |
The DAG case is worth remembering: with a topological order and an incremental LCA structure, you get the dominator tree in with code you probably already have.
What it answers
| Question | On the dominator tree |
|---|---|
| Which vertices become unreachable if fails? | ’s subtree |
| How many vertices depend on ? | subtree size |
| Is required to reach ? | is an ancestor of |
| Critical single points of failure | vertices with large subtrees |
| Vertices reachable only through | subtree minus |
Where it comes from
Dominators were invented for compilers. In SSA (static single assignment) construction, a -function for variable is needed exactly at the dominance frontier of ‘s definitions. Loop detection, code motion, and control dependence all use the dominator tree. Every optimising compiler computes one.
Post-dominators
Run the same algorithm on the reversed graph from the exit vertex. post-dominates if every path from to the exit passes through . The combination of dominators and post-dominators gives control dependence, which is what tells a compiler which statements are conditionally executed.
See also: Lengauer-Tarjan · LCA · SCC