use std::cmp::Ordering;
use rucc_mir::{Block, Func, Reg, Role};
use crate::order::{Order, Point};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Range {
pub start: Point,
pub end: Point,
}
impl Range {
#[must_use]
pub fn covers(self, point: Point) -> bool {
self.start <= point && point <= self.end
}
#[must_use]
pub fn overlaps(self, other: Self) -> bool {
self.start <= other.end && other.start <= self.end
}
fn with(self, point: Point) -> Self {
Self { start: self.start.min(point), end: self.end.max(point) }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Area<'a> {
pieces: &'a [Range],
also: Option<Point>,
}
impl<'a> Area<'a> {
#[must_use]
pub fn with(self, point: Point) -> Self {
Self { also: Some(point), ..self }
}
#[must_use]
pub fn hull(self) -> Range {
Range { start: self.piece(0).start, end: self.pieces[self.pieces.len() - 1].end }
}
#[must_use]
pub fn covers(self, point: Point) -> bool {
(0..self.pieces.len()).any(|piece| self.piece(piece).covers(point))
}
#[must_use]
pub fn overlaps(self, other: Self) -> bool {
let (mut mine, mut theirs) = (0, 0);
while mine < self.pieces.len() && theirs < other.pieces.len() {
let (one, two) = (self.piece(mine), other.piece(theirs));
if one.overlaps(two) {
return true;
}
if one.end < two.end {
mine += 1;
} else {
theirs += 1;
}
}
false
}
pub fn pieces(self) -> impl Iterator<Item = Range> + 'a {
(0..self.pieces.len()).map(move |piece| self.piece(piece))
}
fn piece(self, index: usize) -> Range {
let piece = self.pieces[index];
match self.also {
Some(also) if also + 1 == piece.start => Range { start: also, end: piece.end },
_ => piece,
}
}
}
#[derive(Debug, Clone)]
pub struct Live {
live_in: Rows,
live_out: Rows,
pieces: Vec<Range>,
spans: Vec<(usize, usize)>,
}
impl Live {
#[must_use]
pub fn of(func: &Func, order: &Order) -> Self {
let vregs = func.vregs();
let (used, defined) = exposed(func, order);
let (live_in, live_out) = flow(func, order, &used, &defined);
let (pieces, spans) = carve(func, order, &live_in, &live_out, vregs);
Self { live_in, live_out, pieces, spans }
}
#[must_use]
pub fn area(&self, reg: Reg) -> Option<Area<'_>> {
let pieces = self.pieces(reg);
if pieces.is_empty() {
return None;
}
Some(Area { pieces, also: None })
}
#[must_use]
pub fn range(&self, reg: Reg) -> Option<Range> {
self.area(reg).map(Area::hull)
}
pub fn live_in(&self, block: Block) -> impl Iterator<Item = Reg> + '_ {
self.live_in.iter(block.index())
}
pub fn live_out(&self, block: Block) -> impl Iterator<Item = Reg> + '_ {
self.live_out.iter(block.index())
}
fn pieces(&self, reg: Reg) -> &[Range] {
let number = reg.number().and_then(|number| usize::try_from(number).ok());
let Some(&(from, to)) = number.and_then(|number| self.spans.get(number)) else {
return &[];
};
&self.pieces[from..to]
}
}
fn carve(
func: &Func,
order: &Order,
live_in: &Rows,
live_out: &Rows,
vregs: usize,
) -> (Vec<Range>, Vec<(usize, usize)>) {
let mut lists: Vec<Vec<Range>> = vec![Vec::new(); vregs];
let mut here: Vec<Option<Range>> = vec![None; vregs];
let mut touched: Vec<usize> = Vec::new();
for &block in order.blocks() {
for reg in live_in.iter(block.index()) {
note(&mut here, &mut touched, reg, order.start(block));
}
for reg in live_out.iter(block.index()) {
note(&mut here, &mut touched, reg, order.end(block));
}
for param in &func[block].params {
note(&mut here, &mut touched, param.reg, order.start(block));
}
for inst in func.insts(block) {
for operand in &func[func[inst].operands] {
match operand.role {
Role::Use => note(&mut here, &mut touched, operand.reg, order.early(inst)),
Role::Def => note(&mut here, &mut touched, operand.reg, order.late(inst)),
Role::EarlyDef => {
note(&mut here, &mut touched, operand.reg, order.early(inst));
note(&mut here, &mut touched, operand.reg, order.late(inst));
}
}
}
}
for call in &func[block].succs {
for &arg in &call.args {
note(&mut here, &mut touched, arg, order.end(block));
}
}
for &number in &touched {
let Some(piece) = here[number].take() else { continue };
match lists[number].last_mut() {
Some(last) if last.end + 1 == piece.start => last.end = piece.end,
_ => lists[number].push(piece),
}
}
touched.clear();
}
let mut pieces = Vec::new();
let mut spans = Vec::with_capacity(vregs);
for list in &lists {
let from = pieces.len();
pieces.extend_from_slice(list);
spans.push((from, pieces.len()));
}
(pieces, spans)
}
fn note(here: &mut [Option<Range>], touched: &mut Vec<usize>, reg: Reg, point: Point) {
let Some(number) = reg.number().and_then(|number| usize::try_from(number).ok()) else {
return;
};
let Some(slot) = here.get_mut(number) else { return };
match slot {
Some(range) => *range = range.with(point),
None => {
*slot = Some(Range { start: point, end: point });
touched.push(number);
}
}
}
fn exposed(func: &Func, order: &Order) -> (Rows, Rows) {
let vregs = func.vregs();
let mut used = Rows::new(func.block_count());
let mut defined = Rows::new(func.block_count());
let mut reads = Building::new(vregs);
let mut writes = Building::new(vregs);
for &block in order.blocks() {
let row = block.index();
for call in &func[block].succs {
for &arg in &call.args {
reads.insert(arg);
}
}
let insts: Vec<_> = func.insts(block).collect();
for &inst in insts.iter().rev() {
let operands = &func[func[inst].operands];
for operand in operands.iter().filter(|operand| operand.role.is_def()) {
reads.remove(operand.reg);
writes.insert(operand.reg);
}
for operand in operands.iter().filter(|operand| !operand.role.is_def()) {
reads.insert(operand.reg);
}
}
for param in &func[block].params {
reads.remove(param.reg);
writes.insert(param.reg);
}
used.set(row, &reads.take());
defined.set(row, &writes.take());
}
(used, defined)
}
fn flow(func: &Func, order: &Order, used: &Rows, defined: &Rows) -> (Rows, Rows) {
let mut live_in = Rows::new(func.block_count());
let mut live_out = Rows::new(func.block_count());
let (mut out, mut scratch, mut rest, mut next) =
(Vec::new(), Vec::new(), Vec::new(), Vec::new());
let mut changed = true;
while changed {
changed = false;
for &block in order.blocks().iter().rev() {
let row = block.index();
out.clear();
for call in &func[block].succs {
union(&out, live_in.row(call.block.index()), &mut scratch);
std::mem::swap(&mut out, &mut scratch);
}
without(&out, defined.row(row), &mut rest);
union(used.row(row), &rest, &mut next);
if live_in.row(row) != next.as_slice() {
live_in.set(row, &next);
changed = true;
}
live_out.set(row, &out);
}
}
(live_in, live_out)
}
fn union(one: &[u32], two: &[u32], out: &mut Vec<u32>) {
out.clear();
let (mut here, mut there) = (0, 0);
while here < one.len() && there < two.len() {
match one[here].cmp(&two[there]) {
Ordering::Less => {
out.push(one[here]);
here += 1;
}
Ordering::Greater => {
out.push(two[there]);
there += 1;
}
Ordering::Equal => {
out.push(one[here]);
here += 1;
there += 1;
}
}
}
out.extend_from_slice(&one[here..]);
out.extend_from_slice(&two[there..]);
}
fn without(one: &[u32], two: &[u32], out: &mut Vec<u32>) {
out.clear();
let mut there = 0;
for &number in one {
while there < two.len() && two[there] < number {
there += 1;
}
if there < two.len() && two[there] == number {
continue;
}
out.push(number);
}
}
#[derive(Debug, Clone)]
struct Rows {
rows: Vec<Vec<u32>>,
}
impl Rows {
fn new(rows: usize) -> Self {
Self { rows: vec![Vec::new(); rows] }
}
fn row(&self, row: usize) -> &[u32] {
&self.rows[row]
}
fn set(&mut self, row: usize, numbers: &[u32]) {
let row = &mut self.rows[row];
row.clear();
row.extend_from_slice(numbers);
}
fn iter(&self, row: usize) -> impl Iterator<Item = Reg> + '_ {
self.rows[row].iter().copied().map(Reg::virtual_reg)
}
}
struct Building {
flags: Vec<bool>,
touched: Vec<u32>,
}
impl Building {
fn new(vregs: usize) -> Self {
Self { flags: vec![false; vregs], touched: Vec::new() }
}
fn number(&self, reg: Reg) -> Option<usize> {
let number = usize::try_from(reg.number()?).ok()?;
(number < self.flags.len()).then_some(number)
}
fn insert(&mut self, reg: Reg) {
let Some(number) = self.number(reg) else { return };
if !self.flags[number] {
self.flags[number] = true;
self.touched.push(u32::try_from(number).expect("a register number"));
}
}
fn remove(&mut self, reg: Reg) {
if let Some(number) = self.number(reg) {
self.flags[number] = false;
}
}
fn take(&mut self) -> Vec<u32> {
let flags = &mut self.flags;
let mut out: Vec<u32> = self
.touched
.drain(..)
.filter(|&number| std::mem::replace(&mut flags[number as usize], false))
.collect();
out.sort_unstable();
out
}
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use rucc_mir::{BlockCall, Constraint, Opcode, Operand};
use rucc_target::x86_64::GPR;
use super::*;
fn regs(of: impl Iterator<Item = Reg>) -> Vec<u32> {
of.filter_map(Reg::number).collect()
}
#[test]
fn a_value_is_live_from_where_it_is_written_to_where_it_is_last_read() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let block = func.create_block();
let value = func.new_vreg(GPR);
let other = func.new_vreg(GPR);
let write = func.build(block, opcode).def(value, GPR).finish();
let idle = func.build(block, opcode).def(other, GPR).finish();
let read = func.build(block, opcode).uses(value, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let range = live.range(value).expect("the value is live somewhere");
assert_eq!(range, Range { start: order.late(write), end: order.early(read) });
assert!(range.covers(order.early(idle)));
assert_eq!(
live.range(other),
Some(Range { start: order.late(idle), end: order.late(idle) })
);
assert!(!range.overlaps(Range { start: order.late(read), end: order.late(read) }));
assert_eq!(regs(live.live_in(block)), Vec::<u32>::new());
}
#[test]
fn a_value_read_in_another_block_is_live_between_them() {
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 middle = func.create_block();
let tail = func.create_block();
let value = func.new_vreg(GPR);
func.build(head, opcode).def(value, GPR).finish();
*func.succs_mut(head) = vec![BlockCall::to(middle)];
*func.succs_mut(middle) = vec![BlockCall::to(tail)];
let read = func.build(tail, opcode).uses(value, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
assert_eq!(regs(live.live_in(middle)), vec![0]);
assert_eq!(regs(live.live_out(middle)), vec![0]);
assert!(live.range(value).expect("live somewhere").covers(order.start(middle)));
assert_eq!(live.range(value).expect("live somewhere").end, order.early(read));
}
#[test]
fn a_block_the_value_never_reaches_is_a_hole_between_two_pieces() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let entry = func.create_block();
let arm = func.create_block();
let tail = func.create_block();
let value = func.new_vreg(GPR);
let write = func.build(entry, opcode).def(value, GPR).finish();
*func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
let idle = func.build(arm, opcode).finish();
let read = func.build(tail, opcode).uses(value, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let area = live.area(value).expect("live somewhere");
assert!(live.range(value).expect("live somewhere").covers(order.early(idle)));
assert!(!area.covers(order.early(idle)));
assert_eq!(
area.pieces().collect::<Vec<_>>(),
vec![
Range { start: order.late(write), end: order.end(entry) },
Range { start: order.start(tail), end: order.early(read) },
]
);
}
#[test]
fn a_value_in_a_hole_of_another_may_have_its_register() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let entry = func.create_block();
let arm = func.create_block();
let tail = func.create_block();
let value = func.new_vreg(GPR);
let inside = func.new_vreg(GPR);
func.build(entry, opcode).def(value, GPR).finish();
*func.succs_mut(entry) = vec![BlockCall::to(arm), BlockCall::to(tail)];
func.build(arm, opcode).def(inside, GPR).finish();
func.build(arm, opcode).uses(inside, GPR).finish();
func.build(tail, opcode).uses(value, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let value = live.area(value).expect("live somewhere");
let inside = live.area(inside).expect("live somewhere");
assert!(value.hull().overlaps(inside.hull()));
assert!(!value.overlaps(inside));
assert!(!inside.overlaps(value));
}
#[test]
fn one_point_added_in_front_of_a_piece_is_part_of_the_area() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let block = func.create_block();
let first = func.new_vreg(GPR);
let second = func.new_vreg(GPR);
let write = func.build(block, opcode).def(first, GPR).finish();
let both = func.build(block, opcode).def(second, GPR).uses(first, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let first = live.area(first).expect("live somewhere");
let second = live.area(second).expect("live somewhere");
assert!(!first.overlaps(second));
assert!(first.overlaps(second.with(order.early(both))));
assert!(second.with(order.early(both)).covers(order.early(both)));
assert_eq!(second.with(order.early(both)).hull().start, order.early(both));
assert_eq!(first.hull().start, order.late(write));
}
#[test]
fn the_point_added_in_front_joins_the_piece_it_belongs_to_and_not_the_first_one() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let nop = Opcode::new(names.intern("x64.nop"));
let add = Opcode::new(names.intern("x64.add"));
let entry = func.create_block();
let head = func.create_block();
let arm = func.create_block();
let latch = func.create_block();
let out = func.create_block();
let seed = func.new_vreg(GPR);
let sum = func.new_vreg(GPR);
let inside = func.new_vreg(GPR);
let loaded = func.new_vreg(GPR);
func.build(entry, nop).def(seed, GPR).finish();
func.build(entry, nop).def(sum, GPR).finish();
*func.succs_mut(entry) = vec![BlockCall::to(head)];
func.build(head, nop).uses(sum, GPR).finish();
*func.succs_mut(head) = vec![BlockCall::to(arm), BlockCall::to(latch)];
func.build(arm, nop).def(inside, GPR).finish();
func.build(arm, nop).uses(inside, GPR).finish();
*func.succs_mut(arm) = vec![BlockCall::to(out)];
func.build(latch, nop).def(loaded, GPR).finish();
let carry = func
.build(latch, add)
.operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
.uses(seed, GPR)
.uses(loaded, GPR)
.finish();
*func.succs_mut(latch) = vec![BlockCall::to(head), BlockCall::to(out)];
let order = Order::of(&func);
let live = Live::of(&func, &order);
let sum = live.area(sum).expect("live somewhere");
let loaded = live.area(loaded).expect("live somewhere");
assert_eq!(sum.pieces().count(), 2);
assert!(!sum.covers(order.early(carry)));
assert!(sum.with(order.early(carry)).covers(order.early(carry)));
assert!(!loaded.overlaps(sum));
assert!(loaded.overlaps(sum.with(order.early(carry))));
}
#[test]
fn a_value_carried_round_a_loop_is_live_round_all_of_it() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let header = func.create_block();
let body = func.create_block();
let carried = func.append_param(header, GPR);
let next = func.new_vreg(GPR);
*func.succs_mut(header) = vec![BlockCall::to(body)];
func.build(body, opcode).def(next, GPR).uses(carried, GPR).finish();
*func.succs_mut(body) = vec![BlockCall::with(header, vec![next])];
let order = Order::of(&func);
let live = Live::of(&func, &order);
assert_eq!(regs(live.live_in(header)), Vec::<u32>::new());
assert_eq!(regs(live.live_in(body)), vec![carried.number().expect("virtual")]);
let range = live.range(next).expect("live somewhere");
assert_eq!(range.end, order.end(body));
}
#[test]
fn two_values_that_are_never_both_wanted_do_not_overlap() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let block = func.create_block();
let first = func.new_vreg(GPR);
let second = func.new_vreg(GPR);
let write = func.build(block, opcode).def(first, GPR).finish();
func.build(block, opcode).def(second, GPR).uses(first, GPR).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let first = live.range(first).expect("live somewhere");
let second = live.range(second).expect("live somewhere");
assert!(!first.overlaps(second));
assert!(first.start > order.start(block));
assert_eq!(first.start, order.late(write));
}
#[test]
fn an_operand_written_early_is_wanted_where_the_operands_are_read() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let block = func.create_block();
let source = func.new_vreg(GPR);
let early = func.new_vreg(GPR);
func.build(block, opcode).def(source, GPR).finish();
func.build(block, opcode)
.operand(Operand::write_early(early, GPR))
.operand(Operand::read(source, GPR))
.finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
let source = live.range(source).expect("live somewhere");
let early = live.range(early).expect("live somewhere");
assert!(source.overlaps(early));
}
#[test]
fn a_register_a_memory_operand_names_is_read_like_any_other() {
use rucc_mir::Mem;
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let opcode = Opcode::new(names.intern("x64.nop"));
let block = func.create_block();
let address = func.new_vreg(GPR);
let write = func.build(block, opcode).def(address, GPR).finish();
let load = func.build(block, opcode).mem(Mem::at(Operand::read(address, GPR))).finish();
let order = Order::of(&func);
let live = Live::of(&func, &order);
assert_eq!(
live.range(address),
Some(Range { start: order.late(write), end: order.early(load) })
);
}
}