cranelift_codegen/ir/
stackslot.rs

1//! Stack slots.
2//!
3//! The `StackSlotData` struct keeps track of a single stack slot in a function.
4//!
5
6use crate::entity::PrimaryMap;
7use crate::ir::StackSlot;
8use crate::ir::entities::{DynamicStackSlot, DynamicType};
9use core::fmt;
10use core::str::FromStr;
11
12#[cfg(feature = "enable-serde")]
13use serde_derive::{Deserialize, Serialize};
14
15/// The size of an object on the stack, or the size of a stack frame.
16///
17/// We don't use `usize` to represent object sizes on the target platform because Cranelift supports
18/// cross-compilation, and `usize` is a type that depends on the host platform, not the target
19/// platform.
20pub type StackSize = u32;
21
22/// The kind of a stack slot.
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
25pub enum StackSlotKind {
26    /// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load`
27    /// and `stack_store` instructions.
28    ExplicitSlot,
29    /// An explicit stack slot for dynamic vector types. This is a chunk of stack memory
30    /// for use by the `dynamic_stack_load` and `dynamic_stack_store` instructions.
31    ExplicitDynamicSlot,
32}
33
34impl FromStr for StackSlotKind {
35    type Err = ();
36
37    fn from_str(s: &str) -> Result<Self, ()> {
38        use self::StackSlotKind::*;
39        match s {
40            "explicit_slot" => Ok(ExplicitSlot),
41            "explicit_dynamic_slot" => Ok(ExplicitDynamicSlot),
42            _ => Err(()),
43        }
44    }
45}
46
47impl fmt::Display for StackSlotKind {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        use self::StackSlotKind::*;
50        f.write_str(match *self {
51            ExplicitSlot => "explicit_slot",
52            ExplicitDynamicSlot => "explicit_dynamic_slot",
53        })
54    }
55}
56
57/// Contents of a stack slot.
58#[derive(Clone, Debug, PartialEq, Eq, Hash)]
59#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
60pub struct StackSlotData {
61    /// The kind of stack slot.
62    pub kind: StackSlotKind,
63
64    /// Size of stack slot in bytes.
65    pub size: StackSize,
66
67    /// Alignment of stack slot as a power-of-two exponent (log2
68    /// value). The stack slot will be at least this aligned; it may
69    /// be aligned according to other considerations, such as minimum
70    /// stack slot size or machine word size, as well.
71    pub align_shift: u8,
72
73    /// Opaque stackslot metadata handle, passed through to
74    /// compilation result metadata describing stackslot location.
75    ///
76    /// In the face of compiler transforms like inlining that may move
77    /// stackslots between functions, when an embedder wants to
78    /// externally observe stackslots, it needs a first-class way for
79    /// the identity of stackslots to be carried along with the IR
80    /// entities. This opaque `StackSlotKey` allows the embedder to do
81    /// so.
82    pub key: Option<StackSlotKey>,
83}
84
85/// An opaque key uniquely identifying a stack slot.
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
87#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
88pub struct StackSlotKey(u64);
89impl StackSlotKey {
90    /// Construct a [`StackSlotKey`] from raw bits.
91    ///
92    /// An embedder can use any 64-bit value to describe a stack slot;
93    /// there are no restrictions, and the value does not mean
94    /// anything to Cranelift itself.
95    pub fn new(value: u64) -> StackSlotKey {
96        StackSlotKey(value)
97    }
98
99    /// Get the raw bits from the [`StackSlotKey`].
100    pub fn bits(&self) -> u64 {
101        self.0
102    }
103}
104
105impl StackSlotData {
106    /// Create a stack slot with the specified byte size and alignment.
107    pub fn new(kind: StackSlotKind, size: StackSize, align_shift: u8) -> Self {
108        Self {
109            kind,
110            size,
111            align_shift,
112            key: None,
113        }
114    }
115
116    /// Create a stack slot with the specified byte size and alignment
117    /// and the given user-defined key.
118    pub fn new_with_key(
119        kind: StackSlotKind,
120        size: StackSize,
121        align_shift: u8,
122        key: StackSlotKey,
123    ) -> Self {
124        Self {
125            kind,
126            size,
127            align_shift,
128            key: Some(key),
129        }
130    }
131}
132
133impl fmt::Display for StackSlotData {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        let align_shift = if self.align_shift != 0 {
136            format!(", align = {}", 1u32 << self.align_shift)
137        } else {
138            "".into()
139        };
140        let key = match self.key {
141            Some(value) => format!(", key = {}", value.bits()),
142            None => "".into(),
143        };
144
145        write!(f, "{} {}{align_shift}{key}", self.kind, self.size)
146    }
147}
148
149/// Contents of a dynamic stack slot.
150#[derive(Clone, Debug, PartialEq, Eq, Hash)]
151#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
152pub struct DynamicStackSlotData {
153    /// The kind of stack slot.
154    pub kind: StackSlotKind,
155
156    /// The type of this slot.
157    pub dyn_ty: DynamicType,
158}
159
160impl DynamicStackSlotData {
161    /// Create a stack slot with the specified byte size.
162    pub fn new(kind: StackSlotKind, dyn_ty: DynamicType) -> Self {
163        assert!(kind == StackSlotKind::ExplicitDynamicSlot);
164        Self { kind, dyn_ty }
165    }
166
167    /// Get the alignment in bytes of this stack slot given the stack pointer alignment.
168    pub fn alignment(&self, max_align: StackSize) -> StackSize {
169        debug_assert!(max_align.is_power_of_two());
170        max_align
171    }
172}
173
174impl fmt::Display for DynamicStackSlotData {
175    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176        write!(f, "{} {}", self.kind, self.dyn_ty)
177    }
178}
179
180/// All allocated stack slots.
181pub type StackSlots = PrimaryMap<StackSlot, StackSlotData>;
182
183/// All allocated dynamic stack slots.
184pub type DynamicStackSlots = PrimaryMap<DynamicStackSlot, DynamicStackSlotData>;
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::ir::Function;
190    use crate::ir::types::*;
191    use crate::ir::{DynamicTypeData, GlobalValueData};
192    use alloc::string::ToString;
193
194    #[test]
195    fn stack_slot() {
196        let mut func = Function::new();
197
198        let ss0 =
199            func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
200        let ss1 =
201            func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8, 0));
202        assert_eq!(ss0.to_string(), "ss0");
203        assert_eq!(ss1.to_string(), "ss1");
204
205        assert_eq!(func.sized_stack_slots[ss0].size, 4);
206        assert_eq!(func.sized_stack_slots[ss1].size, 8);
207
208        assert_eq!(func.sized_stack_slots[ss0].to_string(), "explicit_slot 4");
209        assert_eq!(func.sized_stack_slots[ss1].to_string(), "explicit_slot 8");
210    }
211
212    #[test]
213    fn dynamic_stack_slot() {
214        let mut func = Function::new();
215
216        let int_vector_ty = I32X4;
217        let fp_vector_ty = F64X2;
218        let scale0 = GlobalValueData::DynScaleTargetConst {
219            vector_type: int_vector_ty,
220        };
221        let scale1 = GlobalValueData::DynScaleTargetConst {
222            vector_type: fp_vector_ty,
223        };
224        let gv0 = func.create_global_value(scale0);
225        let gv1 = func.create_global_value(scale1);
226        let dtd0 = DynamicTypeData::new(int_vector_ty, gv0);
227        let dtd1 = DynamicTypeData::new(fp_vector_ty, gv1);
228        let dt0 = func.dfg.make_dynamic_ty(dtd0);
229        let dt1 = func.dfg.make_dynamic_ty(dtd1);
230
231        let dss0 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
232            StackSlotKind::ExplicitDynamicSlot,
233            dt0,
234        ));
235        let dss1 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
236            StackSlotKind::ExplicitDynamicSlot,
237            dt1,
238        ));
239        assert_eq!(dss0.to_string(), "dss0");
240        assert_eq!(dss1.to_string(), "dss1");
241
242        assert_eq!(
243            func.dynamic_stack_slots[dss0].to_string(),
244            "explicit_dynamic_slot dt0"
245        );
246        assert_eq!(
247            func.dynamic_stack_slots[dss1].to_string(),
248            "explicit_dynamic_slot dt1"
249        );
250    }
251}