cranelift_codegen/binemit/
stack_map.rs

1use cranelift_bitset::CompoundBitSet;
2
3/// Stack maps record which words in a stack frame contain live GC references at
4/// a given instruction pointer.
5///
6/// Logically, a set of stack maps for a function record a table of the form:
7///
8/// ```text
9/// +---------------------+-------------------------------------------+
10/// | Instruction Pointer | SP-Relative Offsets of Live GC References |
11/// +---------------------+-------------------------------------------+
12/// | 0x12345678          | 2, 6, 12                                  |
13/// | 0x1234abcd          | 2, 6                                      |
14/// | ...                 | ...                                       |
15/// +---------------------+-------------------------------------------+
16/// ```
17///
18/// Where "instruction pointer" is an instruction pointer within the function,
19/// and "offsets of live GC references" contains the offsets (in units of words)
20/// from the frame's stack pointer where live GC references are stored on the
21/// stack. Instruction pointers within the function that do not have an entry in
22/// this table are not GC safepoints.
23///
24/// Because
25///
26/// * offsets of live GC references are relative from the stack pointer, and
27/// * stack frames grow down from higher addresses to lower addresses,
28///
29/// to get a pointer to a live reference at offset `x` within a stack frame, you
30/// add `x` from the frame's stack pointer.
31///
32/// For example, to calculate the pointer to the live GC reference inside "frame
33/// 1" below, you would do `frame_1_sp + x`:
34///
35/// ```text
36///           Stack
37///         +-------------------+
38///         | Frame 0           |
39///         |                   |
40///    |    |                   |
41///    |    +-------------------+ <--- Frame 0's SP
42///    |    | Frame 1           |
43///  Grows  |                   |
44///  down   |                   |
45///    |    | Live GC reference | --+--
46///    |    |                   |   |
47///    |    |                   |   |
48///    V    |                   |   x = offset of live GC reference
49///         |                   |   |
50///         |                   |   |
51///         +-------------------+ --+--  <--- Frame 1's SP
52///         | Frame 2           |
53///         | ...               |
54/// ```
55///
56/// An individual `StackMap` is associated with just one instruction pointer
57/// within the function, contains the size of the stack frame, and represents
58/// the stack frame as a bitmap. There is one bit per word in the stack frame,
59/// and if the bit is set, then the word contains a live GC reference.
60///
61/// Note that a caller's `OutgoingArg` stack slots and callee's `IncomingArg`
62/// stack slots overlap, so we must choose which function's stack maps record
63/// live GC references in these slots. We record the `IncomingArg`s in the
64/// callee's stack map.
65#[derive(Clone, Debug, PartialEq, Eq)]
66#[cfg_attr(
67    feature = "enable-serde",
68    derive(serde_derive::Deserialize, serde_derive::Serialize)
69)]
70pub struct StackMap {
71    bitset: CompoundBitSet,
72    mapped_words: u32,
73}
74
75impl StackMap {
76    /// Create a stack map from a slice of booleans.
77    pub fn from_slice(bools: &[bool]) -> Self {
78        let mut bitset = CompoundBitSet::with_capacity(bools.len());
79        for (i, b) in bools.iter().enumerate() {
80            if *b {
81                bitset.insert(i);
82            }
83        }
84        Self {
85            mapped_words: u32::try_from(bools.len()).unwrap(),
86            bitset,
87        }
88    }
89
90    /// Returns a specified bit.
91    pub fn get_bit(&self, bit_index: usize) -> bool {
92        self.bitset.contains(bit_index)
93    }
94
95    /// Returns the raw bitmap that represents this stack map.
96    pub fn into_bitset(self) -> CompoundBitSet {
97        self.bitset
98    }
99
100    /// Returns the number of words represented by this stack map.
101    pub fn mapped_words(&self) -> u32 {
102        self.mapped_words
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use alloc::vec::Vec;
110
111    type Num = u32;
112    const NUM_BITS: usize = core::mem::size_of::<Num>() * 8;
113
114    #[test]
115    fn stack_maps() {
116        let vec: Vec<bool> = Vec::new();
117        assert!(StackMap::from_slice(&vec).bitset.is_empty());
118
119        let mut vec: [bool; NUM_BITS] = Default::default();
120        let set_true_idx = [5, 7, 24, 31];
121
122        for &idx in &set_true_idx {
123            vec[idx] = true;
124        }
125
126        let mut vec = vec.to_vec();
127        let stack_map = StackMap::from_slice(&vec);
128        for idx in 0..32 {
129            assert_eq!(stack_map.get_bit(idx), set_true_idx.contains(&idx));
130        }
131
132        vec.push(false);
133        vec.push(true);
134        let res = StackMap::from_slice(&vec);
135        for idx in 0..32 {
136            assert_eq!(stack_map.get_bit(idx), set_true_idx.contains(&idx));
137        }
138        assert!(!res.get_bit(32));
139        assert!(res.get_bit(33));
140    }
141}