Basic-block control-flow graphs derived from Layer-1 facts, with dominators and loop headers.
Built per module from the instruction/label_at/jump/branch/
select_branch facts, so the graph is memoized with the module's
extraction rather than re-derived on every solve: instructions are grouped
into maximal straight-line blocks (the classic leader algorithm), edges
carry their kind (Argus.Cfg.Block.edge_kind/0), and each function gets a
dominator tree (iterative Cooper–Harvey–Kennedy over reverse postorder) and
the set of natural-loop headers (back-edge targets).
The entry block is the one holding the function's entry label from
function_def — not instruction 0, which is the func_info failure
landing pad that never falls through.
Known imprecision, by design: a call to erlang:raise/erlang:error is an
ordinary call followed by a (never-taken) fallthrough edge — fail-edge and
terminator classification is fact-driven, not BIF-name-driven.
Summary
Functions
Build per-function CFGs from typed facts (Argus.Pipeline.extract/2 with
format: :typed).
The graph of one function of a module given as disassembly (%{module: ..., functions: ...}), built from the facts the emitter would produce.
What an extractor uses when it was handed bare disassembly rather than
a pipeline module whose graphs are already attached.
Functions
@spec build(Argus.Facts.t()) :: %{ required({String.t(), non_neg_integer()}) => Argus.Cfg.Function.t() }
Build per-function CFGs from typed facts (Argus.Pipeline.extract/2 with
format: :typed).
@spec build_for(map(), atom(), arity()) :: Argus.Cfg.Function.t() | nil
The graph of one function of a module given as disassembly (%{module: ..., functions: ...}), built from the facts the emitter would produce.
What an extractor uses when it was handed bare disassembly rather than
a pipeline module whose graphs are already attached.