pub enum Terminator<'d> {
Fallthrough(BlockId),
Switch {
on: SwitchOn<'d>,
arms: Vec<BlockId>,
default_blk: BlockId,
report: Option<SourceLoc>,
},
Branch {
cond: Expression<'d>,
then_blk: BlockId,
else_blk: BlockId,
},
Assertion {
stmt: Statement<'d>,
pass_blk: BlockId,
fail_blk: BlockId,
done_blk: BlockId,
},
Suspend {
point: SuspendPoint<'d>,
resume_blk: BlockId,
},
End {
loop_back: Option<BlockId>,
},
Loop {
header: BlockId,
body: BlockId,
exit: BlockId,
entry: BlockId,
},
ForkJoin {
kind: StatementBlockKind,
branches: Vec<ProcessCfg<'d>>,
join_blk: BlockId,
},
RepeatCheck {
counter: RepeatCounterId,
then_blk: BlockId,
else_blk: BlockId,
},
Case {
stmt: Statement<'d>,
arms: Vec<BlockId>,
default_blk: BlockId,
},
}Expand description
A BasicBlock’s single exit (PR-008, extended by Task 1).
Variants§
Fallthrough(BlockId)
Falls straight into BlockId with no branch/suspend (e.g. the merge
point after an if/else).
Switch
Goes to arms[i] when on evaluates to i, else to default_blk
– reporting an $error at source offset report first, when set
(a wait_order with no else).
Branch
if/case (PR-044 – expanded to a br-chain at the Terminator
level; the ternary operator ?: never appears here, only inside a
straight-line expression within a BasicBlock’s stmts).
Assertion
An immediate assertion (LRM 16.3) whose pass or fail statement waits,
so the statement cannot stay a straight-line leaf. Goes to pass_blk
(fail_blk) when the expression is true (not true) and the assertion
is checking with that action on (20.11), else to done_blk; the
default $error of an assert/assume with no fail statement is
reported inline before done_blk.
Suspend
End
PR-048: loop_back is None for initial/final procedures and
Some(entry) for always/always_ff/always_comb/always_latch
procedures – see ProcessLoopKind.
Loop
PR-045 (Task 1): for/while/do-while/forever/foreach.
header’s own terminator (a plain Branch for a real per-iteration
condition, or Fallthrough when there is none – forever) carries
the actual per-iteration decision, and body’s own terminator closes
the back edge to header. entry is the block cur (the one this
Terminator::Loop closes) actually transfers control to on first
entry – header for while/for/forever/foreach (the
condition, if any, is checked before the first iteration), but
body for do-while (the body always runs once before any check) –
a deviation from docs/design/process.md §2’s literal 3-field
Loop{header,body,exit} (recorded in this task’s deviations): a
do-while’s real entry edge genuinely cannot be recovered from
header/body/exit alone once header is required (as this
task’s own tests require) to remain a distinct, inspectable
post-body recheck block rather than being aliased to body. Codegen
may fully unroll a static-bound for, but the CFG representation
itself always has a real Loop terminator so the liveness/segment-
lowering passes never special-case “was this unrolled” (this file’s
own lower_terminator maps it losslessly to
SegTerminator::Goto(entry), since whichever real block entry
names already carries its own correct terminator). foreach’s
per-dimension bound is a ConstantRange, not an Expression slang
exposes anywhere on the statement – see walk_foreach_loop’s own
doc comment for why that one kind’s body is a chain of unrolled
copies rather than a real back edge (a second, separately-recorded
deviation).
ForkJoin
fork ... join/join_any/join_none (Task 1, PR-069/docs/design/
process.md §7): construction only, never scheduled. Each
branches[i] is a full nested ProcessCfg (a fork branch can
contain its own suspends). See contains_unschedulable_fork_join
– a real caller must check this before calling lower_to_segments
on a CFG that might contain one.
RepeatCheck
R-run Task 8 (PR-045/PR-006): a repeat(N) body; loop’s header
terminator, when body contains a suspend point (see
RepeatCounterId’s own doc comment for why this is a distinct
shape from Branch, never a fabricated cond: Expression). Mirrors
while’s own header/Branch role exactly – then_blk is the
loop body, else_blk is the loop’s exit – except the re-checked
condition is counter != 0 against this loop’s own compiler-
synthesized RepeatCounterId slot, not a re-evaluated user
expression.
Case
A case/casez/casex/case ... inside statement whose arms
cannot stay one straight-line leaf (an arm suspends, or breaks /
continues an enclosing process loop, or returns from an inlined
task): stmt supplies the selector and labels, matched in item
order, arms[i] is stmt.case_items()[i]’s block and default_blk
runs when no item matches. Also a unique/unique0/priority if
series: stmt is its first if (if_chain), arms[i] the
then-statement of the i-th condition and default_blk the final else,
so the dispatch can report a violation where the series executes.
Also an if with a pattern-matching or &&&-chained predicate (one
arm, is_clause_if) and a case ... matches (arms[i] is
pattern_case_items()[i]’s block, default_blk its default).