lib/std/lang/gen/bitset.rad 6.1 KiB raw
1
//! Bitset utilities for register tracking.
2
//!
3
//! Provides efficient bit-level set operations for tracking live registers
4
//! during liveness analysis and register allocation.
5
@test mod tests;
6
7
use std::lang::alloc;
8
9
/// Return the minimum of two 32-bit values.
10
fn min(a: u32, b: u32) -> u32 {
11
    if a < b {
12
        return a;
13
    }
14
    return b;
15
}
16
17
/// Calculate the number of 32-bit words needed to store `n` bits.
18
export fn wordsFor(n: u32) -> u32 {
19
    return (n + 31) / 32;
20
}
21
22
/// A fixed-size bitset backed by an array of 32-bit words.
23
export record Bitset: Copy {
24
    /// Backing words. The storage must outlive the bitset and its iterators.
25
    bits: *unsafe mut [u32],
26
    /// Number of bits this bitset can hold.
27
    len: u32,
28
}
29
30
/// Create a new bitset backed by the given zero-initialized storage.
31
/// The storage must outlive the bitset and its iterators.
32
export unsafe fn new(bits: &mut [u32]) -> Bitset {
33
    let len = bits.len * 32;
34
    return Bitset { bits: bits as *unsafe mut [u32], len };
35
}
36
37
/// Create a new bitset backed by the given storage, zeroing it first.
38
/// The storage must outlive the bitset and its iterators.
39
export unsafe fn init(bits: &mut [u32]) -> Bitset {
40
    for i in 0..bits.len {
41
        set bits[i] = 0;
42
    }
43
    return new(bits);
44
}
45
46
/// Create a bitset from arena allocation.
47
export unsafe fn allocate(arena: &mut alloc::Arena, len: u32) -> Bitset throws (alloc::AllocError) {
48
    let numWords = wordsFor(len);
49
    let bits = try alloc::allocRawSlice(arena, @sizeOf(u32), @alignOf(u32), numWords) as *unsafe mut [u32];
50
51
    return init(bits);
52
}
53
54
/// Set bit `n` in the bitset.
55
export unsafe fn put(bs: &mut Bitset, n: u32) {
56
    if n >= bs.len {
57
        return;
58
    }
59
    let word = n / 32;
60
    let b = n % 32;
61
62
    set bs.bits[word] |= (1 << b);
63
}
64
65
/// Clear bit `n` in the bitset.
66
export unsafe fn clear(bs: &mut Bitset, n: u32) {
67
    if n >= bs.len {
68
        return;
69
    }
70
    let word = n / 32;
71
    let b = n % 32;
72
73
    set bs.bits[word] &= ~(1 << b);
74
}
75
76
/// Check if bit `n` is set.
77
export unsafe fn contains(bs: &Bitset, n: u32) -> bool {
78
    if n >= bs.len {
79
        return false;
80
    }
81
    let word = n / 32;
82
    let b = n % 32;
83
84
    return (bs.bits[word] & (1 << b)) <> 0;
85
}
86
87
/// Count the number of set bits.
88
export unsafe fn count(bs: &Bitset) -> u32 {
89
    let mut total: u32 = 0;
90
    let numWords = bs.bits.len;
91
    for i in 0..numWords {
92
        let word = bs.bits[i];
93
        if word <> 0 {
94
            set total += popCount(word);
95
        }
96
    }
97
    return total;
98
}
99
100
/// Population count for a 32-bit word.
101
fn popCount(x: u32) -> u32 {
102
    let mut n = x;
103
    set n -= ((n >> 1) & 0x55555555);
104
    set n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
105
    set n = (n + (n >> 4)) & 0x0F0F0F0F;
106
    set n += (n >> 8);
107
    set n += (n >> 16);
108
109
    return n & 0x3F;
110
}
111
112
/// Union: `dst = dst | src`.
113
export unsafe fn union_(dst: &mut Bitset, src: &Bitset) {
114
    let numWords = dst.bits.len;
115
    let srcWords = src.bits.len;
116
    let minWords = min(numWords, srcWords);
117
    for i in 0..minWords {
118
        set dst.bits[i] |= src.bits[i];
119
    }
120
}
121
122
/// Subtract: `dst = dst - src`.
123
export unsafe fn subtract(dst: &mut Bitset, src: &Bitset) {
124
    let numWords = dst.bits.len;
125
    let srcWords = src.bits.len;
126
    let minWords = min(numWords, srcWords);
127
    for i in 0..minWords {
128
        set dst.bits[i] &= ~src.bits[i];
129
    }
130
}
131
132
/// Check if two bitsets are equal.
133
export unsafe fn eq(a: &Bitset, b: &Bitset) -> bool {
134
    let numWordsA = a.bits.len;
135
    let numWordsB = b.bits.len;
136
    let minWords = min(numWordsA, numWordsB);
137
138
    for i in 0..minWords {
139
        if a.bits[i] <> b.bits[i] {
140
            return false;
141
        }
142
    }
143
    for i in minWords..numWordsA {
144
        if a.bits[i] <> 0 {
145
            return false;
146
        }
147
    }
148
    for i in minWords..numWordsB {
149
        if b.bits[i] <> 0 {
150
            return false;
151
        }
152
    }
153
    return true;
154
}
155
156
/// Copy bits from source to destination.
157
export unsafe fn copy(dst: &mut Bitset, src: &Bitset) {
158
    let numWords = dst.bits.len;
159
    let srcWords = src.bits.len;
160
    let minWords = min(numWords, srcWords);
161
162
    for i in 0..minWords {
163
        set dst.bits[i] = src.bits[i];
164
    }
165
    // Clear remaining words if destination is larger.
166
    for i in minWords..numWords {
167
        set dst.bits[i] = 0;
168
    }
169
}
170
171
/// Clear all bits.
172
export unsafe fn clearAll(bs: &mut Bitset) {
173
    let numWords = bs.bits.len;
174
    for i in 0..numWords {
175
        set bs.bits[i] = 0;
176
    }
177
}
178
179
/// Iterator state for iterating set bits.
180
export record BitIter: Copy {
181
    /// Bitset being iterated. It must outlive this iterator.
182
    bs: *unsafe Bitset,
183
    /// Current word index.
184
    wordIdx: u32,
185
    /// Remaining bits in the current word (visited bits cleared).
186
    remaining: u32,
187
}
188
189
/// Create an iterator over set bits.
190
/// The bitset and its backing words must outlive the iterator.
191
export unsafe fn iter(bs: &Bitset) -> BitIter {
192
    let remaining = bs.bits[0] if bs.len > 0 else 0;
193
    return BitIter { bs: bs as *unsafe Bitset, wordIdx: 0, remaining };
194
}
195
196
/// Get the next set bit, or nil if none remain.
197
export unsafe fn iterNext(it: &mut BitIter) -> ?u32 {
198
    let numWords = it.bs.bits.len;
199
    // Skip to next non-zero word.
200
    while it.remaining == 0 {
201
        set it.wordIdx += 1;
202
        if it.wordIdx >= numWords {
203
            return nil;
204
        }
205
        set it.remaining = it.bs.bits[it.wordIdx];
206
    }
207
    // Find lowest set bit position using de Bruijn sequence.
208
    let b = ctz(it.remaining);
209
    let n = it.wordIdx * 32 + b;
210
    if n >= it.bs.len {
211
        return nil;
212
    }
213
    // Clear the lowest set bit.
214
    set it.remaining &= it.remaining - 1;
215
216
    return n;
217
}
218
219
/// Count trailing zeros in a 32-bit value.
220
/// Returns the bit position of the lowest set bit (0-31).
221
/// Behavior is undefined if `x` is 0.
222
fn ctz(x: u32) -> u32 {
223
    // De Bruijn sequence for 32-bit CTZ.
224
    constant DEBRUIJN: u32 = 0x077CB531;
225
    constant TABLE: [u8; 32] = [
226
         0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8,
227
        31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9,
228
    ];
229
    // Isolate lowest set bit, multiply by de Bruijn constant, look up.
230
    let isolated = x & (~x + 1);
231
232
    return TABLE[(isolated * DEBRUIJN) >> 27] as u32;
233
}