1 //! Stack slots.
2 //!
3 //! The `StackSlotData` struct keeps track of a single stack slot in a function.
4 //!
5 
6 use crate::entity::PrimaryMap;
7 use crate::ir::StackSlot;
8 use crate::ir::entities::{DynamicStackSlot, DynamicType};
9 use core::fmt;
10 use core::str::FromStr;
11 
12 #[cfg(feature = "enable-serde")]
13 use 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.
20 pub 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))]
25 pub 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 
34 impl FromStr for StackSlotKind {
35     type Err = ();
36 
from_str(s: &str) -> Result<Self, ()>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 
47 impl fmt::Display for StackSlotKind {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result48     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))]
60 pub 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))]
88 pub struct StackSlotKey(u64);
89 impl 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.
new(value: u64) -> StackSlotKey95     pub fn new(value: u64) -> StackSlotKey {
96         StackSlotKey(value)
97     }
98 
99     /// Get the raw bits from the [`StackSlotKey`].
bits(&self) -> u64100     pub fn bits(&self) -> u64 {
101         self.0
102     }
103 }
104 
105 impl StackSlotData {
106     /// Create a stack slot with the specified byte size and alignment.
new(kind: StackSlotKind, size: StackSize, align_shift: u8) -> Self107     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.
new_with_key( kind: StackSlotKind, size: StackSize, align_shift: u8, key: StackSlotKey, ) -> Self118     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 
133 impl fmt::Display for StackSlotData {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result134     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))]
152 pub 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 
160 impl DynamicStackSlotData {
161     /// Create a stack slot with the specified byte size.
new(kind: StackSlotKind, dyn_ty: DynamicType) -> Self162     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.
alignment(&self, max_align: StackSize) -> StackSize168     pub fn alignment(&self, max_align: StackSize) -> StackSize {
169         debug_assert!(max_align.is_power_of_two());
170         max_align
171     }
172 }
173 
174 impl fmt::Display for DynamicStackSlotData {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result175     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.
181 pub type StackSlots = PrimaryMap<StackSlot, StackSlotData>;
182 
183 /// All allocated dynamic stack slots.
184 pub type DynamicStackSlots = PrimaryMap<DynamicStackSlot, DynamicStackSlotData>;
185 
186 #[cfg(test)]
187 mod 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]
stack_slot()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]
dynamic_stack_slot()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 }
252