From ab7d884fd46b8b0d37c9596755007086035e65b3 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 1 Jun 2025 06:31:29 +0000 Subject: [PATCH] HW3~4 Refactoring --- src/opt/mem2reg.rs | 275 +------------------------------------ src/opt/opt_utils.rs | 293 ++++++++++++++++++++++++++++++++++++++++ src/opt/simplify_cfg.rs | 74 +--------- 3 files changed, 297 insertions(+), 345 deletions(-) diff --git a/src/opt/mem2reg.rs b/src/opt/mem2reg.rs index 3e0e8ba..9bc285e 100644 --- a/src/opt/mem2reg.rs +++ b/src/opt/mem2reg.rs @@ -146,189 +146,6 @@ fn mark_as_inpromotable(inpromotables: &mut HashSet, operand: &Operand) { } } -fn make_cfg(fdef: &FunctionDefinition) -> HashMap> { - fdef.blocks - .iter() - .map(|(bid, block)| { - let mut args = Vec::new(); - match &block.exit { - BlockExit::Jump { arg } => args.push(arg.clone()), - BlockExit::ConditionalJump { - arg_then, arg_else, .. - } => { - args.push(arg_then.clone()); - args.push(arg_else.clone()); - } - BlockExit::Switch { default, cases, .. } => { - args.push(default.clone()); - for (_, arg) in cases { - args.push(arg.clone()); - } - } - _ => (), - } - (*bid, args) - }) - .collect() -} - -fn reverse_cfg(cfg: &HashMap>) -> HashMap> { - let mut result = HashMap::new(); - for (bid, jumps) in cfg { - for jump in jumps { - result - .entry(jump.bid) - .or_insert_with(Vec::new) - .push((*bid, jump.clone())); - } - } - result -} - -fn traverse_rpo(bid_init: BlockId, cfg: &HashMap>) -> Vec { - fn traverse_po( - bid: BlockId, - cfg: &HashMap>, - visited: &mut HashSet, - post_order: &mut Vec, - ) { - for jump in &cfg[&bid] { - if visited.insert(jump.bid) { - traverse_po(jump.bid, cfg, visited, post_order); - } - } - post_order.push(bid); - } - - let mut visited = HashSet::new(); - let _ = visited.insert(bid_init); - let mut order = Vec::new(); - - traverse_po(bid_init, cfg, &mut visited, &mut order); - order.reverse(); - order -} - -struct Domtree { - idoms: HashMap, - inverse_idoms: HashMap>, - frontiers: HashMap>, -} - -impl Domtree { - fn new( - bid_init: BlockId, - cfg: &HashMap>, - reverse_cfg: &HashMap>, - ) -> Self { - let rpo = traverse_rpo(bid_init, cfg); - let irpo = rpo.iter().enumerate().map(|(i, bid)| (*bid, i)).collect(); - let mut idoms: HashMap = HashMap::new(); - - loop { - let mut changed = false; - - for bid in &rpo { - if *bid == bid_init { - continue; - } - - let mut idom = None; - for (bid_pred, _) in &reverse_cfg[bid] { - if *bid_pred == bid_init || idoms.contains_key(bid_pred) { - idom = Some(intersect_idom(idom, *bid_pred, &irpo, &idoms)); - } - } - - if let Some(idom) = idom { - let _ = idoms - .entry(*bid) - .and_modify(|v| { - if *v != idom { - changed = true; - *v = idom; - } - }) - .or_insert_with(|| { - changed = true; - idom - }); - } - } - - if !changed { - break; - } - } - - let mut inverse_idoms = HashMap::new(); - for (bid, idom) in &idoms { - let _ = inverse_idoms - .entry(*idom) - .or_insert_with(HashSet::new) - .insert(*bid); - } - - let mut frontiers = HashMap::new(); - for (bid, preds) in reverse_cfg.iter().filter(|(_, preds)| preds.len() > 1) { - let idom = if let Some(idom) = idoms.get(bid) { - idom - } else { - continue; - }; - for (bid_pred, _) in preds { - let mut runner = *bid_pred; - while !Self::dominates(&idoms, runner, *bid) { - frontiers.entry(runner).or_insert_with(Vec::new).push(*bid); - runner = idoms[&runner]; - } - } - } - - Self { - idoms, - inverse_idoms, - frontiers, - } - } - - fn dominates(idoms: &HashMap, bid1: BlockId, mut bid2: BlockId) -> bool { - loop { - bid2 = if let Some(idom2) = idoms.get(&bid2) { - *idom2 - } else { - return false; - }; - if bid1 == bid2 { - return true; - } - } - } - - fn frontiers(&self, bid: &BlockId) -> Option<&Vec> { - self.frontiers.get(bid) - } -} - -fn intersect_idom( - lhs: Option, - mut rhs: BlockId, - irpo: &HashMap, - idoms: &HashMap, -) -> BlockId { - let mut lhs = if let Some(lhs) = lhs { lhs } else { return rhs }; - loop { - if lhs == rhs { - return lhs; - } - match irpo[&lhs].cmp(&irpo[&rhs]) { - Ordering::Less => rhs = idoms[&rhs], - Ordering::Greater => lhs = idoms[&lhs], - Ordering::Equal => unreachable!(), - } - } -} - type PhinodeInfo<'a> = ( &'a HashMap<(usize, BlockId), usize>, &'a HashMap>, @@ -405,7 +222,7 @@ fn traverse_po( _ => (), } - if let Some(bids_successors) = domtree.inverse_idoms.get(&bid) { + if let Some(bids_successors) = domtree.successors(&bid) { for bid_successor in bids_successors { traverse_po( *bid_successor, @@ -436,7 +253,7 @@ fn find_end_value( if let Some(phinode_index) = phinode_indexes.get(&(aid, bid)) { Operand::register(RegisterId::arg(bid, *phinode_index), dtype) - } else if let Some(bid_idom) = domtree.idoms.get(&bid) { + } else if let Some(bid_idom) = domtree.idom(&bid) { find_end_value( aid, dtype, @@ -461,91 +278,3 @@ fn fill_jump_args( } } } - -fn replace_instruction_operands( - inst: &mut Instruction, - replaces: &HashMap, -) -> bool { - match inst { - Instruction::BinOp { lhs, rhs, .. } => { - let a = replace_operand(lhs, replaces); - let b = replace_operand(rhs, replaces); - a || b - } - Instruction::UnaryOp { operand, .. } => replace_operand(operand, replaces), - Instruction::Store { ptr, value } => { - let a = replace_operand(ptr, replaces); - let b = replace_operand(value, replaces); - a || b - } - Instruction::Load { ptr } => replace_operand(ptr, replaces), - Instruction::Call { callee, args, .. } => { - let mut changed = replace_operand(callee, replaces); - for arg in args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - changed - } - Instruction::TypeCast { value, .. } => replace_operand(value, replaces), - Instruction::GetElementPtr { ptr, offset, .. } => { - let a = replace_operand(ptr, replaces); - let b = replace_operand(offset, replaces); - a || b - } - _ => unreachable!(), - } -} - -fn replace_exit_operands(exit: &mut BlockExit, replaces: &HashMap) -> bool { - match exit { - BlockExit::Jump { arg } => { - let mut changed = false; - for arg in arg.args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - changed - } - BlockExit::ConditionalJump { - condition, - arg_then, - arg_else, - } => { - let mut changed = replace_operand(condition, replaces); - for arg in arg_then.args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - for arg in arg_else.args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - changed - } - BlockExit::Switch { - value, - default, - cases, - } => { - let mut changed = replace_operand(value, replaces); - for arg in default.args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - for (_, arg) in cases { - for arg in arg.args.iter_mut() { - changed = replace_operand(arg, replaces) || changed; - } - } - changed - } - BlockExit::Return { value } => replace_operand(value, replaces), - _ => false, - } -} - -fn replace_operand(operand: &mut Operand, replaces: &HashMap) -> bool { - if let Operand::Register { rid, .. } = operand { - if let Some(new_operand) = replaces.get(rid) { - *operand = new_operand.clone(); - return true; - } - } - false -} diff --git a/src/opt/opt_utils.rs b/src/opt/opt_utils.rs index fd0dc2d..49d1c7b 100644 --- a/src/opt/opt_utils.rs +++ b/src/opt/opt_utils.rs @@ -3,3 +3,296 @@ //! You can freely add utilities commonly used in the implementation of multiple optimizations here. #![allow(dead_code)] + +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet}, +}; + +use crate::ir::*; + +pub(crate) fn make_cfg(fdef: &FunctionDefinition) -> HashMap> { + fdef.blocks + .iter() + .map(|(bid, block)| { + let mut args = Vec::new(); + match &block.exit { + BlockExit::Jump { arg } => args.push(arg.clone()), + BlockExit::ConditionalJump { + arg_then, arg_else, .. + } => { + args.push(arg_then.clone()); + args.push(arg_else.clone()); + } + BlockExit::Switch { default, cases, .. } => { + args.push(default.clone()); + for (_, arg) in cases { + args.push(arg.clone()); + } + } + _ => (), + } + (*bid, args) + }) + .collect() +} + +pub(crate) fn reverse_cfg( + cfg: &HashMap>, +) -> HashMap> { + let mut result = HashMap::new(); + for (bid, jumps) in cfg { + for jump in jumps { + result + .entry(jump.bid) + .or_insert_with(Vec::new) + .push((*bid, jump.clone())); + } + } + result +} +fn traverse_rpo(bid_init: BlockId, cfg: &HashMap>) -> Vec { + fn traverse_po( + bid: BlockId, + cfg: &HashMap>, + visited: &mut HashSet, + post_order: &mut Vec, + ) { + for jump in &cfg[&bid] { + if visited.insert(jump.bid) { + traverse_po(jump.bid, cfg, visited, post_order); + } + } + post_order.push(bid); + } + + let mut visited = HashSet::new(); + let _ = visited.insert(bid_init); + let mut order = Vec::new(); + + traverse_po(bid_init, cfg, &mut visited, &mut order); + order.reverse(); + order +} + +pub(crate) struct Domtree { + idoms: HashMap, + inverse_idoms: HashMap>, + frontiers: HashMap>, +} + +impl Domtree { + pub(crate) fn new( + bid_init: BlockId, + cfg: &HashMap>, + reverse_cfg: &HashMap>, + ) -> Self { + let rpo = traverse_rpo(bid_init, cfg); + let irpo = rpo.iter().enumerate().map(|(i, bid)| (*bid, i)).collect(); + let mut idoms: HashMap = HashMap::new(); + + loop { + let mut changed = false; + + for bid in &rpo { + if *bid == bid_init { + continue; + } + + let mut idom = None; + for (bid_pred, _) in &reverse_cfg[bid] { + if *bid_pred == bid_init || idoms.contains_key(bid_pred) { + idom = Some(intersect_idom(idom, *bid_pred, &irpo, &idoms)); + } + } + + if let Some(idom) = idom { + let _ = idoms + .entry(*bid) + .and_modify(|v| { + if *v != idom { + changed = true; + *v = idom; + } + }) + .or_insert_with(|| { + changed = true; + idom + }); + } + } + + if !changed { + break; + } + } + + let mut inverse_idoms = HashMap::new(); + for (bid, idom) in &idoms { + let _ = inverse_idoms + .entry(*idom) + .or_insert_with(HashSet::new) + .insert(*bid); + } + + let mut frontiers = HashMap::new(); + for (bid, preds) in reverse_cfg.iter().filter(|(_, preds)| preds.len() > 1) { + let idom = if let Some(idom) = idoms.get(bid) { + idom + } else { + continue; + }; + for (bid_pred, _) in preds { + let mut runner = *bid_pred; + while !Self::dominates(&idoms, runner, *bid) { + frontiers.entry(runner).or_insert_with(Vec::new).push(*bid); + runner = idoms[&runner]; + } + } + } + + Self { + idoms, + inverse_idoms, + frontiers, + } + } + + fn dominates(idoms: &HashMap, bid1: BlockId, mut bid2: BlockId) -> bool { + loop { + bid2 = if let Some(idom2) = idoms.get(&bid2) { + *idom2 + } else { + return false; + }; + if bid1 == bid2 { + return true; + } + } + } + + pub(crate) fn idom(&self, bid: &BlockId) -> Option<&BlockId> { + self.idoms.get(bid) + } + + pub(crate) fn successors(&self, bid: &BlockId) -> Option<&HashSet> { + self.inverse_idoms.get(bid) + } + + pub(crate) fn frontiers(&self, bid: &BlockId) -> Option<&Vec> { + self.frontiers.get(bid) + } +} + +fn intersect_idom( + lhs: Option, + mut rhs: BlockId, + irpo: &HashMap, + idoms: &HashMap, +) -> BlockId { + let mut lhs = if let Some(lhs) = lhs { lhs } else { return rhs }; + loop { + if lhs == rhs { + return lhs; + } + match irpo[&lhs].cmp(&irpo[&rhs]) { + Ordering::Less => rhs = idoms[&rhs], + Ordering::Greater => lhs = idoms[&lhs], + Ordering::Equal => unreachable!(), + } + } +} + +pub(crate) fn replace_instruction_operands( + inst: &mut Instruction, + replaces: &HashMap, +) -> bool { + match inst { + Instruction::BinOp { lhs, rhs, .. } => { + let a = replace_operand(lhs, replaces); + let b = replace_operand(rhs, replaces); + a || b + } + Instruction::UnaryOp { operand, .. } => replace_operand(operand, replaces), + Instruction::Store { ptr, value } => { + let a = replace_operand(ptr, replaces); + let b = replace_operand(value, replaces); + a || b + } + Instruction::Load { ptr } => replace_operand(ptr, replaces), + Instruction::Call { callee, args, .. } => { + let mut changed = replace_operand(callee, replaces); + for arg in args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + changed + } + Instruction::TypeCast { value, .. } => replace_operand(value, replaces), + Instruction::GetElementPtr { ptr, offset, .. } => { + let a = replace_operand(ptr, replaces); + let b = replace_operand(offset, replaces); + a || b + } + _ => unreachable!(), + } +} + +pub(crate) fn replace_exit_operands( + exit: &mut BlockExit, + replaces: &HashMap, +) -> bool { + match exit { + BlockExit::Jump { arg } => { + let mut changed = false; + for arg in arg.args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + changed + } + BlockExit::ConditionalJump { + condition, + arg_then, + arg_else, + } => { + let mut changed = replace_operand(condition, replaces); + for arg in arg_then.args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + for arg in arg_else.args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + changed + } + BlockExit::Switch { + value, + default, + cases, + } => { + let mut changed = replace_operand(value, replaces); + for arg in default.args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + for (_, arg) in cases { + for arg in arg.args.iter_mut() { + changed = replace_operand(arg, replaces) || changed; + } + } + changed + } + BlockExit::Return { value } => replace_operand(value, replaces), + _ => false, + } +} + +pub(crate) fn replace_operand( + operand: &mut Operand, + replaces: &HashMap, +) -> bool { + if let Operand::Register { rid, .. } = operand { + if let Some(new_operand) = replaces.get(rid) { + *operand = new_operand.clone(); + return true; + } + } + false +} diff --git a/src/opt/simplify_cfg.rs b/src/opt/simplify_cfg.rs index 7810e88..635bc74 100644 --- a/src/opt/simplify_cfg.rs +++ b/src/opt/simplify_cfg.rs @@ -117,9 +117,9 @@ impl Optimize for SimplifyCfgMerge { for (bid, block) in &mut code.blocks { for inst in block.instructions.iter_mut() { - replace_instruction_operands(inst, &replaces); + let _ = replace_instruction_operands(inst, &replaces); } - replace_exit_operands(&mut block.exit, &replaces); + let _ = replace_exit_operands(&mut block.exit, &replaces); } !merged_to.is_empty() @@ -235,73 +235,3 @@ impl SimplifyCfgEmpty { } } } - -fn make_cfg(fdef: &FunctionDefinition) -> HashMap> { - fdef.blocks - .iter() - .map(|(bid, block)| { - let mut args = Vec::new(); - match &block.exit { - BlockExit::Jump { arg } => args.push(arg.clone()), - BlockExit::ConditionalJump { - arg_then, arg_else, .. - } => { - args.push(arg_then.clone()); - args.push(arg_else.clone()); - } - BlockExit::Switch { default, cases, .. } => { - args.push(default.clone()); - for (_, arg) in cases { - args.push(arg.clone()); - } - } - _ => (), - } - (*bid, args) - }) - .collect() -} - -fn replace_instruction_operands(inst: &mut Instruction, replaces: &HashMap) { - match inst { - Instruction::BinOp { lhs, rhs, .. } => { - replace_operand(lhs, replaces); - replace_operand(rhs, replaces); - } - Instruction::UnaryOp { operand, .. } => replace_operand(operand, replaces), - Instruction::Store { ptr, value } => { - replace_operand(ptr, replaces); - replace_operand(value, replaces); - } - Instruction::Load { ptr } => replace_operand(ptr, replaces), - Instruction::Call { callee, args, .. } => { - replace_operand(callee, replaces); - for arg in args.iter_mut() { - replace_operand(arg, replaces); - } - } - Instruction::TypeCast { value, .. } => replace_operand(value, replaces), - Instruction::GetElementPtr { ptr, offset, .. } => { - replace_operand(ptr, replaces); - replace_operand(offset, replaces); - } - _ => unreachable!(), - } -} - -fn replace_exit_operands(exit: &mut BlockExit, replaces: &HashMap) { - match exit { - BlockExit::ConditionalJump { condition, .. } => replace_operand(condition, replaces), - BlockExit::Switch { value, .. } => replace_operand(value, replaces), - BlockExit::Return { value } => replace_operand(value, replaces), - _ => (), - } -} - -fn replace_operand(operand: &mut Operand, replaces: &HashMap) { - if let Operand::Register { rid, .. } = operand { - if let Some(new_operand) = replaces.get(rid) { - *operand = new_operand.clone(); - } - } -}