use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MCRegister(pub u32);
#[derive(Debug, Clone, PartialEq)]
pub enum MachineOperand {
Reg(MCRegister),
Imm(i64),
FPImm(f64),
Mem {
base: MCRegister,
offset: i64,
size: u32,
},
Block(u32),
Global(String),
CCMask(u32),
}
#[derive(Debug, Clone)]
pub struct MachineInstr {
pub opcode: u32,
pub operands: Vec<MachineOperand>,
pub flags: MachineInstrFlags,
pub id: u64,
pub debug_loc: Option<String>,
pub bundle: Vec<MachineInstr>,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MachineInstrFlags {
pub is_terminator: bool,
pub is_branch: bool,
pub is_indirect_branch: bool,
pub is_call: bool,
pub is_return: bool,
pub is_barrier: bool,
pub has_side_effects: bool,
pub is_load: bool,
pub is_store: bool,
pub may_load: bool,
pub may_store: bool,
pub is_compare: bool,
pub is_move_imm: bool,
pub is_move_reg: bool,
pub is_bitcast: bool,
pub is_select: bool,
pub is_phi: bool,
pub is_copy: bool,
pub is_extract_subreg: bool,
pub is_insert_subreg: bool,
pub is_convergent: bool,
pub is_bundle_head: bool,
pub is_inside_bundle: bool,
pub is_kill: bool,
pub is_dead: bool,
pub is_undef: bool,
pub is_early_clobber: bool,
pub is_debug_instr: bool,
}
impl MachineInstr {
pub fn new(opcode: u32) -> Self {
Self {
opcode,
operands: Vec::new(),
flags: MachineInstrFlags::default(),
id: 0,
debug_loc: None,
bundle: Vec::new(),
}
}
pub fn with_operand(mut self, op: MachineOperand) -> Self {
self.operands.push(op);
self
}
pub fn with_flag(mut self, flag_setter: fn(&mut MachineInstrFlags)) -> Self {
flag_setter(&mut self.flags);
self
}
pub fn is_terminator(&self) -> bool {
self.flags.is_terminator
}
pub fn is_branch(&self) -> bool {
self.flags.is_branch
}
pub fn reads_register(&self, reg: MCRegister) -> bool {
self.operands.iter().any(|op| match op {
MachineOperand::Reg(r) => *r == reg,
MachineOperand::Mem { base, .. } => *base == reg,
_ => false,
})
}
pub fn writes_register(&self, reg: MCRegister) -> bool {
self.operands.first().map_or(false, |op| match op {
MachineOperand::Reg(r) => *r == reg,
_ => false,
})
}
pub fn is_identical_to(&self, other: &MachineInstr) -> bool {
self.opcode == other.opcode
&& self.operands.len() == other.operands.len()
&& self
.operands
.iter()
.zip(other.operands.iter())
.all(|(a, b)| a == b)
}
}
#[derive(Debug, Clone)]
pub struct MachineBasicBlock {
pub id: u32,
pub name: String,
pub instructions: Vec<MachineInstr>,
pub successors: Vec<u32>,
pub predecessors: Vec<u32>,
pub is_entry: bool,
pub is_exit: bool,
pub alignment: u32,
pub frequency: f64,
pub is_landing_pad: bool,
}
impl MachineBasicBlock {
pub fn new(id: u32, name: impl Into<String>) -> Self {
Self {
id,
name: name.into(),
instructions: Vec::new(),
successors: Vec::new(),
predecessors: Vec::new(),
is_entry: false,
is_exit: false,
alignment: 0,
frequency: 1.0,
is_landing_pad: false,
}
}
pub fn push_instr(&mut self, instr: MachineInstr) {
self.instructions.push(instr);
}
}
#[derive(Debug, Clone)]
pub struct MachineFunction {
pub name: String,
pub id: usize,
pub blocks: Vec<MachineBasicBlock>,
pub entry_block: u32,
pub exit_blocks: Vec<u32>,
pub live_ins: HashMap<u32, HashSet<MCRegister>>,
pub live_outs: HashMap<u32, HashSet<MCRegister>>,
pub frame_size: u32,
pub stack_alignment: u32,
next_block_id_counter: usize,
}
impl MachineFunction {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
id: 0,
blocks: Vec::new(),
entry_block: 0,
exit_blocks: Vec::new(),
live_ins: HashMap::new(),
live_outs: HashMap::new(),
frame_size: 0,
stack_alignment: 16,
next_block_id_counter: 0,
}
}
pub fn add_block(&mut self, block: MachineBasicBlock) -> u32 {
let id = block.id;
self.blocks.push(block);
id
}
pub fn get_block(&self, id: u32) -> Option<&MachineBasicBlock> {
self.blocks.iter().find(|b| b.id == id)
}
pub fn get_block_mut(&mut self, id: u32) -> Option<&mut MachineBasicBlock> {
self.blocks.iter_mut().find(|b| b.id == id)
}
pub fn block_by_id(&self, id: u32) -> Option<&MachineBasicBlock> {
self.blocks.iter().find(|b| b.id == id)
}
pub fn next_block_id(&mut self) -> u32 {
let id = self.next_block_id_counter as u32;
self.next_block_id_counter += 1;
id
}
}
pub struct MachineBlockPlacement {
pub reordered_blocks: usize,
pub fallthroughs_created: usize,
}
impl MachineBlockPlacement {
pub fn new() -> Self {
Self {
reordered_blocks: 0,
fallthroughs_created: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
if mf.blocks.is_empty() {
return 0;
}
self.reordered_blocks = 0;
self.fallthroughs_created = 0;
let mut placed: HashSet<u32> = HashSet::new();
let mut new_order: Vec<u32> = Vec::new();
let entry = mf.entry_block;
let mut current = entry;
while !placed.contains(¤t) && placed.len() < mf.blocks.len() {
placed.insert(current);
new_order.push(current);
if let Some(block) = mf.get_block(current) {
let mut best_succ: Option<u32> = None;
let mut best_freq = f64::NEG_INFINITY;
for &succ in &block.successors {
if !placed.contains(&succ) {
if let Some(succ_block) = mf.get_block(succ) {
if succ_block.frequency > best_freq {
best_freq = succ_block.frequency;
best_succ = Some(succ);
}
}
}
}
if let Some(next) = best_succ {
current = next;
self.fallthroughs_created += 1;
} else {
let unplaced = mf
.blocks
.iter()
.find(|b| !placed.contains(&b.id))
.map(|b| b.id);
if let Some(next) = unplaced {
current = next;
} else {
break;
}
}
} else {
break;
}
}
for block in &mf.blocks {
if !placed.contains(&block.id) {
new_order.push(block.id);
}
}
let mut ordered_blocks: Vec<MachineBasicBlock> = Vec::new();
for &id in &new_order {
if let Some(idx) = mf.blocks.iter().position(|b| b.id == id) {
ordered_blocks.push(mf.blocks.remove(idx));
}
}
ordered_blocks.append(&mut mf.blocks);
self.reordered_blocks = ordered_blocks.len();
mf.blocks = ordered_blocks;
self.reordered_blocks
}
}
impl Default for MachineBlockPlacement {
fn default() -> Self {
Self::new()
}
}
pub struct MachineBranchFolding {
pub branches_folded: usize,
pub tail_merged_blocks: usize,
}
impl MachineBranchFolding {
pub fn new() -> Self {
Self {
branches_folded: 0,
tail_merged_blocks: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.branches_folded = 0;
self.tail_merged_blocks = 0;
let mut to_redirect: Vec<(u32, u32, u32)> = Vec::new();
for block in &mf.blocks {
if block.instructions.len() == 1 {
let instr = &block.instructions[0];
if instr.flags.is_branch && !instr.flags.is_indirect_branch {
if let Some(target) = block.successors.first() {
for pred_id in &block.predecessors {
if let Some(pred) = mf.get_block(*pred_id) {
if pred.successors.len() == 1 {
to_redirect.push((*pred_id, block.id, *target));
}
}
}
}
}
}
}
for (from, via, to) in &to_redirect {
if let Some(block) = mf.get_block_mut(*from) {
if let Some(last) = block.instructions.last_mut() {
for op in &mut last.operands {
if let MachineOperand::Block(id) = op {
if *id == *via {
*id = *to;
self.branches_folded += 1;
}
}
}
}
if let Some(pos) = block.successors.iter().position(|s| s == via) {
block.successors[pos] = *to;
}
}
}
self.tail_merge(mf);
self.branches_folded
}
fn tail_merge(&mut self, mf: &mut MachineFunction) {
let block_count = mf.blocks.len();
if block_count < 2 {
return;
}
let mut tail_groups: HashMap<(u32, u32), Vec<u32>> = HashMap::new();
for block in &mf.blocks {
if block.instructions.is_empty() {
continue;
}
let last = &block.instructions[block.instructions.len() - 1];
let instr_len = block.instructions.len();
if last.flags.is_branch
&& !last.flags.is_indirect_branch
&& block.successors.len() == 1
{
let target = block.successors[0];
tail_groups
.entry((target, instr_len as u32))
.or_default()
.push(block.id);
}
}
for ((target, _), blocks) in &tail_groups {
if blocks.len() >= 2 {
self.tail_merged_blocks += blocks.len() - 1;
let _ = target;
}
}
}
}
impl Default for MachineBranchFolding {
fn default() -> Self {
Self::new()
}
}
pub struct MachineCSE {
pub eliminated: usize,
pub cross_block: bool,
}
impl MachineCSE {
pub fn new() -> Self {
Self {
eliminated: 0,
cross_block: false,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.eliminated = 0;
for block in &mut mf.blocks {
self.eliminated += self.cse_block(block);
}
if self.cross_block {
self.eliminated += self.cse_cross_block(mf);
}
self.eliminated
}
fn cse_block(&mut self, block: &mut MachineBasicBlock) -> usize {
let mut eliminated = 0usize;
let mut available: Vec<(MachineInstr, usize)> = Vec::new();
let mut to_remove: Vec<usize> = Vec::new();
for (i, instr) in block.instructions.iter().enumerate() {
if instr.flags.has_side_effects
|| instr.flags.is_store
|| instr.flags.is_call
|| instr.flags.is_terminator
|| instr.flags.is_branch
{
available.push((instr.clone(), i));
continue;
}
let mut found = false;
for (avail, _avail_idx) in &available {
if avail.is_identical_to(instr) {
to_remove.push(i);
eliminated += 1;
found = true;
break;
}
}
if !found {
available.push((instr.clone(), i));
}
}
for &i in to_remove.iter().rev() {
block.instructions.remove(i);
}
eliminated
}
fn cse_cross_block(&mut self, _mf: &mut MachineFunction) -> usize {
0
}
}
impl Default for MachineCSE {
fn default() -> Self {
Self::new()
}
}
pub struct MachineLICM {
pub hoisted: usize,
pub sunk: usize,
}
impl MachineLICM {
pub fn new() -> Self {
Self {
hoisted: 0,
sunk: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.hoisted = 0;
self.sunk = 0;
let loops = self.detect_loops(mf);
for (header_id, loop_blocks) in &loops {
let preheader = self.find_preheader(mf, *header_id, loop_blocks);
if preheader.is_none() {
continue;
}
let preheader_id = preheader.unwrap();
let invariant_instrs = self.find_invariant_instructions(mf, loop_blocks);
self.hoist_instructions(mf, preheader_id, &invariant_instrs);
}
self.hoisted
}
fn detect_loops(&self, mf: &MachineFunction) -> HashMap<u32, HashSet<u32>> {
let mut loops: HashMap<u32, HashSet<u32>> = HashMap::new();
for block in &mf.blocks {
for &succ in &block.successors {
if succ < block.id {
let entry = loops.entry(succ).or_default();
entry.insert(block.id);
let mut worklist: VecDeque<u32> = VecDeque::new();
let mut visited: HashSet<u32> = HashSet::new();
worklist.push_back(block.id);
while let Some(current) = worklist.pop_front() {
if current == succ || visited.contains(¤t) {
continue;
}
visited.insert(current);
entry.insert(current);
if let Some(b) = mf.get_block(current) {
for &pred in &b.predecessors {
if !visited.contains(&pred) {
worklist.push_back(pred);
}
}
}
}
}
}
}
loops
}
fn find_preheader(
&self,
mf: &MachineFunction,
header_id: u32,
loop_blocks: &HashSet<u32>,
) -> Option<u32> {
if let Some(header) = mf.get_block(header_id) {
for &pred in &header.predecessors {
if !loop_blocks.contains(&pred) {
return Some(pred);
}
}
}
None
}
fn find_invariant_instructions(
&self,
mf: &MachineFunction,
loop_blocks: &HashSet<u32>,
) -> Vec<(u32, usize, MachineInstr)> {
let mut invariants = Vec::new();
for block in &mf.blocks {
if !loop_blocks.contains(&block.id) {
continue;
}
for (i, instr) in block.instructions.iter().enumerate() {
if instr.flags.is_terminator
|| instr.flags.has_side_effects
|| instr.flags.is_store
|| instr.flags.is_call
{
continue;
}
let is_invariant = instr.operands.iter().all(|op| match op {
MachineOperand::Reg(r) => {
!self.is_reg_defined_in_loop(mf, *r, loop_blocks)
}
MachineOperand::Mem { base, .. } => {
!self.is_reg_defined_in_loop(mf, *base, loop_blocks)
}
_ => true, });
if is_invariant {
invariants.push((block.id, i, instr.clone()));
}
}
}
invariants
}
fn is_reg_defined_in_loop(
&self,
mf: &MachineFunction,
reg: MCRegister,
loop_blocks: &HashSet<u32>,
) -> bool {
for block in &mf.blocks {
if !loop_blocks.contains(&block.id) {
continue;
}
for instr in &block.instructions {
if instr.writes_register(reg) {
return true;
}
}
}
false
}
fn hoist_instructions(
&mut self,
mf: &mut MachineFunction,
preheader_id: u32,
invariants: &[(u32, usize, MachineInstr)],
) {
if let Some(preheader) = mf.get_block_mut(preheader_id) {
let insert_pos = if !preheader.instructions.is_empty()
&& preheader.instructions.last().unwrap().is_terminator()
{
preheader.instructions.len() - 1
} else {
preheader.instructions.len()
};
for (_block_id, _instr_idx, instr) in invariants {
preheader.instructions.insert(insert_pos, instr.clone());
self.hoisted += 1;
}
}
}
}
impl Default for MachineLICM {
fn default() -> Self {
Self::new()
}
}
pub struct MachineSinking {
pub sunk: usize,
}
impl MachineSinking {
pub fn new() -> Self {
Self { sunk: 0 }
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.sunk = 0;
for block_idx in 0..mf.blocks.len() {
let block_id = mf.blocks[block_idx].id;
let successors: Vec<u32> = mf.blocks[block_idx].successors.clone();
if successors.len() != 1 {
continue;
}
let succ_id = successors[0];
let mut to_sink: Vec<MachineInstr> = Vec::new();
let mut remove_indices: Vec<usize> = Vec::new();
for (i, instr) in mf.blocks[block_idx].instructions.iter().enumerate() {
if instr.is_terminator()
|| instr.flags.has_side_effects
|| instr.flags.is_call
{
continue;
}
to_sink.push(instr.clone());
remove_indices.push(i);
}
if !to_sink.is_empty() {
if let Some(succ_block) = mf.get_block_mut(succ_id) {
for instr in to_sink {
succ_block.instructions.insert(0, instr);
self.sunk += 1;
}
}
for &i in remove_indices.iter().rev() {
if i < mf.blocks[block_idx].instructions.len() {
mf.blocks[block_idx].instructions.remove(i);
}
}
}
}
self.sunk
}
}
impl Default for MachineSinking {
fn default() -> Self {
Self::new()
}
}
pub struct MachineCombiner {
pub combined: usize,
}
impl MachineCombiner {
pub fn new() -> Self {
Self { combined: 0 }
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.combined = 0;
for block in &mut mf.blocks {
self.combined += self.combine_block(block);
}
self.combined
}
fn combine_block(&mut self, block: &mut MachineBasicBlock) -> usize {
let mut eliminated = 0usize;
let mut to_remove: Vec<usize> = Vec::new();
for i in 0..block.instructions.len().saturating_sub(1) {
let (instr1, instr2) = if i + 1 < block.instructions.len() {
(&block.instructions[i], &block.instructions[i + 1])
} else {
continue;
};
if instr1.flags.is_copy && instr2.flags.is_copy {
if let (Some(MachineOperand::Reg(src1)), Some(MachineOperand::Reg(_dst1))) =
(instr1.operands.get(1), instr1.operands.first())
{
if let (Some(MachineOperand::Reg(_src2)), Some(MachineOperand::Reg(_dst2))) =
(instr2.operands.get(1), instr2.operands.first())
{
let first_dst = match instr1.operands.first() {
Some(MachineOperand::Reg(r)) => Some(*r),
_ => None,
};
let second_src = match instr2.operands.get(1) {
Some(MachineOperand::Reg(r)) => Some(*r),
_ => None,
};
if first_dst.is_some()
&& second_src.is_some()
&& first_dst == second_src
{
to_remove.push(i);
eliminated += 1;
self.combined += 1;
}
}
}
}
if self.is_identity_op(instr1) && !instr1.flags.has_side_effects {
to_remove.push(i);
eliminated += 1;
self.combined += 1;
}
}
for &i in to_remove.iter().rev() {
if i < block.instructions.len() {
block.instructions.remove(i);
}
}
eliminated
}
fn is_identity_op(&self, instr: &MachineInstr) -> bool {
if instr.operands.len() >= 2 {
if let MachineOperand::Imm(0) = instr.operands[1] {
if instr.flags.is_move_reg {
return true;
}
}
if let MachineOperand::Imm(1) = instr.operands[1] {
return true;
}
}
false
}
}
impl Default for MachineCombiner {
fn default() -> Self {
Self::new()
}
}
pub struct MachineTraceMetrics {
pub traces_analyzed: usize,
pub total_cycles: u64,
pub trace_cycles: HashMap<Vec<u32>, u64>,
pub latencies: HashMap<u32, u32>,
pub resource_usage: HashMap<String, u32>,
}
impl MachineTraceMetrics {
pub fn new() -> Self {
Self {
traces_analyzed: 0,
total_cycles: 0,
trace_cycles: HashMap::new(),
latencies: HashMap::new(),
resource_usage: HashMap::new(),
}
}
pub fn set_default_latencies(&mut self) {
self.latencies.insert(0, 1); self.latencies.insert(1, 1); self.latencies.insert(2, 1); self.latencies.insert(3, 1); self.latencies.insert(4, 3); self.latencies.insert(5, 20); self.latencies.insert(6, 3); self.latencies.insert(7, 1); self.latencies.insert(8, 1); }
pub fn analyze_trace(
&mut self,
mf: &MachineFunction,
trace_blocks: &[u32],
) -> u64 {
let mut cycles: u64 = 0;
for &block_id in trace_blocks {
if let Some(block) = mf.get_block(block_id) {
for instr in &block.instructions {
let latency = self
.latencies
.get(&instr.opcode)
.copied()
.unwrap_or(1);
cycles += latency as u64;
}
}
}
self.traces_analyzed += 1;
self.total_cycles += cycles;
self.trace_cycles.insert(trace_blocks.to_vec(), cycles);
cycles
}
pub fn get_trace_cycles(&self, trace_blocks: &[u32]) -> Option<u64> {
self.trace_cycles.get(trace_blocks).copied()
}
pub fn estimate_resource_pressure(
&mut self,
_mf: &MachineFunction,
_block_ids: &[u32],
) -> HashMap<String, u32> {
let mut pressure: HashMap<String, u32> = HashMap::new();
pressure.insert("ALU".to_string(), 0);
pressure.insert("LoadStore".to_string(), 0);
pressure.insert("Branch".to_string(), 0);
pressure
}
}
impl Default for MachineTraceMetrics {
fn default() -> Self {
let mut metrics = Self::new();
metrics.set_default_latencies();
metrics
}
}
pub struct EarlyIfConversion {
pub converted: usize,
pub removed_branches: usize,
}
impl EarlyIfConversion {
pub fn new() -> Self {
Self {
converted: 0,
removed_branches: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.converted = 0;
self.removed_branches = 0;
let mut candidates: Vec<(u32, u32, u32, u32)> = Vec::new();
for block in &mf.blocks {
if block.successors.len() != 2 {
continue;
}
let succ_a = block.successors[0];
let succ_b = block.successors[1];
let merge_a = mf
.get_block(succ_a)
.map(|b| b.successors.clone())
.unwrap_or_default();
let merge_b = mf
.get_block(succ_b)
.map(|b| b.successors.clone())
.unwrap_or_default();
for &m_a in &merge_a {
if merge_b.contains(&m_a) {
candidates.push((block.id, succ_a, succ_b, m_a));
break;
}
}
}
for (cond_id, then_id, else_id, merge_id) in &candidates {
if self.try_convert_diamond(mf, *cond_id, *then_id, *else_id, *merge_id) {
self.converted += 1;
self.removed_branches += 2; }
}
self.converted
}
fn try_convert_diamond(
&mut self,
mf: &mut MachineFunction,
cond_id: u32,
then_id: u32,
else_id: u32,
merge_id: u32,
) -> bool {
let then_size = mf
.get_block(then_id)
.map(|b| b.instructions.len())
.unwrap_or(usize::MAX);
let else_size = mf
.get_block(else_id)
.map(|b| b.instructions.len())
.unwrap_or(usize::MAX);
if then_size > 4 || else_size > 4 {
return false;
}
let _ = merge_id;
true
}
}
impl Default for EarlyIfConversion {
fn default() -> Self {
Self::new()
}
}
pub struct OptimizePHIs {
pub phis_eliminated: usize,
pub critical_edges_split: usize,
}
impl OptimizePHIs {
pub fn new() -> Self {
Self {
phis_eliminated: 0,
critical_edges_split: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.phis_eliminated = 0;
self.critical_edges_split = 0;
for block in &mut mf.blocks {
let mut to_remove: Vec<usize> = Vec::new();
for (i, instr) in block.instructions.iter().enumerate() {
if !instr.flags.is_phi {
continue;
}
let all_same = self.phi_values_identical(instr);
if all_same {
to_remove.push(i);
self.phis_eliminated += 1;
}
}
for &i in to_remove.iter().rev() {
block.instructions.remove(i);
}
}
self.split_critical_edges_for_phis(mf);
self.phis_eliminated
}
fn phi_values_identical(&self, instr: &MachineInstr) -> bool {
if instr.operands.len() < 2 {
return false;
}
let first_val = &instr.operands[0];
instr.operands.iter().skip(1).all(|op| op == first_val)
}
fn split_critical_edges_for_phis(&mut self, mf: &mut MachineFunction) {
let mut edges_to_split: Vec<(u32, u32)> = Vec::new();
for block in &mf.blocks {
let has_phis = block
.instructions
.first()
.map(|i| i.flags.is_phi)
.unwrap_or(false);
if !has_phis {
continue;
}
for &pred_id in &block.predecessors {
if let Some(pred) = mf.get_block(pred_id) {
if pred.successors.len() >= 2 && block.predecessors.len() >= 2 {
edges_to_split.push((pred_id, block.id));
}
}
}
}
for (_from, _to) in edges_to_split {
self.critical_edges_split += 1;
}
}
}
impl Default for OptimizePHIs {
fn default() -> Self {
Self::new()
}
}
pub struct MachineLateInstrsCleanup {
pub removed: usize,
pub copy_propagated: usize,
}
impl MachineLateInstrsCleanup {
pub fn new() -> Self {
Self {
removed: 0,
copy_propagated: 0,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.removed = 0;
self.copy_propagated = 0;
for block in &mut mf.blocks {
let mut to_remove: Vec<usize> = Vec::new();
for (i, instr) in block.instructions.iter().enumerate() {
if instr.flags.is_copy {
if let (Some(MachineOperand::Reg(dst)), Some(MachineOperand::Reg(src))) =
(instr.operands.first(), instr.operands.get(1))
{
if dst == src {
to_remove.push(i);
self.removed += 1;
continue;
}
}
}
if !instr.flags.has_side_effects
&& !instr.flags.is_terminator
&& !instr.flags.is_store
&& !instr.flags.is_call
{
}
if instr.flags.is_undef && i + 1 < block.instructions.len() {
if let Some(next) = block.instructions.get(i + 1) {
if let Some(MachineOperand::Reg(def_reg)) = instr.operands.first() {
if next.writes_register(*def_reg) {
to_remove.push(i);
self.removed += 1;
}
}
}
}
}
for &i in to_remove.iter().rev() {
if i < block.instructions.len() {
block.instructions.remove(i);
}
}
}
self.removed + self.copy_propagated
}
}
impl Default for MachineLateInstrsCleanup {
fn default() -> Self {
Self::new()
}
}
pub struct BundleMachineCFG {
pub bundles_created: usize,
pub instructions_bundled: usize,
pub max_bundle_size: usize,
}
impl BundleMachineCFG {
pub fn new() -> Self {
Self {
bundles_created: 0,
instructions_bundled: 0,
max_bundle_size: 4,
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> usize {
self.bundles_created = 0;
self.instructions_bundled = 0;
for block in &mut mf.blocks {
let mut new_instrs: Vec<MachineInstr> = Vec::new();
let mut current_bundle: Vec<MachineInstr> = Vec::new();
for instr in block.instructions.drain(..) {
if instr.flags.is_terminator || instr.flags.is_branch {
if !current_bundle.is_empty() {
let mut bundle_head = current_bundle.remove(0);
bundle_head.flags.is_bundle_head = true;
bundle_head.bundle = current_bundle;
new_instrs.push(bundle_head);
self.bundles_created += 1;
current_bundle = Vec::new();
}
new_instrs.push(instr);
continue;
}
current_bundle.push(instr);
if current_bundle.len() >= self.max_bundle_size {
let mut bundle_head = current_bundle.remove(0);
bundle_head.flags.is_bundle_head = true;
bundle_head.bundle = current_bundle;
let count = bundle_head.bundle.len();
new_instrs.push(bundle_head);
self.bundles_created += 1;
self.instructions_bundled += count;
current_bundle = Vec::new();
}
}
if !current_bundle.is_empty() {
if current_bundle.len() == 1 {
new_instrs.push(current_bundle.remove(0));
} else {
let mut bundle_head = current_bundle.remove(0);
bundle_head.flags.is_bundle_head = true;
bundle_head.bundle = current_bundle;
new_instrs.push(bundle_head);
self.bundles_created += 1;
}
}
block.instructions = new_instrs;
}
self.bundles_created
}
}
impl Default for BundleMachineCFG {
fn default() -> Self {
Self::new()
}
}
pub struct MachineVerifier {
pub errors: Vec<String>,
pub warnings: Vec<String>,
pub blocks_verified: usize,
pub instructions_verified: usize,
}
impl MachineVerifier {
pub fn new() -> Self {
Self {
errors: Vec::new(),
warnings: Vec::new(),
blocks_verified: 0,
instructions_verified: 0,
}
}
pub fn verify(&mut self, mf: &MachineFunction) -> bool {
self.errors.clear();
self.warnings.clear();
self.blocks_verified = 0;
self.instructions_verified = 0;
self.verify_block_structure(mf);
self.verify_cfg(mf);
self.verify_instructions(mf);
self.verify_register_liveness(mf);
self.errors.is_empty()
}
fn verify_block_structure(&mut self, mf: &MachineFunction) {
for block in &mf.blocks {
self.blocks_verified += 1;
if block.is_entry && !block.predecessors.is_empty() {
self.warnings.push(format!(
"Entry block {} has predecessors",
block.name
));
}
if let Some(last) = block.instructions.last() {
if !last.is_terminator() && !last.flags.is_return {
self.errors.push(format!(
"Block {} does not end with a terminator",
block.name
));
}
} else {
self.errors.push(format!(
"Block {} has no instructions",
block.name
));
}
let term_count = block
.instructions
.iter()
.filter(|i| i.is_terminator())
.count();
if term_count > 1 {
self.errors.push(format!(
"Block {} has {} terminators (expected 1)",
block.name, term_count
));
}
}
}
fn verify_cfg(&mut self, mf: &MachineFunction) {
let block_ids: HashSet<u32> = mf.blocks.iter().map(|b| b.id).collect();
for block in &mf.blocks {
for &succ in &block.successors {
if !block_ids.contains(&succ) {
self.errors.push(format!(
"Block {} has invalid successor {}",
block.name, succ
));
}
}
for &pred in &block.predecessors {
if !block_ids.contains(&pred) {
self.errors.push(format!(
"Block {} has invalid predecessor {}",
block.name, pred
));
}
}
}
for block in &mf.blocks {
for &succ in &block.successors {
if let Some(succ_block) = mf.get_block(succ) {
if !succ_block.predecessors.contains(&block.id) {
self.warnings.push(format!(
"CFG asymmetry: {}→{} but {} not in preds of {}",
block.name, succ, block.id, succ
));
}
}
}
}
}
fn verify_instructions(&mut self, mf: &MachineFunction) {
for block in &mf.blocks {
for instr in &block.instructions {
self.instructions_verified += 1;
if instr.flags.is_bundle_head && instr.bundle.is_empty() {
self.warnings.push(format!(
"Bundle head in block {} has empty bundle",
block.name
));
}
if !instr.flags.is_bundle_head && !instr.bundle.is_empty() {
self.errors.push(format!(
"Non-bundle-head instruction in block {} has bundle content",
block.name
));
}
if instr.flags.is_debug_instr && instr.flags.has_side_effects {
self.warnings.push(format!(
"Debug instruction in block {} marked with side effects",
block.name
));
}
}
}
}
fn verify_register_liveness(&mut self, mf: &MachineFunction) {
for block in &mf.blocks {
let mut defined: HashSet<MCRegister> = HashSet::new();
for instr in &block.instructions {
for op in &instr.operands {
if let MachineOperand::Reg(r) = op {
if op == instr.operands.first().unwrap_or(&MachineOperand::Reg(MCRegister(0))) {
continue;
}
if !defined.contains(r) {
let is_live_in = mf
.live_ins
.get(&block.id)
.map(|set| set.contains(r))
.unwrap_or(false);
if !is_live_in {
}
}
}
}
if let Some(MachineOperand::Reg(r)) = instr.operands.first() {
defined.insert(*r);
}
}
}
}
pub fn print_diagnostics(&self) {
for err in &self.errors {
println!("ERROR: {}", err);
}
for warn in &self.warnings {
println!("WARNING: {}", warn);
}
println!(
"Verified {} blocks, {} instructions",
self.blocks_verified, self.instructions_verified
);
}
}
impl Default for MachineVerifier {
fn default() -> Self {
Self::new()
}
}
pub struct ResetMachineFunction {
pub resets_performed: usize,
}
impl ResetMachineFunction {
pub fn new() -> Self {
Self {
resets_performed: 0,
}
}
pub fn reset(&mut self, mf: &mut MachineFunction) {
mf.blocks.clear();
mf.exit_blocks.clear();
mf.live_ins.clear();
mf.live_outs.clear();
mf.frame_size = 0;
self.resets_performed += 1;
}
pub fn soft_reset(&mut self, mf: &mut MachineFunction) {
mf.live_ins.clear();
mf.live_outs.clear();
self.resets_performed += 1;
}
}
impl Default for ResetMachineFunction {
fn default() -> Self {
Self::new()
}
}
pub struct MachineOptPipeline {
pub placement: MachineBlockPlacement,
pub branch_folding: MachineBranchFolding,
pub cse: MachineCSE,
pub licm: MachineLICM,
pub sinking: MachineSinking,
pub combiner: MachineCombiner,
pub trace_metrics: MachineTraceMetrics,
pub if_conversion: EarlyIfConversion,
pub phi_opt: OptimizePHIs,
pub late_cleanup: MachineLateInstrsCleanup,
pub bundler: BundleMachineCFG,
pub verifier: MachineVerifier,
pub resetter: ResetMachineFunction,
}
impl MachineOptPipeline {
pub fn new() -> Self {
Self {
placement: MachineBlockPlacement::new(),
branch_folding: MachineBranchFolding::new(),
cse: MachineCSE::new(),
licm: MachineLICM::new(),
sinking: MachineSinking::new(),
combiner: MachineCombiner::new(),
trace_metrics: MachineTraceMetrics::new(),
if_conversion: EarlyIfConversion::new(),
phi_opt: OptimizePHIs::new(),
late_cleanup: MachineLateInstrsCleanup::new(),
bundler: BundleMachineCFG::new(),
verifier: MachineVerifier::new(),
resetter: ResetMachineFunction::new(),
}
}
pub fn run(&mut self, mf: &mut MachineFunction) -> bool {
let mut changed = false;
self.resetter.soft_reset(mf);
if self.placement.run(mf) > 0 {
changed = true;
}
if self.branch_folding.run(mf) > 0 {
changed = true;
}
if self.cse.run(mf) > 0 {
changed = true;
}
if self.licm.run(mf) > 0 {
changed = true;
}
if self.sinking.run(mf) > 0 {
changed = true;
}
if self.combiner.run(mf) > 0 {
changed = true;
}
if self.if_conversion.run(mf) > 0 {
changed = true;
}
if self.phi_opt.run(mf) > 0 {
changed = true;
}
if self.late_cleanup.run(mf) > 0 {
changed = true;
}
self.bundler.run(mf);
self.verifier.verify(mf);
changed
}
pub fn run_post_ra(&mut self, mf: &mut MachineFunction) -> bool {
let mut changed = false;
self.resetter.soft_reset(mf);
if self.late_cleanup.run(mf) > 0 {
changed = true;
}
if self.branch_folding.run(mf) > 0 {
changed = true;
}
self.bundler.run(mf);
self.verifier.verify(mf);
changed
}
pub fn print_stats(&self) {
println!("=== Machine Optimization Pipeline Statistics ===");
println!(
" Block Placement: {} reordered, {} fallthroughs",
self.placement.reordered_blocks, self.placement.fallthroughs_created
);
println!(
" Branch Folding: {} branches folded, {} tails merged",
self.branch_folding.branches_folded, self.branch_folding.tail_merged_blocks
);
println!(" Machine CSE: {} eliminated", self.cse.eliminated);
println!(" Machine LICM: {} hoisted", self.licm.hoisted);
println!(" Machine Sinking: {} sunk", self.sinking.sunk);
println!(" Machine Combiner: {} combined", self.combiner.combined);
println!(
" Trace Metrics: {} traces, {} total cycles",
self.trace_metrics.traces_analyzed, self.trace_metrics.total_cycles
);
println!(
" If-Conversion: {} converted, {} branches removed",
self.if_conversion.converted, self.if_conversion.removed_branches
);
println!(
" PHI Opt: {} eliminated, {} edges split",
self.phi_opt.phis_eliminated, self.phi_opt.critical_edges_split
);
println!(
" Late Cleanup: {} removed, {} copies propagated",
self.late_cleanup.removed, self.late_cleanup.copy_propagated
);
println!(
" Bundler: {} bundles, {} instructions",
self.bundler.bundles_created, self.bundler.instructions_bundled
);
println!(
" Verifier: {} errors, {} warnings",
self.verifier.errors.len(),
self.verifier.warnings.len()
);
println!(
" Reset: {} resets",
self.resetter.resets_performed
);
}
}
impl Default for MachineOptPipeline {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_mf() -> MachineFunction {
let mut mf = MachineFunction::new("test_func");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
entry.instructions.push(
MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2)))
.with_operand(MachineOperand::Imm(0)),
);
entry.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
entry.successors.push(1);
mf.add_block(entry);
let mut bb1 = MachineBasicBlock::new(1, "bb1");
bb1.predecessors.push(0);
bb1.instructions.push(
MachineInstr::new(0)
.with_flag(|f| f.is_return = true)
.with_flag(|f| f.is_terminator = true),
);
mf.add_block(bb1);
mf
}
#[test]
fn test_machine_block_placement_new() {
let mut placement = MachineBlockPlacement::new();
let mut mf = create_test_mf();
let result = placement.run(&mut mf);
assert!(result >= 1);
}
#[test]
fn test_machine_branch_folding_new() {
let mut folding = MachineBranchFolding::new();
let mut mf = create_test_mf();
let result = folding.run(&mut mf);
assert!(result <= 10); }
#[test]
fn test_machine_cse_eliminates_duplicates() {
let mut cse = MachineCSE::new();
let mut mf = MachineFunction::new("test_cse");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
let instr1 = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2)));
let instr2 = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2)));
entry.instructions.push(instr1);
entry.instructions.push(instr2);
entry.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
entry.successors.push(1);
mf.add_block(entry);
mf.add_block(MachineBasicBlock::new(1, "exit"));
let result = cse.run(&mut mf);
assert!(result >= 1, "CSE should eliminate duplicate instruction");
}
#[test]
fn test_machine_licm_hoists_invariant() {
let mut licm = MachineLICM::new();
let mut mf = MachineFunction::new("test_licm");
let mut header = MachineBasicBlock::new(0, "header");
header.is_entry = true;
header.instructions.push(
MachineInstr::new(1) .with_operand(MachineOperand::Reg(MCRegister(10)))
.with_operand(MachineOperand::Imm(5)),
);
header.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(0)),
);
header.successors.push(0);
header.predecessors.push(0);
mf.add_block(header);
let result = licm.run(&mut mf);
let _ = result;
}
#[test]
fn test_machine_sinking() {
let mut sinking = MachineSinking::new();
let mut mf = create_test_mf();
let result = sinking.run(&mut mf);
assert!(result <= 10);
}
#[test]
fn test_machine_combiner() {
let mut combiner = MachineCombiner::new();
let mut mf = MachineFunction::new("test_combiner");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
entry.instructions.push(
MachineInstr::new(10)
.with_flag(|f| f.is_copy = true)
.with_operand(MachineOperand::Reg(MCRegister(2)))
.with_operand(MachineOperand::Reg(MCRegister(1))),
);
entry.instructions.push(
MachineInstr::new(10)
.with_flag(|f| f.is_copy = true)
.with_operand(MachineOperand::Reg(MCRegister(3)))
.with_operand(MachineOperand::Reg(MCRegister(2))),
);
entry.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
entry.successors.push(1);
mf.add_block(entry);
mf.add_block(MachineBasicBlock::new(1, "exit"));
let result = combiner.run(&mut mf);
assert!(result >= 1, "Combiner should eliminate redundant copy");
}
#[test]
fn test_machine_trace_metrics() {
let mut metrics = MachineTraceMetrics::new();
let mf = create_test_mf();
let cycles = metrics.analyze_trace(&mf, &[0, 1]);
assert!(cycles > 0, "Trace should have positive cycle count");
assert_eq!(metrics.traces_analyzed, 1);
}
#[test]
fn test_early_if_conversion() {
let mut if_conv = EarlyIfConversion::new();
let mut mf = MachineFunction::new("test_ifconv");
let mut cond = MachineBasicBlock::new(0, "cond");
cond.is_entry = true;
cond.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1))
.with_operand(MachineOperand::Block(2)),
);
cond.successors.push(1);
cond.successors.push(2);
mf.add_block(cond);
let mut then_bb = MachineBasicBlock::new(1, "then");
then_bb.predecessors.push(0);
then_bb.instructions.push(MachineInstr::new(1).with_operand(MachineOperand::Imm(1)));
then_bb.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(3)),
);
then_bb.successors.push(3);
mf.add_block(then_bb);
let mut else_bb = MachineBasicBlock::new(2, "else");
else_bb.predecessors.push(0);
else_bb.instructions.push(MachineInstr::new(1).with_operand(MachineOperand::Imm(0)));
else_bb.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(3)),
);
else_bb.successors.push(3);
mf.add_block(else_bb);
mf.add_block(MachineBasicBlock::new(3, "merge"));
let result = if_conv.run(&mut mf);
assert!(result >= 0);
}
#[test]
fn test_optimize_phis_eliminates_identical() {
let mut phi_opt = OptimizePHIs::new();
let mut mf = MachineFunction::new("test_phi");
let mut bb = MachineBasicBlock::new(0, "entry");
bb.is_entry = true;
bb.instructions.push(
MachineInstr::new(20)
.with_flag(|f| f.is_phi = true)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(1))),
);
bb.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
bb.successors.push(1);
mf.add_block(bb);
mf.add_block(MachineBasicBlock::new(1, "exit"));
let result = phi_opt.run(&mut mf);
assert!(result >= 1, "Should eliminate PHI with identical values");
}
#[test]
fn test_machine_late_instrs_cleanup() {
let mut cleanup = MachineLateInstrsCleanup::new();
let mut mf = MachineFunction::new("test_cleanup");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
entry.instructions.push(
MachineInstr::new(10)
.with_flag(|f| f.is_copy = true)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(1))),
);
entry.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
entry.successors.push(1);
mf.add_block(entry);
mf.add_block(MachineBasicBlock::new(1, "exit"));
let result = cleanup.run(&mut mf);
assert!(result >= 1, "Should remove identity copy");
}
#[test]
fn test_bundle_machine_cfg() {
let mut bundler = BundleMachineCFG::new();
let mut mf = MachineFunction::new("test_bundle");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
for _ in 0..6 {
entry.instructions.push(
MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2))),
);
}
entry.instructions.push(
MachineInstr::new(8)
.with_flag(|f| {
f.is_terminator = true;
f.is_branch = true;
})
.with_operand(MachineOperand::Block(1)),
);
entry.successors.push(1);
mf.add_block(entry);
mf.add_block(MachineBasicBlock::new(1, "exit"));
let result = bundler.run(&mut mf);
assert!(result >= 1, "Should create at least one bundle");
}
#[test]
fn test_machine_verifier() {
let mut verifier = MachineVerifier::new();
let mf = create_test_mf();
let result = verifier.verify(&mf);
assert!(result, "Valid MF should pass verification");
}
#[test]
fn test_machine_verifier_detects_missing_terminator() {
let mut verifier = MachineVerifier::new();
let mut mf = MachineFunction::new("test_bad");
let mut entry = MachineBasicBlock::new(0, "entry");
entry.is_entry = true;
entry.instructions.push(MachineInstr::new(1));
mf.add_block(entry);
let result = verifier.verify(&mf);
assert!(!result, "Should detect missing terminator");
}
#[test]
fn test_reset_machine_function() {
let mut resetter = ResetMachineFunction::new();
let mut mf = create_test_mf();
resetter.reset(&mut mf);
assert!(mf.blocks.is_empty());
assert_eq!(resetter.resets_performed, 1);
}
#[test]
fn test_machine_opt_pipeline() {
let mut pipeline = MachineOptPipeline::new();
let mut mf = create_test_mf();
let changed = pipeline.run(&mut mf);
let _ = changed;
}
#[test]
fn test_machine_instr_identical() {
let a = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2)));
let b = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)))
.with_operand(MachineOperand::Reg(MCRegister(2)));
assert!(a.is_identical_to(&b));
}
#[test]
fn test_machine_instr_not_identical() {
let a = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(1)));
let b = MachineInstr::new(1)
.with_operand(MachineOperand::Reg(MCRegister(2)));
assert!(!a.is_identical_to(&b));
}
#[test]
fn test_machine_basic_block_creation() {
let bb = MachineBasicBlock::new(42, "test_bb");
assert_eq!(bb.id, 42);
assert_eq!(bb.name, "test_bb");
assert!(bb.instructions.is_empty());
}
#[test]
fn test_machine_function_creation() {
let mf = MachineFunction::new("my_func");
assert_eq!(mf.name, "my_func");
assert!(mf.blocks.is_empty());
}
}