use crate::ir;
use cranelift_bitset::CompoundBitSet;
use cranelift_entity::PrimaryMap;
use smallvec::SmallVec;
pub(crate) type UserStackMapEntryVec = SmallVec<[UserStackMapEntry; 4]>;
#[derive(Clone, Debug, PartialEq, Hash)]
#[cfg_attr(
feature = "enable-serde",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub struct UserStackMapEntry {
pub ty: ir::Type,
pub slot: ir::StackSlot,
pub offset: u32,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(
feature = "enable-serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
pub struct UserStackMap {
by_type: SmallVec<[(ir::Type, CompoundBitSet); 1]>,
sp_to_sized_stack_slots: Option<u32>,
}
impl UserStackMap {
pub(crate) fn new(
entries: &[UserStackMapEntry],
stack_slot_offsets: &PrimaryMap<ir::StackSlot, u32>,
) -> Self {
let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default();
for entry in entries {
let offset = stack_slot_offsets[entry.slot] + entry.offset;
let offset = usize::try_from(offset).unwrap();
let index = by_type
.iter()
.position(|(ty, _)| *ty == entry.ty)
.unwrap_or_else(|| {
by_type.push((entry.ty, CompoundBitSet::with_capacity(offset + 1)));
by_type.len() - 1
});
by_type[index].1.insert(offset);
}
UserStackMap {
by_type,
sp_to_sized_stack_slots: None,
}
}
pub fn from_sp_offsets(offsets: impl IntoIterator<Item = u32>) -> Self {
let offsets: alloc::vec::Vec<u32> = offsets.into_iter().collect();
let max = offsets.iter().max().copied().unwrap_or(0);
let mut bitset = CompoundBitSet::with_capacity(usize::try_from(max).unwrap() + 1);
for offset in offsets {
bitset.insert(usize::try_from(offset).unwrap());
}
let mut by_type = SmallVec::<[(ir::Type, CompoundBitSet); 1]>::default();
by_type.push((ir::types::I32, bitset));
UserStackMap {
by_type,
sp_to_sized_stack_slots: Some(0),
}
}
pub(crate) fn finalize(&mut self, sp_to_sized_stack_slots: u32) {
debug_assert!(self.sp_to_sized_stack_slots.is_none());
self.sp_to_sized_stack_slots = Some(sp_to_sized_stack_slots);
}
pub fn entries(&self) -> impl Iterator<Item = (ir::Type, u32)> + '_ {
let sp_to_sized_stack_slots = self.sp_to_sized_stack_slots.expect(
"`sp_to_sized_stack_slots` should have been filled in before this stack map was used",
);
self.by_type.iter().flat_map(move |(ty, bitset)| {
bitset.iter().map(move |slot_offset| {
(
*ty,
sp_to_sized_stack_slots + u32::try_from(slot_offset).unwrap(),
)
})
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_sp_offsets_roundtrip() {
let map = UserStackMap::from_sp_offsets([8, 16, 40]);
let entries: alloc::vec::Vec<_> = map.entries().collect();
assert_eq!(entries.len(), 3);
assert_eq!(entries[0], (ir::types::I32, 8));
assert_eq!(entries[1], (ir::types::I32, 16));
assert_eq!(entries[2], (ir::types::I32, 40));
}
#[test]
fn from_sp_offsets_empty() {
let map = UserStackMap::from_sp_offsets([]);
assert_eq!(map.entries().count(), 0);
}
#[test]
fn from_sp_offsets_dedups_and_sorts() {
let map = UserStackMap::from_sp_offsets([40, 8, 8, 16]);
let offsets: alloc::vec::Vec<u32> = map.entries().map(|(_, o)| o).collect();
assert_eq!(offsets, [8, 16, 40]);
}
}