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 (in
29//! contrast to the old system and its `r64` values).
30
31use crate::ir;
32use cranelift_bitset::CompoundBitSet;
33use cranelift_entity::PrimaryMap;
34use smallvec::SmallVec;
35
36pub(crate) type UserStackMapEntryVec = SmallVec<[UserStackMapEntry; 4]>;
37
38/// A stack map entry describes a single GC-managed value and its location on
39/// the stack.
40///
41/// A stack map entry is associated with a particular instruction, and that
42/// instruction must be a safepoint. The GC-managed value must be stored in the
43/// described location across this entry's instruction.
44#[derive(Clone, Debug, PartialEq, Hash)]
45#[cfg_attr(
46    feature = "enable-serde",
47    derive(serde_derive::Serialize, serde_derive::Deserialize)
48)]
49pub struct UserStackMapEntry {
50    /// The type of the value stored in this stack map entry.
51    pub ty: ir::Type,
52
53    /// The stack slot that this stack map entry is within.
54    pub slot: ir::StackSlot,
55
56    /// The offset within the stack slot where this entry's value can be found.
57    pub offset: u32,
58}
59
60/// A compiled stack map, describing the location of many GC-managed values.
61///
62/// A stack map is associated with a particular instruction, and that
63/// instruction is a safepoint.
64#[derive(Clone, Debug, PartialEq)]
65#[cfg_attr(
66    feature = "enable-serde",
67    derive(serde_derive::Deserialize, serde_derive::Serialize)
68)]
69pub struct UserStackMap {
70    by_type: SmallVec<[(ir::Type, CompoundBitSet); 1]>,
71}
72
73impl UserStackMap {
74    /// Coalesce the given entries into a new `UserStackMap`.
75    pub fn new(
76        entries: &[UserStackMapEntry],
77        stack_slot_offsets: &PrimaryMap<ir::StackSlot, u32>,
78    ) -> Self {
79        let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default();
80
81        for entry in entries {
82            let offset = stack_slot_offsets[entry.slot] + entry.offset;
83            let offset = usize::try_from(offset).unwrap();
84
85            // Don't bother trying to avoid an `O(n)` search here: `n` is
86            // basically always one in practice; even if it isn't, there aren't
87            // that many different CLIF types.
88            let index = by_type
89                .iter()
90                .position(|(ty, _)| *ty == entry.ty)
91                .unwrap_or_else(|| {
92                    by_type.push((entry.ty, CompoundBitSet::with_capacity(offset + 1)));
93                    by_type.len() - 1
94                });
95
96            by_type[index].1.insert(offset);
97        }
98
99        UserStackMap { by_type }
100    }
101}