Merge branch 'bytecodealliance:main' into main
This commit is contained in:
@@ -3,14 +3,9 @@
|
|||||||
//! The `StackSlotData` struct keeps track of a single stack slot in a function.
|
//! The `StackSlotData` struct keeps track of a single stack slot in a function.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::entity::{Iter, IterMut, Keys, PrimaryMap};
|
use crate::entity::PrimaryMap;
|
||||||
use crate::ir::{StackSlot, Type};
|
use crate::ir::StackSlot;
|
||||||
use crate::packed_option::PackedOption;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::cmp;
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::{Index, IndexMut};
|
|
||||||
use core::slice;
|
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
#[cfg(feature = "enable-serde")]
|
#[cfg(feature = "enable-serde")]
|
||||||
@@ -23,61 +18,13 @@ use serde::{Deserialize, Serialize};
|
|||||||
/// platform.
|
/// platform.
|
||||||
pub type StackSize = u32;
|
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.
|
/// The kind of a stack slot.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
pub enum StackSlotKind {
|
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`
|
/// An explicit stack slot. This is a chunk of stack memory for use by the `stack_load`
|
||||||
/// and `stack_store` instructions.
|
/// and `stack_store` instructions.
|
||||||
ExplicitSlot,
|
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 {
|
impl FromStr for StackSlotKind {
|
||||||
@@ -87,11 +34,6 @@ impl FromStr for StackSlotKind {
|
|||||||
use self::StackSlotKind::*;
|
use self::StackSlotKind::*;
|
||||||
match s {
|
match s {
|
||||||
"explicit_slot" => Ok(ExplicitSlot),
|
"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(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,11 +44,6 @@ impl fmt::Display for StackSlotKind {
|
|||||||
use self::StackSlotKind::*;
|
use self::StackSlotKind::*;
|
||||||
f.write_str(match *self {
|
f.write_str(match *self {
|
||||||
ExplicitSlot => "explicit_slot",
|
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.
|
/// Size of stack slot in bytes.
|
||||||
pub size: StackSize,
|
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<StackOffset>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackSlotData {
|
impl StackSlotData {
|
||||||
/// Create a stack slot with the specified byte size.
|
/// Create a stack slot with the specified byte size.
|
||||||
pub fn new(kind: StackSlotKind, size: StackSize) -> Self {
|
pub fn new(kind: StackSlotKind, size: StackSize) -> Self {
|
||||||
Self {
|
Self { kind, size }
|
||||||
kind,
|
|
||||||
size,
|
|
||||||
offset: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the alignment in bytes of this stack slot given the stack pointer alignment.
|
/// 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 {
|
impl fmt::Display for StackSlotData {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} {}", self.kind, self.size)?;
|
write!(f, "{} {}", self.kind, self.size)
|
||||||
if let Some(offset) = self.offset {
|
|
||||||
write!(f, ", offset {}", offset)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stack frame manager.
|
/// All allocated stack slots.
|
||||||
///
|
pub type StackSlots = PrimaryMap<StackSlot, StackSlotData>;
|
||||||
/// 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<StackSlot, StackSlotData>,
|
|
||||||
|
|
||||||
/// All the outgoing stack slots, ordered by offset.
|
|
||||||
outgoing: Vec<StackSlot>,
|
|
||||||
|
|
||||||
/// All the emergency slots.
|
|
||||||
emergency: Vec<StackSlot>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<StackSlot, StackSlotData> {
|
|
||||||
self.slots.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over all the stack slot keys, mutable edition.
|
|
||||||
pub fn iter_mut(&mut self) -> IterMut<StackSlot, StackSlotData> {
|
|
||||||
self.slots.iter_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over all the stack slot records.
|
|
||||||
pub fn values(&self) -> slice::Iter<StackSlotData> {
|
|
||||||
self.slots.values()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over all the stack slot records, mutable edition.
|
|
||||||
pub fn values_mut(&mut self) -> slice::IterMut<StackSlotData> {
|
|
||||||
self.slots.values_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an iterator over all the stack slot keys.
|
|
||||||
pub fn keys(&self) -> Keys<StackSlot> {
|
|
||||||
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<StackSlot> for StackSlots {
|
|
||||||
type Output = StackSlotData;
|
|
||||||
|
|
||||||
fn index(&self, ss: StackSlot) -> &StackSlotData {
|
|
||||||
&self.slots[ss]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexMut<StackSlot> 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>],
|
|
||||||
) -> 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ir::types;
|
|
||||||
use crate::ir::Function;
|
use crate::ir::Function;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
|
|
||||||
@@ -343,43 +95,21 @@ mod tests {
|
|||||||
fn stack_slot() {
|
fn stack_slot() {
|
||||||
let mut func = Function::new();
|
let mut func = Function::new();
|
||||||
|
|
||||||
let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::IncomingArg, 4));
|
let ss0 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
|
||||||
let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::SpillSlot, 8));
|
let ss1 = func.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8));
|
||||||
assert_eq!(ss0.to_string(), "ss0");
|
assert_eq!(ss0.to_string(), "ss0");
|
||||||
assert_eq!(ss1.to_string(), "ss1");
|
assert_eq!(ss1.to_string(), "ss1");
|
||||||
|
|
||||||
assert_eq!(func.stack_slots[ss0].size, 4);
|
assert_eq!(func.stack_slots[ss0].size, 4);
|
||||||
assert_eq!(func.stack_slots[ss1].size, 8);
|
assert_eq!(func.stack_slots[ss1].size, 8);
|
||||||
|
|
||||||
assert_eq!(func.stack_slots[ss0].to_string(), "incoming_arg 4");
|
assert_eq!(func.stack_slots[ss0].to_string(), "explicit_slot 4");
|
||||||
assert_eq!(func.stack_slots[ss1].to_string(), "spill_slot 8");
|
assert_eq!(func.stack_slots[ss1].to_string(), "explicit_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alignment() {
|
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(4), 4);
|
||||||
assert_eq!(slot.alignment(8), 8);
|
assert_eq!(slot.alignment(8), 8);
|
||||||
@@ -392,31 +122,4 @@ mod tests {
|
|||||||
assert_eq!(slot2.alignment(16), 8);
|
assert_eq!(slot2.alignment(16), 8);
|
||||||
assert_eq!(slot2.alignment(32), 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,22 +296,6 @@ block0(v0: i128):
|
|||||||
; nextln: ldp fp, lr, [sp], #16
|
; 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) {
|
function %i128_stack_store_inst_offset(i128) {
|
||||||
ss0 = explicit_slot 16
|
ss0 = explicit_slot 16
|
||||||
ss1 = explicit_slot 16
|
ss1 = explicit_slot 16
|
||||||
@@ -374,26 +358,6 @@ block0:
|
|||||||
; nextln: ret
|
; 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 {
|
function %i128_stack_load_inst_offset() -> i128 {
|
||||||
ss0 = explicit_slot 16
|
ss0 = explicit_slot 16
|
||||||
|
|||||||
@@ -123,11 +123,8 @@ block0(v90: i32, v91: f32):
|
|||||||
|
|
||||||
; Stack slot references
|
; Stack slot references
|
||||||
function %stack() {
|
function %stack() {
|
||||||
ss10 = spill_slot 8
|
ss10 = explicit_slot 8
|
||||||
ss2 = explicit_slot 4
|
ss2 = explicit_slot 4
|
||||||
ss3 = incoming_arg 4, offset 8
|
|
||||||
ss4 = outgoing_arg 4
|
|
||||||
ss5 = emergency_slot 4
|
|
||||||
|
|
||||||
block0:
|
block0:
|
||||||
v1 = stack_load.i32 ss10
|
v1 = stack_load.i32 ss10
|
||||||
@@ -137,10 +134,7 @@ block0:
|
|||||||
}
|
}
|
||||||
; sameln: function %stack() fast {
|
; sameln: function %stack() fast {
|
||||||
; check: ss2 = explicit_slot 4
|
; check: ss2 = explicit_slot 4
|
||||||
; check: ss3 = incoming_arg 4, offset 8
|
; check: ss10 = explicit_slot 8
|
||||||
; check: ss4 = outgoing_arg 4
|
|
||||||
; check: ss5 = emergency_slot 4
|
|
||||||
; check: ss10 = spill_slot 8
|
|
||||||
|
|
||||||
; check: block0:
|
; check: block0:
|
||||||
; nextln: v1 = stack_load.i32 ss10
|
; nextln: v1 = stack_load.i32 ss10
|
||||||
|
|||||||
@@ -22,25 +22,6 @@ block0(v0: i128):
|
|||||||
; run: %i128_stack_store_load(0xDECAFFFF_C0FFEEEE_C0FFEEEE_DECAFFFF) == true
|
; 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 {
|
function %i128_stack_store_load_inst_offset(i128) -> b1 {
|
||||||
ss0 = explicit_slot 16
|
ss0 = explicit_slot 16
|
||||||
ss1 = explicit_slot 16
|
ss1 = explicit_slot 16
|
||||||
|
|||||||
@@ -17,18 +17,6 @@ block0(v0: i64):
|
|||||||
; run: %stack_simple(-1) == -1
|
; 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 {
|
function %stack_offset(i64) -> i64 {
|
||||||
ss0 = explicit_slot 16
|
ss0 = explicit_slot 16
|
||||||
|
|
||||||
@@ -89,7 +77,7 @@ block0(v0: i8, v1: i64):
|
|||||||
|
|
||||||
|
|
||||||
function %multi_slot_offset_writes(i8, i64) -> i8, i64 {
|
function %multi_slot_offset_writes(i8, i64) -> i8, i64 {
|
||||||
ss0 = explicit_slot 8, offset 8
|
ss0 = explicit_slot 8
|
||||||
ss1 = explicit_slot 8
|
ss1 = explicit_slot 8
|
||||||
|
|
||||||
block0(v0: i8, v1: i64):
|
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(0, 1) == [0, 1]
|
||||||
; run: %multi_slot_offse(1, 2) == [1, 2]
|
; 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 {
|
function %huge_slots(i64) -> i64 {
|
||||||
ss0 = explicit_slot 1048576 ; 1MB Slot
|
ss0 = explicit_slot 1048576 ; 1MB Slot
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ impl Context {
|
|||||||
self.map.def_ss(ss, loc)?;
|
self.map.def_ss(ss, loc)?;
|
||||||
while self.function.stack_slots.next_key().index() <= ss.index() {
|
while self.function.stack_slots.next_key().index() <= ss.index() {
|
||||||
self.function
|
self.function
|
||||||
.create_stack_slot(StackSlotData::new(StackSlotKind::SpillSlot, 0));
|
.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 0));
|
||||||
}
|
}
|
||||||
self.function.stack_slots[ss] = data;
|
self.function.stack_slots[ss] = data;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1431,15 +1431,7 @@ impl<'a> Parser<'a> {
|
|||||||
if bytes > i64::from(u32::MAX) {
|
if bytes > i64::from(u32::MAX) {
|
||||||
return err!(self.loc, "stack slot too large");
|
return err!(self.loc, "stack slot too large");
|
||||||
}
|
}
|
||||||
let mut data = StackSlotData::new(kind, bytes as u32);
|
let 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect any trailing comments.
|
// Collect any trailing comments.
|
||||||
self.token();
|
self.token();
|
||||||
@@ -3122,8 +3114,8 @@ mod tests {
|
|||||||
fn stack_slot_decl() {
|
fn stack_slot_decl() {
|
||||||
let (func, _) = Parser::new(
|
let (func, _) = Parser::new(
|
||||||
"function %foo() system_v {
|
"function %foo() system_v {
|
||||||
ss3 = incoming_arg 13
|
ss3 = explicit_slot 13
|
||||||
ss1 = spill_slot 1
|
ss1 = explicit_slot 1
|
||||||
}",
|
}",
|
||||||
)
|
)
|
||||||
.parse_function()
|
.parse_function()
|
||||||
@@ -3133,12 +3125,12 @@ mod tests {
|
|||||||
let _ss0 = iter.next().unwrap();
|
let _ss0 = iter.next().unwrap();
|
||||||
let ss1 = iter.next().unwrap();
|
let ss1 = iter.next().unwrap();
|
||||||
assert_eq!(ss1.to_string(), "ss1");
|
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);
|
assert_eq!(func.stack_slots[ss1].size, 1);
|
||||||
let _ss2 = iter.next().unwrap();
|
let _ss2 = iter.next().unwrap();
|
||||||
let ss3 = iter.next().unwrap();
|
let ss3 = iter.next().unwrap();
|
||||||
assert_eq!(ss3.to_string(), "ss3");
|
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!(func.stack_slots[ss3].size, 13);
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
|
|
||||||
@@ -3146,8 +3138,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
Parser::new(
|
Parser::new(
|
||||||
"function %bar() system_v {
|
"function %bar() system_v {
|
||||||
ss1 = spill_slot 13
|
ss1 = explicit_slot 13
|
||||||
ss1 = spill_slot 1
|
ss1 = explicit_slot 1
|
||||||
}",
|
}",
|
||||||
)
|
)
|
||||||
.parse_function()
|
.parse_function()
|
||||||
@@ -3338,7 +3330,7 @@ mod tests {
|
|||||||
let (func, Details { comments, .. }) = Parser::new(
|
let (func, Details { comments, .. }) = Parser::new(
|
||||||
"; before
|
"; before
|
||||||
function %comment() system_v { ; decl
|
function %comment() system_v { ; decl
|
||||||
ss10 = outgoing_arg 13 ; stackslot.
|
ss10 = explicit_slot 13 ; stackslot.
|
||||||
; Still stackslot.
|
; Still stackslot.
|
||||||
jt10 = jump_table [block0]
|
jt10 = jump_table [block0]
|
||||||
; Jumptable
|
; Jumptable
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ mod tests {
|
|||||||
fn details() {
|
fn details() {
|
||||||
let tf = parse_test(
|
let tf = parse_test(
|
||||||
"function %detail() {
|
"function %detail() {
|
||||||
ss10 = incoming_arg 13
|
ss10 = explicit_slot 13
|
||||||
jt10 = jump_table [block0]
|
jt10 = jump_table [block0]
|
||||||
block0(v4: i32, v7: i32):
|
block0(v4: i32, v7: i32):
|
||||||
v10 = iadd v4, v7
|
v10 = iadd v4, v7
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
1. Announcements
|
1. Announcements
|
||||||
1. _Sumbit a PR to add your announcement here_
|
1. _Sumbit a PR to add your announcement here_
|
||||||
1. Other agenda items
|
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_
|
1. _Sumbit a PR to add your item here_
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|||||||
Reference in New Issue
Block a user