Skip to content

Commit 5279b19

Browse files
author
Arnaud Riess
committed
refactor: streamline branch resolution by introducing resolve_branch_info method and enhance phi handling
1 parent e3c154b commit 5279b19

2 files changed

Lines changed: 66 additions & 59 deletions

File tree

crates/herkos/src/ir/builder/core.rs

Lines changed: 56 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,6 @@ impl ControlFrame {
326326
}
327327
}
328328

329-
/// Returns `true` if this is a Loop frame (backward-branch target).
330-
pub(super) fn is_loop(&self) -> bool {
331-
matches!(self, ControlFrame::Loop { .. })
332-
}
333-
334329
/// Loop phi vars (one per Wasm local); empty slice for non-Loop frames.
335330
pub(super) fn loop_phi_vars(&self) -> &[UseVar] {
336331
match self {
@@ -505,7 +500,9 @@ impl IrBuilder {
505500
if let Some(block) = self.blocks.iter_mut().find(|b| b.id == self.current_block) {
506501
block.instructions.push(instr);
507502
} else {
508-
// Current block doesn't exist yet, create it
503+
// Current block doesn't exist yet, create it as a fallback.
504+
// This handles cases where instructions are emitted before explicit block creation,
505+
// which is valid in the IR builder's lazy block creation model.
509506
self.blocks.push(IrBlock {
510507
id: self.current_block,
511508
instructions: vec![instr],
@@ -732,6 +729,32 @@ impl IrBuilder {
732729
Ok(target)
733730
}
734731

732+
/// Resolve branch target and metadata for relative depth N.
733+
///
734+
/// Returns `(target_block, is_loop, frame_idx)` needed for recording phi predecessors
735+
/// and determining terminator type.
736+
pub(super) fn resolve_branch_info(&self, depth: u32) -> Result<(BlockId, bool, usize)> {
737+
let frame_idx = self
738+
.control_stack
739+
.len()
740+
.checked_sub(depth as usize + 1)
741+
.ok_or_else(|| {
742+
anyhow::anyhow!(
743+
"Branch depth {} exceeds control stack depth {}",
744+
depth,
745+
self.control_stack.len()
746+
)
747+
})?;
748+
749+
let frame = &self.control_stack[frame_idx];
750+
let (target, is_loop) = match frame {
751+
ControlFrame::Loop { start_block, .. } => (*start_block, true),
752+
_ => (frame.end_block(), false),
753+
};
754+
755+
Ok((target, is_loop, frame_idx))
756+
}
757+
735758
/// Start a new block (create and switch to it).
736759
pub(super) fn start_block(&mut self, block_id: BlockId) {
737760
self.current_block = block_id;
@@ -799,10 +822,10 @@ impl IrBuilder {
799822
&mut self,
800823
join_block: BlockId,
801824
predecessors: &[(BlockId, Vec<UseVar>)],
802-
) {
825+
) -> Result<()> {
803826
let num_locals = self.local_vars.len();
804827
if predecessors.is_empty() || num_locals == 0 {
805-
return;
828+
return Ok(());
806829
}
807830

808831
// Collect phis to insert: allocate dest vars before touching self.blocks.
@@ -838,12 +861,18 @@ impl IrBuilder {
838861
self.local_vars = new_locals;
839862

840863
if !phi_instrs.is_empty() {
841-
if let Some(block) = self.blocks.iter_mut().find(|b| b.id == join_block) {
842-
let old = std::mem::take(&mut block.instructions);
843-
block.instructions = phi_instrs;
844-
block.instructions.extend(old);
845-
}
864+
let block = self
865+
.blocks
866+
.iter_mut()
867+
.find(|b| b.id == join_block)
868+
.ok_or_else(|| {
869+
anyhow::anyhow!("join block {:?} not found in blocks", join_block)
870+
})?;
871+
let old = std::mem::take(&mut block.instructions);
872+
block.instructions = phi_instrs;
873+
block.instructions.extend(old);
846874
}
875+
Ok(())
847876
}
848877

849878
/// Emit phi instructions for a loop frame into its header block.
@@ -906,11 +935,20 @@ impl IrBuilder {
906935
});
907936
}
908937

909-
if let Some(block) = self.blocks.iter_mut().find(|b| b.id == start_block) {
910-
let old = std::mem::take(&mut block.instructions);
911-
block.instructions = phi_instrs;
912-
block.instructions.extend(old);
913-
}
938+
let block = self
939+
.blocks
940+
.iter_mut()
941+
.find(|b| b.id == start_block)
942+
.unwrap_or_else(|| {
943+
panic!(
944+
"Loop header block {:?} not found when emitting loop phis. \
945+
This indicates a bug in the IR builder's block or control frame management.",
946+
start_block
947+
);
948+
});
949+
let old = std::mem::take(&mut block.instructions);
950+
block.instructions = phi_instrs;
951+
block.instructions.extend(old);
914952
}
915953
}
916954

crates/herkos/src/ir/builder/translate.rs

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ impl IrBuilder {
471471
if branch_incoming.is_empty() {
472472
self.dead_code = true;
473473
} else {
474-
self.insert_phis_at_join(end_block, &branch_incoming);
474+
self.insert_phis_at_join(end_block, &branch_incoming)?;
475475
}
476476
}
477477

@@ -495,7 +495,7 @@ impl IrBuilder {
495495
preds.push((then_block, then_locals));
496496
}
497497
self.push_predecessor_if_live(&mut preds);
498-
preds.extend(branch_incoming.iter().cloned());
498+
preds.extend(branch_incoming.clone());
499499

500500
// Assign result if needed (else-branch fall-through)
501501
if let Some(result_var) = rv {
@@ -516,7 +516,7 @@ impl IrBuilder {
516516
if preds.is_empty() {
517517
self.dead_code = true;
518518
} else {
519-
self.insert_phis_at_join(end_block, &preds);
519+
self.insert_phis_at_join(end_block, &preds)?;
520520
}
521521
}
522522

@@ -553,7 +553,7 @@ impl IrBuilder {
553553
if branch_incoming.is_empty() {
554554
self.dead_code = true;
555555
} else {
556-
self.insert_phis_at_join(end_block, &branch_incoming);
556+
self.insert_phis_at_join(end_block, &branch_incoming)?;
557557
}
558558
}
559559

@@ -588,7 +588,7 @@ impl IrBuilder {
588588
if branch_incoming.is_empty() {
589589
self.dead_code = true;
590590
} else {
591-
self.insert_phis_at_join(end_block, &branch_incoming);
591+
self.insert_phis_at_join(end_block, &branch_incoming)?;
592592
}
593593
}
594594
}
@@ -980,22 +980,7 @@ impl IrBuilder {
980980
// Before terminating the block we snapshot `local_vars` and record it
981981
// as a phi predecessor for the target frame. This snapshot is used by
982982
// `insert_phis_at_join` (or `emit_loop_phis`) to build the phi sources.
983-
let depth = *relative_depth as usize;
984-
let frame_idx =
985-
self.control_stack
986-
.len()
987-
.checked_sub(depth + 1)
988-
.ok_or_else(|| {
989-
anyhow::anyhow!("br: depth {} exceeds control stack", relative_depth)
990-
})?;
991-
992-
let (target, is_loop) = {
993-
let frame = &self.control_stack[frame_idx];
994-
match frame {
995-
super::core::ControlFrame::Loop { start_block, .. } => (*start_block, true),
996-
_ => (frame.end_block(), false),
997-
}
998-
};
983+
let (target, is_loop, frame_idx) = self.resolve_branch_info(*relative_depth)?;
999984

1000985
// Record snapshot *before* terminating so local_vars is still valid.
1001986
if is_loop {
@@ -1035,22 +1020,7 @@ impl IrBuilder {
10351020
.ok_or_else(|| anyhow::anyhow!("Stack underflow for br_if"))?
10361021
.var_id();
10371022

1038-
let depth = *relative_depth as usize;
1039-
let frame_idx =
1040-
self.control_stack
1041-
.len()
1042-
.checked_sub(depth + 1)
1043-
.ok_or_else(|| {
1044-
anyhow::anyhow!("br_if: depth {} exceeds control stack", relative_depth)
1045-
})?;
1046-
1047-
let (target, is_loop) = {
1048-
let frame = &self.control_stack[frame_idx];
1049-
match frame {
1050-
super::core::ControlFrame::Loop { start_block, .. } => (*start_block, true),
1051-
_ => (frame.end_block(), false),
1052-
}
1053-
};
1023+
let (target, is_loop, frame_idx) = self.resolve_branch_info(*relative_depth)?;
10541024

10551025
// Record the taken branch as a phi predecessor (snapshot before termination).
10561026
if is_loop {
@@ -1091,18 +1061,17 @@ impl IrBuilder {
10911061
let target_depths: Vec<u32> = targets.targets().collect::<Result<Vec<_>, _>>()?;
10921062
let default_depth = targets.default();
10931063

1094-
let stack_len = self.control_stack.len();
1064+
// Collect all branch targets and record phi predecessors, deduplicating by frame_idx
1065+
// (multiple table entries may point to the same target frame).
10951066
let mut recorded: std::collections::HashSet<usize> =
10961067
std::collections::HashSet::new();
10971068
for depth in target_depths
10981069
.iter()
10991070
.copied()
11001071
.chain(std::iter::once(default_depth))
11011072
{
1102-
let depth = depth as usize;
1103-
let frame_idx = stack_len.saturating_sub(depth + 1);
1073+
let (_, is_loop, frame_idx) = self.resolve_branch_info(depth)?;
11041074
if recorded.insert(frame_idx) {
1105-
let is_loop = self.control_stack[frame_idx].is_loop();
11061075
if is_loop {
11071076
self.record_loop_back_branch(frame_idx);
11081077
} else {

0 commit comments

Comments
 (0)