duskphantom_middle/transform/redundance_elim.rs
1// Copyright 2024 Duskphantom Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// SPDX-License-Identifier: Apache-2.0
16
17use std::collections::HashMap;
18
19use anyhow::Result;
20
21use crate::{
22 analysis::{
23 dominator_tree::DominatorTree,
24 effect_analysis::EffectAnalysis,
25 memory_ssa::MemorySSA,
26 simple_gvn::{Expr, SimpleGVN},
27 },
28 ir::{InstPtr, Operand, ValueType},
29 Program,
30};
31
32use super::Transform;
33
34pub fn optimize_program(program: &mut Program) -> Result<bool> {
35 let effect_analysis = EffectAnalysis::new(program);
36 let memory_ssa = MemorySSA::new(program, &effect_analysis);
37 let mut gvn = SimpleGVN::new(&memory_ssa);
38 RedundanceElim::new(program, &mut gvn).run_and_log()
39}
40
41pub struct RedundanceElim<'a> {
42 program: &'a mut Program,
43 gvn: &'a mut SimpleGVN<'a>,
44}
45
46impl<'a> Transform for RedundanceElim<'a> {
47 fn get_program_mut(&mut self) -> &mut Program {
48 self.program
49 }
50
51 fn name() -> String {
52 "redundance_elim".to_string()
53 }
54
55 fn run(&mut self) -> Result<bool> {
56 // Get instruction leader from GVN
57 let mut changed = false;
58 for func in self.program.module.functions.clone() {
59 if func.is_lib() {
60 continue;
61 }
62 let mut dom_tree = DominatorTree::new(func);
63
64 // Implementation of Expr::Hash does not use it's mutable content,
65 // so it's false positive according to:
66 // https://rust-lang.github.io/rust-clippy/master/index.html#/mutable_key_type
67 #[allow(clippy::mutable_key_type)]
68 let mut expr_leader: HashMap<Expr, InstPtr> = HashMap::new();
69
70 // Iterate all instructions
71 for bb in func.rpo_iter() {
72 for inst in bb.iter() {
73 // Refuse to replace instruction that returns void
74 if inst.get_value_type() == ValueType::Void {
75 continue;
76 }
77 let expr = self.gvn.get_expr(Operand::Instruction(inst));
78 match expr_leader.get(&expr) {
79 // Expression appeared before, move leader and inst to lowest common ancestor
80 Some(&leader) => {
81 let inst_bb = inst.get_parent_bb().unwrap();
82 let leader_bb = leader.get_parent_bb().unwrap();
83 let lca = dom_tree.get_lca(inst_bb, leader_bb);
84 if lca != leader_bb {
85 // Remove partial redundancy and hoist at the same time
86 lca.get_last_inst().insert_before(leader);
87 }
88 inst.clone().replace_self(&leader.into());
89 changed = true;
90 }
91 // Expression not appeared before, set as leader
92 None => {
93 expr_leader.insert(expr, inst);
94 }
95 }
96 }
97 }
98 }
99 Ok(changed)
100 }
101}
102
103impl<'a> RedundanceElim<'a> {
104 pub fn new(program: &'a mut Program, gvn: &'a mut SimpleGVN<'a>) -> Self {
105 Self { program, gvn }
106 }
107}