use alloc::vec::Vec;
use crate::cursor::{Cursor, EncCursor};
use crate::dominator_tree::DominatorTree;
use crate::flowgraph::ControlFlowGraph;
use crate::ir::{Block, Function, Inst, InstBuilder, InstructionData, Opcode, ValueList};
use crate::isa::TargetIsa;
use crate::topo_order::TopoOrder;
pub fn run(
isa: &dyn TargetIsa,
func: &mut Function,
cfg: &mut ControlFlowGraph,
domtree: &mut DominatorTree,
topo: &mut TopoOrder,
) {
let mut ctx = Context {
has_new_blocks: false,
cur: EncCursor::new(func, isa),
domtree,
topo,
cfg,
};
ctx.run()
}
struct Context<'a> {
has_new_blocks: bool,
cur: EncCursor<'a>,
domtree: &'a mut DominatorTree,
topo: &'a mut TopoOrder,
cfg: &'a mut ControlFlowGraph,
}
impl<'a> Context<'a> {
fn run(&mut self) {
self.topo.reset(self.cur.func.layout.blocks());
while let Some(block) = self.topo.next(&self.cur.func.layout, self.domtree) {
self.cur.goto_last_inst(block);
let terminator_inst = self.cur.current_inst().expect("terminator");
if let Some(inst) = self.cur.prev_inst() {
let opcode = self.cur.func.dfg[inst].opcode();
if opcode.is_branch() {
self.visit_conditional_branch(inst, opcode);
self.cur.goto_inst(terminator_inst);
self.visit_terminator_branch(terminator_inst);
}
}
}
if self.has_new_blocks {
self.cfg.compute(&self.cur.func);
self.domtree.compute(&self.cur.func, self.cfg);
}
}
fn visit_conditional_branch(&mut self, branch: Inst, opcode: Opcode) {
let target = match self.cur.func.dfg[branch] {
InstructionData::Branch { destination, .. }
| InstructionData::BranchIcmp { destination, .. }
| InstructionData::BranchInt { destination, .. }
| InstructionData::BranchFloat { destination, .. } => destination,
_ => panic!("Unexpected instruction in visit_conditional_branch"),
};
if self.should_split_edge(target) {
let new_block = self.cur.func.dfg.make_block();
assert_ne!(Some(target), self.cur.layout().entry_block());
self.cur.layout_mut().insert_block(new_block, target);
self.has_new_blocks = true;
let num_fixed = opcode.constraints().num_fixed_value_arguments();
let dfg = &mut self.cur.func.dfg;
let old_args: Vec<_> = {
let args = dfg[branch].take_value_list().expect("block parameters");
args.as_slice(&dfg.value_lists).iter().copied().collect()
};
let (branch_args, block_params) = old_args.split_at(num_fixed);
{
let branch_args = ValueList::from_slice(branch_args, &mut dfg.value_lists);
let data = &mut dfg[branch];
*data.branch_destination_mut().expect("branch") = new_block;
data.put_value_list(branch_args);
}
let ok = self.cur.func.update_encoding(branch, self.cur.isa).is_ok();
debug_assert!(ok);
self.cur.goto_first_insertion_point(new_block);
self.cur.ins().jump(target, block_params);
self.cur.goto_inst(branch);
}
}
fn visit_terminator_branch(&mut self, inst: Inst) {
let inst_data = &self.cur.func.dfg[inst];
let opcode = inst_data.opcode();
if opcode != Opcode::Jump && opcode != Opcode::Fallthrough {
if opcode != Opcode::IndirectJumpTableBr {
debug_assert!(!opcode.is_branch())
}
return;
}
let target = match inst_data {
InstructionData::Jump { destination, .. } => destination,
_ => panic!(
"Unexpected instruction {} in visit_terminator_branch",
self.cur.display_inst(inst)
),
};
debug_assert!(self.cur.func.dfg[inst].opcode().is_terminator());
if self.should_split_edge(*target) {
let new_block = self.cur.func.dfg.make_block();
self.has_new_blocks = true;
let jump = self.cur.ins().jump(new_block, &[]);
self.cur.insert_block(new_block);
self.cur.goto_inst(jump);
}
}
fn should_split_edge(&self, target: Block) -> bool {
if !self.cur.func.dfg.block_params(target).is_empty() {
return true;
};
debug_assert!(self.cfg.pred_iter(target).next() != None);
self.cfg.pred_iter(target).nth(1).is_some()
}
}