duskphantom_middle/transform/
dead_code_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::analysis::effect_analysis::EffectAnalysis;
20use crate::ir::instruction::InstType;
21use crate::ir::{InstPtr, Operand};
22use crate::Program;
23
24use super::Transform;
25
26#[allow(unused)]
27pub fn optimize_program(program: &mut Program) -> Result<bool> {
28    let effect_analysis = EffectAnalysis::new(program);
29    DeadCodeElim::new(program, &effect_analysis).run_and_log()
30}
31
32pub struct DeadCodeElim<'a> {
33    program: &'a mut Program,
34    effect_analysis: &'a EffectAnalysis,
35}
36
37impl<'a> Transform for DeadCodeElim<'a> {
38    fn get_program_mut(&mut self) -> &mut Program {
39        self.program
40    }
41
42    fn name() -> String {
43        "dead_code_elim".to_string()
44    }
45
46    fn run(&mut self) -> Result<bool> {
47        let mut changed = false;
48        for func in self.program.module.functions.clone().iter() {
49            if func.is_lib() {
50                continue;
51            }
52            for bb in func.po_iter() {
53                for inst in bb.iter() {
54                    changed |= self.dead_code_elim_inst(inst)?;
55                }
56            }
57        }
58
59        // Global variable does not require revisit, remove unused variables at the end
60        let len0 = self.program.module.global_variables.len();
61        self.program
62            .module
63            .global_variables
64            .retain(|var| !var.as_ref().get_user().is_empty());
65        let len1 = self.program.module.global_variables.len();
66        changed |= len0 != len1;
67        Ok(changed)
68    }
69}
70
71impl<'a> DeadCodeElim<'a> {
72    pub fn new(program: &'a mut Program, effect_analysis: &'a EffectAnalysis) -> Self {
73        Self {
74            program,
75            effect_analysis,
76        }
77    }
78
79    fn dead_code_elim_inst(&mut self, mut inst: InstPtr) -> Result<bool> {
80        if !inst.get_user().is_empty() || self.has_side_effect(inst) {
81            return Ok(false);
82        }
83        let operands: Vec<_> = inst.get_operand().into();
84        inst.remove_self();
85        for op in operands {
86            if let Operand::Instruction(inst) = op {
87                self.dead_code_elim_inst(inst)?;
88            }
89        }
90        Ok(true)
91    }
92
93    fn has_side_effect(&mut self, inst: InstPtr) -> bool {
94        matches!(
95            inst.get_type(),
96            InstType::Store | InstType::Ret | InstType::Br
97        ) || self.effect_analysis.has_effect(inst)
98    }
99}