Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.91.1/src/simple_gvn.rs
Line
Count
Source (jump to first uncovered line)
1
//! A simple GVN pass.
2
3
use crate::cursor::{Cursor, FuncCursor};
4
use crate::dominator_tree::DominatorTree;
5
use crate::ir::{Function, Inst, InstructionData, Opcode, Type};
6
use crate::scoped_hash_map::ScopedHashMap;
7
use crate::timing;
8
use alloc::vec::Vec;
9
use core::cell::{Ref, RefCell};
10
use core::hash::{Hash, Hasher};
11
12
/// Test whether the given opcode is unsafe to even consider for GVN.
13
3.98M
fn trivially_unsafe_for_gvn(opcode: Opcode) -> bool {
14
3.98M
    opcode.is_call()
15
3.77M
        || opcode.is_branch()
16
3.39M
        || opcode.is_terminator()
17
3.06M
        || opcode.is_return()
18
3.06M
        || opcode.can_trap()
19
3.01M
        || opcode.other_side_effects()
20
3.00M
        || opcode.can_store()
21
2.95M
        || opcode.writes_cpu_flags()
22
3.98M
}
23
24
/// Test that, if the specified instruction is a load, it doesn't have the `readonly` memflag.
25
2.95M
fn is_load_and_not_readonly(inst_data: &InstructionData) -> bool {
26
2.95M
    match *inst_data {
27
454k
        InstructionData::Load { flags, .. } => !flags.readonly(),
28
2.49M
        _ => inst_data.opcode().can_load(),
29
    }
30
2.95M
}
31
32
/// Wrapper around `InstructionData` which implements `Eq` and `Hash`
33
#[derive(Clone)]
34
struct HashKey<'a, 'f: 'a> {
35
    inst: InstructionData,
36
    ty: Type,
37
    pos: &'a RefCell<FuncCursor<'f>>,
38
}
39
impl<'a, 'f: 'a> Hash for HashKey<'a, 'f> {
40
4.44M
    fn hash<H: Hasher>(&self, state: &mut H) {
41
4.44M
        let pool = &self.pos.borrow().func.dfg.value_lists;
42
4.44M
        self.inst.hash(state, pool);
43
4.44M
        self.ty.hash(state);
44
4.44M
    }
45
}
46
impl<'a, 'f: 'a> PartialEq for HashKey<'a, 'f> {
47
1.30M
    fn eq(&self, other: &Self) -> bool {
48
1.30M
        let pool = &self.pos.borrow().func.dfg.value_lists;
49
1.30M
        self.inst.eq(&other.inst, pool) && self.ty == other.ty
50
1.30M
    }
51
}
52
impl<'a, 'f: 'a> Eq for HashKey<'a, 'f> {}
53
54
/// Perform simple GVN on `func`.
55
///
56
278k
pub fn do_simple_gvn(func: &mut Function, domtree: &mut DominatorTree) {
57
278k
    let _tt = timing::gvn();
58
278k
    debug_assert!(domtree.is_valid());
59
60
    // Visit blocks in a reverse post-order.
61
    //
62
    // The RefCell here is a bit ugly since the HashKeys in the ScopedHashMap
63
    // need a reference to the function.
64
278k
    let pos = RefCell::new(FuncCursor::new(func));
65
278k
66
278k
    let mut visible_values: ScopedHashMap<HashKey, Inst> = ScopedHashMap::new();
67
278k
    let mut scope_stack: Vec<Inst> = Vec::new();
68
69
656k
    for &block in domtree.cfg_postorder().iter().rev() {
70
        {
71
            // Pop any scopes that we just exited.
72
656k
            let layout = &pos.borrow().func.layout;
73
            loop {
74
792k
                if let Some(current) = scope_stack.last() {
75
513k
                    if domtree.dominates(*current, block, layout) {
76
377k
                        break;
77
136k
                    }
78
                } else {
79
278k
                    break;
80
                }
81
136k
                scope_stack.pop();
82
136k
                visible_values.decrement_depth();
83
            }
84
85
            // Push a scope for the current block.
86
656k
            scope_stack.push(layout.first_inst(block).unwrap());
87
656k
            visible_values.increment_depth();
88
656k
        }
89
656k
90
656k
        pos.borrow_mut().goto_top(block);
91
4.64M
        while let Some(inst) = {
92
4.64M
            let mut pos = pos.borrow_mut();
93
4.64M
            pos.next_inst()
94
4.64M
        } {
95
            // Resolve aliases, particularly aliases we created earlier.
96
3.98M
            pos.borrow_mut().func.dfg.resolve_aliases_in_arguments(inst);
97
3.98M
98
3.98M
            let func = Ref::map(pos.borrow(), |pos| &pos.func);
99
3.98M
100
3.98M
            let opcode = func.dfg[inst].opcode();
101
3.98M
102
3.98M
            if opcode.is_branch() && !opcode.is_terminator() {
103
51.1k
                scope_stack.push(func.layout.next_inst(inst).unwrap());
104
51.1k
                visible_values.increment_depth();
105
3.93M
            }
106
107
3.98M
            if trivially_unsafe_for_gvn(opcode) {
108
1.03M
                continue;
109
2.95M
            }
110
2.95M
111
2.95M
            // These are split up to separate concerns.
112
2.95M
            if is_load_and_not_readonly(&func.dfg[inst]) {
113
345k
                continue;
114
2.60M
            }
115
2.60M
116
2.60M
            let ctrl_typevar = func.dfg.ctrl_typevar(inst);
117
2.60M
            let key = HashKey {
118
2.60M
                inst: func.dfg[inst],
119
2.60M
                ty: ctrl_typevar,
120
2.60M
                pos: &pos,
121
2.60M
            };
122
2.60M
            use crate::scoped_hash_map::Entry::*;
123
2.60M
            match visible_values.entry(key) {
124
1.20M
                Occupied(entry) => {
125
1.20M
                    #[allow(clippy::debug_assert_with_mut_call)]
126
1.20M
                    {
127
1.20M
                        // Clippy incorrectly believes `&func.layout` should not be used here:
128
1.20M
                        // https://github.com/rust-lang/rust-clippy/issues/4737
129
1.20M
                        debug_assert!(domtree.dominates(*entry.get(), inst, &func.layout));
130
                    }
131
132
                    // If the redundant instruction is representing the current
133
                    // scope, pick a new representative.
134
1.20M
                    let old = scope_stack.last_mut().unwrap();
135
1.20M
                    if *old == inst {
136
51.6k
                        *old = func.layout.next_inst(inst).unwrap();
137
1.14M
                    }
138
                    // Replace the redundant instruction and remove it.
139
1.20M
                    drop(func);
140
1.20M
                    let mut pos = pos.borrow_mut();
141
1.20M
                    pos.func.dfg.replace_with_aliases(inst, *entry.get());
142
1.20M
                    pos.remove_inst_and_step_back();
143
                }
144
1.40M
                Vacant(entry) => {
145
1.40M
                    entry.insert(inst);
146
1.40M
                }
147
            }
148
        }
149
    }
150
278k
}