Path: blob/main/cranelift/codegen/src/ir/user_stack_maps.rs
1693 views
//! User-defined stack maps.1//!2//! This module provides types allowing users to define stack maps and associate3//! them with safepoints.4//!5//! A **safepoint** is a program point (i.e. CLIF instruction) where it must be6//! safe to run GC. Currently all non-tail call instructions are considered7//! safepoints. (This does *not* allow, for example, skipping safepoints for8//! calls that are statically known not to trigger collections, or to have a9//! safepoint on a volatile load to a page that gets protected when it is time10//! to GC, triggering a fault that pauses the mutator and lets the collector do11//! its work before resuming the mutator. We can lift this restriction in the12//! future, if necessary.)13//!14//! A **stack map** is a description of where to find all the GC-managed values15//! that are live at a particular safepoint. Stack maps let the collector find16//! on-stack roots. Each stack map is logically a set of offsets into the stack17//! frame and the type of value at that associated offset. However, because the18//! stack layout isn't defined until much later in the compiler's pipeline, each19//! stack map entry instead includes both an `ir::StackSlot` and an offset20//! within that slot.21//!22//! These stack maps are **user-defined** in that it is the CLIF producer's23//! responsibility to identify and spill the live GC-managed values and attach24//! the associated stack map entries to each safepoint themselves (see25//! `cranelift_frontend::Function::declare_needs_stack_map` and26//! `cranelift_codegen::ir::DataFlowGraph::append_user_stack_map_entry`). Cranelift27//! will not insert spills and record these stack map entries automatically.28//!29//! Logically, a set of stack maps for a function record a table of the form:30//!31//! ```text32//! +---------------------+-------------------------------------------+33//! | Instruction Pointer | SP-Relative Offsets of Live GC References |34//! +---------------------+-------------------------------------------+35//! | 0x12345678 | 2, 6, 12 |36//! | 0x1234abcd | 2, 6 |37//! | ... | ... |38//! +---------------------+-------------------------------------------+39//! ```40//!41//! Where "instruction pointer" is an instruction pointer within the function,42//! and "offsets of live GC references" contains the offsets (in units of words)43//! from the frame's stack pointer where live GC references are stored on the44//! stack. Instruction pointers within the function that do not have an entry in45//! this table are not GC safepoints.46//!47//! Because48//!49//! * offsets of live GC references are relative from the stack pointer, and50//! * stack frames grow down from higher addresses to lower addresses,51//!52//! to get a pointer to a live reference at offset `x` within a stack frame, you53//! add `x` to the frame's stack pointer.54//!55//! For example, to calculate the pointer to the live GC reference inside "frame56//! 1" below, you would do `frame_1_sp + x`:57//!58//! ```text59//! Stack60//! +-------------------+61//! | Frame 0 |62//! | |63//! | | |64//! | +-------------------+ <--- Frame 0's SP65//! | | Frame 1 |66//! Grows | |67//! down | |68//! | | Live GC reference | --+--69//! | | | |70//! | | | |71//! V | | x = offset of live GC reference72//! | | |73//! | | |74//! +-------------------+ --+-- <--- Frame 1's SP75//! | Frame 2 |76//! | ... |77//! ```78//!79//! An individual `UserStackMap` is associated with just one instruction pointer80//! within the function, contains the size of the stack frame, and represents81//! the stack frame as a bitmap. There is one bit per word in the stack frame,82//! and if the bit is set, then the word contains a live GC reference.83//!84//! Note that a caller's outgoing argument stack slots (if any) and callee's85//! incoming argument stack slots (if any) overlap, so we must choose which86//! function's stack maps record live GC references in these slots. We record87//! the incoming arguments in the callee's stack map. This choice plays nice88//! with tail calls, where by the time we transfer control to the callee, the89//! caller no longer exists.9091use crate::ir;92use cranelift_bitset::CompoundBitSet;93use cranelift_entity::PrimaryMap;94use smallvec::SmallVec;9596pub(crate) type UserStackMapEntryVec = SmallVec<[UserStackMapEntry; 4]>;9798/// A stack map entry describes a single GC-managed value and its location on99/// the stack.100///101/// A stack map entry is associated with a particular instruction, and that102/// instruction must be a safepoint. The GC-managed value must be stored in the103/// described location across this entry's instruction.104#[derive(Clone, Debug, PartialEq, Hash)]105#[cfg_attr(106feature = "enable-serde",107derive(serde_derive::Serialize, serde_derive::Deserialize)108)]109pub struct UserStackMapEntry {110/// The type of the value stored in this stack map entry.111pub ty: ir::Type,112113/// The stack slot that this stack map entry is within.114pub slot: ir::StackSlot,115116/// The offset within the stack slot where this entry's value can be found.117pub offset: u32,118}119120/// A compiled stack map, describing the location of many GC-managed values.121///122/// A stack map is associated with a particular instruction, and that123/// instruction is a safepoint.124#[derive(Clone, Debug, PartialEq)]125#[cfg_attr(126feature = "enable-serde",127derive(serde_derive::Deserialize, serde_derive::Serialize)128)]129pub struct UserStackMap {130// Offsets into the frame's sized stack slots that are GC references, by type.131by_type: SmallVec<[(ir::Type, CompoundBitSet); 1]>,132133// The offset of the sized stack slots, from SP, for this stack map's134// associated PC.135//136// This is initially `None` upon construction during lowering, but filled in137// after regalloc during emission when we have the precise frame layout.138sp_to_sized_stack_slots: Option<u32>,139}140141impl UserStackMap {142/// Coalesce the given entries into a new `UserStackMap`.143pub(crate) fn new(144entries: &[UserStackMapEntry],145stack_slot_offsets: &PrimaryMap<ir::StackSlot, u32>,146) -> Self {147let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default();148149for entry in entries {150let offset = stack_slot_offsets[entry.slot] + entry.offset;151let offset = usize::try_from(offset).unwrap();152153// Don't bother trying to avoid an `O(n)` search here: `n` is154// basically always one in practice; even if it isn't, there aren't155// that many different CLIF types.156let index = by_type157.iter()158.position(|(ty, _)| *ty == entry.ty)159.unwrap_or_else(|| {160by_type.push((entry.ty, CompoundBitSet::with_capacity(offset + 1)));161by_type.len() - 1162});163164by_type[index].1.insert(offset);165}166167UserStackMap {168by_type,169sp_to_sized_stack_slots: None,170}171}172173/// Finalize this stack map by filling in the SP-to-stack-slots offset.174pub(crate) fn finalize(&mut self, sp_to_sized_stack_slots: u32) {175debug_assert!(self.sp_to_sized_stack_slots.is_none());176self.sp_to_sized_stack_slots = Some(sp_to_sized_stack_slots);177}178179/// Iterate over the entries in this stack map.180///181/// Yields pairs of the type of GC reference that is at the offset, and the182/// offset from SP. If a pair `(i64, 0x42)` is yielded, for example, then183/// when execution is at this stack map's associated PC, `SP + 0x42` is a184/// pointer to an `i64`, and that `i64` is a live GC reference.185pub fn entries(&self) -> impl Iterator<Item = (ir::Type, u32)> + '_ {186let sp_to_sized_stack_slots = self.sp_to_sized_stack_slots.expect(187"`sp_to_sized_stack_slots` should have been filled in before this stack map was used",188);189self.by_type.iter().flat_map(move |(ty, bitset)| {190bitset.iter().map(move |slot_offset| {191(192*ty,193sp_to_sized_stack_slots + u32::try_from(slot_offset).unwrap(),194)195})196})197}198}199200201