use { crate::{ instrs, lexer, 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, } struct StackReloc { 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 encode(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) { let name = instrs::NAMES[instr[0] as usize]; println!( "{}: {}", name, instr .iter() .take(len) .skip(1) .map(|b| format!("{:02x}", b)) .collect::() ); self.code.extend_from_slice(&instr[..len]); } fn push(&mut self, value: Reg, size: usize) { self.subi64(STACK_PTR, STACK_PTR, size as _); self.encode(instrs::st(value, STACK_PTR, 0, size as _)); } fn pop(&mut self, value: Reg, size: usize) { self.encode(instrs::ld(value, STACK_PTR, 0, size as _)); self.encode(instrs::addi64(STACK_PTR, STACK_PTR, size as _)); } fn subi64(&mut self, dest: Reg, src: Reg, imm: u64) { self.encode(instrs::addi64(dest, src, imm.wrapping_neg())); } fn call(&mut self, func: LabelId) { self.offset(func, 3, 4); self.encode(instrs::jal(RET_ADDR, ZERO, 0)); } fn ret(&mut self) { self.pop(RET_ADDR, 8); self.encode(instrs::jala(ZERO, RET_ADDR, 0)); } fn prelude(&mut self, entry: LabelId) { self.call(entry); self.encode(instrs::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, } struct Variable<'a> { name: Rc, offset: u64, ty: Expr<'a>, } pub struct Codegen<'a> { path: &'a std::path::Path, ret: Expr<'a>, gpa: RegAlloc, code: Func, temp: Func, labels: Vec