use { crate::parser::{self, Expr}, std::rc::Rc, }; type LabelId = u32; type Reg = u8; type MaskElem = u64; const STACK_PTR: Reg = 254; const ZERO: Reg = 0; const RET_ADDR: Reg = 31; const ELEM_WIDTH: usize = std::mem::size_of::() * 8; struct Frame { label: LabelId, prev_relocs: usize, offset: u32, } struct Reloc { id: LabelId, offset: u32, size: u16, } #[derive(Default)] pub struct Func { code: Vec, relocs: Vec, } impl Func { pub fn extend(&mut self, bytes: &[u8]) { self.code.extend_from_slice(bytes); } pub fn offset(&mut self, id: LabelId, offset: u32, size: u16) { self.relocs.push(Reloc { id, offset: self.code.len() as u32 + offset, size, }); } fn push(&mut self, value: Reg, size: usize) { self.st(value, STACK_PTR, 0, size as _); self.addi64(STACK_PTR, STACK_PTR, size as _); } fn pop(&mut self, value: Reg, size: usize) { self.addi64(STACK_PTR, STACK_PTR, (size as u64).wrapping_neg()); self.ld(value, STACK_PTR, 0, size as _); } fn call(&mut self, func: LabelId) { self.jal(RET_ADDR, ZERO, func); } fn ret(&mut self) { self.jala(ZERO, RET_ADDR, 0); } fn prelude(&mut self, entry: LabelId) { self.call(entry); self.tx(); } fn relocate(&mut self, labels: &[Label], shift: i64) { for reloc in self.relocs.drain(..) { let label = &labels[reloc.id as usize]; let offset = if reloc.size == 8 { reloc.offset as i64 } else { label.offset as i64 - reloc.offset as i64 } + shift; let dest = &mut self.code[reloc.offset as usize..][..reloc.size as usize]; match reloc.size { 2 => dest.copy_from_slice(&(offset as i16).to_le_bytes()), 4 => dest.copy_from_slice(&(offset as i32).to_le_bytes()), 8 => dest.copy_from_slice(&(offset as i64).to_le_bytes()), _ => unreachable!(), }; } } } #[derive(Default)] pub struct RegAlloc { free: Vec, // TODO:use 256 bit mask instead used: Vec, } impl RegAlloc { fn init_caller(&mut self) { self.clear(); self.free.extend(1..=31); } fn clear(&mut self) { self.free.clear(); self.used.clear(); } fn allocate(&mut self) -> Reg { let reg = self.free.pop().expect("TODO: we need to spill"); if self.used.binary_search_by_key(&!reg, |&r| !r).is_err() { self.used.push(reg); } reg } fn free(&mut self, reg: Reg) { self.free.push(reg); } } struct Label { offset: u32, // TODO: use different stile of identifier that does not allocate, eg. index + length into a // file name: Rc, } pub struct Codegen<'a> { path: &'a std::path::Path, ret: Expr<'a>, gpa: RegAlloc, code: Func, temp: Func, labels: Vec