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::entities::{DynamicStackSlot, DynamicType};
8use crate::ir::StackSlot;
9use core::fmt;
10use core::str::FromStr;
11
12/// imports only needed for testing.
13#[allow(unused_imports)]
14use crate::ir::{DynamicTypeData, GlobalValueData};
15
16#[allow(unused_imports)]
17use crate::ir::types::*;
18
19#[cfg(feature = "enable-serde")]
20use serde::{Deserialize, Serialize};
21
22/// The size of an object on the stack, or the size of a stack frame.
23///
24/// We don't use `usize` to represent object sizes on the target platform because Cranelift supports
25/// cross-compilation, and `usize` is a type that depends on the host platform, not the target
26/// platform.
27pub type StackSize = u32;
28
29/// The kind of a stack slot.
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
31#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
32pub enum StackSlotKind {
33    /// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load`
34    /// and `stack_store` instructions.
35    ExplicitSlot,
36    /// An explicit stack slot for dynamic vector types. This is a chunk of stack memory
37    /// for use by the `dynamic_stack_load` and `dynamic_stack_store` instructions.
38    ExplicitDynamicSlot,
39}
40
41impl FromStr for StackSlotKind {
42    type Err = ();
43
44    fn from_str(s: &str) -> Result<Self, ()> {
45        use self::StackSlotKind::*;
46        match s {
47            "explicit_slot" => Ok(ExplicitSlot),
48            "explicit_dynamic_slot" => Ok(ExplicitDynamicSlot),
49            _ => Err(()),
50        }
51    }
52}
53
54impl fmt::Display for StackSlotKind {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        use self::StackSlotKind::*;
57        f.write_str(match *self {
58            ExplicitSlot => "explicit_slot",
59            ExplicitDynamicSlot => "explicit_dynamic_slot",
60        })
61    }
62}
63
64/// Contents of a stack slot.
65#[derive(Clone, Debug, PartialEq, Eq, Hash)]
66#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
67pub struct StackSlotData {
68    /// The kind of stack slot.
69    pub kind: StackSlotKind,
70
71    /// Size of stack slot in bytes.
72    pub size: StackSize,
73}
74
75impl StackSlotData {
76    /// Create a stack slot with the specified byte size.
77    pub fn new(kind: StackSlotKind, size: StackSize) -> Self {
78        Self { kind, size }
79    }
80
81    /// Get the alignment in bytes of this stack slot given the stack pointer alignment.
82    pub fn alignment(&self, max_align: StackSize) -> StackSize {
83        debug_assert!(max_align.is_power_of_two());
84        if self.kind == StackSlotKind::ExplicitDynamicSlot {
85            max_align
86        } else {
87            // We want to find the largest power of two that divides both `self.size` and `max_align`.
88            // That is the same as isolating the rightmost bit in `x`.
89            let x = self.size | max_align;
90            // C.f. Hacker's delight.
91            x & x.wrapping_neg()
92        }
93    }
94}
95
96impl fmt::Display for StackSlotData {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        write!(f, "{} {}", self.kind, self.size)
99    }
100}
101
102/// Contents of a dynamic stack slot.
103#[derive(Clone, Debug, PartialEq, Eq, Hash)]
104#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
105pub struct DynamicStackSlotData {
106    /// The kind of stack slot.
107    pub kind: StackSlotKind,
108
109    /// The type of this slot.
110    pub dyn_ty: DynamicType,
111}
112
113impl DynamicStackSlotData {
114    /// Create a stack slot with the specified byte size.
115    pub fn new(kind: StackSlotKind, dyn_ty: DynamicType) -> Self {
116        assert!(kind == StackSlotKind::ExplicitDynamicSlot);
117        Self { kind, dyn_ty }
118    }
119
120    /// Get the alignment in bytes of this stack slot given the stack pointer alignment.
121    pub fn alignment(&self, max_align: StackSize) -> StackSize {
122        debug_assert!(max_align.is_power_of_two());
123        max_align
124    }
125}
126
127impl fmt::Display for DynamicStackSlotData {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        write!(f, "{} {}", self.kind, self.dyn_ty)
130    }
131}
132
133/// All allocated stack slots.
134pub type StackSlots = PrimaryMap<StackSlot, StackSlotData>;
135
136/// All allocated dynamic stack slots.
137pub type DynamicStackSlots = PrimaryMap<DynamicStackSlot, DynamicStackSlotData>;
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::ir::Function;
143    use alloc::string::ToString;
144
145    #[test]
146    fn stack_slot() {
147        let mut func = Function::new();
148
149        let ss0 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
150        let ss1 = func.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8));
151        assert_eq!(ss0.to_string(), "ss0");
152        assert_eq!(ss1.to_string(), "ss1");
153
154        assert_eq!(func.sized_stack_slots[ss0].size, 4);
155        assert_eq!(func.sized_stack_slots[ss1].size, 8);
156
157        assert_eq!(func.sized_stack_slots[ss0].to_string(), "explicit_slot 4");
158        assert_eq!(func.sized_stack_slots[ss1].to_string(), "explicit_slot 8");
159    }
160
161    #[test]
162    fn dynamic_stack_slot() {
163        let mut func = Function::new();
164
165        let int_vector_ty = I32X4;
166        let fp_vector_ty = F64X2;
167        let scale0 = GlobalValueData::DynScaleTargetConst {
168            vector_type: int_vector_ty,
169        };
170        let scale1 = GlobalValueData::DynScaleTargetConst {
171            vector_type: fp_vector_ty,
172        };
173        let gv0 = func.create_global_value(scale0);
174        let gv1 = func.create_global_value(scale1);
175        let dtd0 = DynamicTypeData::new(int_vector_ty, gv0);
176        let dtd1 = DynamicTypeData::new(fp_vector_ty, gv1);
177        let dt0 = func.dfg.make_dynamic_ty(dtd0);
178        let dt1 = func.dfg.make_dynamic_ty(dtd1);
179
180        let dss0 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
181            StackSlotKind::ExplicitDynamicSlot,
182            dt0,
183        ));
184        let dss1 = func.create_dynamic_stack_slot(DynamicStackSlotData::new(
185            StackSlotKind::ExplicitDynamicSlot,
186            dt1,
187        ));
188        assert_eq!(dss0.to_string(), "dss0");
189        assert_eq!(dss1.to_string(), "dss1");
190
191        assert_eq!(
192            func.dynamic_stack_slots[dss0].to_string(),
193            "explicit_dynamic_slot dt0"
194        );
195        assert_eq!(
196            func.dynamic_stack_slots[dss1].to_string(),
197            "explicit_dynamic_slot dt1"
198        );
199    }
200
201    #[test]
202    fn alignment() {
203        let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 8);
204
205        assert_eq!(slot.alignment(4), 4);
206        assert_eq!(slot.alignment(8), 8);
207        assert_eq!(slot.alignment(16), 8);
208
209        let slot2 = StackSlotData::new(StackSlotKind::ExplicitSlot, 24);
210
211        assert_eq!(slot2.alignment(4), 4);
212        assert_eq!(slot2.alignment(8), 8);
213        assert_eq!(slot2.alignment(16), 8);
214        assert_eq!(slot2.alignment(32), 8);
215    }
216}