use super::*;
impl StructuredBodyLowerer<'_, '_> {
pub(super) fn repeat_backedge_pad(
&self,
header: BlockRef,
loop_backedge_target: BlockRef,
target_overrides: &BTreeMap<TempId, HirLValue>,
) -> Option<Option<BlockRef>> {
if loop_backedge_target == header {
return Some(None);
}
if self
.lowering
.cfg
.unique_reachable_successor(loop_backedge_target)
!= Some(header)
{
return None;
}
if !self
.lower_block_prefix(loop_backedge_target, false, target_overrides)?
.is_empty()
{
return None;
}
Some(Some(loop_backedge_target))
}
pub(super) fn lower_break_exit_pad(
&self,
block: BlockRef,
post_loop: BlockRef,
downstream_post_loop: Option<BlockRef>,
target_overrides: &BTreeMap<TempId, HirLValue>,
states: &[LoopStateSlot],
) -> Option<BreakExitBlock> {
let combined = self.break_pad_target_overrides(block, target_overrides, states);
if matches!(
self.block_terminator(block),
Some((_instr_ref, LowInstr::Branch(_)))
) {
return self.lower_branch_break_exit_pad(
block,
post_loop,
downstream_post_loop,
&combined,
states,
);
}
let mut stmts = self.lower_block_prefix(block, false, &combined)?;
let target = match self.block_terminator(block) {
Some((_instr_ref, LowInstr::Jump(jump))) => {
self.lowering.cfg.instr_to_block[jump.target.index()]
}
Some((_instr_ref, instr)) if !is_control_terminator(instr) => {
self.lowering.cfg.unique_reachable_successor(block)?
}
None => self.lowering.cfg.unique_reachable_successor(block)?,
Some(_) => return None,
};
if target != post_loop && Some(target) != downstream_post_loop {
return None;
}
stmts.push(HirStmt::Break);
Some(BreakExitBlock {
block: HirBlock { stmts },
blocks: BTreeSet::from([block]),
})
}
fn break_pad_target_overrides(
&self,
block: BlockRef,
target_overrides: &BTreeMap<TempId, HirLValue>,
states: &[LoopStateSlot],
) -> BTreeMap<TempId, HirLValue> {
let mut combined = target_overrides.clone();
if states.is_empty() {
return combined;
}
let state_by_reg = state_slots_by_reg(states);
let range = self.lowering.cfg.blocks[block.index()].instrs;
for instr_index in range.start.index()..range.end() {
for def_id in &self.lowering.dataflow.instr_defs[instr_index] {
let def = &self.lowering.dataflow.defs[def_id.index()];
if let Some(state) = state_by_reg.get(&def.reg) {
let temp = self.lowering.bindings.fixed_temps[def_id.index()];
combined.insert(temp, state.target.clone());
}
}
}
combined
}
fn lower_branch_break_exit_pad(
&self,
block: BlockRef,
post_loop: BlockRef,
downstream_post_loop: Option<BlockRef>,
target_overrides: &BTreeMap<TempId, HirLValue>,
states: &[LoopStateSlot],
) -> Option<BreakExitBlock> {
let plan = self
.try_build_short_circuit_plan(block, Some(post_loop))?
.or_else(|| self.build_plain_branch_plan(block))?;
let merge = plan.merge?;
let tail = if merge == post_loop || Some(merge) == downstream_post_loop {
BreakExitBlock {
block: HirBlock {
stmts: vec![HirStmt::Break],
},
blocks: BTreeSet::new(),
}
} else {
self.lower_break_exit_pad(
merge,
post_loop,
downstream_post_loop,
target_overrides,
states,
)?
};
let mut blocks = plan
.consumed_headers
.iter()
.copied()
.collect::<BTreeSet<_>>();
let mut stmts = self.lower_block_prefix(block, true, target_overrides)?;
let mut cond = plan.cond;
if let Some(entry_expr_overrides) = self.block_entry_expr_overrides(block) {
rewrite_expr_temps(&mut cond, entry_expr_overrides);
}
let then_pad = self.lower_break_exit_pad_arm(
plan.then_entry,
merge,
post_loop,
downstream_post_loop,
target_overrides,
states,
)?;
blocks.extend(then_pad.blocks.iter().copied());
let else_pad = match plan.else_entry {
Some(else_entry) => {
let pad = self.lower_break_exit_pad_arm(
else_entry,
merge,
post_loop,
downstream_post_loop,
target_overrides,
states,
)?;
blocks.extend(pad.blocks.iter().copied());
Some(pad.block)
}
None => None,
};
stmts.push(branch_stmt(cond, then_pad.block, else_pad));
stmts.extend(tail.block.stmts);
blocks.extend(tail.blocks);
Some(BreakExitBlock {
block: HirBlock { stmts },
blocks,
})
}
fn lower_break_exit_pad_arm(
&self,
block: BlockRef,
merge: BlockRef,
post_loop: BlockRef,
downstream_post_loop: Option<BlockRef>,
target_overrides: &BTreeMap<TempId, HirLValue>,
states: &[LoopStateSlot],
) -> Option<BreakExitBlock> {
if block == merge || block == post_loop || Some(block) == downstream_post_loop {
return Some(BreakExitBlock {
block: HirBlock::default(),
blocks: BTreeSet::new(),
});
}
let target_overrides = self.break_pad_target_overrides(block, target_overrides, states);
let stmts = self.lower_block_prefix(block, false, &target_overrides)?;
let target = match self.block_terminator(block) {
Some((_instr_ref, LowInstr::Jump(jump))) => {
self.lowering.cfg.instr_to_block[jump.target.index()]
}
Some((_instr_ref, instr)) if !is_control_terminator(instr) => {
self.lowering.cfg.unique_reachable_successor(block)?
}
None => self.lowering.cfg.unique_reachable_successor(block)?,
Some(_) => return None,
};
if target != merge && target != post_loop && Some(target) != downstream_post_loop {
return None;
}
Some(BreakExitBlock {
block: HirBlock { stmts },
blocks: BTreeSet::from([block]),
})
}
}