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