This also paves the way for unifying TargetIsa and MachBackend, since now they map one to one. In theory the two traits could be merged, which would be nice to limit the number of total concepts. Also they have quite different responsibilities, so it might be fine to keep them separate. Interestingly, this PR started as removing RegInfo from the TargetIsa trait since the adapter returned a dummy value there. From the fallout, noticed that all Display implementations didn't needed an ISA anymore (since these were only used to render ISA specific registers). Also the whole family of RegInfo / ValueLoc / RegUnit was exclusively used for the old backend, and these could be removed. Notably, some IR instructions needed to be removed, because they were using RegUnit too: this was the oddball of regfill / regmove / regspill / copy_special, which were IR instructions inserted by the old regalloc. Fare thee well!
153 lines
5.1 KiB
Rust
153 lines
5.1 KiB
Rust
use crate::bitset::BitSet;
|
|
use alloc::vec::Vec;
|
|
|
|
type Num = u32;
|
|
const NUM_BITS: usize = core::mem::size_of::<Num>() * 8;
|
|
|
|
/// Stack maps record which words in a stack frame contain live GC references at
|
|
/// a given instruction pointer.
|
|
///
|
|
/// Logically, a set of stack maps for a function record a table of the form:
|
|
///
|
|
/// ```text
|
|
/// +---------------------+-------------------------------------------+
|
|
/// | Instruction Pointer | SP-Relative Offsets of Live GC References |
|
|
/// +---------------------+-------------------------------------------+
|
|
/// | 0x12345678 | 2, 6, 12 |
|
|
/// | 0x1234abcd | 2, 6 |
|
|
/// | ... | ... |
|
|
/// +---------------------+-------------------------------------------+
|
|
/// ```
|
|
///
|
|
/// Where "instruction pointer" is an instruction pointer within the function,
|
|
/// and "offsets of live GC references" contains the offsets (in units of words)
|
|
/// from the frame's stack pointer where live GC references are stored on the
|
|
/// stack. Instruction pointers within the function that do not have an entry in
|
|
/// this table are not GC safepoints.
|
|
///
|
|
/// Because
|
|
///
|
|
/// * offsets of live GC references are relative from the stack pointer, and
|
|
/// * stack frames grow down from higher addresses to lower addresses,
|
|
///
|
|
/// to get a pointer to a live reference at offset `x` within a stack frame, you
|
|
/// add `x` from the frame's stack pointer.
|
|
///
|
|
/// For example, to calculate the pointer to the live GC reference inside "frame
|
|
/// 1" below, you would do `frame_1_sp + x`:
|
|
///
|
|
/// ```text
|
|
/// Stack
|
|
/// +-------------------+
|
|
/// | Frame 0 |
|
|
/// | |
|
|
/// | | |
|
|
/// | +-------------------+ <--- Frame 0's SP
|
|
/// | | Frame 1 |
|
|
/// Grows | |
|
|
/// down | |
|
|
/// | | Live GC reference | --+--
|
|
/// | | | |
|
|
/// | | | |
|
|
/// V | | x = offset of live GC reference
|
|
/// | | |
|
|
/// | | |
|
|
/// +-------------------+ --+-- <--- Frame 1's SP
|
|
/// | Frame 2 |
|
|
/// | ... |
|
|
/// ```
|
|
///
|
|
/// An individual `StackMap` is associated with just one instruction pointer
|
|
/// within the function, contains the size of the stack frame, and represents
|
|
/// the stack frame as a bitmap. There is one bit per word in the stack frame,
|
|
/// and if the bit is set, then the word contains a live GC reference.
|
|
///
|
|
/// Note that a caller's `OutgoingArg` stack slots and callee's `IncomingArg`
|
|
/// stack slots overlap, so we must choose which function's stack maps record
|
|
/// live GC references in these slots. We record the `IncomingArg`s in the
|
|
/// callee's stack map.
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "enable-serde", derive(serde::Deserialize, serde::Serialize))]
|
|
pub struct StackMap {
|
|
bitmap: Vec<BitSet<Num>>,
|
|
mapped_words: u32,
|
|
}
|
|
|
|
impl StackMap {
|
|
/// Create a vec of Bitsets from a slice of bools.
|
|
pub fn from_slice(vec: &[bool]) -> Self {
|
|
let len = vec.len();
|
|
let num_word = len / NUM_BITS + (len % NUM_BITS != 0) as usize;
|
|
let mut bitmap = Vec::with_capacity(num_word);
|
|
|
|
for segment in vec.chunks(NUM_BITS) {
|
|
let mut curr_word = 0;
|
|
for (i, set) in segment.iter().enumerate() {
|
|
if *set {
|
|
curr_word |= 1 << i;
|
|
}
|
|
}
|
|
bitmap.push(BitSet(curr_word));
|
|
}
|
|
Self {
|
|
mapped_words: len as u32,
|
|
bitmap,
|
|
}
|
|
}
|
|
|
|
/// Returns a specified bit.
|
|
pub fn get_bit(&self, bit_index: usize) -> bool {
|
|
assert!(bit_index < NUM_BITS * self.bitmap.len());
|
|
let word_index = bit_index / NUM_BITS;
|
|
let word_offset = (bit_index % NUM_BITS) as u8;
|
|
self.bitmap[word_index].contains(word_offset)
|
|
}
|
|
|
|
/// Returns the raw bitmap that represents this stack map.
|
|
pub fn as_slice(&self) -> &[BitSet<u32>] {
|
|
&self.bitmap
|
|
}
|
|
|
|
/// Returns the number of words represented by this stack map.
|
|
pub fn mapped_words(&self) -> u32 {
|
|
self.mapped_words
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn stack_maps() {
|
|
let vec: Vec<bool> = Vec::new();
|
|
assert!(StackMap::from_slice(&vec).bitmap.is_empty());
|
|
|
|
let mut vec: [bool; NUM_BITS] = Default::default();
|
|
let set_true_idx = [5, 7, 24, 31];
|
|
|
|
for &idx in &set_true_idx {
|
|
vec[idx] = true;
|
|
}
|
|
|
|
let mut vec = vec.to_vec();
|
|
assert_eq!(
|
|
vec![BitSet::<Num>(2164261024)],
|
|
StackMap::from_slice(&vec).bitmap
|
|
);
|
|
|
|
vec.push(false);
|
|
vec.push(true);
|
|
let res = StackMap::from_slice(&vec);
|
|
assert_eq!(
|
|
vec![BitSet::<Num>(2164261024), BitSet::<Num>(2)],
|
|
res.bitmap
|
|
);
|
|
|
|
assert!(res.get_bit(5));
|
|
assert!(res.get_bit(31));
|
|
assert!(res.get_bit(33));
|
|
assert!(!res.get_bit(1));
|
|
}
|
|
}
|