Argus.Dataflow (Panoptes v0.13.0)

Copy Markdown View Source

Reaching definitions over Layer-1 facts: which instruction's register write feeds which instruction's register read.

For every use row, the analysis finds the def rows that can have produced the value being read — true def→use edges that respect register reuse, which simple register-name matching cannot.

Control flow

The successor relation comes from the explicit control-transfer facts: next (fallthrough), jump, branch (the fail edge), and select_branch, with labels resolved through label_at. This is deliberately not Argus.Cfg's block-edge relation:

  • Exception edges (try_start handlers) are not followed. At the handler the VM materializes the exception class/reason/stacktrace in x0x2 without any def fact, so following the edge would attribute pre-try writes of those registers to handler reads — a false flow. Handler code instead flows from its own writes (its reads of the VM-materialized registers resolve to nothing, honestly).
  • bif_call/bs_start fail edges are not followed; like the generic branch fallthrough, the fail path is reached through the facts that carry it explicitly.

Algorithm

Per function (edges never cross functions): instructions chain into maximal straight-line blocks, each block gets a gen/kill summary, the summaries propagate over the block graph to a fixpoint (classic iterative reaching definitions), and the per-instruction edges fall out of one local walk per block. This is observationally identical to the per-instruction fixpoint, just proportional to blocks rather than instructions.

Summary

Types

A def→use edge: the writing instruction feeds the reading one.

Functions

Compute the def→use edge set from typed facts (Argus.Pipeline.extract/2 with format: :typed).

Types

edge()

@type edge() :: {Argus.InstrId.t(), Argus.InstrId.t()}

A def→use edge: the writing instruction feeds the reading one.

Functions

def_use_edges(facts)

@spec def_use_edges(Argus.Facts.t()) :: MapSet.t(edge())

Compute the def→use edge set from typed facts (Argus.Pipeline.extract/2 with format: :typed).

One module at a time

Functions are grouped by InstrId.fa/1, which is {name, arity} and carries no module. Pass facts for a single module. Over a merged multi-module fact set every init/1 in the program lands in one group and unrelated control-flow graphs are spliced together, which silently loses real edges and invents others — measured at 25,409 edges instead of 45,319 on one project.

Every caller does this correctly today (Planchette.Flow.build/1 and Gloss.Adapters.dataflow/1 are both per module, as is the derivation in Argus.Pipeline), so this documents a precondition that was being met by convention rather than fixing a live defect.