One function's control-flow graph: basic blocks, typed edges, dominators, post-dominators, and loop headers, plus lookup helpers.
rpo, idom, dom_children and loop_headers cover only blocks reachable
from the entry block (id 0); unreachable blocks still exist in blocks so
the ranges always partition the instruction stream. ipdom covers only
blocks with a path to the function exit — a block whose post-dominator is
the virtual exit maps to :exit; blocks that never reach it (genuine
infinite loops) are absent.
Summary
Functions
The block containing the instruction at idx, or nil.
Block-level control dependence (Ferrante–Ottenstein–Warren): block t is
control-dependent on branch block b when b's decision determines
whether t runs — t post-dominates one of b's successors but not b
itself. Returns %{dependent => [deciding blocks]}.
Whether block a dominates block b (reflexively). false when b is
unreachable from the entry.
Whether block a post-dominates block b (reflexively): every path from
b to the function exit passes through a. false when b has no path
to the exit.
The single-entry region rooted at block_id: the block plus everything it
dominates, in ascending block order.
Types
@type select() :: %{ idx: non_neg_integer(), arms: %{required(String.t()) => Argus.Cfg.Block.id()}, default: Argus.Cfg.Block.id() | nil }
@type t() :: %Argus.Cfg.Function{ arity: non_neg_integer(), blocks: %{required(Argus.Cfg.Block.id()) => Argus.Cfg.Block.t()}, dom_children: %{required(Argus.Cfg.Block.id()) => [Argus.Cfg.Block.id()]}, entry: Argus.Cfg.Block.id(), func: String.t(), idom: %{required(Argus.Cfg.Block.id()) => Argus.Cfg.Block.id()}, ipdom: %{required(Argus.Cfg.Block.id()) => Argus.Cfg.Block.id() | :exit}, labels: %{required(non_neg_integer()) => Argus.Cfg.Block.id()}, loop_headers: MapSet.t(Argus.Cfg.Block.id()), rpo: [Argus.Cfg.Block.id()], selects: [select()] }
Functions
@spec block_at(t(), non_neg_integer()) :: Argus.Cfg.Block.t() | nil
The block containing the instruction at idx, or nil.
@spec control_deps(t()) :: %{required(Argus.Cfg.Block.id()) => [Argus.Cfg.Block.id()]}
Block-level control dependence (Ferrante–Ottenstein–Warren): block t is
control-dependent on branch block b when b's decision determines
whether t runs — t post-dominates one of b's successors but not b
itself. Returns %{dependent => [deciding blocks]}.
Computed by walking each successor of a multi-way branch up the
post-dominator tree until (exclusive) ipdom(b). Blocks with no
post-dominator end the walk, so control dependence through code that
never reaches the exit is conservatively absent.
@spec dominates?(t(), Argus.Cfg.Block.id(), Argus.Cfg.Block.id()) :: boolean()
Whether block a dominates block b (reflexively). false when b is
unreachable from the entry.
@spec postdominates?(t(), Argus.Cfg.Block.id(), Argus.Cfg.Block.id()) :: boolean()
Whether block a post-dominates block b (reflexively): every path from
b to the function exit passes through a. false when b has no path
to the exit.
@spec region(t(), Argus.Cfg.Block.id()) :: [Argus.Cfg.Block.id()]
The single-entry region rooted at block_id: the block plus everything it
dominates, in ascending block order.