diff --git a/cranelift/codegen/src/ir/stackslot.rs b/cranelift/codegen/src/ir/stackslot.rs index 993fcc8ee1..4c30eb48b6 100644 --- a/cranelift/codegen/src/ir/stackslot.rs +++ b/cranelift/codegen/src/ir/stackslot.rs @@ -3,14 +3,9 @@ //! The `StackSlotData` struct keeps track of a single stack slot in a function. //! -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::entity::PrimaryMap; +use crate::ir::StackSlot; use core::fmt; -use core::ops::{Index, IndexMut}; -use core::slice; use core::str::FromStr; #[cfg(feature = "enable-serde")] @@ -23,61 +18,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 +34,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 +44,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 +57,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,188 +78,16 @@ 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) } } -/// Stack frame manager. -/// -/// Keep track of all the stack slots used by a function. -#[derive(Clone, Debug, PartialEq, Eq, Default)] -#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -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. -impl StackSlots { - /// Create an empty stack slot manager. - pub fn new() -> Self { - StackSlots::default() - } - - /// Clear out everything. - pub fn clear(&mut self) { - self.slots.clear(); - self.outgoing.clear(); - self.emergency.clear(); - } - - /// Allocate a new stack slot. - /// - /// This function should be primarily used by the text format parser. There are more convenient - /// functions for creating specific kinds of stack slots below. - pub fn push(&mut self, data: StackSlotData) -> StackSlot { - self.slots.push(data) - } - - /// Check if `ss` is a valid stack slot reference. - pub fn is_valid(&self, ss: StackSlot) -> bool { - self.slots.is_valid(ss) - } - - /// Get an iterator over all the stack slot keys. - pub fn iter(&self) -> Iter { - self.slots.iter() - } - - /// Get an iterator over all the stack slot keys, mutable edition. - pub fn iter_mut(&mut self) -> IterMut { - self.slots.iter_mut() - } - - /// Get an iterator over all the stack slot records. - pub fn values(&self) -> slice::Iter { - self.slots.values() - } - - /// Get an iterator over all the stack slot records, mutable edition. - pub fn values_mut(&mut self) -> slice::IterMut { - self.slots.values_mut() - } - - /// Get an iterator over all the stack slot keys. - pub fn keys(&self) -> Keys { - self.slots.keys() - } - - /// Get a reference to the next stack slot that would be created by `push()`. - /// - /// This should just be used by the parser. - pub fn next_key(&self) -> StackSlot { - self.slots.next_key() - } -} - -impl Index for StackSlots { - type Output = StackSlotData; - - fn index(&self, ss: StackSlot) -> &StackSlotData { - &self.slots[ss] - } -} - -impl IndexMut for StackSlots { - fn index_mut(&mut self, ss: StackSlot) -> &mut StackSlotData { - &mut self.slots[ss] - } -} - -/// 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 - } -} +/// All allocated stack slots. +pub type StackSlots = PrimaryMap; #[cfg(test)] mod tests { use super::*; - use crate::ir::types; use crate::ir::Function; use alloc::string::ToString; @@ -343,43 +95,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 +122,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); - } } diff --git a/cranelift/filetests/filetests/isa/aarch64/stack.clif b/cranelift/filetests/filetests/isa/aarch64/stack.clif index 17deccf7fd..a816eeb4d6 100644 --- a/cranelift/filetests/filetests/isa/aarch64/stack.clif +++ b/cranelift/filetests/filetests/isa/aarch64/stack.clif @@ -296,22 +296,6 @@ block0(v0: i128): ; nextln: ldp fp, lr, [sp], #16 -function %i128_stack_store_slot_offset(i128) { -ss0 = explicit_slot 16, offset 16 - -block0(v0: i128): - stack_store.i128 v0, ss0 - return -} -; check: stp fp, lr, [sp, #-16]! -; nextln: mov fp, sp -; nextln: sub sp, sp, #16 -; nextln: mov x2, sp -; nextln: stp x0, x1, [x2] -; nextln: add sp, sp, #16 -; nextln: ldp fp, lr, [sp], #16 - - function %i128_stack_store_inst_offset(i128) { ss0 = explicit_slot 16 ss1 = explicit_slot 16 @@ -374,26 +358,6 @@ block0: ; nextln: ret -function %i128_stack_load_slot_offset() -> i128 { -ss0 = explicit_slot 16, offset 16 - -block0: - v0 = stack_load.i128 ss0 - return v0 -} -; check: stp fp, lr, [sp, #-16]! -; nextln: mov fp, sp -; nextln: sub sp, sp, #16 -; nextln: mov x0, sp -; nextln: ldp x1, x0, [x0] -; nextln: mov x2, x0 -; nextln: mov x0, x1 -; nextln: mov x1, x2 -; nextln: add sp, sp, #16 -; nextln: ldp fp, lr, [sp], #16 -; nextln: ret - - function %i128_stack_load_inst_offset() -> i128 { ss0 = explicit_slot 16 diff --git a/cranelift/filetests/filetests/parser/tiny.clif b/cranelift/filetests/filetests/parser/tiny.clif index 35cf075022..5f82b4ac27 100644 --- a/cranelift/filetests/filetests/parser/tiny.clif +++ b/cranelift/filetests/filetests/parser/tiny.clif @@ -123,11 +123,8 @@ block0(v90: i32, v91: f32): ; Stack slot references function %stack() { - ss10 = spill_slot 8 + ss10 = explicit_slot 8 ss2 = explicit_slot 4 - ss3 = incoming_arg 4, offset 8 - ss4 = outgoing_arg 4 - ss5 = emergency_slot 4 block0: v1 = stack_load.i32 ss10 @@ -137,10 +134,7 @@ block0: } ; sameln: function %stack() fast { ; check: ss2 = explicit_slot 4 -; check: ss3 = incoming_arg 4, offset 8 -; check: ss4 = outgoing_arg 4 -; check: ss5 = emergency_slot 4 -; check: ss10 = spill_slot 8 +; check: ss10 = explicit_slot 8 ; check: block0: ; nextln: v1 = stack_load.i32 ss10 diff --git a/cranelift/filetests/filetests/runtests/i128-load-store.clif b/cranelift/filetests/filetests/runtests/i128-load-store.clif index dc389bc049..db85e0005b 100644 --- a/cranelift/filetests/filetests/runtests/i128-load-store.clif +++ b/cranelift/filetests/filetests/runtests/i128-load-store.clif @@ -22,25 +22,6 @@ block0(v0: i128): ; run: %i128_stack_store_load(0xDECAFFFF_C0FFEEEE_C0FFEEEE_DECAFFFF) == true -function %i128_stack_store_load_offset(i128) -> b1 { - ss0 = explicit_slot 16, offset 16 - -block0(v0: i128): - stack_store.i128 v0, ss0 - v1 = stack_load.i128 ss0 - - v2 = icmp.i128 eq v0, v1 - return v2 -} -; run: %i128_stack_store_load_offset(0) == true -; run: %i128_stack_store_load_offset(-1) == true -; run: %i128_stack_store_load_offset(0x00000000_00000000_FFFFFFFF_FFFFFFFF) == true -; run: %i128_stack_store_load_offset(0xFFFFFFFF_FFFFFFFF_00000000_00000000) == true -; run: %i128_stack_store_load_offset(0xFEDCBA98_76543210_01234567_89ABCDEF) == true -; run: %i128_stack_store_load_offset(0xA00A00A0_0A00A00A_06060606_06060606) == true -; run: %i128_stack_store_load_offset(0xDECAFFFF_C0FFEEEE_C0FFEEEE_DECAFFFF) == true - - function %i128_stack_store_load_inst_offset(i128) -> b1 { ss0 = explicit_slot 16 ss1 = explicit_slot 16 diff --git a/cranelift/filetests/filetests/runtests/stack.clif b/cranelift/filetests/filetests/runtests/stack.clif index efcc25f02e..363b2a5489 100644 --- a/cranelift/filetests/filetests/runtests/stack.clif +++ b/cranelift/filetests/filetests/runtests/stack.clif @@ -17,18 +17,6 @@ block0(v0: i64): ; run: %stack_simple(-1) == -1 -function %slot_offset(i64) -> i64 { - ss0 = explicit_slot 8, offset 8 - -block0(v0: i64): - stack_store.i64 v0, ss0 - v1 = stack_load.i64 ss0 - return v1 -} -; run: %slot_offset(0) == 0 -; run: %slot_offset(1) == 1 -; run: %slot_offset(-1) == -1 - function %stack_offset(i64) -> i64 { ss0 = explicit_slot 16 @@ -89,7 +77,7 @@ block0(v0: i8, v1: i64): function %multi_slot_offset_writes(i8, i64) -> i8, i64 { - ss0 = explicit_slot 8, offset 8 + ss0 = explicit_slot 8 ss1 = explicit_slot 8 block0(v0: i8, v1: i64): @@ -102,21 +90,6 @@ block0(v0: i8, v1: i64): ; run: %multi_slot_offse(0, 1) == [0, 1] ; run: %multi_slot_offse(1, 2) == [1, 2] -function %slot_offset_negative(i64, i64) -> i64, i64 { - ss0 = explicit_slot 8 - ss1 = explicit_slot 8, offset -8 - -block0(v0: i64, v1: i64): - stack_store.i64 v0, ss0 - stack_store.i64 v1, ss1 - v2 = stack_load.i64 ss0 - v3 = stack_load.i64 ss1 - return v2, v3 -} -; run: %slot_offset_nega(0, 1) == [0, 1] -; run: %slot_offset_nega(2, 3) == [2, 3] - - function %huge_slots(i64) -> i64 { ss0 = explicit_slot 1048576 ; 1MB Slot diff --git a/cranelift/reader/src/parser.rs b/cranelift/reader/src/parser.rs index 0f63a1c4fd..46ac424957 100644 --- a/cranelift/reader/src/parser.rs +++ b/cranelift/reader/src/parser.rs @@ -251,7 +251,7 @@ impl Context { self.map.def_ss(ss, loc)?; while self.function.stack_slots.next_key().index() <= ss.index() { self.function - .create_stack_slot(StackSlotData::new(StackSlotKind::SpillSlot, 0)); + .create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 0)); } self.function.stack_slots[ss] = data; Ok(()) @@ -1431,15 +1431,7 @@ impl<'a> Parser<'a> { if bytes > i64::from(u32::MAX) { return err!(self.loc, "stack slot too large"); } - let mut data = StackSlotData::new(kind, bytes as u32); - - // Take additional options. - while self.optional(Token::Comma) { - match self.match_any_identifier("expected stack slot flags")? { - "offset" => data.offset = Some(self.match_imm32("expected byte offset")?), - other => return err!(self.loc, "Unknown stack slot flag '{}'", other), - } - } + let data = StackSlotData::new(kind, bytes as u32); // Collect any trailing comments. self.token(); @@ -3122,8 +3114,8 @@ mod tests { fn stack_slot_decl() { let (func, _) = Parser::new( "function %foo() system_v { - ss3 = incoming_arg 13 - ss1 = spill_slot 1 + ss3 = explicit_slot 13 + ss1 = explicit_slot 1 }", ) .parse_function() @@ -3133,12 +3125,12 @@ mod tests { let _ss0 = iter.next().unwrap(); let ss1 = iter.next().unwrap(); assert_eq!(ss1.to_string(), "ss1"); - assert_eq!(func.stack_slots[ss1].kind, StackSlotKind::SpillSlot); + assert_eq!(func.stack_slots[ss1].kind, StackSlotKind::ExplicitSlot); assert_eq!(func.stack_slots[ss1].size, 1); let _ss2 = iter.next().unwrap(); let ss3 = iter.next().unwrap(); assert_eq!(ss3.to_string(), "ss3"); - assert_eq!(func.stack_slots[ss3].kind, StackSlotKind::IncomingArg); + assert_eq!(func.stack_slots[ss3].kind, StackSlotKind::ExplicitSlot); assert_eq!(func.stack_slots[ss3].size, 13); assert_eq!(iter.next(), None); @@ -3146,8 +3138,8 @@ mod tests { assert_eq!( Parser::new( "function %bar() system_v { - ss1 = spill_slot 13 - ss1 = spill_slot 1 + ss1 = explicit_slot 13 + ss1 = explicit_slot 1 }", ) .parse_function() @@ -3338,7 +3330,7 @@ mod tests { let (func, Details { comments, .. }) = Parser::new( "; before function %comment() system_v { ; decl - ss10 = outgoing_arg 13 ; stackslot. + ss10 = explicit_slot 13 ; stackslot. ; Still stackslot. jt10 = jump_table [block0] ; Jumptable diff --git a/cranelift/reader/src/sourcemap.rs b/cranelift/reader/src/sourcemap.rs index 16cd9e5684..2a9298b8c1 100644 --- a/cranelift/reader/src/sourcemap.rs +++ b/cranelift/reader/src/sourcemap.rs @@ -227,7 +227,7 @@ mod tests { fn details() { let tf = parse_test( "function %detail() { - ss10 = incoming_arg 13 + ss10 = explicit_slot 13 jt10 = jump_table [block0] block0(v4: i32, v7: i32): v10 = iadd v4, v7 diff --git a/meetings/wasmtime/2021/wasmtime-10-14.md b/meetings/wasmtime/2021/wasmtime-10-14.md index b7aa1739f2..2b82b0da53 100644 --- a/meetings/wasmtime/2021/wasmtime-10-14.md +++ b/meetings/wasmtime/2021/wasmtime-10-14.md @@ -11,6 +11,9 @@ 1. Announcements 1. _Sumbit a PR to add your announcement here_ 1. Other agenda items + 1. Discuss [Wasm exception handling](https://github.com/WebAssembly/exception-handling) + implementation strategy (see + [#3427](https://github.com/bytecodealliance/wasmtime/issues/3427)). 1. _Sumbit a PR to add your item here_ ## Notes