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