use rucc_mir::{Block, Func, Inst};
pub type Point = u32;
#[derive(Debug, Clone)]
pub struct Order {
blocks: Vec<Block>,
start: Vec<Point>,
end: Vec<Point>,
early: Vec<Point>,
points: Point,
}
impl Order {
#[must_use]
pub fn of(func: &Func) -> Self {
let mut order = Self {
blocks: Vec::with_capacity(func.block_count()),
start: vec![0; func.block_count()],
end: vec![0; func.block_count()],
early: vec![0; func.inst_count()],
points: 0,
};
let mut point = 0;
for block in func.blocks() {
order.blocks.push(block);
order.start[block.index()] = point;
point += 1;
for inst in func.insts(block) {
order.early[inst.index()] = point;
point += 2;
}
order.end[block.index()] = point;
point += 1;
}
order.points = point;
order
}
#[must_use]
pub fn blocks(&self) -> &[Block] {
&self.blocks
}
#[must_use]
pub fn points(&self) -> Point {
self.points
}
#[must_use]
pub fn start(&self, block: Block) -> Point {
self.start[block.index()]
}
#[must_use]
pub fn end(&self, block: Block) -> Point {
self.end[block.index()]
}
#[must_use]
pub fn early(&self, inst: Inst) -> Point {
self.early[inst.index()]
}
#[must_use]
pub fn late(&self, inst: Inst) -> Point {
self.early[inst.index()] + 1
}
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use rucc_mir::{BlockCall, Opcode};
use super::*;
fn func() -> (Func, Vec<Block>, Vec<Inst>) {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let head = func.create_block();
let tail = func.create_block();
let first = func.build(head, opcode).finish();
let second = func.build(head, opcode).finish();
*func.succs_mut(head) = vec![BlockCall::to(tail)];
let third = func.build(tail, opcode).finish();
(func, vec![head, tail], vec![first, second, third])
}
#[test]
fn the_order_is_the_one_the_function_is_written_in() {
let (func, blocks, _) = func();
assert_eq!(Order::of(&func).blocks(), blocks);
}
#[test]
fn an_instruction_reads_before_it_writes() {
let (func, _, insts) = func();
let order = Order::of(&func);
for &inst in &insts {
assert!(order.early(inst) < order.late(inst));
}
}
#[test]
fn nothing_in_a_function_shares_a_point_with_anything_else() {
let (func, blocks, insts) = func();
let order = Order::of(&func);
let mut points: Vec<Point> = Vec::new();
for &block in &blocks {
points.push(order.start(block));
points.push(order.end(block));
}
for &inst in &insts {
points.push(order.early(inst));
points.push(order.late(inst));
}
points.sort_unstable();
let count = points.len();
points.dedup();
assert_eq!(points.len(), count);
assert_eq!(order.points(), Point::try_from(count).expect("a small function"));
}
#[test]
fn a_block_holds_everything_in_it() {
let (func, blocks, insts) = func();
let order = Order::of(&func);
for &block in &blocks {
for inst in func.insts(block) {
assert!(order.start(block) < order.early(inst));
assert!(order.late(inst) < order.end(block));
}
}
assert!(order.end(blocks[0]) < order.start(blocks[1]));
assert!(order.late(insts[1]) < order.early(insts[2]));
}
}