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