cfg.pl
Control-flow analyses over one function's blocks, as pure functions from IR terms to assocs keyed by block label.
func_cfg/2 builds cfgEntry, Rpo, Succs, Preds, which the other analyses take. Each export documents its relation below.
| Analysis | Predicates |
|---|---|
| dominators | cfg_idoms/2, dom_children/3, cfg_frontiers/3, dominates/3, dom_intervals/3, dominates_by_interval/3 |
| natural loops | natural_loops/3 |
| backward liveness | liveness/5, and temp_liveness/3 over temps |
PLAN asks for these as tabled relations. cfg_tabled.pl has that version; docs/m5.md has the measurement that made this one the one passes use.
Variable conventions
| Variable | Means |
|---|---|
| L, B | a block's label |
| S | a successor's label |
| P | a predecessor's label |
| Ls, Ss, Ps | lists of labels |
| T, D | temp names |
CFG
Succs is the ordset of labels Jump can branch to.
Cfg = cfg(Entry, Rpo, Succs, Preds) is the control-flow graph of Blocks, whose first block is the entry:
- Rpo: the reachable labels in reverse postorder, Entry first; - Succs: assoc Label -> ordset of successor labels (every block); - Preds: assoc Label -> ordset of predecessor labels (every block, including unreachable predecessors, as phis must list them).
Preds is Preds0 with Pred in the (unsorted) predecessor list of each label in Ss.
Post is a reverse postorder, in front of Post0, of the labels reachable from Ls (depth first, each of Ls in turn) without passing through Seen0; Seen is Seen0 with those labels.
Dominators
Idom is the assoc Label -> immediate dominator, for the reachable labels other than the entry (Cooper, Harvey and Kennedy, "A Simple, Fast Dominance Algorithm").
Pairs is Label-N for each of Labels, counting from N0.
Idom is the fixed point, from Idom0, of sweeps over Labels in reverse postorder.
as idom_fixpoint/5, after a sweep that changed an idom (Changed is true) or none (false).
in Idom, the idom of B is the nearest common dominator of those of its predecessors that have an idom in Idom0. Changed is true if that differs from B's idom in Idom0, and Changed0 if not.
Common is the nearest common dominator of A and B: A if they are equal, and otherwise the nearest common dominator of the earlier one in reverse postorder and the idom of the later one.
Kids is the assoc Label -> the children of Label in the dominator tree, in reverse postorder, for every reachable label.
Kids is Kids0 with L in front of the children of its idom, if it has one.
Intervals is the assoc Label -> Pre-Last for every reachable label: Pre is the preorder number of Label in the dominator tree, and Last the last one in its subtree.
Intervals is Intervals0 with the intervals of the dominator subtree at L, numbered in preorder from N0; N is the next free number.
A dominates B, in constant time, given Intervals from dom_intervals/3.
DF is the assoc Label -> ordset of the labels in its dominance frontier, for every reachable label. As in CHK, a join point is in the frontier of each label on the idom path from each of its predecessors up to (not including) its own idom.
DF is DF0 with B in the frontiers it joins, if it has two or more reachable predecessors. DF0 has a key for every reachable label, so it also tells which predecessors are reachable.
DF is DF0 with B in the frontier of every label on the idom path from Runner up to (not including) Stop.
Natural loops
Loops is the assoc Header -> loop(Latches, Body), both ordsets of labels. A latch has an edge to a header that dominates it. Body is the union of their natural loops. Only reachable blocks participate. Nested loops have separate entries; an irreducible cycle without a dominating header is NOT a natural loop. Empty does not mean acyclic, and this analysis alone cannot justify removing polls.
Seen is Seen0 with the reachable labels from which a label in Pending is reached without passing through Seen0; a label without an interval is unreachable. Invariant: Seen0 holds the header and the body blocks found so far, and every reachable predecessor of a body block in Seen0 is in Seen0 or Pending.
Liveness
LiveIn and LiveOut are the assocs Label -> ordset of the names live on entry to and exit from each block (every block, reachable or not), the least solution of the backward dataflow equations
LiveOut(B) = union over successors S of LiveIn(S) and EdgeUses(B-S) LiveIn(B) = Gen(B) ∪ (LiveOut(B) \ Kill(B))
GenKill is the assoc Label -> Gen-Kill (ordsets: the names read before any write in the block, and the names written). EdgeUses is the assoc (Pred-Succ) -> ordset of the names read on that edge only (phi arguments). Names are anything ordsets can hold, so the same relation serves temps (temp_liveness/3) and promote's memory slots.
LiveIn is the least solution above LiveIn0, given that only the blocks in Pending may violate the equations. Pending is an ordset of N-L, N the position of L in postorder, so the pending block first in postorder is next; when a block's live-in set grows, its predecessors are pending again.
Out is the live-out set of L, given the live-in sets LiveIn.
Temps
LiveIn and LiveOut are liveness/5 of Blocks over temp names. A phi defines its temp at block entry (in Kill, never in LiveIn) and reads its argument on the edge.
GenKill is the assoc Label -> Gen-Kill for each block: Gen is the ordset of temps read before they are written in it, and Kill the ordset of temps written (phi destinations included).
EdgeUses is the assoc (Pred-Succ) -> ordset of the temps that the phis of Succ read on the edge from Pred, for the edges that have any.
Uses is the ordset of temps that the instruction Inst reads.
Names is the ordset of temp names among Values.