| Safe Haskell | Safe |
|---|---|
| Language | Haskell2010 |
ReWire.Hyle.Transform
Description
Hyle-to-Hyle transformations: defn inlining (the uses-at-most-once
heuristic, plus flatten-everything), dead-defn purging, the --rtl-opt
optimization fixpoint (partial evaluation, zero-width purging, dedupe),
and hoisting clocked-extern calls into device instances. Inlining is
sound by the substitution property (doc/hyle.md, section 7); bound
names in inlined bodies are freshened to avoid capture. Defns are
processed callee-first (the call graph is acyclic), so chains of
single-use defns collapse fully.
Synopsis
- inline :: Bool -> Program -> Program
- inlineBy :: (GId -> Bool) -> Program -> Program
- purgeUnused :: Program -> Program
- partialEval :: Program -> Program
- purgeDevLets :: Program -> Program
- purgeZeroWidth :: Program -> Program
- dedupe :: Program -> Program
- optimize :: Natural -> Program -> Program
- hoistInstances :: MonadError AstError m => Program -> m Program
Documentation
inline :: Bool -> Program -> Program Source #
Inline definitions into their call sites: all of them when flatten,
otherwise those used at most once. Definitions referenced as extern
models are never inlined (the reference is by name), and neither are
defns marked noinline -- the pin wins over --flatten, so a
noinline defn is always findable as a module in the output. The
inlined-away defns are removed by the final purge; a follow-up
partialEval fuses the slice/concat plumbing exposed by inlining and
drops the argument wires that fusion leaves unused.
inlineBy :: (GId -> Bool) -> Program -> Program Source #
Inline definitions satisfying the predicate into their call sites.
purgeUnused :: Program -> Program Source #
Drop defns unreachable from the device body (following calls and the models of called externs) and extern decls that are never referenced. Not gated on noinline: pinning affects inlining, not liveness, so dead pinned defns don't accumulate.
partialEval :: Program -> Program Source #
Constant folding and wire fusion. Primitives, muxes, slices, and
concatenations of literals fold via the interpreter's own evaluator;
calls (and extern calls with models) whose arguments are all literals
evaluate fully. Lets bound to atomic expressions (literals, variables,
don't-cares) substitute away; lets bound to pure wiring (slices and
concatenations of atoms) are fused through where they are sliced, so
slice-of-concat plumbing simplifies across named wires (both expression
lets and device-level wires). Unused expression lets are dropped here;
unused device wires are dropped by purgeDevLets.
purgeDevLets :: Program -> Program Source #
Drop device wires (SLets) never read by a later statement (fusion in
partialEval is what strands them).
purgeZeroWidth :: Program -> Program Source #
Drop zero-width parameters (and the corresponding arguments at call sites) and zero-width-result defns (calls to them become nil).
dedupe :: Program -> Program Source #
Redirect calls to definitions with identical signatures and bodies to a single representative. Should be followed by purgeUnused. Defns marked noinline are name-pinned: they take no part in merging (neither as survivors nor as redirected losers). Each survivor records the losers redirected to it with an appended "also: ..." doc line.
optimize :: Natural -> Program -> Program Source #
The standard optimization pipeline, iterated to a fixpoint (bounded by the --rtl-opt level).
hoistInstances :: MonadError AstError m => Program -> m Program Source #
Defns containing (transitively) calls to sequential externs are inlined into the device body; sequential-extern calls in the device body then become instances: nested lets are flattened to device-level lets (sound: the language is pure and total) and each seq-XCall is replaced by the concatenation of a fresh instance's output ports, with its inputs driven from the call's arguments.