Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/codegen/src/ir/user_stack_maps.rs
1693 views
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
92
use crate::ir;
93
use cranelift_bitset::CompoundBitSet;
94
use cranelift_entity::PrimaryMap;
95
use smallvec::SmallVec;
96
97
pub(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
)]
110
pub 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
)]
130
pub 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
142
impl 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
}
200
201