1 //! Stack slots.
2 //!
3 //! The `StackSlotData` struct keeps track of a single stack slot in a function.
4 //!
5 
6 use crate::entity::{Iter, IterMut, Keys, PrimaryMap};
7 use crate::ir::{StackSlot, Type};
8 use crate::packed_option::PackedOption;
9 use alloc::vec::Vec;
10 use core::cmp;
11 use core::fmt;
12 use core::ops::{Index, IndexMut};
13 use core::slice;
14 use core::str::FromStr;
15 
16 #[cfg(feature = "enable-serde")]
17 use serde::{Deserialize, Serialize};
18 
19 /// The size of an object on the stack, or the size of a stack frame.
20 ///
21 /// We don't use `usize` to represent object sizes on the target platform because Cranelift supports
22 /// cross-compilation, and `usize` is a type that depends on the host platform, not the target
23 /// platform.
24 pub type StackSize = u32;
25 
26 /// A stack offset.
27 ///
28 /// The location of a stack offset relative to a stack pointer or frame pointer.
29 pub type StackOffset = i32;
30 
31 /// The minimum size of a spill slot in bytes.
32 ///
33 /// ISA implementations are allowed to assume that small types like `b1` and `i8` get a full 4-byte
34 /// spill slot.
35 const MIN_SPILL_SLOT_SIZE: StackSize = 4;
36 
37 /// Get the spill slot size to use for `ty`.
spill_size(ty: Type) -> StackSize38 fn spill_size(ty: Type) -> StackSize {
39     cmp::max(MIN_SPILL_SLOT_SIZE, ty.bytes())
40 }
41 
42 /// The kind of a stack slot.
43 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
44 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
45 pub enum StackSlotKind {
46     /// A spill slot. This is a stack slot created by the register allocator.
47     SpillSlot,
48 
49     /// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load`
50     /// and `stack_store` instructions.
51     ExplicitSlot,
52 
53     /// An incoming function argument.
54     ///
55     /// If the current function has more arguments than fits in registers, the remaining arguments
56     /// are passed on the stack by the caller. These incoming arguments are represented as SSA
57     /// values assigned to incoming stack slots.
58     IncomingArg,
59 
60     /// An outgoing function argument.
61     ///
62     /// When preparing to call a function whose arguments don't fit in registers, outgoing argument
63     /// stack slots are used to represent individual arguments in the outgoing call frame. These
64     /// stack slots are only valid while setting up a call.
65     OutgoingArg,
66 
67     /// Space allocated in the caller's frame for the callee's return values
68     /// that are passed out via return pointer.
69     ///
70     /// If there are more return values than registers available for the callee's calling
71     /// convention, or the return value is larger than the available registers' space, then we
72     /// allocate stack space in this frame and pass a pointer to the callee, which then writes its
73     /// return values into this space.
74     StructReturnSlot,
75 
76     /// An emergency spill slot.
77     ///
78     /// Emergency slots are allocated late when the register's constraint solver needs extra space
79     /// to shuffle registers around. They are only used briefly, and can be reused.
80     EmergencySlot,
81 }
82 
83 impl FromStr for StackSlotKind {
84     type Err = ();
85 
from_str(s: &str) -> Result<Self, ()>86     fn from_str(s: &str) -> Result<Self, ()> {
87         use self::StackSlotKind::*;
88         match s {
89             "explicit_slot" => Ok(ExplicitSlot),
90             "spill_slot" => Ok(SpillSlot),
91             "incoming_arg" => Ok(IncomingArg),
92             "outgoing_arg" => Ok(OutgoingArg),
93             "sret_slot" => Ok(StructReturnSlot),
94             "emergency_slot" => Ok(EmergencySlot),
95             _ => Err(()),
96         }
97     }
98 }
99 
100 impl fmt::Display for StackSlotKind {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         use self::StackSlotKind::*;
103         f.write_str(match *self {
104             ExplicitSlot => "explicit_slot",
105             SpillSlot => "spill_slot",
106             IncomingArg => "incoming_arg",
107             OutgoingArg => "outgoing_arg",
108             StructReturnSlot => "sret_slot",
109             EmergencySlot => "emergency_slot",
110         })
111     }
112 }
113 
114 /// Contents of a stack slot.
115 #[derive(Clone, Debug, PartialEq, Eq)]
116 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
117 pub struct StackSlotData {
118     /// The kind of stack slot.
119     pub kind: StackSlotKind,
120 
121     /// Size of stack slot in bytes.
122     pub size: StackSize,
123 
124     /// Offset of stack slot relative to the stack pointer in the caller.
125     ///
126     /// On x86, the base address is the stack pointer *before* the return address was pushed. On
127     /// RISC ISAs, the base address is the value of the stack pointer on entry to the function.
128     ///
129     /// For `OutgoingArg` stack slots, the offset is relative to the current function's stack
130     /// pointer immediately before the call.
131     pub offset: Option<StackOffset>,
132 }
133 
134 impl StackSlotData {
135     /// Create a stack slot with the specified byte size.
new(kind: StackSlotKind, size: StackSize) -> Self136     pub fn new(kind: StackSlotKind, size: StackSize) -> Self {
137         Self {
138             kind,
139             size,
140             offset: None,
141         }
142     }
143 
144     /// Get the alignment in bytes of this stack slot given the stack pointer alignment.
alignment(&self, max_align: StackSize) -> StackSize145     pub fn alignment(&self, max_align: StackSize) -> StackSize {
146         debug_assert!(max_align.is_power_of_two());
147         // We want to find the largest power of two that divides both `self.size` and `max_align`.
148         // That is the same as isolating the rightmost bit in `x`.
149         let x = self.size | max_align;
150         // C.f. Hacker's delight.
151         x & x.wrapping_neg()
152     }
153 }
154 
155 impl fmt::Display for StackSlotData {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result156     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157         write!(f, "{} {}", self.kind, self.size)?;
158         if let Some(offset) = self.offset {
159             write!(f, ", offset {}", offset)?;
160         }
161         Ok(())
162     }
163 }
164 
165 /// Stack frame layout information.
166 ///
167 /// This is computed by the `layout_stack()` method.
168 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
169 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
170 pub struct StackLayoutInfo {
171     /// The total size of the stack frame.
172     ///
173     /// This is the distance from the stack pointer in the current function to the stack pointer in
174     /// the calling function, so it includes a pushed return address as well as space for outgoing
175     /// call arguments.
176     pub frame_size: StackSize,
177 
178     /// The total size of the stack frame for inbound arguments pushed by the caller.
179     pub inbound_args_size: StackSize,
180 }
181 
182 /// Stack frame manager.
183 ///
184 /// Keep track of all the stack slots used by a function.
185 #[derive(Clone, Debug, PartialEq, Eq, Default)]
186 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
187 pub struct StackSlots {
188     /// All allocated stack slots.
189     slots: PrimaryMap<StackSlot, StackSlotData>,
190 
191     /// All the outgoing stack slots, ordered by offset.
192     outgoing: Vec<StackSlot>,
193 
194     /// All the emergency slots.
195     emergency: Vec<StackSlot>,
196 
197     /// Layout information computed from `layout_stack`.
198     pub layout_info: Option<StackLayoutInfo>,
199 }
200 
201 /// Stack slot manager functions that behave mostly like an entity map.
202 impl StackSlots {
203     /// Create an empty stack slot manager.
new() -> Self204     pub fn new() -> Self {
205         StackSlots::default()
206     }
207 
208     /// Clear out everything.
clear(&mut self)209     pub fn clear(&mut self) {
210         self.slots.clear();
211         self.outgoing.clear();
212         self.emergency.clear();
213         self.layout_info = None;
214     }
215 
216     /// Allocate a new stack slot.
217     ///
218     /// This function should be primarily used by the text format parser. There are more convenient
219     /// functions for creating specific kinds of stack slots below.
push(&mut self, data: StackSlotData) -> StackSlot220     pub fn push(&mut self, data: StackSlotData) -> StackSlot {
221         self.slots.push(data)
222     }
223 
224     /// Check if `ss` is a valid stack slot reference.
is_valid(&self, ss: StackSlot) -> bool225     pub fn is_valid(&self, ss: StackSlot) -> bool {
226         self.slots.is_valid(ss)
227     }
228 
229     /// Get an iterator over all the stack slot keys.
iter(&self) -> Iter<StackSlot, StackSlotData>230     pub fn iter(&self) -> Iter<StackSlot, StackSlotData> {
231         self.slots.iter()
232     }
233 
234     /// Get an iterator over all the stack slot keys, mutable edition.
iter_mut(&mut self) -> IterMut<StackSlot, StackSlotData>235     pub fn iter_mut(&mut self) -> IterMut<StackSlot, StackSlotData> {
236         self.slots.iter_mut()
237     }
238 
239     /// Get an iterator over all the stack slot records.
values(&self) -> slice::Iter<StackSlotData>240     pub fn values(&self) -> slice::Iter<StackSlotData> {
241         self.slots.values()
242     }
243 
244     /// Get an iterator over all the stack slot records, mutable edition.
values_mut(&mut self) -> slice::IterMut<StackSlotData>245     pub fn values_mut(&mut self) -> slice::IterMut<StackSlotData> {
246         self.slots.values_mut()
247     }
248 
249     /// Get an iterator over all the stack slot keys.
keys(&self) -> Keys<StackSlot>250     pub fn keys(&self) -> Keys<StackSlot> {
251         self.slots.keys()
252     }
253 
254     /// Get a reference to the next stack slot that would be created by `push()`.
255     ///
256     /// This should just be used by the parser.
next_key(&self) -> StackSlot257     pub fn next_key(&self) -> StackSlot {
258         self.slots.next_key()
259     }
260 }
261 
262 impl Index<StackSlot> for StackSlots {
263     type Output = StackSlotData;
264 
index(&self, ss: StackSlot) -> &StackSlotData265     fn index(&self, ss: StackSlot) -> &StackSlotData {
266         &self.slots[ss]
267     }
268 }
269 
270 impl IndexMut<StackSlot> for StackSlots {
index_mut(&mut self, ss: StackSlot) -> &mut StackSlotData271     fn index_mut(&mut self, ss: StackSlot) -> &mut StackSlotData {
272         &mut self.slots[ss]
273     }
274 }
275 
276 /// Higher-level stack frame manipulation functions.
277 impl StackSlots {
278     /// Create a new spill slot for spilling values of type `ty`.
make_spill_slot(&mut self, ty: Type) -> StackSlot279     pub fn make_spill_slot(&mut self, ty: Type) -> StackSlot {
280         self.push(StackSlotData::new(StackSlotKind::SpillSlot, spill_size(ty)))
281     }
282 
283     /// Create a stack slot representing an incoming function argument.
make_incoming_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot284     pub fn make_incoming_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot {
285         let mut data = StackSlotData::new(StackSlotKind::IncomingArg, size);
286         debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset);
287         data.offset = Some(offset);
288         self.push(data)
289     }
290 
291     /// Get a stack slot representing an outgoing argument.
292     ///
293     /// This may create a new stack slot, or reuse an existing outgoing stack slot with the
294     /// requested offset and size.
295     ///
296     /// The requested offset is relative to this function's stack pointer immediately before making
297     /// the call.
get_outgoing_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot298     pub fn get_outgoing_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot {
299         // Look for an existing outgoing stack slot with the same offset and size.
300         let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| {
301             (self[ss].offset.unwrap(), self[ss].size)
302         }) {
303             Ok(idx) => return self.outgoing[idx],
304             Err(idx) => idx,
305         };
306 
307         // No existing slot found. Make one and insert it into `outgoing`.
308         let mut data = StackSlotData::new(StackSlotKind::OutgoingArg, size);
309         debug_assert!(offset <= StackOffset::max_value() - size as StackOffset);
310         data.offset = Some(offset);
311         let ss = self.slots.push(data);
312         self.outgoing.insert(inspos, ss);
313         ss
314     }
315 
316     /// Get an emergency spill slot that can be used to store a `ty` value.
317     ///
318     /// This may allocate a new slot, or it may reuse an existing emergency spill slot, excluding
319     /// any slots in the `in_use` list.
get_emergency_slot( &mut self, ty: Type, in_use: &[PackedOption<StackSlot>], ) -> StackSlot320     pub fn get_emergency_slot(
321         &mut self,
322         ty: Type,
323         in_use: &[PackedOption<StackSlot>],
324     ) -> StackSlot {
325         let size = spill_size(ty);
326 
327         // Find the smallest existing slot that can fit the type.
328         if let Some(&ss) = self
329             .emergency
330             .iter()
331             .filter(|&&ss| self[ss].size >= size && !in_use.contains(&ss.into()))
332             .min_by_key(|&&ss| self[ss].size)
333         {
334             return ss;
335         }
336 
337         // Alternatively, use the largest available slot and make it larger.
338         if let Some(&ss) = self
339             .emergency
340             .iter()
341             .filter(|&&ss| !in_use.contains(&ss.into()))
342             .max_by_key(|&&ss| self[ss].size)
343         {
344             self.slots[ss].size = size;
345             return ss;
346         }
347 
348         // No existing slot found. Make one and insert it into `emergency`.
349         let data = StackSlotData::new(StackSlotKind::EmergencySlot, size);
350         let ss = self.slots.push(data);
351         self.emergency.push(ss);
352         ss
353     }
354 }
355 
356 #[cfg(test)]
357 mod tests {
358     use super::*;
359     use crate::ir::types;
360     use crate::ir::Function;
361     use alloc::string::ToString;
362 
363     #[test]
stack_slot()364     fn stack_slot() {
365         let mut func = Function::new();
366 
367         let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::IncomingArg, 4));
368         let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::SpillSlot, 8));
369         assert_eq!(ss0.to_string(), "ss0");
370         assert_eq!(ss1.to_string(), "ss1");
371 
372         assert_eq!(func.stack_slots[ss0].size, 4);
373         assert_eq!(func.stack_slots[ss1].size, 8);
374 
375         assert_eq!(func.stack_slots[ss0].to_string(), "incoming_arg 4");
376         assert_eq!(func.stack_slots[ss1].to_string(), "spill_slot 8");
377     }
378 
379     #[test]
outgoing()380     fn outgoing() {
381         let mut sss = StackSlots::new();
382 
383         let ss0 = sss.get_outgoing_arg(4, 8);
384         let ss1 = sss.get_outgoing_arg(4, 4);
385         let ss2 = sss.get_outgoing_arg(8, 8);
386 
387         assert_eq!(sss[ss0].offset, Some(8));
388         assert_eq!(sss[ss0].size, 4);
389 
390         assert_eq!(sss[ss1].offset, Some(4));
391         assert_eq!(sss[ss1].size, 4);
392 
393         assert_eq!(sss[ss2].offset, Some(8));
394         assert_eq!(sss[ss2].size, 8);
395 
396         assert_eq!(sss.get_outgoing_arg(4, 8), ss0);
397         assert_eq!(sss.get_outgoing_arg(4, 4), ss1);
398         assert_eq!(sss.get_outgoing_arg(8, 8), ss2);
399     }
400 
401     #[test]
alignment()402     fn alignment() {
403         let slot = StackSlotData::new(StackSlotKind::SpillSlot, 8);
404 
405         assert_eq!(slot.alignment(4), 4);
406         assert_eq!(slot.alignment(8), 8);
407         assert_eq!(slot.alignment(16), 8);
408 
409         let slot2 = StackSlotData::new(StackSlotKind::ExplicitSlot, 24);
410 
411         assert_eq!(slot2.alignment(4), 4);
412         assert_eq!(slot2.alignment(8), 8);
413         assert_eq!(slot2.alignment(16), 8);
414         assert_eq!(slot2.alignment(32), 8);
415     }
416 
417     #[test]
emergency()418     fn emergency() {
419         let mut sss = StackSlots::new();
420 
421         let ss0 = sss.get_emergency_slot(types::I32, &[]);
422         assert_eq!(sss[ss0].size, 4);
423 
424         // When a smaller size is requested, we should simply get the same slot back.
425         assert_eq!(sss.get_emergency_slot(types::I8, &[]), ss0);
426         assert_eq!(sss[ss0].size, 4);
427         assert_eq!(sss.get_emergency_slot(types::F32, &[]), ss0);
428         assert_eq!(sss[ss0].size, 4);
429 
430         // Ask for a larger size and the slot should grow.
431         assert_eq!(sss.get_emergency_slot(types::F64, &[]), ss0);
432         assert_eq!(sss[ss0].size, 8);
433 
434         // When one slot is in use, we should get a new one.
435         let ss1 = sss.get_emergency_slot(types::I32, &[None.into(), ss0.into()]);
436         assert_eq!(sss[ss0].size, 8);
437         assert_eq!(sss[ss1].size, 4);
438 
439         // Now we should get the smallest fit of the two available slots.
440         assert_eq!(sss.get_emergency_slot(types::F32, &[]), ss1);
441         assert_eq!(sss.get_emergency_slot(types::F64, &[]), ss0);
442     }
443 }
444