Skip to content

Commit

Permalink
feat: implement dce pass and add test case for pass
Browse files Browse the repository at this point in the history
  • Loading branch information
Solo-steven committed Oct 11, 2024
1 parent 11e6104 commit 5893030
Show file tree
Hide file tree
Showing 20 changed files with 906 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<BasicBlock, PostDomTableEntry>,
extra_exit_block: Option<BasicBlock>,
pub table: HashMap<BasicBlock, PostDomTableEntry>,
pub extra_exit_block: Option<BasicBlock>,
}
/// Entry for record dom, idom and dom-frontier for a bb.
#[derive(Debug, Clone, PartialEq)]
Expand Down
50 changes: 50 additions & 0 deletions compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/debugger.rs
Original file line number Diff line number Diff line change
@@ -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::<Vec<_>>());
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);
}
}
}
}
187 changes: 187 additions & 0 deletions compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Instruction>,
marked_insts: HashSet<Instruction>,
marked_blocks: HashSet<BasicBlock>,
use_def_table: &'a UseDefTable,
post_dom_table: &'a PostDomTable,
// Ad-hoc data structure only for debug usage
debug_mark_inst_map_string: Option<HashMap<Instruction, String>>,
}

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::<Vec<_>>();
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(&current) {
continue;
}
visit_block.insert(current);
for &sucessor in &function.blocks.get(&current).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);
}
}
}
58 changes: 58 additions & 0 deletions compilers/rustyc/optimizer/src/ir_optimizer/pass/dce/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::ir::{instructions::InstructionData, value::Value};

pub(super) fn get_rhs_values(inst: &InstructionData) -> Vec<Value> {
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::<Vec<_>>(),
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![],
}
}
1 change: 1 addition & 0 deletions compilers/rustyc/optimizer/src/ir_optimizer/pass/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod copy_propagation;
pub mod dce;
pub mod gvn;
pub mod lcm;
pub mod licm;
Expand Down
Loading

0 comments on commit 5893030

Please sign in to comment.