duskphantom_middle/transform/
load_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 anyhow::Result;
18
19use crate::{
20    analysis::memory_ssa::{MemorySSA, Node},
21    ir::{instruction::InstType, FunPtr, InstPtr},
22    Program,
23};
24
25use super::Transform;
26
27pub fn optimize_program<'a>(
28    program: &'a mut Program,
29    memory_ssa: &'a mut MemorySSA,
30) -> Result<bool> {
31    LoadElim::new(program, memory_ssa).run_and_log()
32}
33
34pub struct LoadElim<'a, 'b> {
35    program: &'a mut Program,
36    memory_ssa: &'a mut MemorySSA<'b>,
37}
38
39impl<'a, 'b> Transform for LoadElim<'a, 'b> {
40    fn get_program_mut(&mut self) -> &mut Program {
41        self.program
42    }
43
44    fn name() -> String {
45        "load_elim".to_string()
46    }
47
48    fn run(&mut self) -> Result<bool> {
49        let mut changed = false;
50        for func in self.program.module.functions.clone() {
51            if func.is_lib() {
52                continue;
53            }
54            for bb in func.rpo_iter() {
55                for inst in bb.iter() {
56                    changed |= self.process_inst(inst, func)?;
57                }
58            }
59        }
60        Ok(changed)
61    }
62}
63
64impl<'a, 'b> LoadElim<'a, 'b> {
65    pub fn new(program: &'a mut Program, memory_ssa: &'a mut MemorySSA<'b>) -> Self {
66        Self {
67            program,
68            memory_ssa,
69        }
70    }
71
72    fn process_inst(&mut self, mut inst: InstPtr, func: FunPtr) -> Result<bool> {
73        // Instruction must be load (instead of function call), otherwise it can't be optimized
74        if inst.get_type() != InstType::Load {
75            return Ok(false);
76        }
77
78        // Get corresponding MemorySSA node
79        let Some(load_node) = self.memory_ssa.get_inst_node(inst) else {
80            return Ok(false);
81        };
82
83        // It should be a MemoryUse node (not entry or phi)
84        let Node::Normal(_, Some(src), _, _) = load_node.as_ref() else {
85            return Ok(false);
86        };
87
88        // Predict value in MemorySSA
89        let predicted = self.memory_ssa.predict_read(*src, inst, func)?;
90
91        // Replace if value can be predicted
92        if let Some(predicted) = predicted {
93            inst.replace_self(&predicted);
94            self.memory_ssa.remove_node(load_node);
95            return Ok(true);
96        }
97        Ok(false)
98    }
99}