diff --git a/cranelift/codegen/src/ir/stackslot.rs b/cranelift/codegen/src/ir/stackslot.rs index 993fcc8ee1..64d643f43d 100644 --- a/cranelift/codegen/src/ir/stackslot.rs +++ b/cranelift/codegen/src/ir/stackslot.rs @@ -4,10 +4,7 @@ //! use crate::entity::{Iter, IterMut, Keys, PrimaryMap}; -use crate::ir::{StackSlot, Type}; -use crate::packed_option::PackedOption; -use alloc::vec::Vec; -use core::cmp; +use crate::ir::StackSlot; use core::fmt; use core::ops::{Index, IndexMut}; use core::slice; @@ -23,61 +20,13 @@ use serde::{Deserialize, Serialize}; /// platform. pub type StackSize = u32; -/// A stack offset. -/// -/// The location of a stack offset relative to a stack pointer or frame pointer. -pub type StackOffset = i32; - -/// The minimum size of a spill slot in bytes. -/// -/// ISA implementations are allowed to assume that small types like `b1` and `i8` get a full 4-byte -/// spill slot. -const MIN_SPILL_SLOT_SIZE: StackSize = 4; - -/// Get the spill slot size to use for `ty`. -fn spill_size(ty: Type) -> StackSize { - cmp::max(MIN_SPILL_SLOT_SIZE, ty.bytes()) -} - /// The kind of a stack slot. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum StackSlotKind { - /// A spill slot. This is a stack slot created by the register allocator. - SpillSlot, - /// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load` /// and `stack_store` instructions. ExplicitSlot, - - /// An incoming function argument. - /// - /// If the current function has more arguments than fits in registers, the remaining arguments - /// are passed on the stack by the caller. These incoming arguments are represented as SSA - /// values assigned to incoming stack slots. - IncomingArg, - - /// An outgoing function argument. - /// - /// When preparing to call a function whose arguments don't fit in registers, outgoing argument - /// stack slots are used to represent individual arguments in the outgoing call frame. These - /// stack slots are only valid while setting up a call. - OutgoingArg, - - /// Space allocated in the caller's frame for the callee's return values - /// that are passed out via return pointer. - /// - /// If there are more return values than registers available for the callee's calling - /// convention, or the return value is larger than the available registers' space, then we - /// allocate stack space in this frame and pass a pointer to the callee, which then writes its - /// return values into this space. - StructReturnSlot, - - /// An emergency spill slot. - /// - /// Emergency slots are allocated late when the register's constraint solver needs extra space - /// to shuffle registers around. They are only used briefly, and can be reused. - EmergencySlot, } impl FromStr for StackSlotKind { @@ -87,11 +36,6 @@ impl FromStr for StackSlotKind { use self::StackSlotKind::*; match s { "explicit_slot" => Ok(ExplicitSlot), - "spill_slot" => Ok(SpillSlot), - "incoming_arg" => Ok(IncomingArg), - "outgoing_arg" => Ok(OutgoingArg), - "sret_slot" => Ok(StructReturnSlot), - "emergency_slot" => Ok(EmergencySlot), _ => Err(()), } } @@ -102,11 +46,6 @@ impl fmt::Display for StackSlotKind { use self::StackSlotKind::*; f.write_str(match *self { ExplicitSlot => "explicit_slot", - SpillSlot => "spill_slot", - IncomingArg => "incoming_arg", - OutgoingArg => "outgoing_arg", - StructReturnSlot => "sret_slot", - EmergencySlot => "emergency_slot", }) } } @@ -120,25 +59,12 @@ pub struct StackSlotData { /// Size of stack slot in bytes. pub size: StackSize, - - /// Offset of stack slot relative to the stack pointer in the caller. - /// - /// On x86, the base address is the stack pointer *before* the return address was pushed. On - /// RISC ISAs, the base address is the value of the stack pointer on entry to the function. - /// - /// For `OutgoingArg` stack slots, the offset is relative to the current function's stack - /// pointer immediately before the call. - pub offset: Option, } impl StackSlotData { /// Create a stack slot with the specified byte size. pub fn new(kind: StackSlotKind, size: StackSize) -> Self { - Self { - kind, - size, - offset: None, - } + Self { kind, size } } /// Get the alignment in bytes of this stack slot given the stack pointer alignment. @@ -154,11 +80,7 @@ impl StackSlotData { impl fmt::Display for StackSlotData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.kind, self.size)?; - if let Some(offset) = self.offset { - write!(f, ", offset {}", offset)?; - } - Ok(()) + write!(f, "{} {}", self.kind, self.size) } } @@ -170,12 +92,6 @@ impl fmt::Display for StackSlotData { pub struct StackSlots { /// All allocated stack slots. slots: PrimaryMap, - - /// All the outgoing stack slots, ordered by offset. - outgoing: Vec, - - /// All the emergency slots. - emergency: Vec, } /// Stack slot manager functions that behave mostly like an entity map. @@ -188,8 +104,6 @@ impl StackSlots { /// Clear out everything. pub fn clear(&mut self) { self.slots.clear(); - self.outgoing.clear(); - self.emergency.clear(); } /// Allocate a new stack slot. @@ -252,90 +166,9 @@ impl IndexMut for StackSlots { } } -/// Higher-level stack frame manipulation functions. -impl StackSlots { - /// Create a new spill slot for spilling values of type `ty`. - pub fn make_spill_slot(&mut self, ty: Type) -> StackSlot { - self.push(StackSlotData::new(StackSlotKind::SpillSlot, spill_size(ty))) - } - - /// Create a stack slot representing an incoming function argument. - pub fn make_incoming_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot { - let mut data = StackSlotData::new(StackSlotKind::IncomingArg, size); - debug_assert!(offset <= StackOffset::max_value() - data.size as StackOffset); - data.offset = Some(offset); - self.push(data) - } - - /// Get a stack slot representing an outgoing argument. - /// - /// This may create a new stack slot, or reuse an existing outgoing stack slot with the - /// requested offset and size. - /// - /// The requested offset is relative to this function's stack pointer immediately before making - /// the call. - pub fn get_outgoing_arg(&mut self, size: u32, offset: StackOffset) -> StackSlot { - // Look for an existing outgoing stack slot with the same offset and size. - let inspos = match self.outgoing.binary_search_by_key(&(offset, size), |&ss| { - (self[ss].offset.unwrap(), self[ss].size) - }) { - Ok(idx) => return self.outgoing[idx], - Err(idx) => idx, - }; - - // No existing slot found. Make one and insert it into `outgoing`. - let mut data = StackSlotData::new(StackSlotKind::OutgoingArg, size); - debug_assert!(offset <= StackOffset::max_value() - size as StackOffset); - data.offset = Some(offset); - let ss = self.slots.push(data); - self.outgoing.insert(inspos, ss); - ss - } - - /// Get an emergency spill slot that can be used to store a `ty` value. - /// - /// This may allocate a new slot, or it may reuse an existing emergency spill slot, excluding - /// any slots in the `in_use` list. - pub fn get_emergency_slot( - &mut self, - ty: Type, - in_use: &[PackedOption], - ) -> StackSlot { - let size = spill_size(ty); - - // Find the smallest existing slot that can fit the type. - if let Some(&ss) = self - .emergency - .iter() - .filter(|&&ss| self[ss].size >= size && !in_use.contains(&ss.into())) - .min_by_key(|&&ss| self[ss].size) - { - return ss; - } - - // Alternatively, use the largest available slot and make it larger. - if let Some(&ss) = self - .emergency - .iter() - .filter(|&&ss| !in_use.contains(&ss.into())) - .max_by_key(|&&ss| self[ss].size) - { - self.slots[ss].size = size; - return ss; - } - - // No existing slot found. Make one and insert it into `emergency`. - let data = StackSlotData::new(StackSlotKind::EmergencySlot, size); - let ss = self.slots.push(data); - self.emergency.push(ss); - ss - } -} - #[cfg(test)] mod tests { use super::*; - use crate::ir::types; use crate::ir::Function; use alloc::string::ToString; @@ -343,43 +176,21 @@ mod tests { fn stack_slot() { let mut func = Function::new(); - let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::IncomingArg, 4)); - let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::SpillSlot, 8)); + let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); + let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); assert_eq!(ss0.to_string(), "ss0"); assert_eq!(ss1.to_string(), "ss1"); assert_eq!(func.stack_slots[ss0].size, 4); assert_eq!(func.stack_slots[ss1].size, 8); - assert_eq!(func.stack_slots[ss0].to_string(), "incoming_arg 4"); - assert_eq!(func.stack_slots[ss1].to_string(), "spill_slot 8"); - } - - #[test] - fn outgoing() { - let mut sss = StackSlots::new(); - - let ss0 = sss.get_outgoing_arg(4, 8); - let ss1 = sss.get_outgoing_arg(4, 4); - let ss2 = sss.get_outgoing_arg(8, 8); - - assert_eq!(sss[ss0].offset, Some(8)); - assert_eq!(sss[ss0].size, 4); - - assert_eq!(sss[ss1].offset, Some(4)); - assert_eq!(sss[ss1].size, 4); - - assert_eq!(sss[ss2].offset, Some(8)); - assert_eq!(sss[ss2].size, 8); - - assert_eq!(sss.get_outgoing_arg(4, 8), ss0); - assert_eq!(sss.get_outgoing_arg(4, 4), ss1); - assert_eq!(sss.get_outgoing_arg(8, 8), ss2); + assert_eq!(func.stack_slots[ss0].to_string(), "explicit_slot 4"); + assert_eq!(func.stack_slots[ss1].to_string(), "explicit_slot 8"); } #[test] fn alignment() { - let slot = StackSlotData::new(StackSlotKind::SpillSlot, 8); + let slot = StackSlotData::new(StackSlotKind::ExplicitSlot, 8); assert_eq!(slot.alignment(4), 4); assert_eq!(slot.alignment(8), 8); @@ -392,31 +203,4 @@ mod tests { assert_eq!(slot2.alignment(16), 8); assert_eq!(slot2.alignment(32), 8); } - - #[test] - fn emergency() { - let mut sss = StackSlots::new(); - - let ss0 = sss.get_emergency_slot(types::I32, &[]); - assert_eq!(sss[ss0].size, 4); - - // When a smaller size is requested, we should simply get the same slot back. - assert_eq!(sss.get_emergency_slot(types::I8, &[]), ss0); - assert_eq!(sss[ss0].size, 4); - assert_eq!(sss.get_emergency_slot(types::F32, &[]), ss0); - assert_eq!(sss[ss0].size, 4); - - // Ask for a larger size and the slot should grow. - assert_eq!(sss.get_emergency_slot(types::F64, &[]), ss0); - assert_eq!(sss[ss0].size, 8); - - // When one slot is in use, we should get a new one. - let ss1 = sss.get_emergency_slot(types::I32, &[None.into(), ss0.into()]); - assert_eq!(sss[ss0].size, 8); - assert_eq!(sss[ss1].size, 4); - - // Now we should get the smallest fit of the two available slots. - assert_eq!(sss.get_emergency_slot(types::F32, &[]), ss1); - assert_eq!(sss.get_emergency_slot(types::F64, &[]), ss0); - } }