Coverage Report

Created: 2025-12-09 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/winch/codegen/src/regset.rs
Line
Count
Source
1
use crate::isa::reg::{Reg, RegClass};
2
3
/// A bit set to track register availability.
4
pub(crate) struct RegSet {
5
    /// Bitset to track general purpose register availability.
6
    gpr: RegBitSet,
7
    /// Bitset to track floating-point register availability.
8
    fpr: RegBitSet,
9
}
10
11
use std::ops::{Index, IndexMut};
12
13
impl Index<RegClass> for RegSet {
14
    type Output = RegBitSet;
15
16
14.1M
    fn index(&self, class: RegClass) -> &Self::Output {
17
14.1M
        match class {
18
10.6M
            RegClass::Int => &self.gpr,
19
3.47M
            RegClass::Float => &self.fpr,
20
0
            c => unreachable!("Unexpected register class {:?}", c),
21
        }
22
14.1M
    }
23
}
24
25
impl IndexMut<RegClass> for RegSet {
26
5.42M
    fn index_mut(&mut self, class: RegClass) -> &mut Self::Output {
27
5.42M
        match class {
28
4.02M
            RegClass::Int => &mut self.gpr,
29
1.40M
            RegClass::Float => &mut self.fpr,
30
0
            c => unreachable!("Unexpected register class {:?}", c),
31
        }
32
5.42M
    }
33
}
34
35
/// Bitset for a particular register class.
36
pub struct RegBitSet {
37
    /// The register class.
38
    class: RegClass,
39
    /// The set of allocatable
40
    allocatable: u64,
41
    /// The set of non-alloctable registers.
42
    non_allocatable: u64,
43
    /// The max number of registers.
44
    /// Invariant:
45
    /// When allocating or freeing a register the encoding (index) of the
46
    /// register must be less than the max property.
47
    max: usize,
48
}
49
50
impl RegBitSet {
51
    /// Creates an integer register class bitset.
52
77.3k
    pub fn int(allocatable: u64, non_allocatable: u64, max: usize) -> Self {
53
        // Assert that one set is the complement of the other.
54
77.3k
        debug_assert!(allocatable & non_allocatable == 0);
55
77.3k
        Self {
56
77.3k
            class: RegClass::Int,
57
77.3k
            allocatable,
58
77.3k
            non_allocatable,
59
77.3k
            max,
60
77.3k
        }
61
77.3k
    }
62
63
    /// Creates a float register class bitset.
64
77.3k
    pub fn float(allocatable: u64, non_allocatable: u64, max: usize) -> Self {
65
        // Assert that one set is the complement of the other.
66
77.3k
        debug_assert!(allocatable & non_allocatable == 0);
67
77.3k
        Self {
68
77.3k
            class: RegClass::Float,
69
77.3k
            allocatable,
70
77.3k
            non_allocatable,
71
77.3k
            max,
72
77.3k
        }
73
77.3k
    }
74
}
75
76
impl RegSet {
77
    /// Create a new register set.
78
77.3k
    pub fn new(gpr: RegBitSet, fpr: RegBitSet) -> Self {
79
77.3k
        debug_assert!(gpr.class == RegClass::Int);
80
77.3k
        debug_assert!(fpr.class == RegClass::Float);
81
82
77.3k
        Self { gpr, fpr }
83
77.3k
    }
84
85
    /// Allocate the next available register of the given class,
86
    /// returning `None` if there are no more registers available.
87
2.36M
    pub fn reg_for_class(&mut self, class: RegClass) -> Option<Reg> {
88
2.36M
        self.available(class).then(|| {
89
2.36M
            let bitset = &self[class];
90
2.36M
            let index = bitset.allocatable.trailing_zeros();
91
2.36M
            self.allocate(class, index.into());
92
2.36M
            Reg::from(class, index as usize)
93
2.36M
        })
94
2.36M
    }
95
96
    /// Request a specific register.
97
355k
    pub fn reg(&mut self, reg: Reg) -> Option<Reg> {
98
355k
        let index = reg.hw_enc();
99
355k
        self.named_reg_available(reg).then(|| {
100
349k
            self.allocate(reg.class(), index.try_into().unwrap());
101
349k
            reg
102
349k
        })
103
355k
    }
104
105
    /// Marks the specified register as available, utilizing the
106
    /// register class to determine the bitset that requires updating.
107
3.08M
    pub fn free(&mut self, reg: Reg) {
108
3.08M
        let bitset = &self[reg.class()];
109
3.08M
        let index = reg.hw_enc();
110
3.08M
        assert!(index < bitset.max);
111
3.08M
        let index = u64::try_from(index).unwrap();
112
3.08M
        if !self.is_non_allocatable(reg.class(), index) {
113
2.71M
            self[reg.class()].allocatable |= 1 << index;
114
2.71M
        }
115
3.08M
    }
116
117
    /// Returns true if the specified register is allocatable.
118
498k
    pub fn named_reg_available(&self, reg: Reg) -> bool {
119
498k
        let bitset = &self[reg.class()];
120
498k
        assert!(reg.hw_enc() < bitset.max);
121
498k
        let index = 1 << reg.hw_enc();
122
123
498k
        (!bitset.allocatable & index) == 0
124
5.91k
            || self.is_non_allocatable(reg.class(), reg.hw_enc().try_into().unwrap())
125
498k
    }
126
127
2.36M
    fn available(&self, class: RegClass) -> bool {
128
2.36M
        let bitset = &self[class];
129
2.36M
        bitset.allocatable != 0
130
2.36M
    }
131
132
2.71M
    fn allocate(&mut self, class: RegClass, index: u64) {
133
2.71M
        if !self.is_non_allocatable(class, index) {
134
2.71M
            self[class].allocatable &= !(1 << index);
135
2.71M
        }
136
2.71M
    }
137
138
5.79M
    fn is_non_allocatable(&self, class: RegClass, index: u64) -> bool {
139
5.79M
        let bitset = &self[class];
140
5.79M
        let non_allocatable = bitset.non_allocatable;
141
5.79M
        non_allocatable != 0 && !non_allocatable & (1 << index) == 0
142
5.79M
    }
143
}
144
145
#[cfg(test)]
146
mod tests {
147
    use super::{Reg, RegBitSet, RegClass, RegSet};
148
149
    const UNIVERSE: u64 = (1 << 16) - 1;
150
    const MAX: usize = 16;
151
152
    #[test]
153
    fn test_any_gpr() {
154
        let bitset = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX);
155
        let zero = RegBitSet::float(0, 0, MAX);
156
        let mut set = RegSet::new(bitset, zero);
157
        for _ in 0..16 {
158
            let gpr = set.reg_for_class(RegClass::Int);
159
            assert!(gpr.is_some())
160
        }
161
162
        assert!(!set.available(RegClass::Int));
163
        assert!(set.reg_for_class(RegClass::Int).is_none())
164
    }
165
166
    #[test]
167
    fn test_gpr() {
168
        let non_allocatable: u64 = 1 << 5;
169
        let all = UNIVERSE & !non_allocatable;
170
        let non_alloc = Reg::int(5);
171
        let alloc = Reg::int(2);
172
        let bitset = RegBitSet::int(all, non_allocatable, MAX);
173
        let zero = RegBitSet::float(0, 0, MAX);
174
        let mut set = RegSet::new(bitset, zero);
175
        // Requesting a non alloctable register returns the register
176
        // and doesn't allocate it.
177
        assert!(set.reg(non_alloc).is_some());
178
        assert!(set.reg(non_alloc).is_some());
179
        // Requesting an allocatable register twice returns none the
180
        // second time.
181
        assert!(set.reg(alloc).is_some());
182
        assert!(set.reg(alloc).is_none());
183
    }
184
185
    #[test]
186
    fn test_free_reg() {
187
        let set = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX);
188
        let zero = RegBitSet::float(0, 0, MAX);
189
        let mut set = RegSet::new(set, zero);
190
        let gpr = set.reg_for_class(RegClass::Int).unwrap();
191
        set.free(gpr);
192
        assert!(set.reg(gpr).is_some());
193
    }
194
}