Coverage Report

Created: 2026-04-09 08:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/ion/redundant_moves.rs
Line
Count
Source
1
//! Redundant-move elimination.
2
3
use crate::{Allocation, FxHashMap, VReg};
4
use smallvec::{smallvec, SmallVec};
5
6
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
7
pub enum RedundantMoveState {
8
    Copy(Allocation, Option<VReg>),
9
    Orig(VReg),
10
    None,
11
}
12
#[derive(Clone, Debug, Default)]
13
pub struct RedundantMoveEliminator {
14
    allocs: FxHashMap<Allocation, RedundantMoveState>,
15
    reverse_allocs: FxHashMap<Allocation, SmallVec<[Allocation; 4]>>,
16
}
17
#[derive(Copy, Clone, Debug)]
18
pub struct RedundantMoveAction {
19
    pub elide: bool,
20
}
21
22
impl RedundantMoveEliminator {
23
0
    pub fn process_move(
24
0
        &mut self,
25
0
        from: Allocation,
26
0
        to: Allocation,
27
0
        to_vreg: Option<VReg>,
28
0
    ) -> RedundantMoveAction {
29
        // Look up the src and dest.
30
0
        let from_state = self
31
0
            .allocs
32
0
            .get(&from)
33
0
            .map(|&p| p)
34
0
            .unwrap_or(RedundantMoveState::None);
35
0
        let to_state = self
36
0
            .allocs
37
0
            .get(&to)
38
0
            .map(|&p| p)
39
0
            .unwrap_or(RedundantMoveState::None);
40
41
0
        trace!(
42
            "     -> redundant move tracker: from {} to {} to_vreg {:?}",
43
            from,
44
            to,
45
            to_vreg
46
        );
47
0
        trace!(
48
            "       -> from_state {:?} to_state {:?}",
49
            from_state,
50
            to_state
51
        );
52
53
0
        if from == to && to_vreg.is_some() {
54
0
            self.clear_alloc(to);
55
0
            self.allocs
56
0
                .insert(to, RedundantMoveState::Orig(to_vreg.unwrap()));
57
0
            return RedundantMoveAction { elide: true };
58
0
        }
59
60
0
        let src_vreg = match from_state {
61
0
            RedundantMoveState::Copy(_, opt_r) => opt_r,
62
0
            RedundantMoveState::Orig(r) => Some(r),
63
0
            _ => None,
64
        };
65
0
        trace!("      -> src_vreg {:?}", src_vreg);
66
0
        let dst_vreg = to_vreg.or(src_vreg);
67
0
        trace!("      -> dst_vreg {:?}", dst_vreg);
68
0
        let existing_dst_vreg = match to_state {
69
0
            RedundantMoveState::Copy(_, opt_r) => opt_r,
70
0
            RedundantMoveState::Orig(r) => Some(r),
71
0
            _ => None,
72
        };
73
0
        trace!("      -> existing_dst_vreg {:?}", existing_dst_vreg);
74
75
0
        let elide = match (from_state, to_state) {
76
0
            (_, RedundantMoveState::Copy(orig_alloc, _)) if orig_alloc == from => true,
77
0
            (RedundantMoveState::Copy(new_alloc, _), _) if new_alloc == to => true,
78
0
            _ => false,
79
        };
80
0
        trace!("      -> elide {}", elide);
81
82
        // Invalidate all existing copies of `to` if `to` actually changed value.
83
0
        if !elide {
84
0
            self.clear_alloc(to);
85
0
        }
86
87
        // Set up forward and reverse mapping. Don't track stack-to-stack copies.
88
0
        if from.is_reg() || to.is_reg() {
89
0
            self.allocs
90
0
                .insert(to, RedundantMoveState::Copy(from, dst_vreg));
91
0
            trace!(
92
                "     -> create mapping {} -> {:?}",
93
                to,
94
0
                RedundantMoveState::Copy(from, dst_vreg)
95
            );
96
0
            self.reverse_allocs
97
0
                .entry(from)
98
0
                .or_insert_with(|| smallvec![])
99
0
                .push(to);
100
0
        }
101
102
0
        RedundantMoveAction { elide }
103
0
    }
104
105
0
    pub fn clear(&mut self) {
106
0
        trace!("   redundant move eliminator cleared");
107
0
        self.allocs.clear();
108
0
        self.reverse_allocs.clear();
109
0
    }
110
111
0
    pub fn clear_alloc(&mut self, alloc: Allocation) {
112
0
        trace!("   redundant move eliminator: clear {:?}", alloc);
113
0
        if let Some(ref mut existing_copies) = self.reverse_allocs.get_mut(&alloc) {
114
0
            for to_inval in existing_copies.drain(..) {
115
0
                trace!("     -> clear existing copy: {:?}", to_inval);
116
0
                if let Some(val) = self.allocs.get_mut(&to_inval) {
117
0
                    match val {
118
0
                        RedundantMoveState::Copy(_, Some(vreg)) => {
119
0
                            *val = RedundantMoveState::Orig(*vreg);
120
0
                        }
121
0
                        _ => *val = RedundantMoveState::None,
122
                    }
123
0
                }
124
0
                self.allocs.remove(&to_inval);
125
            }
126
0
        }
127
0
        self.allocs.remove(&alloc);
128
0
    }
129
}