From 589303027fec2ebd5e887378dd9d4f0dcc00b2be Mon Sep 17 00:00:00 2001 From: Solo-steven Date: Sat, 12 Oct 2024 02:01:42 +0800 Subject: [PATCH] feat: implement dce pass and add test case for pass --- .../ir_optimizer/anaylsis/post_domtree/mod.rs | 4 +- .../src/ir_optimizer/pass/dce/debugger.rs | 50 ++++ .../src/ir_optimizer/pass/dce/mod.rs | 187 +++++++++++++++ .../src/ir_optimizer/pass/dce/util.rs | 58 +++++ .../optimizer/src/ir_optimizer/pass/mod.rs | 1 + compilers/rustyc/optimizer/src/main.rs | 220 ++++++++++++++++-- .../rustyc/optimizer/tests/build_ir_graph.rs | 178 +++++++++++++- .../ir_optimizer/dce/diamond/after.txt | 21 ++ .../ir_optimizer/dce/diamond/before.txt | 22 ++ .../ir_optimizer/dce/diamond/output.txt | 10 + .../ir_optimizer/dce/diamond_like/after.txt | 19 ++ .../ir_optimizer/dce/diamond_like/before.txt | 20 ++ .../ir_optimizer/dce/diamond_like/output.txt | 10 + .../ir_optimizer/dce/if_change_cfg/after.txt | 12 + .../ir_optimizer/dce/if_change_cfg/before.txt | 18 ++ .../ir_optimizer/dce/if_change_cfg/output.txt | 5 + .../ir_optimizer/dce/if_critical/after.txt | 18 ++ .../ir_optimizer/dce/if_critical/before.txt | 19 ++ .../ir_optimizer/dce/if_critical/output.txt | 10 + .../tests/ir_optimizer_test_runner.rs | 53 +++++ 20 files changed, 906 insertions(+), 29 deletions(-) create mode 100644 compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/debugger.rs create mode 100644 compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/mod.rs create mode 100644 compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/util.rs create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/after.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/before.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/output.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/after.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/before.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/output.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/after.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/before.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/output.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/after.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/before.txt create mode 100644 compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/output.txt diff --git a/compilers/rustyc/optimizer/src/ir_optimizer/anaylsis/post_domtree/mod.rs b/compilers/rustyc/optimizer/src/ir_optimizer/anaylsis/post_domtree/mod.rs index beeabe92..51ed420e 100644 --- a/compilers/rustyc/optimizer/src/ir_optimizer/anaylsis/post_domtree/mod.rs +++ b/compilers/rustyc/optimizer/src/ir_optimizer/anaylsis/post_domtree/mod.rs @@ -7,8 +7,8 @@ use std::mem::replace; /// Table for record every block's dom and idom and dom-frontier pub struct PostDomTable { - table: HashMap, - extra_exit_block: Option, + pub table: HashMap, + pub extra_exit_block: Option, } /// Entry for record dom, idom and dom-frontier for a bb. #[derive(Debug, Clone, PartialEq)] diff --git a/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/debugger.rs b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/debugger.rs new file mode 100644 index 00000000..3cdcbab3 --- /dev/null +++ b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/debugger.rs @@ -0,0 +1,50 @@ +use super::DCEPass; +use crate::ir::function::Function; +use crate::ir_optimizer::pass::DebuggerPass; +use crate::ir_optimizer::print_table_helper::{print_divider, print_header, sort_inst_ids}; + +impl<'a> DebuggerPass for DCEPass<'a> { + fn debugger(&self, _function: &Function) -> String { + if let Some(map) = &self.debug_mark_inst_map_string { + // get max len of instruction + let mut max_len_of_inst = 0 as usize; + for inst_string in map.values() { + if inst_string.len() > max_len_of_inst { + max_len_of_inst = inst_string.len(); + } + } + let row_len = if "Marked Inst".len() > max_len_of_inst { + "Marked Inst".len() + } else { + max_len_of_inst + } + 10; + let mut output_code = String::new(); + output_code.push_str(print_divider(row_len).as_str()); + output_code.push_str(print_header("Marked Inst", row_len).as_str()); + output_code.push_str(print_divider(row_len).as_str()); + print_divider(row_len); + let sorted_insts = sort_inst_ids(self.marked_insts.iter().map(|k| k.clone()).collect::>()); + for inst in sorted_insts { + let inst_string = map.get(&inst).unwrap(); + output_code.push_str(print_header(&inst_string, row_len).as_str()); + } + output_code + } else { + String::new() + } + } +} + +impl<'a> DCEPass<'a> { + pub(super) fn store_debuuger_info(&mut self, function: &Function) { + if let Some(map) = &mut self.debug_mark_inst_map_string { + for inst in self.marked_insts.clone() { + let inst_data = function.instructions.get(&inst).unwrap(); + let mut output = String::new(); + function.print_inst(&mut output, inst_data); + output = String::from(output.trim()); + map.insert(inst, output); + } + } + } +} diff --git a/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/mod.rs b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/mod.rs new file mode 100644 index 00000000..875f3d14 --- /dev/null +++ b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/mod.rs @@ -0,0 +1,187 @@ +mod debugger; +mod util; + +use crate::ir::function::{BasicBlock, Function}; +use crate::ir::instructions::{Instruction, InstructionData}; +use crate::ir_optimizer::anaylsis::use_def_chain::DefKind; +use crate::ir_optimizer::anaylsis::{post_domtree::PostDomTable, use_def_chain::UseDefTable}; +use crate::ir_optimizer::pass::OptimizerPass; +use std::collections::{HashMap, HashSet}; +use std::mem::replace; +use util::get_rhs_values; + +pub struct DCEPass<'a> { + worklist: Vec, + marked_insts: HashSet, + marked_blocks: HashSet, + use_def_table: &'a UseDefTable, + post_dom_table: &'a PostDomTable, + // Ad-hoc data structure only for debug usage + debug_mark_inst_map_string: Option>, +} + +impl<'a> OptimizerPass for DCEPass<'a> { + fn process(&mut self, function: &mut Function) { + self.mark_pass(function); + self.store_debuuger_info(function); + self.swap_pass(function); + self.remove_unreach_block(function); + } +} + +impl<'a> DCEPass<'a> { + pub fn new(use_def_table: &'a UseDefTable, post_dom_table: &'a PostDomTable, debugger: bool) -> Self { + Self { + worklist: Default::default(), + marked_insts: Default::default(), + marked_blocks: Default::default(), + use_def_table, + post_dom_table, + debug_mark_inst_map_string: if debugger { Some(Default::default()) } else { None }, + } + } + fn mark_inst_if_need(&mut self, inst: Instruction) { + if !self.marked_insts.contains(&inst) { + self.worklist.push(inst.clone()); + self.marked_insts.insert(inst); + } + } + fn mark_blocks_if_need(&mut self, block: &BasicBlock) { + self.marked_blocks.insert(block.clone()); + } + fn mark_branch_inst_in_post_df(&mut self, function: &Function, block: &BasicBlock) { + for bb in &self.post_dom_table.table.get(block).unwrap().post_dom_frontier { + for inst_in_post_df in &function.blocks.get(bb).unwrap().instructions { + match function.instructions.get(inst_in_post_df).unwrap() { + InstructionData::Jump { .. } | InstructionData::BrIf { .. } => { + self.mark_inst_if_need(inst_in_post_df.clone()); + self.mark_blocks_if_need(bb); + } + _ => {} + } + } + } + } + fn mark_pass(&mut self, function: &Function) { + // Init stage, mark all return and store instruction as critical + for (inst, inst_data) in &function.instructions { + match *inst_data { + InstructionData::Ret { .. } | InstructionData::StoreRegister { .. } | InstructionData::Call { .. } => { + self.mark_inst_if_need(inst.clone()); + } + _ => {} + } + } + // propagation stage, get all def of instruction as mark + while self.worklist.len() > 0 { + let inst = self.worklist.pop().unwrap(); + // mark the usage + let rhs_values = get_rhs_values(function.instructions.get(&inst).unwrap()); + for rhs_val in rhs_values { + if let Some(def_kind) = self.use_def_table.1.get(&rhs_val) { + if let DefKind::InternalDef(inst) = def_kind { + self.mark_inst_if_need(inst.clone()); + self.mark_blocks_if_need(function.get_block_from_inst(inst).unwrap()); + } + } + } + if let Some(extra_bb) = &self.post_dom_table.extra_exit_block { + self.mark_branch_inst_in_post_df(function, extra_bb); + } + // mark is df + let block_of_inst = function.get_block_from_inst(&inst).unwrap(); + self.mark_branch_inst_in_post_df(function, block_of_inst); + } + } + fn swap_pass(&self, function: &mut Function) { + let mut need_to_remove_insts = Vec::new(); + for inst in function.instructions.keys() { + if !self.marked_insts.contains(inst) { + need_to_remove_insts.push(inst.clone()); + } + } + for inst in need_to_remove_insts { + let block_of_inst = function.get_block_from_inst(&inst).unwrap().clone(); + let is_jump = match function.instructions.get(&inst).unwrap() { + InstructionData::Jump { .. } => true, + InstructionData::BrIf { .. } => true, + _ => false, + }; + function.remove_inst_from_block(&block_of_inst, &inst); + + if is_jump { + function.switch_to_block(block_of_inst); + let nearest_post_idom = self.find_nearest_post_dom(function, &block_of_inst); + function.build_jump_inst(nearest_post_idom); + // adjust block connection + let block_data = function.blocks.get_mut(&block_of_inst).unwrap(); + let origin_successors = replace(&mut block_data.successor, Vec::with_capacity(1)); + // remove then add, since successor might already contain the `block_of_inst`, add and remove it will + // make `block_of_inst` be remove eventually. + for successor in origin_successors { + let successor_block_data = function.blocks.get_mut(&successor).unwrap(); + let predecessor_of_sucessor = replace(&mut successor_block_data.predecessor, Default::default()); + let next_predecessor_of_sucessor = predecessor_of_sucessor + .into_iter() + .filter(|bb| *bb != block_of_inst) + .collect::>(); + successor_block_data.predecessor = next_predecessor_of_sucessor; + } + function.connect_block(block_of_inst, nearest_post_idom); + } + } + // println!("{:#?}", function.blocks); + } + fn find_nearest_post_dom(&self, function: &mut Function, bb: &BasicBlock) -> BasicBlock { + let entry = self.post_dom_table.table.get(bb).unwrap(); + let post_dom_set = &entry.post_dom; + let block_data = function.blocks.get(bb).unwrap(); + let mut successors = block_data.successor.clone(); + loop { + if successors.len() == 0 { + panic!(); + } + // frist iterate over the predecessor to find if predecessor beem + // have dominate bb or not. + for successor in &successors { + if post_dom_set.contains(successor) && self.marked_blocks.contains(successor) { + return successor.clone(); + } + } + // if we can not find idom in cuurent predecessor. we contine find idom + // in predecessor's predecessor + let mut next_successor = Vec::new(); + for successor in &successors { + for successor_of_successor in &function.blocks.get(successor).unwrap().successor { + if !next_successor.contains(successor_of_successor) { + next_successor.push(successor_of_successor.clone()); + } + } + } + successors = next_successor; + } + } + fn remove_unreach_block(&self, function: &mut Function) { + let mut visit_block = HashSet::new(); + let mut worklist = vec![function.entry_block[0].clone()]; + while worklist.len() > 0 { + let current = worklist.pop().unwrap(); + if visit_block.contains(¤t) { + continue; + } + visit_block.insert(current); + for &sucessor in &function.blocks.get(¤t).unwrap().successor { + worklist.push(sucessor); + } + } + let mut blocks_need_remove = Vec::new(); + for (block, _) in &function.blocks { + if !visit_block.contains(&block) { + blocks_need_remove.push(block.clone()); + } + } + for block in blocks_need_remove { + function.blocks.remove(&block); + } + } +} diff --git a/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/util.rs b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/util.rs new file mode 100644 index 00000000..0e830387 --- /dev/null +++ b/compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/util.rs @@ -0,0 +1,58 @@ +use crate::ir::{instructions::InstructionData, value::Value}; + +pub(super) fn get_rhs_values(inst: &InstructionData) -> Vec { + match inst { + #[rustfmt::skip] + InstructionData::Add { src1, src2, .. } | + InstructionData::Sub { src1, src2, .. } | + InstructionData::Mul { src1, src2, .. } | + InstructionData::Divide { src1, src2, .. } | + InstructionData::Reminder { src1, src2, .. } | + InstructionData::FAdd { src1, src2, .. } | + InstructionData::FSub { src1, src2, .. } | + InstructionData::FMul { src1, src2, .. } | + InstructionData::FDivide { src1, src2, .. } | + InstructionData::FReminder { src1, src2, .. } | + InstructionData::BitwiseAnd { src1, src2, .. } | + InstructionData::BitwiseOR { src1, src2, .. } | + InstructionData::LogicalAnd { src1, src2, .. } | + InstructionData::LogicalOR { src1, src2, .. } | + InstructionData::ShiftLeft { src1, src2, .. } | + InstructionData::ShiftRight { src1, src2, .. }| + InstructionData::Fcmp { src1, src2, .. } | + InstructionData::Icmp { src1, src2, .. } => { + vec![src1.clone(), src2.clone()] + } + InstructionData::Move { src, .. } + | InstructionData::Neg { src, .. } + | InstructionData::BitwiseNot { src, .. } + | InstructionData::LogicalNot { src, .. } + | InstructionData::ToU8 { src, .. } + | InstructionData::ToU16 { src, .. } + | InstructionData::ToU32 { src, .. } + | InstructionData::ToU64 { src, .. } + | InstructionData::ToI16 { src, .. } + | InstructionData::ToI32 { src, .. } + | InstructionData::ToI64 { src, .. } + | InstructionData::ToF32 { src, .. } + | InstructionData::ToF64 { src, .. } + | InstructionData::ToAddress { src, .. } => vec![src.clone()], + InstructionData::Phi { from, .. } => from.iter().map(|f| f.1.clone()).collect::>(), + InstructionData::Call { params, .. } => params.clone(), + // branch relate instruction should not be code motion + InstructionData::Jump { .. } => vec![], + InstructionData::BrIf { test, .. } => vec![test.clone()], + InstructionData::Ret { value, .. } => { + if let Some(val) = value { + vec![val.clone()] + } else { + vec![] + } + } + InstructionData::LoadRegister { base, offset, .. } => vec![base.clone(), offset.clone()], + InstructionData::StoreRegister { base, offset, src, .. } => vec![src.clone(), offset.clone(), base.clone()], + InstructionData::StackAlloc { size, .. } => vec![size.clone()], + // comment + InstructionData::Comment(_) => vec![], + } +} diff --git a/compilers/rustyc/optimizer/src/ir_optimizer/pass/mod.rs b/compilers/rustyc/optimizer/src/ir_optimizer/pass/mod.rs index 47a6e543..820fb2c8 100644 --- a/compilers/rustyc/optimizer/src/ir_optimizer/pass/mod.rs +++ b/compilers/rustyc/optimizer/src/ir_optimizer/pass/mod.rs @@ -1,4 +1,5 @@ pub mod copy_propagation; +pub mod dce; pub mod gvn; pub mod lcm; pub mod licm; diff --git a/compilers/rustyc/optimizer/src/main.rs b/compilers/rustyc/optimizer/src/main.rs index 05f56ac4..d964bc76 100644 --- a/compilers/rustyc/optimizer/src/main.rs +++ b/compilers/rustyc/optimizer/src/main.rs @@ -3,15 +3,13 @@ pub mod ir_converter; pub mod ir_optimizer; use crate::ir::function::Function; -use crate::ir::instructions::InstructionData; -use crate::ir::instructions::OpCode; use crate::ir::value::IrValueType; use crate::ir_converter::Converter; use crate::ir_optimizer::anaylsis::domtree::DomAnaylsier; use crate::ir_optimizer::anaylsis::use_def_chain::UseDefAnaylsier; -use ir::function; use ir_optimizer::anaylsis::post_domtree::PostDomAnaylsier; use ir_optimizer::anaylsis::{DebuggerAnaylsis, OptimizerAnaylsis}; +use ir_optimizer::pass::dce::DCEPass; use ir_optimizer::pass::licm::LICMPass; use ir_optimizer::pass::sscp::SSCPPass; use ir_optimizer::pass::{DebuggerPass, OptimizerPass}; @@ -19,8 +17,8 @@ use rustyc_frontend::parser::Parser; use std::fs::File; use std::io::Write; -fn write_string_to_file(file_string: String) { - let mut file1 = File::create("./output.txt").unwrap(); +fn write_string_to_file(file_string: String, file_name: &'static str) { + let mut file1 = File::create(format!("./{}", file_name).as_str()).unwrap(); write!(file1, "{}", file_string).unwrap(); } #[allow(dead_code)] @@ -56,42 +54,67 @@ fn converter_example() { .unwrap(); let mut converter = Converter::new(); let module = converter.convert(&program); - write_string_to_file(module.print_to_string()); + write_string_to_file(module.print_to_string(), "output.txt"); } #[allow(dead_code)] fn anaylsiser_example() { - let fun = create_diamond_dom_graph(); + let fun = create_diamond_like_dom_graph(); let mut anaylsiser = PostDomAnaylsier::new(); let table = anaylsiser.anaylsis(&fun); let output = anaylsiser.debugger(&fun, &table); - write_string_to_file(output); + write_string_to_file(output, "output.txt"); } #[allow(dead_code)] fn optimizer_example() { - let mut fun = Function::new(String::from("test_fun")); - let mut file1 = File::create("./before.txt").unwrap(); - write!(file1, "{}", fun.print_to_string()).unwrap(); - let mut dom = DomAnaylsier::new(); + // before + let mut fun = create_diamond_like_dom_graph(); + write_string_to_file(fun.print_to_string(), "before.txt"); + // pre-requirement + let mut dom = PostDomAnaylsier::new(); let dom_table = dom.anaylsis(&fun); let mut use_def = UseDefAnaylsier::new(); let use_def_table = use_def.anaylsis(&fun); - let mut pass = LICMPass::new(&use_def_table, &dom_table); + // process + let mut pass = DCEPass::new(&use_def_table, &dom_table, true); pass.process(&mut fun); - - write_string_to_file(pass.debugger(&fun)); - let mut file1 = File::create("./after.txt").unwrap(); - write!(file1, "{}", fun.print_to_string()).unwrap(); + write_string_to_file(pass.debugger(&fun), "output.txt"); + // after + write_string_to_file(fun.print_to_string(), "after.txt"); +} +fn main() { + // converter_example(); + optimizer_example(); + // anaylsiser_example(); } + /// ## Generate Simple Diamond shape IR graph for post dom +/// Need to remove t_2. /// ```markdown /// | -> b2 -| /// b1 - | | -> b4 /// | -> b3 -| /// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// jump b4 +/// --- b3 +/// t4 = 10 +/// jump b4 +/// --- b4 +/// t5 = phi b3 t4, b2 t3 +/// t6 = t5 + 10 +/// ret t5 +/// ``` fn create_diamond_dom_graph() -> Function { let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); let header = function.create_block(); let left = function.create_block(); let right = function.create_block(); @@ -103,6 +126,25 @@ fn create_diamond_dom_graph() -> Function { function.connect_block(header, right); function.connect_block(right, exit); + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, left, right); + + function.switch_to_block(left); + let t_3 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(right); + let t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_phi_inst(vec![(left, t_3), (right, t_4)]); + let t_5 = function.build_add_inst(t_5, i10_const); + function.build_ret_inst(Some(t_5)); + function } /// ## Generate Simple Diamond-Like shape IR graph for post dom @@ -111,23 +153,153 @@ fn create_diamond_dom_graph() -> Function { /// b1 - | /// | -> b3 /// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// ret t3 +/// --- b3 +/// t4 = 10 +/// jump b4 +/// --- b4 +/// t5 = t4 + 10 +/// ret t5 +/// ``` fn create_diamond_like_dom_graph() -> Function { let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); let header = function.create_block(); - let left_exit = function.create_block(); + let left = function.create_block(); let right = function.create_block(); let exit = function.create_block(); function.mark_as_entry(header); function.mark_as_exit(exit); - function.mark_as_exit(left_exit); - function.connect_block(header, left_exit); + function.mark_as_exit(left); + function.connect_block(header, left); function.connect_block(header, right); function.connect_block(right, exit); + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, left, right); + + function.switch_to_block(left); + let t_3 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_3)); + + function.switch_to_block(right); + let t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_add_inst(t_4, i10_const); + function.build_ret_inst(Some(t_5)); + function } -fn main() { - // converter_example(); - // optimizer_example(); - anaylsiser_example(); +/// ## Generate Simple Diamond-Like shape IR graph for post dom +/// ```markdown +/// | -> b2--| +/// b1 - | |--> |-----| +/// | | b3 | +/// | ----------> |-----| +/// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// t4 = 10 +/// store t_4, t_3, 10, i16 +/// jump b3 +/// --- b3 +/// t5 = 10 +/// ret t5 +/// ``` +fn create_simple_if_like_graph() -> Function { + let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); + let header = function.create_block(); + let right = function.create_block(); + let exit = function.create_block(); + function.mark_as_entry(header); + function.mark_as_exit(exit); + function.connect_block(header, right); + function.connect_block(right, exit); + function.connect_block(header, exit); + + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, exit, right); + + function.switch_to_block(right); + let t_3 = function.build_mov_inst(i10_const); + let t_4 = function.build_mov_inst(i10_const); + function.build_store_register_inst(t_4, t_3, i10_const, IrValueType::I16); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_5)); + + function +} + +/// ## Generate Simple Diamond-Like shape IR graph for post dom +/// ```markdown +/// | -> b2--| +/// b1 - | |--> |-----| +/// | | b3 | +/// | ----------> |-----| +/// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// t4 = 10 +/// jump b3 +/// --- b3 +/// t5 = 10 +/// ret t5 +/// ``` +fn create_simple_if_like_cfg_change_graph() -> Function { + let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); + let header = function.create_block(); + let right = function.create_block(); + let exit = function.create_block(); + function.mark_as_entry(header); + function.mark_as_exit(exit); + function.connect_block(header, right); + function.connect_block(right, exit); + function.connect_block(header, exit); + + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, exit, right); + + function.switch_to_block(right); + let _t_3 = function.build_mov_inst(i10_const); + let _t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_5)); + + function } diff --git a/compilers/rustyc/optimizer/tests/build_ir_graph.rs b/compilers/rustyc/optimizer/tests/build_ir_graph.rs index c7b51088..4e81eafd 100644 --- a/compilers/rustyc/optimizer/tests/build_ir_graph.rs +++ b/compilers/rustyc/optimizer/tests/build_ir_graph.rs @@ -445,13 +445,31 @@ pub fn create_simple_loop_const_propagation_graph_2() -> Function { } /// ## Generate Simple Diamond shape IR graph for post dom +/// Need to remove t_2. /// ```markdown /// | -> b2 -| /// b1 - | | -> b4 /// | -> b3 -| /// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// jump b4 +/// --- b3 +/// t4 = 10 +/// jump b4 +/// --- b4 +/// t5 = phi b3 t4, b2 t3 +/// t6 = t5 + 10 +/// ret t5 +/// ``` pub fn create_diamond_dom_graph() -> Function { let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); let header = function.create_block(); let left = function.create_block(); let right = function.create_block(); @@ -463,6 +481,25 @@ pub fn create_diamond_dom_graph() -> Function { function.connect_block(header, right); function.connect_block(right, exit); + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, left, right); + + function.switch_to_block(left); + let t_3 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(right); + let t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_phi_inst(vec![(left, t_3), (right, t_4)]); + let t_5 = function.build_add_inst(t_5, i10_const); + function.build_ret_inst(Some(t_5)); + function } /// ## Generate Simple Diamond-Like shape IR graph for post dom @@ -471,18 +508,153 @@ pub fn create_diamond_dom_graph() -> Function { /// b1 - | /// | -> b3 /// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// ret t3 +/// --- b3 +/// t4 = 10 +/// jump b4 +/// --- b4 +/// t5 = t4 + 10 +/// ret t5 +/// ``` pub fn create_diamond_like_dom_graph() -> Function { let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); let header = function.create_block(); - let left_exit = function.create_block(); + let left = function.create_block(); let right = function.create_block(); let exit = function.create_block(); function.mark_as_entry(header); function.mark_as_exit(exit); - function.mark_as_exit(left_exit); - function.connect_block(header, left_exit); + function.mark_as_exit(left); + function.connect_block(header, left); function.connect_block(header, right); function.connect_block(right, exit); + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, left, right); + + function.switch_to_block(left); + let t_3 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_3)); + + function.switch_to_block(right); + let t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_add_inst(t_4, i10_const); + function.build_ret_inst(Some(t_5)); + + function +} +/// ## Generate Simple Diamond-Like shape IR graph for post dom +/// ```markdown +/// | -> b2--| +/// b1 - | |--> |-----| +/// | | b3 | +/// | ----------> |-----| +/// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// t4 = 10 +/// store t_4, t_3, 10, i16 +/// jump b3 +/// --- b3 +/// t5 = 10 +/// ret t5 +/// ``` +pub fn create_simple_if_like_graph() -> Function { + let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); + let header = function.create_block(); + let right = function.create_block(); + let exit = function.create_block(); + function.mark_as_entry(header); + function.mark_as_exit(exit); + function.connect_block(header, right); + function.connect_block(right, exit); + function.connect_block(header, exit); + + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, exit, right); + + function.switch_to_block(right); + let t_3 = function.build_mov_inst(i10_const); + let t_4 = function.build_mov_inst(i10_const); + function.build_store_register_inst(t_4, t_3, i10_const, IrValueType::I16); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_5)); + + function +} + +/// ## Generate Simple Diamond-Like shape IR graph for post dom +/// ```markdown +/// | -> b2--| +/// b1 - | |--> |-----| +/// | | b3 | +/// | ----------> |-----| +/// ``` +/// ```markdown +/// --- b1 +/// t1 = 10 +/// t2 = t1 + 10 +/// brif t1 block 2, block 3 +/// --- b2 +/// t3 = 10 +/// t4 = 10 +/// jump b3 +/// --- b3 +/// t5 = 10 +/// ret t5 +/// ``` +pub fn create_simple_if_like_cfg_change_graph() -> Function { + let mut function = Function::new(String::from("test_fun")); + function.return_type = Some(IrValueType::I16); + let header = function.create_block(); + let right = function.create_block(); + let exit = function.create_block(); + function.mark_as_entry(header); + function.mark_as_exit(exit); + function.connect_block(header, right); + function.connect_block(right, exit); + function.connect_block(header, exit); + + function.switch_to_block(header); + let i10_const = function.create_i16_const(10); + let t_1 = function.build_mov_inst(i10_const); + let _t_2 = function.build_add_inst(t_1, i10_const); + function.build_brif_inst(t_1, exit, right); + + function.switch_to_block(right); + let _t_3 = function.build_mov_inst(i10_const); + let _t_4 = function.build_mov_inst(i10_const); + function.build_jump_inst(exit); + + function.switch_to_block(exit); + let t_5 = function.build_mov_inst(i10_const); + function.build_ret_inst(Some(t_5)); + function } diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/after.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/after.txt new file mode 100644 index 00000000..5430f6b8 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/after.txt @@ -0,0 +1,21 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + brif t1, block2, block3 +block2: + t3 = 10 + jump 4 +block3: + t4 = 10 + jump 4 +block4: + phi t5, block2 t3, block3 t4 + t6 = add t5 10 + ret t6 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 +;; t6 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/before.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/before.txt new file mode 100644 index 00000000..3cbc6ef5 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/before.txt @@ -0,0 +1,22 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + t2 = add t1 10 + brif t1, block2, block3 +block2: + t3 = 10 + jump 4 +block3: + t4 = 10 + jump 4 +block4: + phi t5, block2 t3, block3 t4 + t6 = add t5 10 + ret t6 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 +;; t6 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/output.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/output.txt new file mode 100644 index 00000000..83fb2ff7 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond/output.txt @@ -0,0 +1,10 @@ +|--------------------------------------| +| Marked Inst | +|--------------------------------------| +| t1 = 10 | +| brif t1, block2, block3 | +| t3 = 10 | +| t4 = 10 | +| phi t5, block2 t3, block3 t4 | +| t6 = add t5 10 | +| ret t6 | diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/after.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/after.txt new file mode 100644 index 00000000..222bb5a9 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/after.txt @@ -0,0 +1,19 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + brif t1, block2, block3 +block2: + t3 = 10 + ret t3 +block3: + t4 = 10 + jump 4 +block4: + t5 = add t4 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/before.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/before.txt new file mode 100644 index 00000000..7a6b81b7 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/before.txt @@ -0,0 +1,20 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + t2 = add t1 10 + brif t1, block2, block3 +block2: + t3 = 10 + ret t3 +block3: + t4 = 10 + jump 4 +block4: + t5 = add t4 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/output.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/output.txt new file mode 100644 index 00000000..40c76124 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/diamond_like/output.txt @@ -0,0 +1,10 @@ +|---------------------------------| +| Marked Inst | +|---------------------------------| +| t1 = 10 | +| brif t1, block2, block3 | +| t3 = 10 | +| ret t3 | +| t4 = 10 | +| t5 = add t4 10 | +| ret t5 | diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/after.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/after.txt new file mode 100644 index 00000000..7fa80e40 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/after.txt @@ -0,0 +1,12 @@ +function test_fun () -> i16 { +block1: + jump 3 +block3: + t5 = 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/before.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/before.txt new file mode 100644 index 00000000..bc039e63 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/before.txt @@ -0,0 +1,18 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + t2 = add t1 10 + brif t1, block3, block2 +block2: + t3 = 10 + t4 = 10 + jump 3 +block3: + t5 = 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/output.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/output.txt new file mode 100644 index 00000000..3967fea5 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_change_cfg/output.txt @@ -0,0 +1,5 @@ +|---------------------| +| Marked Inst | +|---------------------| +| t5 = 10 | +| ret t5 | diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/after.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/after.txt new file mode 100644 index 00000000..0011f0e5 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/after.txt @@ -0,0 +1,18 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + brif t1, block3, block2 +block2: + t3 = 10 + t4 = 10 + store i16 t4 [t3, 10] + jump 3 +block3: + t5 = 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/before.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/before.txt new file mode 100644 index 00000000..ab42a419 --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/before.txt @@ -0,0 +1,19 @@ +function test_fun () -> i16 { +block1: + t1 = 10 + t2 = add t1 10 + brif t1, block3, block2 +block2: + t3 = 10 + t4 = 10 + store i16 t4 [t3, 10] + jump 3 +block3: + t5 = 10 + ret t5 +} +;; t1 -> i16 +;; t2 -> i16 +;; t3 -> i16 +;; t4 -> i16 +;; t5 -> i16 diff --git a/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/output.txt b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/output.txt new file mode 100644 index 00000000..6c1e85ff --- /dev/null +++ b/compilers/rustyc/optimizer/tests/fixtures/ir_optimizer/dce/if_critical/output.txt @@ -0,0 +1,10 @@ +|---------------------------------| +| Marked Inst | +|---------------------------------| +| t1 = 10 | +| brif t1, block3, block2 | +| t3 = 10 | +| t4 = 10 | +| store i16 t4 [t3, 10] | +| t5 = 10 | +| ret t5 | diff --git a/compilers/rustyc/optimizer/tests/ir_optimizer_test_runner.rs b/compilers/rustyc/optimizer/tests/ir_optimizer_test_runner.rs index 6cc7d9d3..5f75a4fe 100644 --- a/compilers/rustyc/optimizer/tests/ir_optimizer_test_runner.rs +++ b/compilers/rustyc/optimizer/tests/ir_optimizer_test_runner.rs @@ -7,6 +7,7 @@ use rustyc_optimizer::ir_optimizer::anaylsis::domtree::DomAnaylsier; use rustyc_optimizer::ir_optimizer::anaylsis::post_domtree::PostDomAnaylsier; use rustyc_optimizer::ir_optimizer::anaylsis::use_def_chain::UseDefAnaylsier; use rustyc_optimizer::ir_optimizer::anaylsis::{DebuggerAnaylsis, OptimizerAnaylsis}; +use rustyc_optimizer::ir_optimizer::pass::dce::DCEPass; use rustyc_optimizer::ir_optimizer::pass::gvn::GVNPass; use rustyc_optimizer::ir_optimizer::pass::lcm::LCMPass; use rustyc_optimizer::ir_optimizer::pass::licm::LICMPass; @@ -171,6 +172,58 @@ generate_pass_cases!( let table = pass.debugger(&fun); let after = fun.print_to_string(); (before, table, after) + }), + (test_dce_pass_if_critical, "./dce/if_critical", || { + let mut fun = create_simple_if_like_graph(); + let before = fun.print_to_string(); + let mut use_def = UseDefAnaylsier::new(); + let use_def_table = use_def.anaylsis(&fun); + let mut post_dom = PostDomAnaylsier::new(); + let post_dom_table = post_dom.anaylsis(&fun); + let mut pass = DCEPass::new(&use_def_table, &post_dom_table, true); + pass.process(&mut fun); + let table = pass.debugger(&fun); + let after = fun.print_to_string(); + (before, table, after) + }), + (test_dce_pass_if_non_critical, "./dce/if_change_cfg", || { + let mut fun = create_simple_if_like_cfg_change_graph(); + let before = fun.print_to_string(); + let mut use_def = UseDefAnaylsier::new(); + let use_def_table = use_def.anaylsis(&fun); + let mut post_dom = PostDomAnaylsier::new(); + let post_dom_table = post_dom.anaylsis(&fun); + let mut pass = DCEPass::new(&use_def_table, &post_dom_table, true); + pass.process(&mut fun); + let table = pass.debugger(&fun); + let after = fun.print_to_string(); + (before, table, after) + }), + (test_dce_pass_diamond, "./dce/diamond", || { + let mut fun = create_diamond_dom_graph(); + let before = fun.print_to_string(); + let mut use_def = UseDefAnaylsier::new(); + let use_def_table = use_def.anaylsis(&fun); + let mut post_dom = PostDomAnaylsier::new(); + let post_dom_table = post_dom.anaylsis(&fun); + let mut pass = DCEPass::new(&use_def_table, &post_dom_table, true); + pass.process(&mut fun); + let table = pass.debugger(&fun); + let after = fun.print_to_string(); + (before, table, after) + }), + (test_dce_pass_diamond_like, "./dce/diamond_like", || { + let mut fun = create_diamond_like_dom_graph(); + let before = fun.print_to_string(); + let mut use_def = UseDefAnaylsier::new(); + let use_def_table = use_def.anaylsis(&fun); + let mut post_dom = PostDomAnaylsier::new(); + let post_dom_table = post_dom.anaylsis(&fun); + let mut pass = DCEPass::new(&use_def_table, &post_dom_table, true); + pass.process(&mut fun); + let table = pass.debugger(&fun); + let after = fun.print_to_string(); + (before, table, after) }) );