1 //! Low-level details of stack accesses.
2 //!
3 //! The `ir::StackSlots` type deals with stack slots and stack frame layout. The `StackRef` type
4 //! defined in this module expresses the low-level details of accessing a stack slot from an
5 //! encoded instruction.
6 
7 use crate::ir::stackslot::{StackOffset, StackSlotKind, StackSlots};
8 use crate::ir::StackSlot;
9 
10 /// A method for referencing a stack slot in the current stack frame.
11 ///
12 /// Stack slots are addressed with a constant offset from a base register. The base can be the
13 /// stack pointer, the frame pointer, or (in the future) a zone register pointing to an inner zone
14 /// of a large stack frame.
15 #[derive(Clone, Copy, Debug)]
16 pub struct StackRef {
17     /// The base register to use for addressing.
18     pub base: StackBase,
19 
20     /// Immediate offset from the base register to the first byte of the stack slot.
21     pub offset: StackOffset,
22 }
23 
24 impl StackRef {
25     /// Get a reference to the stack slot `ss` using one of the base pointers in `mask`.
masked(ss: StackSlot, mask: StackBaseMask, frame: &StackSlots) -> Option<Self>26     pub fn masked(ss: StackSlot, mask: StackBaseMask, frame: &StackSlots) -> Option<Self> {
27         // Try an SP-relative reference.
28         if mask.contains(StackBase::SP) {
29             return Some(Self::sp(ss, frame));
30         }
31 
32         // No reference possible with this mask.
33         None
34     }
35 
36     /// Get a reference to `ss` using the stack pointer as a base.
sp(ss: StackSlot, frame: &StackSlots) -> Self37     pub fn sp(ss: StackSlot, frame: &StackSlots) -> Self {
38         let size = frame
39             .layout_info
40             .expect("Stack layout must be computed before referencing stack slots")
41             .frame_size;
42         let slot = &frame[ss];
43         let offset = if slot.kind == StackSlotKind::OutgoingArg {
44             // Outgoing argument slots have offsets relative to our stack pointer.
45             slot.offset.unwrap()
46         } else {
47             // All other slots have offsets relative to our caller's stack frame.
48             // Offset where SP is pointing. (All ISAs have stacks growing downwards.)
49             let sp_offset = -(size as StackOffset);
50             slot.offset.unwrap() - sp_offset
51         };
52         Self {
53             base: StackBase::SP,
54             offset,
55         }
56     }
57 }
58 
59 /// Generic base register for referencing stack slots.
60 ///
61 /// Most ISAs have a stack pointer and an optional frame pointer, so provide generic names for
62 /// those two base pointers.
63 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
64 pub enum StackBase {
65     /// Use the stack pointer.
66     SP = 0,
67 
68     /// Use the frame pointer (if one is present).
69     FP = 1,
70 
71     /// Use an explicit zone pointer in a general-purpose register.
72     ///
73     /// This feature is not yet implemented.
74     Zone = 2,
75 }
76 
77 /// Bit mask of supported stack bases.
78 ///
79 /// Many instruction encodings can use different base registers while others only work with the
80 /// stack pointer, say. A `StackBaseMask` is a bit mask of supported stack bases for a given
81 /// instruction encoding.
82 ///
83 /// This behaves like a set of `StackBase` variants.
84 ///
85 /// The internal representation as a `u8` is public because stack base masks are used in constant
86 /// tables generated from the meta-language encoding definitions.
87 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
88 pub struct StackBaseMask(pub u8);
89 
90 impl StackBaseMask {
91     /// Check if this mask contains the `base` variant.
contains(self, base: StackBase) -> bool92     pub fn contains(self, base: StackBase) -> bool {
93         self.0 & (1 << base as usize) != 0
94     }
95 }
96