cranelift_codegen/ir/user_stack_maps.rs
1//! User-defined stack maps.
2//!
3//! This module provides types allowing users to define stack maps and associate
4//! them with safepoints.
5//!
6//! A **safepoint** is a program point (i.e. CLIF instruction) where it must be
7//! safe to run GC. Currently all non-tail call instructions are considered
8//! safepoints. (This does *not* allow, for example, skipping safepoints for
9//! calls that are statically known not to trigger collections, or to have a
10//! safepoint on a volatile load to a page that gets protected when it is time
11//! to GC, triggering a fault that pauses the mutator and lets the collector do
12//! its work before resuming the mutator. We can lift this restriction in the
13//! future, if necessary.)
14//!
15//! A **stack map** is a description of where to find all the GC-managed values
16//! that are live at a particular safepoint. Stack maps let the collector find
17//! on-stack roots. Each stack map is logically a set of offsets into the stack
18//! frame and the type of value at that associated offset. However, because the
19//! stack layout isn't defined until much later in the compiler's pipeline, each
20//! stack map entry instead includes both an `ir::StackSlot` and an offset
21//! within that slot.
22//!
23//! These stack maps are **user-defined** in that it is the CLIF producer's
24//! responsibility to identify and spill the live GC-managed values and attach
25//! the associated stack map entries to each safepoint themselves (see
26//! `cranelift_frontend::Function::declare_needs_stack_map` and
27//! `cranelift_codegen::ir::DataFlowGraph::append_user_stack_map_entry`). Cranelift
28//! will not insert spills and record these stack map entries automatically.
29//!
30//! Logically, a set of stack maps for a function record a table of the form:
31//!
32//! ```text
33//! +---------------------+-------------------------------------------+
34//! | Instruction Pointer | SP-Relative Offsets of Live GC References |
35//! +---------------------+-------------------------------------------+
36//! | 0x12345678 | 2, 6, 12 |
37//! | 0x1234abcd | 2, 6 |
38//! | ... | ... |
39//! +---------------------+-------------------------------------------+
40//! ```
41//!
42//! Where "instruction pointer" is an instruction pointer within the function,
43//! and "offsets of live GC references" contains the offsets (in units of words)
44//! from the frame's stack pointer where live GC references are stored on the
45//! stack. Instruction pointers within the function that do not have an entry in
46//! this table are not GC safepoints.
47//!
48//! Because
49//!
50//! * offsets of live GC references are relative from the stack pointer, and
51//! * stack frames grow down from higher addresses to lower addresses,
52//!
53//! to get a pointer to a live reference at offset `x` within a stack frame, you
54//! add `x` to the frame's stack pointer.
55//!
56//! For example, to calculate the pointer to the live GC reference inside "frame
57//! 1" below, you would do `frame_1_sp + x`:
58//!
59//! ```text
60//! Stack
61//! +-------------------+
62//! | Frame 0 |
63//! | |
64//! | | |
65//! | +-------------------+ <--- Frame 0's SP
66//! | | Frame 1 |
67//! Grows | |
68//! down | |
69//! | | Live GC reference | --+--
70//! | | | |
71//! | | | |
72//! V | | x = offset of live GC reference
73//! | | |
74//! | | |
75//! +-------------------+ --+-- <--- Frame 1's SP
76//! | Frame 2 |
77//! | ... |
78//! ```
79//!
80//! An individual `UserStackMap` is associated with just one instruction pointer
81//! within the function, contains the size of the stack frame, and represents
82//! the stack frame as a bitmap. There is one bit per word in the stack frame,
83//! and if the bit is set, then the word contains a live GC reference.
84//!
85//! Note that a caller's outgoing argument stack slots (if any) and callee's
86//! incoming argument stack slots (if any) overlap, so we must choose which
87//! function's stack maps record live GC references in these slots. We record
88//! the incoming arguments in the callee's stack map. This choice plays nice
89//! with tail calls, where by the time we transfer control to the callee, the
90//! caller no longer exists.
91
92use crate::ir;
93use cranelift_bitset::CompoundBitSet;
94use cranelift_entity::PrimaryMap;
95use smallvec::SmallVec;
96
97pub(crate) type UserStackMapEntryVec = SmallVec<[UserStackMapEntry; 4]>;
98
99/// A stack map entry describes a single GC-managed value and its location on
100/// the stack.
101///
102/// A stack map entry is associated with a particular instruction, and that
103/// instruction must be a safepoint. The GC-managed value must be stored in the
104/// described location across this entry's instruction.
105#[derive(Clone, Debug, PartialEq, Hash)]
106#[cfg_attr(
107 feature = "enable-serde",
108 derive(serde_derive::Serialize, serde_derive::Deserialize)
109)]
110pub struct UserStackMapEntry {
111 /// The type of the value stored in this stack map entry.
112 pub ty: ir::Type,
113
114 /// The stack slot that this stack map entry is within.
115 pub slot: ir::StackSlot,
116
117 /// The offset within the stack slot where this entry's value can be found.
118 pub offset: u32,
119}
120
121/// A compiled stack map, describing the location of many GC-managed values.
122///
123/// A stack map is associated with a particular instruction, and that
124/// instruction is a safepoint.
125#[derive(Clone, Debug, PartialEq)]
126#[cfg_attr(
127 feature = "enable-serde",
128 derive(serde_derive::Deserialize, serde_derive::Serialize)
129)]
130pub struct UserStackMap {
131 // Offsets into the frame's sized stack slots that are GC references, by type.
132 by_type: SmallVec<[(ir::Type, CompoundBitSet); 1]>,
133
134 // The offset of the sized stack slots, from SP, for this stack map's
135 // associated PC.
136 //
137 // This is initially `None` upon construction during lowering, but filled in
138 // after regalloc during emission when we have the precise frame layout.
139 sp_to_sized_stack_slots: Option<u32>,
140}
141
142impl UserStackMap {
143 /// Coalesce the given entries into a new `UserStackMap`.
144 pub(crate) fn new(
145 entries: &[UserStackMapEntry],
146 stack_slot_offsets: &PrimaryMap<ir::StackSlot, u32>,
147 ) -> Self {
148 let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default();
149
150 for entry in entries {
151 let offset = stack_slot_offsets[entry.slot] + entry.offset;
152 let offset = usize::try_from(offset).unwrap();
153
154 // Don't bother trying to avoid an `O(n)` search here: `n` is
155 // basically always one in practice; even if it isn't, there aren't
156 // that many different CLIF types.
157 let index = by_type
158 .iter()
159 .position(|(ty, _)| *ty == entry.ty)
160 .unwrap_or_else(|| {
161 by_type.push((entry.ty, CompoundBitSet::with_capacity(offset + 1)));
162 by_type.len() - 1
163 });
164
165 by_type[index].1.insert(offset);
166 }
167
168 UserStackMap {
169 by_type,
170 sp_to_sized_stack_slots: None,
171 }
172 }
173
174 /// Finalize this stack map by filling in the SP-to-stack-slots offset.
175 pub(crate) fn finalize(&mut self, sp_to_sized_stack_slots: u32) {
176 debug_assert!(self.sp_to_sized_stack_slots.is_none());
177 self.sp_to_sized_stack_slots = Some(sp_to_sized_stack_slots);
178 }
179
180 /// Iterate over the entries in this stack map.
181 ///
182 /// Yields pairs of the type of GC reference that is at the offset, and the
183 /// offset from SP. If a pair `(i64, 0x42)` is yielded, for example, then
184 /// when execution is at this stack map's associated PC, `SP + 0x42` is a
185 /// pointer to an `i64`, and that `i64` is a live GC reference.
186 pub fn entries(&self) -> impl Iterator<Item = (ir::Type, u32)> + '_ {
187 let sp_to_sized_stack_slots = self.sp_to_sized_stack_slots.expect(
188 "`sp_to_sized_stack_slots` should have been filled in before this stack map was used",
189 );
190 self.by_type.iter().flat_map(move |(ty, bitset)| {
191 bitset.iter().map(move |slot_offset| {
192 (
193 *ty,
194 sp_to_sized_stack_slots + u32::try_from(slot_offset).unwrap(),
195 )
196 })
197 })
198 }
199}