use alloc::vec::Vec;
use crate::entity::{Block, Inst, NONE, StackSlot, Value, ValueRange};
use crate::inst::{InstData, Terminator};
use crate::types::Type;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum ValueDef {
Param {
block: Block,
index: u32,
},
Result {
inst: Inst,
index: u32,
},
Detached,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct SlotData {
pub size: u32,
pub align: u32,
}
impl SlotData {
pub const MAX_ALIGN: u32 = 1 << 16;
}
#[derive(Clone, Debug)]
pub(crate) struct BlockNode {
pub(crate) params: Vec<Value>,
pub(crate) first: u32,
pub(crate) last: u32,
pub(crate) term: Option<Terminator>,
pub(crate) live: bool,
}
impl BlockNode {
pub(crate) fn new(params: Vec<Value>) -> BlockNode {
BlockNode {
params,
first: NONE,
last: NONE,
term: None,
live: true,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct InstNode {
pub(crate) data: InstData,
pub(crate) results: ValueRange,
pub(crate) prev: u32,
pub(crate) next: u32,
pub(crate) block: u32,
}
#[derive(Clone, Debug)]
pub(crate) struct ValueNode {
pub(crate) ty: Type,
pub(crate) def: ValueDef,
pub(crate) alias: u32,
}
pub const MAX_ENTITIES: u32 = u32::MAX - 1;
#[derive(Clone, Debug)]
pub struct Function {
pub(crate) blocks: Vec<BlockNode>,
pub(crate) insts: Vec<InstNode>,
pub(crate) values: Vec<ValueNode>,
pub(crate) slots: Vec<SlotData>,
pub(crate) entry: Block,
}
#[derive(Clone, Debug)]
pub struct Insts<'f> {
func: &'f Function,
next: u32,
budget: usize,
}
impl Iterator for Insts<'_> {
type Item = Inst;
fn next(&mut self) -> Option<Inst> {
if self.next == NONE || self.budget == 0 {
return None;
}
self.budget -= 1;
let inst = Inst::from_u32(self.next);
self.next = self
.func
.insts
.get(self.next as usize)
.map_or(NONE, |n| n.next);
Some(inst)
}
}
impl Function {
pub(crate) fn new(params: &[Type]) -> Function {
let mut f = Function {
blocks: Vec::new(),
insts: Vec::new(),
values: Vec::new(),
slots: Vec::new(),
entry: Block::from_u32(0),
};
let mut values = Vec::with_capacity(params.len());
for (i, &ty) in params.iter().enumerate() {
let v = Value::from_u32(f.values.len() as u32);
f.values.push(ValueNode {
ty,
def: ValueDef::Param {
block: Block::from_u32(0),
index: i as u32,
},
alias: NONE,
});
values.push(v);
}
f.blocks.push(BlockNode::new(values));
f
}
pub(crate) fn empty() -> Function {
Function {
blocks: Vec::new(),
insts: Vec::new(),
values: Vec::new(),
slots: Vec::new(),
entry: Block::from_u32(0),
}
}
#[inline]
#[must_use]
pub const fn entry(&self) -> Block {
self.entry
}
#[inline]
#[must_use]
pub fn block_count(&self) -> usize {
self.blocks.len()
}
pub fn blocks(&self) -> impl Iterator<Item = Block> + '_ {
self.blocks
.iter()
.enumerate()
.filter(|(_, b)| b.live)
.map(|(i, _)| Block::from_u32(i as u32))
}
#[must_use]
pub fn is_live_block(&self, block: Block) -> bool {
self.blocks.get(block.index()).is_some_and(|b| b.live)
}
#[must_use]
pub fn block_params(&self, block: Block) -> &[Value] {
self.blocks
.get(block.index())
.map_or(&[], |b| b.params.as_slice())
}
#[must_use]
pub fn insts(&self, block: Block) -> Insts<'_> {
let first = self.blocks.get(block.index()).map_or(NONE, |b| b.first);
Insts {
func: self,
next: first,
budget: self.insts.len(),
}
}
#[must_use]
pub fn first_inst(&self, block: Block) -> Option<Inst> {
self.blocks
.get(block.index())
.and_then(|b| (b.first != NONE).then(|| Inst::from_u32(b.first)))
}
#[must_use]
pub fn last_inst(&self, block: Block) -> Option<Inst> {
self.blocks
.get(block.index())
.and_then(|b| (b.last != NONE).then(|| Inst::from_u32(b.last)))
}
#[must_use]
pub fn next_inst(&self, inst: Inst) -> Option<Inst> {
self.insts
.get(inst.index())
.filter(|n| n.block != NONE && n.next != NONE)
.map(|n| Inst::from_u32(n.next))
}
#[must_use]
pub fn prev_inst(&self, inst: Inst) -> Option<Inst> {
self.insts
.get(inst.index())
.filter(|n| n.block != NONE && n.prev != NONE)
.map(|n| Inst::from_u32(n.prev))
}
#[inline]
#[must_use]
pub fn inst_count(&self) -> usize {
self.insts.len()
}
#[must_use]
pub fn inst(&self, inst: Inst) -> Option<&InstData> {
self.insts.get(inst.index()).map(|n| &n.data)
}
#[must_use]
pub fn inst_block(&self, inst: Inst) -> Option<Block> {
self.insts
.get(inst.index())
.filter(|n| n.block != NONE)
.map(|n| Block::from_u32(n.block))
}
#[must_use]
pub fn results(&self, inst: Inst) -> ValueRange {
self.insts
.get(inst.index())
.map_or(ValueRange::default(), |n| n.results)
}
#[must_use]
pub fn terminator(&self, block: Block) -> Option<&Terminator> {
self.blocks.get(block.index()).and_then(|b| b.term.as_ref())
}
#[inline]
#[must_use]
pub fn value_count(&self) -> usize {
self.values.len()
}
#[must_use]
pub fn value_type(&self, value: Value) -> Option<Type> {
self.values.get(value.index()).map(|v| v.ty)
}
#[must_use]
pub fn value_def(&self, value: Value) -> Option<ValueDef> {
self.values.get(value.index()).map(|v| v.def)
}
#[must_use]
pub fn defining_inst(&self, value: Value) -> Option<Inst> {
match self.value_def(value) {
Some(ValueDef::Result { inst, .. }) => Some(inst),
_ => None,
}
}
#[must_use]
pub fn resolve(&self, value: Value) -> Value {
let mut v = value.as_u32();
for _ in 0..=self.values.len() {
match self.values.get(v as usize) {
Some(node) if node.alias != NONE => v = node.alias,
_ => return Value::from_u32(v),
}
}
Value::from_u32(v)
}
#[inline]
#[must_use]
pub fn slot_count(&self) -> usize {
self.slots.len()
}
#[must_use]
pub fn slot(&self, slot: StackSlot) -> Option<SlotData> {
self.slots.get(slot.index()).copied()
}
#[must_use]
pub fn predecessors(&self) -> Vec<Vec<Block>> {
let mut preds = alloc::vec![Vec::new(); self.blocks.len()];
for (i, node) in self.blocks.iter().enumerate() {
if !node.live {
continue;
}
if let Some(term) = &node.term {
for dest in term.successors() {
if let Some(p) = preds.get_mut(dest.block.index()) {
p.push(Block::from_u32(i as u32));
}
}
}
}
preds
}
pub(crate) fn push_value(&mut self, ty: Type, def: ValueDef) -> Option<Value> {
let index = u32::try_from(self.values.len())
.ok()
.filter(|&i| i < MAX_ENTITIES)?;
self.values.push(ValueNode {
ty,
def,
alias: NONE,
});
Some(Value::from_u32(index))
}
pub(crate) fn push_block(&mut self) -> Option<Block> {
let index = u32::try_from(self.blocks.len())
.ok()
.filter(|&i| i < MAX_ENTITIES)?;
self.blocks.push(BlockNode::new(Vec::new()));
Some(Block::from_u32(index))
}
pub(crate) fn push_inst(&mut self, data: InstData) -> Option<Inst> {
let index = u32::try_from(self.insts.len())
.ok()
.filter(|&i| i < MAX_ENTITIES)?;
self.insts.push(InstNode {
data,
results: ValueRange::default(),
prev: NONE,
next: NONE,
block: NONE,
});
Some(Inst::from_u32(index))
}
pub(crate) fn link_at_end(&mut self, inst: Inst, block: Block) {
let last = match self.blocks.get(block.index()) {
Some(b) => b.last,
None => return,
};
self.link_between(inst, block, last, NONE);
}
pub(crate) fn link_before(&mut self, inst: Inst, before: Inst) {
let Some(node) = self.insts.get(before.index()) else {
return;
};
let (block, prev) = (node.block, node.prev);
if block == NONE {
return;
}
self.link_between(inst, Block::from_u32(block), prev, before.as_u32());
}
pub(crate) fn link_after(&mut self, inst: Inst, after: Inst) {
let Some(node) = self.insts.get(after.index()) else {
return;
};
let (block, next) = (node.block, node.next);
if block == NONE {
return;
}
self.link_between(inst, Block::from_u32(block), after.as_u32(), next);
}
fn link_between(&mut self, inst: Inst, block: Block, prev: u32, next: u32) {
let i = inst.as_u32();
if let Some(n) = self.insts.get_mut(inst.index()) {
n.prev = prev;
n.next = next;
n.block = block.as_u32();
}
if prev == NONE {
if let Some(b) = self.blocks.get_mut(block.index()) {
b.first = i;
}
} else if let Some(p) = self.insts.get_mut(prev as usize) {
p.next = i;
}
if next == NONE {
if let Some(b) = self.blocks.get_mut(block.index()) {
b.last = i;
}
} else if let Some(n) = self.insts.get_mut(next as usize) {
n.prev = i;
}
}
pub(crate) fn unlink(&mut self, inst: Inst) {
let Some(node) = self.insts.get(inst.index()) else {
return;
};
let (block, prev, next) = (node.block, node.prev, node.next);
if block == NONE {
return;
}
if prev == NONE {
if let Some(b) = self.blocks.get_mut(block as usize) {
b.first = next;
}
} else if let Some(p) = self.insts.get_mut(prev as usize) {
p.next = next;
}
if next == NONE {
if let Some(b) = self.blocks.get_mut(block as usize) {
b.last = prev;
}
} else if let Some(n) = self.insts.get_mut(next as usize) {
n.prev = prev;
}
if let Some(n) = self.insts.get_mut(inst.index()) {
n.prev = NONE;
n.next = NONE;
n.block = NONE;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_link_and_unlink_keep_list_consistent() {
let mut f = Function::new(&[]);
let entry = f.entry();
let mut made = Vec::new();
for _ in 0..3 {
let i = f
.push_inst(InstData::Fence {
order: crate::inst::MemOrder::SeqCst,
})
.unwrap_or(Inst::from_u32(NONE));
f.link_at_end(i, entry);
made.push(i);
}
assert_eq!(f.insts(entry).collect::<Vec<_>>(), made);
f.unlink(made[1]);
assert_eq!(f.insts(entry).collect::<Vec<_>>(), [made[0], made[2]]);
f.link_before(made[1], made[0]);
assert_eq!(
f.insts(entry).collect::<Vec<_>>(),
[made[1], made[0], made[2]]
);
f.unlink(made[2]);
f.link_after(made[2], made[1]);
assert_eq!(
f.insts(entry).collect::<Vec<_>>(),
[made[1], made[2], made[0]]
);
assert_eq!(f.first_inst(entry), Some(made[1]));
assert_eq!(f.last_inst(entry), Some(made[0]));
}
#[test]
fn test_resolve_terminates_on_a_corrupt_cycle() {
let mut f = Function::new(&[Type::I32, Type::I32]);
f.values[0].alias = 1;
f.values[1].alias = 0;
let _ = f.resolve(Value::from_u32(0));
}
#[test]
fn test_unknown_handles_read_as_empty() {
let f = Function::new(&[]);
assert!(f.block_params(Block::from_u32(5)).is_empty());
assert_eq!(f.insts(Block::from_u32(5)).count(), 0);
assert_eq!(f.value_type(Value::from_u32(3)), None);
assert_eq!(f.inst(Inst::from_u32(0)), None);
assert!(f.results(Inst::from_u32(0)).is_empty());
}
}