Coverage Report

Created: 2026-01-22 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/regalloc2/src/fastalloc/lru.rs
Line
Count
Source
1
use crate::{FxHashSet, PReg, PRegSet, RegClass};
2
use alloc::vec;
3
use alloc::vec::Vec;
4
use core::{
5
    fmt,
6
    ops::{Index, IndexMut},
7
};
8
9
/// A least-recently-used cache organized as a linked list based on a vector.
10
pub struct Lru {
11
    /// The list of node information.
12
    ///
13
    /// Each node corresponds to a physical register.
14
    /// The index of a node is the `address` from the perspective of the linked list.
15
    pub data: Vec<LruNode>,
16
    /// Index of the most recently used register.
17
    pub head: u8,
18
    /// Class of registers in the cache.
19
    pub regclass: RegClass,
20
}
21
22
#[derive(Clone, Copy, Debug)]
23
pub struct LruNode {
24
    /// The previous physical register in the list.
25
    pub prev: u8,
26
    /// The next physical register in the list.
27
    pub next: u8,
28
}
29
30
impl Lru {
31
0
    pub fn new(regclass: RegClass, regs: &PRegSet) -> Self {
32
0
        let regs = regs.into_iter().collect::<Vec<_>>();
33
0
        let mut data = vec![
34
0
            LruNode {
35
0
                prev: u8::MAX,
36
0
                next: u8::MAX
37
0
            };
38
0
            PReg::MAX + 1
39
        ];
40
0
        let no_of_regs = regs.len();
41
0
        for i in 0..no_of_regs {
42
0
            let (reg, prev_reg, next_reg) = (
43
0
                regs[i],
44
0
                regs[i.checked_sub(1).unwrap_or(no_of_regs - 1)],
45
0
                regs[if i >= no_of_regs - 1 { 0 } else { i + 1 }],
46
            );
47
0
            data[reg.hw_enc()].prev = prev_reg.hw_enc() as u8;
48
0
            data[reg.hw_enc()].next = next_reg.hw_enc() as u8;
49
        }
50
        Self {
51
0
            head: if regs.is_empty() {
52
0
                u8::MAX
53
            } else {
54
0
                regs[0].hw_enc() as u8
55
            },
56
0
            data,
57
0
            regclass,
58
        }
59
0
    }
60
61
    /// Marks the physical register `preg` as the most recently used
62
0
    pub fn poke(&mut self, preg: PReg) {
63
0
        trace!(
64
0
            "Before poking: {:?} LRU. head: {:?}, Actual data: {:?}",
65
            self.regclass,
66
            self.head,
67
            self.data
68
        );
69
0
        trace!("About to poke {:?} in {:?} LRU", preg, self.regclass);
70
0
        let prev_newest = self.head;
71
0
        let hw_enc = preg.hw_enc() as u8;
72
0
        if hw_enc == prev_newest {
73
0
            return;
74
0
        }
75
0
        if self.data[prev_newest as usize].prev != hw_enc {
76
0
            self.remove(hw_enc as usize);
77
0
            self.insert_before(hw_enc, self.head);
78
0
        }
79
0
        self.head = hw_enc;
80
0
        trace!("Poked {:?} in {:?} LRU", preg, self.regclass);
81
0
        if cfg!(debug_assertions) {
82
0
            self.validate_lru();
83
0
        }
84
0
    }
85
86
    /// Gets the least recently used physical register.
87
0
    pub fn pop(&mut self) -> PReg {
88
0
        trace!(
89
0
            "Before popping: {:?} LRU. head: {:?}, Actual data: {:?}",
90
            self.regclass,
91
            self.head,
92
            self.data
93
        );
94
0
        trace!("Popping {:?} LRU", self.regclass);
95
0
        if self.is_empty() {
96
0
            panic!("LRU is empty");
97
0
        }
98
0
        let oldest = self.data[self.head as usize].prev;
99
0
        trace!("Popped p{oldest} in {:?} LRU", self.regclass);
100
0
        if cfg!(debug_assertions) {
101
0
            self.validate_lru();
102
0
        }
103
0
        PReg::new(oldest as usize, self.regclass)
104
0
    }
105
106
    /// Get the last PReg in the LRU from the set `from`.
107
0
    pub fn last(&self, from: PRegSet) -> Option<PReg> {
108
0
        trace!("Getting the last preg from the LRU in set {from}");
109
0
        self.last_satisfying(|preg| from.contains(preg))
110
0
    }
111
112
    /// Get the last PReg from the LRU for which `f` returns true.
113
0
    pub fn last_satisfying<F: Fn(PReg) -> bool>(&self, f: F) -> Option<PReg> {
114
0
        trace!("Getting the last preg from the LRU satisfying...");
115
0
        if self.is_empty() {
116
0
            panic!("LRU is empty");
117
0
        }
118
0
        let mut last = self.data[self.head as usize].prev;
119
0
        let init_last = last;
120
        loop {
121
0
            let preg = PReg::new(last as usize, self.regclass);
122
0
            if f(preg) {
123
0
                return Some(preg);
124
0
            }
125
0
            last = self.data[last as usize].prev;
126
0
            if last == init_last {
127
0
                return None;
128
0
            }
129
        }
130
0
    }
131
132
    /// Splices out a node from the list.
133
0
    fn remove(&mut self, hw_enc: usize) {
134
0
        trace!(
135
0
            "Before removing: {:?} LRU. head: {:?}, Actual data: {:?}",
136
            self.regclass,
137
            self.head,
138
            self.data
139
        );
140
0
        trace!("Removing p{hw_enc} from {:?} LRU", self.regclass);
141
0
        let (iprev, inext) = (
142
0
            self.data[hw_enc].prev as usize,
143
0
            self.data[hw_enc].next as usize,
144
0
        );
145
0
        self.data[iprev].next = self.data[hw_enc].next;
146
0
        self.data[inext].prev = self.data[hw_enc].prev;
147
0
        self.data[hw_enc].prev = u8::MAX;
148
0
        self.data[hw_enc].next = u8::MAX;
149
0
        if hw_enc == self.head as usize {
150
0
            if hw_enc == inext {
151
0
                // There are no regs in the LRU
152
0
                self.head = u8::MAX;
153
0
            } else {
154
0
                self.head = inext as u8;
155
0
            }
156
0
        }
157
0
        trace!("Removed p{hw_enc} from {:?} LRU", self.regclass);
158
0
        if cfg!(debug_assertions) {
159
0
            self.validate_lru();
160
0
        }
161
0
    }
162
163
    /// Insert node `i` before node `j` in the list.
164
0
    fn insert_before(&mut self, i: u8, j: u8) {
165
0
        trace!(
166
0
            "Before inserting: {:?} LRU. head: {:?}, Actual data: {:?}",
167
            self.regclass,
168
            self.head,
169
            self.data
170
        );
171
0
        trace!("Inserting p{i} before {j} in {:?} LRU", self.regclass);
172
0
        let prev = self.data[j as usize].prev;
173
0
        self.data[prev as usize].next = i;
174
0
        self.data[j as usize].prev = i;
175
0
        self.data[i as usize] = LruNode { next: j, prev };
176
0
        trace!("Done inserting p{i} before {j} in {:?} LRU", self.regclass);
177
0
        if cfg!(debug_assertions) {
178
0
            self.validate_lru();
179
0
        }
180
0
    }
181
182
0
    pub fn is_empty(&self) -> bool {
183
0
        self.head == u8::MAX
184
0
    }
185
186
    // Using this to debug.
187
0
    fn validate_lru(&self) {
188
0
        trace!(
189
0
            "{:?} LRU. head: {:?}, Actual data: {:?}",
190
            self.regclass,
191
            self.head,
192
            self.data
193
        );
194
0
        if self.head != u8::MAX {
195
0
            let mut node = self.data[self.head as usize].next;
196
0
            let mut seen = FxHashSet::default();
197
0
            while node != self.head {
198
0
                if seen.contains(&node) {
199
0
                    panic!(
200
0
                        "Cycle detected in {:?} LRU.\n
201
0
                        head: {:?}, actual data: {:?}",
202
                        self.regclass, self.head, self.data
203
                    );
204
0
                }
205
0
                seen.insert(node);
206
0
                node = self.data[node as usize].next;
207
            }
208
0
            for i in 0..self.data.len() {
209
0
                if self.data[i].prev == u8::MAX && self.data[i].next == u8::MAX {
210
                    // Removed
211
0
                    continue;
212
0
                }
213
0
                if self.data[i].prev == u8::MAX || self.data[i].next == u8::MAX {
214
0
                    panic!(
215
0
                        "Invalid LRU. p{} next or previous is an invalid value, but not both",
216
                        i
217
                    );
218
0
                }
219
0
                if self.data[self.data[i].prev as usize].next != i as u8 {
220
0
                    panic!(
221
0
                        "Invalid LRU. p{i} prev is p{:?}, but p{:?} next is {:?}",
222
0
                        self.data[i].prev,
223
0
                        self.data[i].prev,
224
0
                        self.data[self.data[i].prev as usize].next
225
                    );
226
0
                }
227
0
                if self.data[self.data[i].next as usize].prev != i as u8 {
228
0
                    panic!(
229
0
                        "Invalid LRU. p{i} next is p{:?}, but p{:?} prev is p{:?}",
230
0
                        self.data[i].next,
231
0
                        self.data[i].next,
232
0
                        self.data[self.data[i].next as usize].prev
233
                    );
234
0
                }
235
            }
236
0
        }
237
0
    }
238
}
239
240
impl fmt::Debug for Lru {
241
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242
        use alloc::format;
243
0
        let data_str = if self.head == u8::MAX {
244
0
            format!("<empty>")
245
        } else {
246
0
            let mut data_str = format!("p{}", self.head);
247
0
            let mut node = self.data[self.head as usize].next;
248
0
            let mut seen = FxHashSet::default();
249
0
            while node != self.head {
250
0
                if seen.contains(&node) {
251
0
                    panic!(
252
0
                        "The {:?} LRU is messed up:
253
0
                       head: {:?}, {:?} -> p{node}, actual data: {:?}",
254
                        self.regclass, self.head, data_str, self.data
255
                    );
256
0
                }
257
0
                seen.insert(node);
258
0
                data_str += &format!(" -> p{}", node);
259
0
                node = self.data[node as usize].next;
260
            }
261
0
            data_str
262
        };
263
0
        f.debug_struct("Lru")
264
0
            .field("head", if self.is_empty() { &"none" } else { &self.head })
265
0
            .field("class", &self.regclass)
266
0
            .field("data", &data_str)
267
0
            .finish()
268
0
    }
269
}
270
271
#[derive(Clone)]
272
pub struct PartedByRegClass<T> {
273
    pub items: [T; 3],
274
}
275
276
impl<T: Copy> Copy for PartedByRegClass<T> {}
277
278
impl<T> Index<RegClass> for PartedByRegClass<T> {
279
    type Output = T;
280
281
0
    fn index(&self, index: RegClass) -> &Self::Output {
282
0
        &self.items[index as usize]
283
0
    }
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<core::option::Option<regalloc2::PReg>> as core::ops::index::Index<regalloc2::RegClass>>::index
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<regalloc2::fastalloc::lru::Lru> as core::ops::index::Index<regalloc2::RegClass>>::index
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<regalloc2::PReg> as core::ops::index::Index<regalloc2::RegClass>>::index
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<i16> as core::ops::index::Index<regalloc2::RegClass>>::index
284
}
285
286
impl<T> IndexMut<RegClass> for PartedByRegClass<T> {
287
0
    fn index_mut(&mut self, index: RegClass) -> &mut Self::Output {
288
0
        &mut self.items[index as usize]
289
0
    }
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<core::option::Option<regalloc2::PReg>> as core::ops::index::IndexMut<regalloc2::RegClass>>::index_mut
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<regalloc2::fastalloc::lru::Lru> as core::ops::index::IndexMut<regalloc2::RegClass>>::index_mut
Unexecuted instantiation: <regalloc2::fastalloc::lru::PartedByRegClass<i16> as core::ops::index::IndexMut<regalloc2::RegClass>>::index_mut
290
}
291
292
impl<T: PartialEq> PartialEq for PartedByRegClass<T> {
293
0
    fn eq(&self, other: &Self) -> bool {
294
0
        self.items.eq(&other.items)
295
0
    }
296
}
297
298
/// Least-recently-used caches for register classes Int, Float, and Vector, respectively.
299
pub type Lrus = PartedByRegClass<Lru>;
300
301
impl Lrus {
302
0
    pub fn new(int_regs: &PRegSet, float_regs: &PRegSet, vec_regs: &PRegSet) -> Self {
303
0
        Self {
304
0
            items: [
305
0
                Lru::new(RegClass::Int, int_regs),
306
0
                Lru::new(RegClass::Float, float_regs),
307
0
                Lru::new(RegClass::Vector, vec_regs),
308
0
            ],
309
0
        }
310
0
    }
311
}
312
313
use core::fmt::{Debug, Display};
314
315
impl<T: Display> Display for PartedByRegClass<T> {
316
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317
0
        write!(
318
0
            f,
319
0
            "{{ int: {}, float: {}, vector: {} }}",
320
0
            self.items[0], self.items[1], self.items[2]
321
        )
322
0
    }
323
}
324
325
impl<T: Debug> Debug for PartedByRegClass<T> {
326
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327
0
        write!(
328
0
            f,
329
0
            "{{ int: {:?}, float: {:?}, vector: {:?} }}",
330
0
            self.items[0], self.items[1], self.items[2]
331
        )
332
0
    }
333
}