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