use std::ops::{Index, IndexMut};
use rucc_base::{Idx, IdxRange, Symbol};
use rucc_diag::Span;
use rucc_target::RegClass;
use crate::inst::{
Amode, Block, BlockCall, BlockData, Imm, ImmRef, Inst, InstData, InstLayout, Mem, MemRef,
Opcode, Operand, OperandList, Param, Reg,
};
#[derive(Debug)]
pub struct Func {
pub name: Symbol,
insts: Vec<InstData>,
inst_layout: Vec<InstLayout>,
inst_spans: Vec<Span>,
blocks: Vec<BlockData>,
operands: Vec<Operand>,
imms: Vec<Imm>,
amodes: Vec<Amode>,
vregs: Vec<RegClass>,
first_block: Option<Block>,
last_block: Option<Block>,
}
impl Func {
#[must_use]
pub fn new(name: Symbol) -> Self {
Self {
name,
insts: Vec::new(),
inst_layout: Vec::new(),
inst_spans: Vec::new(),
blocks: Vec::new(),
operands: Vec::new(),
imms: Vec::new(),
amodes: Vec::new(),
vregs: Vec::new(),
first_block: None,
last_block: None,
}
}
pub fn new_vreg(&mut self, class: RegClass) -> Reg {
let number = u32::try_from(self.vregs.len()).expect("too many virtual registers");
self.vregs.push(class);
Reg::virtual_reg(number)
}
#[must_use]
pub fn vregs(&self) -> usize {
self.vregs.len()
}
#[must_use]
pub fn class_of(&self, reg: Reg) -> Option<RegClass> {
self.vregs.get(usize::try_from(reg.number()?).ok()?).copied()
}
pub fn create_block(&mut self) -> Block {
let block = Idx::from_usize(self.blocks.len());
self.blocks.push(BlockData { prev: self.last_block, ..BlockData::default() });
match self.last_block {
Some(last) => self.blocks[last.index()].next = Some(block),
None => self.first_block = Some(block),
}
self.last_block = Some(block);
block
}
#[must_use]
pub fn entry(&self) -> Option<Block> {
self.first_block
}
#[must_use]
pub fn block_count(&self) -> usize {
self.blocks.len()
}
#[must_use]
pub fn inst_count(&self) -> usize {
self.insts.len()
}
pub fn blocks(&self) -> impl Iterator<Item = Block> + use<'_> {
std::iter::successors(self.first_block, |&block| self[block].next)
}
pub fn append_param(&mut self, block: Block, class: RegClass) -> Reg {
let reg = self.new_vreg(class);
self.blocks[block.index()].params.push(Param { reg, class });
reg
}
pub fn append_given_param(&mut self, block: Block, param: Param) {
self.blocks[block.index()].params.push(param);
}
pub fn params_mut(&mut self, block: Block) -> &mut Vec<Param> {
&mut self.blocks[block.index()].params
}
pub fn succs_mut(&mut self, block: Block) -> &mut Vec<BlockCall> {
&mut self.blocks[block.index()].succs
}
pub fn insts(&self, block: Block) -> impl Iterator<Item = Inst> + use<'_> {
std::iter::successors(self[block].first_inst, |&inst| self.inst_layout[inst.index()].next)
}
#[must_use]
pub fn terminator(&self, block: Block) -> Option<Inst> {
self[block].last_inst
}
#[must_use]
pub fn block_of(&self, inst: Inst) -> Option<Block> {
self.inst_layout[inst.index()].block
}
#[must_use]
pub fn span(&self, inst: Inst) -> Span {
self.inst_spans[inst.index()]
}
pub fn build(&mut self, block: Block, opcode: Opcode) -> InstBuilder<'_> {
InstBuilder {
func: self,
block,
opcode,
operands: Vec::new(),
imm: None,
mem: None,
symbol: None,
span: Span::DUMMY,
}
}
pub fn append_inst(&mut self, block: Block, inst: Inst) {
assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
let last = self.blocks[block.index()].last_inst;
self.inst_layout[inst.index()] = InstLayout { block: Some(block), prev: last, next: None };
match last {
Some(last) => self.inst_layout[last.index()].next = Some(inst),
None => self.blocks[block.index()].first_inst = Some(inst),
}
self.blocks[block.index()].last_inst = Some(inst);
}
pub fn insert_after(&mut self, after: Inst, inst: Inst) {
assert!(self.inst_layout[inst.index()].block.is_none(), "the instruction is in a block");
let layout = self.inst_layout[after.index()];
let block = layout.block.expect("the instruction to insert after is in no block");
self.inst_layout[inst.index()] =
InstLayout { block: Some(block), prev: Some(after), next: layout.next };
self.inst_layout[after.index()].next = Some(inst);
match layout.next {
Some(next) => self.inst_layout[next.index()].prev = Some(inst),
None => self.blocks[block.index()].last_inst = Some(inst),
}
}
pub fn remove_inst(&mut self, inst: Inst) {
let layout = self.inst_layout[inst.index()];
let Some(block) = layout.block else { return };
match layout.prev {
Some(prev) => self.inst_layout[prev.index()].next = layout.next,
None => self.blocks[block.index()].first_inst = layout.next,
}
match layout.next {
Some(next) => self.inst_layout[next.index()].prev = layout.prev,
None => self.blocks[block.index()].last_inst = layout.prev,
}
self.inst_layout[inst.index()] = InstLayout::default();
}
pub fn push_operands(&mut self, operands: &[Operand]) -> OperandList {
let start = Idx::from_usize(self.operands.len());
self.operands.extend_from_slice(operands);
IdxRange::new(start, Idx::from_usize(self.operands.len()))
}
pub fn add_imm(&mut self, value: i64) -> ImmRef {
self.imms.push(Imm(value));
Idx::from_usize(self.imms.len() - 1)
}
pub fn add_amode(&mut self, amode: Amode) -> MemRef {
self.amodes.push(amode);
Idx::from_usize(self.amodes.len() - 1)
}
pub fn create_inst(&mut self, data: InstData, span: Span) -> Inst {
self.insts.push(data);
self.inst_layout.push(InstLayout::default());
self.inst_spans.push(span);
Idx::from_usize(self.insts.len() - 1)
}
}
impl Index<Inst> for Func {
type Output = InstData;
fn index(&self, inst: Inst) -> &InstData {
&self.insts[inst.index()]
}
}
impl IndexMut<Inst> for Func {
fn index_mut(&mut self, inst: Inst) -> &mut InstData {
&mut self.insts[inst.index()]
}
}
impl Index<Block> for Func {
type Output = BlockData;
fn index(&self, block: Block) -> &BlockData {
&self.blocks[block.index()]
}
}
impl Index<OperandList> for Func {
type Output = [Operand];
fn index(&self, list: OperandList) -> &[Operand] {
&self.operands[list.as_usize_range()]
}
}
impl IndexMut<OperandList> for Func {
fn index_mut(&mut self, list: OperandList) -> &mut [Operand] {
&mut self.operands[list.as_usize_range()]
}
}
impl Index<ImmRef> for Func {
type Output = Imm;
fn index(&self, at: ImmRef) -> &Imm {
&self.imms[at.index()]
}
}
impl Index<MemRef> for Func {
type Output = Amode;
fn index(&self, at: MemRef) -> &Amode {
&self.amodes[at.index()]
}
}
#[derive(Debug)]
pub struct InstBuilder<'a> {
func: &'a mut Func,
block: Block,
opcode: Opcode,
operands: Vec<Operand>,
imm: Option<i64>,
mem: Option<Amode>,
symbol: Option<Symbol>,
span: Span,
}
impl InstBuilder<'_> {
#[must_use]
pub fn operand(mut self, operand: Operand) -> Self {
assert!(self.mem.is_none(), "the memory operand's registers come last");
if operand.role.is_def() {
let reads = self.operands.iter().any(|earlier| !earlier.role.is_def());
assert!(!reads, "the operands an instruction writes come first");
}
self.operands.push(operand);
self
}
#[must_use]
pub fn def(self, reg: Reg, class: RegClass) -> Self {
self.operand(Operand::write(reg, class))
}
#[must_use]
pub fn uses(self, reg: Reg, class: RegClass) -> Self {
self.operand(Operand::read(reg, class))
}
#[must_use]
pub fn mem(mut self, mem: Mem) -> Self {
assert!(self.mem.is_none(), "the instruction already has a memory operand");
let mut amode = Amode {
base: None,
index: None,
scale: mem.scale.max(1),
disp: mem.disp,
symbol: mem.symbol,
};
if let Some(base) = mem.base {
amode.base = Some(self.next_operand());
self.operands.push(base);
}
if let Some(index) = mem.index {
amode.index = Some(self.next_operand());
self.operands.push(index);
}
self.mem = Some(amode);
self
}
#[must_use]
pub fn imm(mut self, value: i64) -> Self {
self.imm = Some(value);
self
}
#[must_use]
pub fn symbol(mut self, symbol: Symbol) -> Self {
self.symbol = Some(symbol);
self
}
#[must_use]
pub fn at(mut self, span: Span) -> Self {
self.span = span;
self
}
pub fn finish(self) -> Inst {
let InstBuilder { func, block, opcode, operands, imm, mem, symbol, span } = self;
let data = InstData {
opcode,
operands: func.push_operands(&operands),
imm: imm.map(|value| func.add_imm(value)),
mem: mem.map(|amode| func.add_amode(amode)),
symbol,
};
let inst = func.create_inst(data, span);
func.append_inst(block, inst);
inst
}
fn next_operand(&self) -> u8 {
u8::try_from(self.operands.len()).expect("too many operands on one instruction")
}
}
#[must_use]
pub fn defs(operands: &[Operand]) -> usize {
operands.iter().position(|operand| !operand.role.is_def()).unwrap_or(operands.len())
}
#[cfg(test)]
mod tests {
use rucc_base::Interner;
use super::*;
fn class() -> RegClass {
RegClass::new(0)
}
#[test]
fn instructions_come_back_in_the_order_they_were_built() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let opcode = Opcode::new(names.intern("x64.nop"));
let first = func.build(block, opcode).finish();
let second = func.build(block, opcode).finish();
assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, second]);
assert_eq!(func.terminator(block), Some(second));
assert_eq!(func.block_of(first), Some(block));
}
#[test]
fn a_removed_instruction_is_in_no_block_and_the_rest_still_link_up() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let opcode = Opcode::new(names.intern("x64.nop"));
let first = func.build(block, opcode).finish();
let second = func.build(block, opcode).finish();
let third = func.build(block, opcode).finish();
func.remove_inst(second);
assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, third]);
assert_eq!(func.block_of(second), None);
}
#[test]
fn an_instruction_can_be_put_back_between_two_others() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let opcode = Opcode::new(names.intern("x64.nop"));
let first = func.build(block, opcode).finish();
let last = func.build(block, opcode).finish();
let spill = func.create_inst(InstData::new(opcode), Span::DUMMY);
func.insert_after(first, spill);
assert_eq!(func.insts(block).collect::<Vec<_>>(), vec![first, spill, last]);
assert_eq!(func.terminator(block), Some(last));
}
#[test]
fn a_memory_operand_names_the_operands_holding_its_registers() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let base = func.new_vreg(class());
let index = func.new_vreg(class());
let dest = func.new_vreg(class());
let inst = func
.build(block, Opcode::new(names.intern("x64.lea")))
.def(dest, class())
.mem(
Mem::at(Operand::read(base, class()))
.indexed(Operand::read(index, class()), 4)
.plus(16),
)
.finish();
let data = func[inst];
let amode = func[data.mem.expect("it was given a memory operand")];
assert_eq!(amode.base, Some(1));
assert_eq!(amode.index, Some(2));
assert_eq!(amode.scale, 4);
assert_eq!(amode.disp, 16);
assert_eq!(func[data.operands][1].reg, base);
assert_eq!(defs(&func[data.operands]), 1);
}
#[test]
#[should_panic(expected = "the operands an instruction writes come first")]
fn a_def_after_a_use_is_refused() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let reg = func.new_vreg(class());
let _ = func
.build(block, Opcode::new(names.intern("x64.add")))
.uses(reg, class())
.def(reg, class());
}
#[test]
fn a_block_parameter_is_a_virtual_register_of_its_class() {
let mut names = Interner::new();
let mut func = Func::new(names.intern("f"));
let block = func.create_block();
let param = func.append_param(block, class());
assert_eq!(func[block].params, vec![Param { reg: param, class: class() }]);
assert_eq!(func.class_of(param), Some(class()));
assert_eq!(func.vregs(), 1);
assert_eq!(func.entry(), Some(block));
}
}