pruneUnusedLabels
File
src/ReactiveScopes/PruneUnusedLabels.ts
Purpose
The pruneUnusedLabels pass optimizes control flow by:
- Flattening labeled terminals where the label is not reachable via a
breakorcontinuestatement - Marking labels as implicit for terminals where the label exists but is never targeted
This pass removes unnecessary labeled blocks that were introduced during compilation but serve no control flow purpose in the final output. JavaScript labeled statements are only needed when there is a corresponding break label or continue label that targets them.
Input Invariants
- The input is a
ReactiveFunction(after conversion from HIR) - All
breakandcontinueterminals have:- A
target(BlockId) indicating which label they jump to - A
targetKindthat is one of:'implicit','labeled', or'unlabeled'
- A
- Each
ReactiveTerminalStatementhas an optionallabelfield containingidandimplicit - The pass runs after
assertWellFormedBreakTargetswhich validates break/continue targets
Output Guarantees
- Labeled terminals where the label is unreachable are flattened into their parent block
- When flattening, trailing unlabeled
breakstatements (that would just fall through) are removed - Labels that exist but are never targeted have their
implicitflag set totrue - Control flow semantics are preserved - only structurally unnecessary labels are removed
Algorithm
The pass uses a two-phase approach with a single traversal:
Phase 1: Collect reachable labels
if ((terminal.kind === 'break' || terminal.kind === 'continue') &&
terminal.targetKind === 'labeled') {
state.add(terminal.target); // Mark this label as reachable
}
Phase 2: Transform terminals
const isReachableLabel = stmt.label !== null && state.has(stmt.label.id);
if (stmt.terminal.kind === 'label' && !isReachableLabel) {
// Flatten: extract block contents, removing trailing unlabeled break
const block = [...stmt.terminal.block];
const last = block.at(-1);
if (last?.kind === 'terminal' && last.terminal.kind === 'break' &&
last.terminal.target === null) {
block.pop(); // Remove trailing break
}
return {kind: 'replace-many', value: block};
} else {
if (!isReachableLabel && stmt.label != null) {
stmt.label.implicit = true; // Mark as implicit
}
return {kind: 'keep'};
}
Edge Cases
Trailing Break Removal
When flattening a labeled block, if the last statement is an unlabeled break (target === null), it is removed since it would just fall through anyway.
Implicit vs Labeled Breaks
Only breaks with targetKind === 'labeled' count toward label reachability. Implicit breaks (fallthrough) and unlabeled breaks don't make a label "used".
Continue Statements
Both break and continue with labeled targets mark the label as reachable.
Non-Label Terminals with Labels
Other terminal types (like if, while, for) can also have labels. If unreachable, these labels are marked implicit but the terminal is not flattened.
TODOs
None in the source file.
Example
Fixture: unconditional-break-label.js
Input:
function foo(a) {
let x = 0;
bar: {
x = 1;
break bar;
}
return a + x;
}
Output (after full compilation):
function foo(a) {
return a + 1;
}
The labeled block bar: { ... } is removed because after the pass runs, constant propagation and dead code elimination further simplify the code.
Fixture: conditional-break-labeled.js
Input:
function Component(props) {
const a = [];
a.push(props.a);
label: {
if (props.b) {
break label;
}
a.push(props.c);
}
a.push(props.d);
return a;
}
Output:
function Component(props) {
const $ = _c(5);
let a;
if ($[0] !== props.a || $[1] !== props.b ||
$[2] !== props.c || $[3] !== props.d) {
a = [];
a.push(props.a);
bb0: {
if (props.b) {
break bb0;
}
a.push(props.c);
}
a.push(props.d);
// ... cache updates
} else {
a = $[4];
}
return a;
}
The labeled block bb0: { ... } is preserved because the break bb0 inside the conditional targets this label.