Walk blocks in CFG order

This commit is contained in:
2026-09-09 14:04:57 +00:00
parent 57829def5c
commit 75bd52d8ec
+21 -6
View File
@@ -109,10 +109,18 @@ impl RegFn {
let pcs = flat_fn.code.iter().map(|i| i.pc).collect::<Vec<usize>>();
let (starts, mut blocks) = find_basic_blocks(&flat_fn.code, &pcs);
let mut output = Vec::with_capacity(flat_fn.code.len());
let mut next_val = flat_fn.var_count; // Reserve values for locals
for b in 0..blocks.len() {
// Emit per block, so blocks can be WALKED in the order control reaches
// them and still be READ in listing order. Listing order alone is not
// enough: a for-of puts its body before the test, and the body is
// reached only by the back edge, so walking it first finds no stack.
let mut emitted = vec![Vec::new(); blocks.len()];
let mut seen = vec![false; blocks.len()];
let mut todo = vec![0];
seen[0] = true;
while let Some(b) = todo.pop() {
// A block starts with the stack it is ENTERED with, not with
// whatever the block above it in the listing left behind. This one
// line is what stops the walk running off a `return` into a
@@ -212,19 +220,26 @@ impl RegFn {
}
reg_instr.block_start = std::mem::take(&mut first);
output.push(reg_instr);
emitted[b].push(reg_instr);
}
// Every edge out of here arrives at its target with this stack.
// One pass in listing order reaches every block, because a block is
// always entered from earlier in the listing before any back edge
// returns to it.
// Walking a block only after something has arrived at it is what
// makes the entry stack right, and it is why the order is the CFG's
// and not the listing's.
for i in 0..blocks[b].succ.len() {
let to = blocks[b].succ[i];
blocks[to].arrive(&stack);
if !seen[to] {
seen[to] = true;
todo.push(to);
}
}
}
let output = emitted.into_iter().flatten().collect::<Vec<RegInstr>>();
Self {
name: flat_fn.name,
arg_count: flat_fn.arg_count,