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