diff --git a/hblang/build.rs b/hblang/build.rs new file mode 100644 index 0000000..9f72d18 --- /dev/null +++ b/hblang/build.rs @@ -0,0 +1,95 @@ +#![feature(iter_next_chunk)] +fn main() -> Result<(), Box> { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=../hbbytecode/instructions.in"); + + let instructions = include_str!("../hbbytecode/instructions.in"); + + let mut generated = String::new(); + use std::fmt::Write; + + writeln!(&mut generated, "impl crate::codegen::Func {{")?; + + for line in instructions.lines() { + let line = line.strip_suffix(";").unwrap(); + let [opcode, name, ty, doc] = line.splitn(4, ',').map(str::trim).next_chunk().unwrap(); + + writeln!(&mut generated, "/// {}", doc.trim_matches('"'))?; + write!(&mut generated, "pub fn {}(&mut self", name.to_lowercase())?; + for (i, c) in ty.chars().enumerate() { + let (name, ty) = match c { + 'N' => continue, + 'R' => ("reg", "u8"), + 'B' => ("imm", "u8"), + 'H' => ("imm", "u16"), + 'W' => ("imm", "u32"), + 'D' => ("imm", "u64"), + 'P' => ("offset", "u32"), + 'O' => ("offset", "u32"), + 'A' => ("addr", "u64"), + _ => panic!("unknown type: {}", c), + }; + write!(&mut generated, ", {name}{i}: {ty}")?; + } + writeln!(&mut generated, ") {{")?; + + let mut offset = 1; + for (i, c) in ty.chars().enumerate() { + let width = match c { + 'N' => 0, + 'R' => 1, + 'B' => 1, + 'H' => 2, + 'W' => 4, + 'D' => 8, + 'A' => 8, + 'P' => 2, + 'O' => 4, + _ => panic!("unknown type: {}", c), + }; + + if matches!(c, 'P' | 'O') { + writeln!( + &mut generated, + " self.offset(offset{i}, {offset}, {width});", + )?; + } + + offset += width; + } + + write!( + &mut generated, + " self.extend(crate::as_bytes(&crate::Args({opcode}" + )?; + for (i, c) in ty.chars().enumerate() { + let name = match c { + 'N' => continue, + 'R' => "reg", + 'B' | 'H' | 'W' | 'D' => "imm", + 'P' => "0u16", + 'O' => "0u32", + 'A' => "addr", + _ => panic!("unknown type: {}", c), + }; + + if matches!(c, 'P' | 'O') { + write!(&mut generated, ", {name}")?; + } else { + write!(&mut generated, ", {name}{i}")?; + } + } + for _ in ty.len() - (ty == "N") as usize..4 { + write!(&mut generated, ", ()")?; + } + writeln!(&mut generated, ")));")?; + + writeln!(&mut generated, "}}")?; + } + + writeln!(&mut generated, "}}")?; + + std::fs::write("src/instrs.rs", generated)?; + + Ok(()) +} diff --git a/hblang/examples/main_fn.hb b/hblang/examples/main_fn.hb index 2a60bce..d6d2635 100644 --- a/hblang/examples/main_fn.hb +++ b/hblang/examples/main_fn.hb @@ -1,3 +1,3 @@ -main := ||: void { - return; +main := ||: int { + return 1; } diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 7dd1b62..26bc312 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -1,5 +1,9 @@ -use {crate::parser, std::fmt::Write}; +use { + crate::parser::{self, Expr}, + std::rc::Rc, +}; +type LabelId = u32; type Reg = u8; type MaskElem = u64; @@ -8,25 +12,102 @@ 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>, + used: Vec, } impl RegAlloc { - fn callee_general_purpose() -> Self { - Self { - free: (32..=253).collect(), - used: Vec::new(), - } + 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(&std::cmp::Reverse(reg)).is_err() { - self.used.push(std::cmp::Reverse(reg)); + if self.used.binary_search_by_key(&!reg, |&r| !r).is_err() { + self.used.push(reg); } reg } @@ -36,129 +117,171 @@ impl RegAlloc { } } +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, - gpa: RegAlloc, - code: String, - data: String, - prelude_buf: String, + path: &'a std::path::Path, + ret: Expr<'a>, + gpa: RegAlloc, + code: Func, + temp: Func, + labels: Vec