Argus.Cfg.Function (Panoptes v0.13.0)

Copy Markdown View Source

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

select()

@type select() :: %{
  idx: non_neg_integer(),
  arms: %{required(String.t()) => Argus.Cfg.Block.id()},
  default: Argus.Cfg.Block.id() | nil
}

t()

@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

block_at(function, idx)

@spec block_at(t(), non_neg_integer()) :: Argus.Cfg.Block.t() | nil

The block containing the instruction at idx, or nil.

control_deps(fun)

@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.

dominates?(fun, a, b)

@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.

postdominates?(fun, a, b)

@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.

region(fun, block_id)

@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.