diff --git a/hblang/build.rs b/hblang/build.rs index f61acbb..e2d598f 100644 --- a/hblang/build.rs +++ b/hblang/build.rs @@ -7,6 +7,7 @@ fn main() -> Result<(), Box> { let mut generated = String::new(); + writeln!(generated, "#![allow(dead_code)]")?; gen_max_size(&mut generated)?; gen_encodes(&mut generated)?; gen_structs(&mut generated)?; diff --git a/hblang/examples/generic_types.hb b/hblang/examples/generic_types.hb new file mode 100644 index 0000000..fa1e2d0 --- /dev/null +++ b/hblang/examples/generic_types.hb @@ -0,0 +1,17 @@ +Vec := fn(Elem: type): type { + return struct { + data: ^Elem, + len: uint, + cap: uint, + }; +} + +main := fn(): int { + i := 69; + vec := Vec(int).{ + data: &i, + len: 1, + cap: 1, + }; + return *vec.data; +} diff --git a/hblang/examples/pointers.hb b/hblang/examples/pointers.hb index c0250da..a421455 100644 --- a/hblang/examples/pointers.hb +++ b/hblang/examples/pointers.hb @@ -7,7 +7,7 @@ main := fn(): int { return *b - 2; } -modify := fn(a: *int): void { +modify := fn(a: ^int): void { *a = 2; return; } diff --git a/hblang/examples/structs.hb b/hblang/examples/structs.hb index d0aea8a..aa5ec29 100644 --- a/hblang/examples/structs.hb +++ b/hblang/examples/structs.hb @@ -17,7 +17,7 @@ main := fn(): int { return 0; } -pass := fn(t: *Ty): int { +pass := fn(t: ^Ty): int { return t.a - t.b; } diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 1511d79..fdc8663 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -7,7 +7,8 @@ use hbvm::Vm; use crate::{ ident::{self, Ident}, - parser::ExprRef, + parser::{idfl, ExprRef}, + HashMap, }; use { @@ -20,24 +21,21 @@ use { use {lexer::TokenKind as T, parser::Expr as E}; -type LabelId = u32; +type FuncId = u32; type Reg = u8; -type MaskElem = u64; type Type = u32; type GlobalId = u32; +const VM_STACK_SIZE: usize = 1024 * 1024 * 2; + fn align_up(value: u64, align: u64) -> u64 { (value + align - 1) & !(align - 1) } -enum Signature { - Global(Type), - Function(Box<[Type]>, Type), -} - struct ItemId { file: parser::FileId, expr: parser::ExprRef, + id: u32, } #[derive(Debug, PartialEq, Eq)] @@ -165,19 +163,63 @@ impl Ctx { } } +mod traps { + macro_rules! traps { + ($($name:ident;)*) => {$( + pub const $name: u64 = ${index(0)}; + )*}; + } + + traps! { + MAKE_STRUCT; + } +} + pub mod bt { use super::*; + const fn array_to_lower_case(array: [u8; N]) -> [u8; N] { + let mut result = [0; N]; + let mut i = 0; + while i < N { + result[i] = array[i].to_ascii_lowercase(); + i += 1; + } + result + } + // const string to lower case + macro_rules! builtin_type { - ($($name:ident;)*) => {$( - pub const $name: Type = ${index(0)}; - )*}; + ($($name:ident;)*) => { + $(pub const $name: Type = ${index(0)};)* + + mod __lc_names { + use super::*; + $(pub const $name: &[u8] = &array_to_lower_case(unsafe { + *(stringify!($name).as_ptr() as *const [u8; stringify!($name).len()]) });)* + } + + pub fn from_str(name: &str) -> Option { + match name.as_bytes() { + $(__lc_names::$name => Some($name),)* + _ => None, + } + } + + pub fn to_str(ty: Type) -> &'static str { + match ty { + $(${index(0)} => unsafe { std::str::from_utf8_unchecked(__lc_names::$name) },)* + v => unreachable!("invalid type: {}", v), + } + } + }; } builtin_type! { UNDECLARED; NEVER; VOID; + TYPE; BOOL; U8; U16; @@ -220,75 +262,77 @@ pub mod bt { } } -#[derive(Debug)] -enum TypeKind { - Builtin(Type), - Struct(Type), - Pointer(Type), +macro_rules! type_kind { + ($name:ident {$( $variant:ident, )*}) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum $name { + $($variant(Type),)* + } + + impl $name { + const FLAG_BITS: u32 = (${count($variant)} as u32).next_power_of_two().ilog2(); + const FLAG_OFFSET: u32 = std::mem::size_of::() as u32 * 8 - Self::FLAG_BITS; + const INDEX_MASK: u32 = (1 << (32 - Self::FLAG_BITS)) - 1; + + fn from_ty(ty: Type) -> Self { + let (flag, index) = (ty >> Self::FLAG_OFFSET, ty & Self::INDEX_MASK); + match flag { + $(${index(0)} => Self::$variant(index),)* + _ => unreachable!(), + } + } + + const fn encode(self) -> Type { + let (index, flag) = match self { + $(Self::$variant(index) => (index, ${index(0)}),)* + }; + (flag << Self::FLAG_OFFSET) | index + } + } + }; } -impl TypeKind { - const FLAG_BITS: u32 = 2; - const FLAG_OFFSET: u32 = std::mem::size_of::() as u32 * 8 - Self::FLAG_BITS; - const INDEX_MASK: u32 = (1 << (32 - Self::FLAG_BITS)) - 1; - - fn from_ty(ty: Type) -> Self { - let (flag, index) = (ty >> Self::FLAG_OFFSET, ty & Self::INDEX_MASK); - match flag { - 0 => Self::Builtin(index), - 1 => Self::Pointer(index), - 2 => Self::Struct(index), - _ => unreachable!(), - } +type_kind! { + TypeKind { + Builtin, + Struct, + Pointer, + Func, + Global, } +} - const fn encode(self) -> Type { - let (index, flag) = match self { - Self::Builtin(index) => (index, 0), - Self::Pointer(index) => (index, 1), - Self::Struct(index) => (index, 2), - }; - (flag << Self::FLAG_OFFSET) | index +impl Default for TypeKind { + fn default() -> Self { + Self::Builtin(bt::UNDECLARED) } } 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: Result, + id: Type, offset: u32, instr_offset: u16, size: u16, } -struct StackReloc { - offset: u32, - size: u16, -} - #[derive(Default)] -pub struct CodeBlock { +pub struct Block { code: Vec, relocs: Vec, } -impl CodeBlock { +impl Block { pub fn extend(&mut self, bytes: &[u8]) { self.code.extend_from_slice(bytes); } - pub fn offset(&mut self, id: LabelId, instr_offset: u16, size: u16) { + pub fn offset(&mut self, id: FuncId, instr_offset: u16, size: u16) { self.relocs.push(Reloc { - id: Ok(id), + id: TypeKind::Func(id).encode(), offset: self.code.len() as u32, instr_offset, size, @@ -311,16 +355,6 @@ impl CodeBlock { 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.addi64(STACK_PTR, STACK_PTR, size as _); - } - fn short_cut_bin_op(&mut self, dest: Reg, src: Reg, imm: u64) -> bool { if imm == 0 && dest != src { self.encode(instrs::cp(dest, src)); @@ -328,17 +362,13 @@ impl CodeBlock { imm != 0 } - fn subi64(&mut self, dest: Reg, src: Reg, imm: u64) { - self.addi64(dest, src, imm.wrapping_neg()); - } - fn addi64(&mut self, dest: Reg, src: Reg, imm: u64) { if self.short_cut_bin_op(dest, src, imm) { self.encode(instrs::addi64(dest, src, imm)); } } - fn call(&mut self, func: LabelId) { + fn call(&mut self, func: FuncId) { self.offset(func, 3, 4); self.encode(instrs::jal(RET_ADDR, ZERO, 0)); } @@ -347,36 +377,34 @@ impl CodeBlock { self.encode(instrs::jala(ZERO, RET_ADDR, 0)); } - fn prelude(&mut self, entry: LabelId) { - self.call(entry); + fn prelude(&mut self) { + self.encode(instrs::jal(RET_ADDR, ZERO, 0)); self.encode(instrs::tx()); } - fn relocate(&mut self, labels: &[FnLabel], globals: &[Global], shift: i64) { - for reloc in self.relocs.drain(..) { - let offset = match reloc.id { - Ok(id) => labels[id as usize].offset, - Err(id) => globals[id as usize].offset, + fn relocate(&mut self, labels: &[Func], globals: &[Global], shift: i64, skip: usize) { + for reloc in self.relocs.iter().skip(skip) { + let offset = match TypeKind::from_ty(reloc.id) { + TypeKind::Func(id) => labels[id as usize].offset, + TypeKind::Global(id) => globals[id as usize].offset, + v => unreachable!("invalid reloc: {:?}", v), }; - let offset = if reloc.size == 8 && reloc.id.is_ok() { + let offset = if reloc.size == 8 { reloc.offset as i64 } else { offset as i64 - reloc.offset as i64 } + shift; - let dest = &mut self.code[reloc.offset as usize + reloc.instr_offset as usize..] - [..reloc.size as usize]; - debug_assert!(dest.iter().all(|&b| b == 0)); - 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.to_le_bytes()), - _ => unreachable!(), - }; + write_reloc( + &mut self.code, + reloc.offset as usize + reloc.instr_offset as usize, + offset, + reloc.size, + ); } } - fn append(&mut self, data: &mut CodeBlock, code_offset: usize, reloc_offset: usize) { + fn append(&mut self, data: &mut Block, code_offset: usize, reloc_offset: usize) { for reloc in &mut data.relocs[reloc_offset..] { reloc.offset += self.code.len() as u32; reloc.offset -= code_offset as u32; @@ -386,6 +414,18 @@ impl CodeBlock { } } +fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) { + debug_assert!(size <= 8); + debug_assert!(size.is_power_of_two()); + debug_assert!( + doce[offset..offset + size as usize].iter().all(|&b| b == 0), + "{:?}", + &doce[offset..offset + size as usize] + ); + let value = value.to_ne_bytes(); + doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]); +} + #[derive(Default, PartialEq, Eq)] pub struct RegAlloc { free: Vec, @@ -416,14 +456,14 @@ impl RegAlloc { } fn pushed_size(&self) -> usize { - (self.max_used as usize - RET_ADDR as usize + 1) * 8 + ((self.max_used as usize).saturating_sub(RET_ADDR as usize) + 1) * 8 } } #[derive(Clone)] -struct FnLabel { +struct Func { offset: u32, - name: Ident, + relocs: u32, args: Rc<[Type]>, ret: Type, } @@ -446,8 +486,6 @@ struct Loop { } struct Struct { - name: Rc, - id: Ident, fields: Rc<[(Rc, Type)]>, } @@ -469,75 +507,141 @@ impl<'a> TypeDisplay<'a> { impl<'a> std::fmt::Display for TypeDisplay<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use TypeKind as TK; - let str = match TK::from_ty(self.ty) { - TK::Builtin(bt::UNDECLARED) => "undeclared", - TK::Builtin(bt::VOID) => "void", - TK::Builtin(bt::NEVER) => "never", - TK::Builtin(bt::INT) => "int", - TK::Builtin(bt::I32) => "i32", - TK::Builtin(bt::I16) => "i16", - TK::Builtin(bt::I8) => "i8", - TK::Builtin(bt::UINT) => "uint", - TK::Builtin(bt::U32) => "u32", - TK::Builtin(bt::U16) => "u16", - TK::Builtin(bt::U8) => "u8", - TK::Builtin(bt::BOOL) => "bool", - TK::Builtin(_) => unreachable!(), + match TK::from_ty(self.ty) { + TK::Builtin(ty) => write!(f, "{}", bt::to_str(ty)), TK::Pointer(ty) => { - return write!(f, "*{}", self.rety(self.codegen.pointers[ty as usize])) + write!(f, "^{}", self.rety(self.codegen.pointers[ty as usize])) } - TK::Struct(idx) => return write!(f, "{}", self.codegen.records[idx as usize].name), - }; - - f.write_str(str) + _ if let Some((key, _)) = + self.codegen.symbols.iter().find(|(_, &ty)| ty == self.ty) + && let Some(name) = self.codegen.files[key.file as usize] + .exprs() + .iter() + .find_map(|expr| match expr { + E::BinOp { + left: &E::Ident { name, id, .. }, + op: T::Decl, + .. + } if id == key.id => Some(name), + _ => None, + }) => + { + write!(f, "{name}") + } + TK::Struct(idx) => { + let record = &self.codegen.structs[idx as usize]; + write!(f, "{{")?; + for (i, &(ref name, ty)) in record.fields.iter().enumerate() { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{}: {}", name, self.rety(ty))?; + } + write!(f, "}}") + } + TK::Func(idx) => write!(f, "fn{}", idx), + TK::Global(idx) => write!(f, "global{}", idx), + } } } struct Global { - id: Ident, code: u32, offset: u32, dep: GlobalId, ty: Type, } +#[derive(Default)] +struct Linked { + globals: usize, + relocs: usize, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct SymKey { + id: Ident, + file: parser::FileId, +} + #[derive(Default)] pub struct Codegen { cf: parser::Ast, cf_id: parser::FileId, - ret: Type, - ret_reg: Option, - cur_global: GlobalId, - main: Option, - to_generate: Vec, + ret: Type, + ret_reg: Option, + cur_item: TypeKind, gpa: Rc>, sa: Rc>, ret_relocs: Vec, loops: Vec, + vars: Vec, - code: CodeBlock, - data: CodeBlock, - temp: CodeBlock, + to_generate: Vec, - labels: Vec, + code: Block, + data: Block, + + symbols: HashMap, + funcs: Vec, globals: Vec, - vars: Vec, - records: Vec, + structs: Vec, pointers: Vec, pub files: Vec, - vm: Vm, + vm: Vm, + stack: Vec, + linked: Linked, } impl Codegen { + fn with_cached_progress(&mut self, f: impl FnOnce(&mut Self)) { + let ret = std::mem::take(&mut self.ret); + let ret_reg = std::mem::take(&mut self.ret_reg); + let cur_item = std::mem::take(&mut self.cur_item); + + let gpa = std::mem::take(&mut *self.gpa.borrow_mut()); + let sa = std::mem::take(&mut *self.sa.borrow_mut()); + let ret_relocs = self.ret_relocs.len(); + let loops = self.loops.len(); + let vars = self.vars.len(); + + f(self); + + self.ret = ret; + self.ret_reg = ret_reg; + self.cur_item = cur_item; + + *self.gpa.borrow_mut() = gpa; + *self.sa.borrow_mut() = sa; + self.ret_relocs.truncate(ret_relocs); + self.loops.truncate(loops); + self.vars.truncate(vars); + } + + fn lazy_init(&mut self) { + if self.stack.capacity() == 0 { + self.stack.reserve(VM_STACK_SIZE); + self.vm.write_reg( + STACK_PTR, + unsafe { self.stack.as_ptr().add(self.stack.capacity()) } as u64, + ); + } + } + pub fn generate(&mut self) { - self.cur_global = GlobalId::MAX; + self.lazy_init(); self.find_and_declare(0, Err("main")); + self.code.prelude(); + self.complete_call_graph(); + } + + fn complete_call_graph(&mut self) { while let Some(item) = self.to_generate.pop() { - self.generate_item(item); + self.with_cached_progress(|s| s.generate_item(item)); } } @@ -550,22 +654,24 @@ impl Codegen { match expr { E::BinOp { - left: E::Ident { name, id, .. }, + left: E::Ident { name, .. }, op: T::Decl, right: E::Closure { body, args, .. }, } => { log::dbg!("fn: {}", name); - let frame = self.define_fn_label(*id); - if *name == "main" { - self.main = Some(frame.label); - } - let fn_label = self.labels[frame.label as usize].clone(); + self.cur_item = TypeKind::Func(item.id); + self.funcs[item.id as usize].offset = self.code.code.len() as _; + self.funcs[item.id as usize].relocs = self.code.relocs.len() as _; + + let func = self.funcs[item.id as usize].clone(); self.gpa.borrow_mut().init_callee(); + self.gen_prelude(); + log::dbg!("fn-args"); - let mut parama = self.param_alloc(fn_label.ret); - for (arg, &ty) in args.iter().zip(fn_label.args.iter()) { + let mut parama = self.param_alloc(func.ret); + for (arg, &ty) in args.iter().zip(func.args.iter()) { let sym = parser::find_symbol(&self.cf.symbols, arg.id); let loc = self.load_arg(sym.flags, ty, &mut parama); self.vars.push(Variable { @@ -574,7 +680,7 @@ impl Codegen { }); } - if self.size_of(fn_label.ret) > 16 { + if self.size_of(func.ret) > 16 { let reg = self.gpa.borrow_mut().allocate(); self.code.encode(instrs::cp(reg, 1)); self.ret_reg = Some(reg); @@ -582,32 +688,31 @@ impl Codegen { self.ret_reg = None; } - self.ret = fn_label.ret; + self.ret = func.ret; log::dbg!("fn-body"); if self.expr(body).is_some() { self.report(body.pos(), "expected all paths in the fucntion to return"); } - self.vars.clear(); log::dbg!("fn-prelude, stack: {:x}", self.sa.borrow().height); log::dbg!("fn-relocs"); - self.write_fn_prelude(frame); + self.reloc_prelude(item.id); log::dbg!("fn-ret"); self.reloc_rets(); self.ret(); self.sa.borrow_mut().clear(); } - value => todo!(), + value => todo!("{value:?}"), } } fn align_of(&self, ty: Type) -> u64 { use TypeKind as TK; match TypeKind::from_ty(ty) { - TK::Struct(t) => self.records[t as usize] + TK::Struct(t) => self.structs[t as usize] .fields .iter() .map(|&(_, ty)| self.align_of(ty)) @@ -624,13 +729,12 @@ impl Codegen { TK::Builtin(bt::VOID) => 0, TK::Builtin(bt::NEVER) => unreachable!(), TK::Builtin(bt::INT | bt::UINT) => 8, - TK::Builtin(bt::I32 | bt::U32) => 4, + TK::Builtin(bt::I32 | bt::U32 | bt::TYPE) => 4, TK::Builtin(bt::I16 | bt::U16) => 2, TK::Builtin(bt::I8 | bt::U8 | bt::BOOL) => 1, - TK::Builtin(e) => unreachable!("{:?}", e), TK::Struct(ty) => { let mut offset = 0; - let record = &self.records[ty as usize]; + let record = &self.structs[ty as usize]; for &(_, ty) in record.fields.iter() { let align = self.align_of(ty); offset = align_up(offset, align); @@ -638,6 +742,7 @@ impl Codegen { } offset } + _ => unimplemented!("size_of: {}", self.display_ty(ty)), } } @@ -657,15 +762,15 @@ impl Codegen { fn offset_of(&self, pos: parser::Pos, ty: Type, field: Result<&str, usize>) -> (u64, Type) { let idx = self.unwrap_struct(ty, pos, "field access"); - let record = &self.records[idx as usize]; + let record = &self.structs[idx as usize]; let mut offset = 0; - for (i, (name, ty)) in record.fields.iter().enumerate() { + for (i, &(ref name, ty)) in record.fields.iter().enumerate() { if Ok(name.as_ref()) == field || Err(i) == field { - return (offset, *ty); + return (offset, ty); } - let align = self.align_of(*ty); + let align = self.align_of(ty); offset = align_up(offset, align); - offset += self.size_of(*ty); + offset += self.size_of(ty); } match field { @@ -729,33 +834,130 @@ impl Codegen { } fn reloc_rets(&mut self) { - let len = self.code.code.len() as i32; - for reloc in self.ret_relocs.drain(..) { - let dest = &mut self.code.code[reloc.offset as usize + reloc.instr_offset as usize..] - [..reloc.size as usize]; - debug_assert!(dest.iter().all(|&b| b == 0)); - let offset = len - reloc.offset as i32; - dest.copy_from_slice(&offset.to_ne_bytes()); + let len = self.code.code.len() as i64; + for reloc in self.ret_relocs.iter() { + write_reloc( + &mut self.code.code, + reloc.offset as usize + reloc.instr_offset as usize, + len - reloc.offset as i64, + reloc.size, + ); } } fn ty(&mut self, expr: &parser::Expr) -> Type { - match *expr { - E::Ident { id, .. } if ident::is_null(id) => id, - E::UnOp { - op: T::Mul, val, .. - } => { - let ty = self.ty(val); - self.alloc_pointer(ty) + let offset = self.code.code.len(); + let reloc_offset = self.code.relocs.len(); + + let value = self.expr(expr).unwrap(); + _ = self.assert_ty(expr.pos(), value.ty, bt::TYPE); + if let Loc::Imm(ty) = value.loc { + return ty as _; + } + + self.code.encode(instrs::tx()); + + let mut curr_temp = Block::default(); + curr_temp.append(&mut self.code, offset, reloc_offset); + + let mut curr_fn = Block::default(); + match self.cur_item { + TypeKind::Func(id) => { + let func = &self.funcs[id as usize]; + curr_fn.append(&mut self.code, func.offset as _, func.relocs as _); + log::dbg!("{:?}", curr_fn.code); } - E::Ident { id, .. } => { - let index = match self.records.iter().position(|r| r.id == id) { - Some(index) => index as Type, - None => self.find_and_declare(0, Ok(id)), + foo => todo!("{foo:?}"), + } + + let offset = self.code.code.len(); + self.code.append(&mut curr_temp, 0, 0); + + self.complete_call_graph(); + + self.link(); + + self.vm.pc = hbvm::mem::Address::new(&self.code.code[offset] as *const u8 as _); + loop { + match self.vm.run().unwrap() { + hbvm::VmRunOk::End => break, + hbvm::VmRunOk::Ecall => self.handle_ecall(), + _ => unreachable!(), + } + } + + match self.cur_item { + TypeKind::Func(id) => { + self.funcs[id as usize].offset = self.code.code.len() as _; + self.funcs[id as usize].relocs = self.code.relocs.len() as _; + self.code.append(&mut curr_fn, 0, 0); + } + foo => todo!("{foo:?}"), + } + + match value.loc { + Loc::RegRef(reg) | Loc::Reg(LinReg(reg, ..)) => self.vm.read_reg(reg).0 as _, + _ => unreachable!(), + } + } + + fn handle_ecall(&mut self) { + // the ecalls have exception, we cant pass the return value in two registers otherwise its + // hard to tell where the trap code is + match self.vm.read_reg(2).0 { + traps::MAKE_STRUCT => unsafe { + let file_id = self.vm.read_reg(3).0 as u32; + let expr = std::mem::transmute::<_, parser::ExprRef>(self.vm.read_reg(4)); + let mut captures_addr = (self.vm.read_reg(STACK_PTR).0 as *const u8) + .add(self.vm.read_reg(5).0 as usize); + let ast = self.files[file_id as usize].clone(); + let &E::Struct { + pos, + fields, + captured, + } = expr.get(&ast).unwrap() + else { + unreachable!() }; - TypeKind::Struct(index).encode() - } - expr => unimplemented!("type: {:#?}", expr), + + let prev_len = self.vars.len(); + for &id in captured { + let ty: Type = std::ptr::read_unaligned(captures_addr.cast()); + captures_addr = captures_addr.add(4); + let mut imm = [0u8; 8]; + assert!(self.size_of(ty) as usize <= imm.len()); + std::ptr::copy_nonoverlapping( + captures_addr, + imm.as_mut_ptr(), + self.size_of(ty) as usize, + ); + self.vars.push(Variable { + id, + value: Value { + ty, + loc: Loc::Imm(u64::from_ne_bytes(imm)), + }, + }); + } + + let Value { + loc: Loc::Imm(ty), .. + } = self + .expr(&E::Struct { + pos, + fields, + captured: &[], + }) + .unwrap() + else { + unreachable!() + }; + + self.vars.truncate(prev_len); + + self.vm.write_reg(1, ty); + }, + trap => todo!("unknown trap: {trap}"), } } @@ -763,9 +965,112 @@ impl Codegen { self.expr_ctx(expr, Ctx::default()) } + fn handle_global(&mut self, id: GlobalId) -> Option { + let ptr = self.alloc_reg(); + + let global = &mut self.globals[id as usize]; + match self.cur_item { + TypeKind::Global(gl) => global.dep = global.dep.max(gl), + _ => {} + } + + self.code.relocs.push(Reloc { + id: TypeKind::Global(id as _).encode(), + offset: self.code.code.len() as u32, + instr_offset: 3, + size: 4, + }); + self.code.encode(instrs::lra(ptr.0, 0, 0)); + + Some(Value { + ty: global.ty, + loc: Loc::Deref(ptr, None, 0), + }) + } + fn expr_ctx(&mut self, expr: &parser::Expr, mut ctx: Ctx) -> Option { use instrs as i; + let value = match *expr { + E::Struct { + fields, captured, .. + } => { + if captured.is_empty() { + let fields = fields + .iter() + .map(|&(name, ty)| (name.into(), self.ty(&ty))) + .collect(); + self.structs.push(Struct { fields }); + Some(Value::ty( + TypeKind::Struct(self.structs.len() as u32 - 1).encode(), + )) + } else { + let values = captured + .iter() + .map(|&id| E::Ident { + id, + name: "booodab", + index: u32::MAX, + }) + .map(|expr| self.expr(&expr)) + .collect::>>()?; + let values_size = values + .iter() + .map(|value| 4 + self.size_of(value.ty)) + .sum::(); + + let stack = self.alloc_stack(values_size); + let ptr = Loc::DerefRef(STACK_PTR, None, stack.offset); + let mut offset = 0; + for value in values { + self.assign(bt::TYPE, ptr.offset_ref(offset), Loc::Imm(value.ty as _)); + offset += 4; + self.assign(value.ty, ptr.offset_ref(offset), value.loc); + offset += self.size_of(value.ty); + } + + // eca MAKE_STRUCT(FileId, ExprRef, *Captures) -> Type; + let mut parama = self.param_alloc(bt::TYPE); + self.pass_arg(&Value::imm(traps::MAKE_STRUCT), &mut parama); + self.pass_arg(&Value::imm(self.cf_id as _), &mut parama); + self.pass_arg( + &Value::imm(unsafe { std::mem::transmute(parser::ExprRef::new(expr)) }), + &mut parama, + ); + self.pass_arg(&Value::imm(stack.offset), &mut parama); + self.code.encode(i::eca()); + + Some(Value { + ty: bt::TYPE, + loc: Loc::RegRef(1), + }) + } + } + E::UnOp { + op: T::Xor, val, .. + } => { + let val = self.ty(val); + Some(Value::ty(self.alloc_pointer(val))) + } + E::Directive { + name: "TypeOf", + args: [expr], + .. + } => { + let offset = self.code.code.len() as u32; + let reloc_offset = self.code.relocs.len(); + let ty = self + .expr_ctx(expr, Ctx::DestUntyped(Loc::DerefRef(0, None, 0))) + .unwrap() + .ty; + self.code.code.truncate(offset as usize); + self.code.relocs.truncate(reloc_offset); + + Some(Value { + ty: bt::TYPE, + loc: Loc::Imm(ty as _), + }) + } E::Directive { name: "eca", args: [ret_ty, args @ ..], @@ -903,7 +1208,7 @@ impl Codegen { }; let stuct = self.unwrap_struct(ty, pos, "struct literal"); - let field_count = self.records[stuct as usize].fields.len(); + let field_count = self.structs[stuct as usize].fields.len(); if field_count != fields.len() { self.report( pos, @@ -1001,7 +1306,7 @@ impl Codegen { let loc = self.make_loc_owned(val.loc, val.ty); let sym = parser::find_symbol(&self.cf.symbols, *id); let loc = match loc { - Loc::Reg(r) if sym.flags & parser::REFERENCED != 0 => { + Loc::Reg(r) if sym.flags & idfl::REFERENCED != 0 => { let size = self.size_of(val.ty); let stack = self.alloc_stack(size); self.store_stack(r.0, stack.offset, size as _); @@ -1015,15 +1320,13 @@ impl Codegen { }); Some(Value::VOID) } - E::Call { - func: &E::Ident { id, .. }, - args, - } => { - let func = match self.get_label(id) { - Some(func) => func, - None => self.find_and_declare(0, Ok(id)), + E::Call { func, args } => { + let func = self.ty(func); + let TypeKind::Func(func) = TypeKind::from_ty(func) else { + todo!() }; - let fn_label = self.labels[func as usize].clone(); + + let fn_label = self.funcs[func as usize].clone(); let mut parama = self.param_alloc(fn_label.ret); let mut values = Vec::with_capacity(args.len()); @@ -1046,13 +1349,14 @@ impl Codegen { loc, }); } + E::Ident { id, .. } if ident::is_null(id) => Some(Value::ty(id)), E::Ident { id, index, .. } if let Some((var_index, var)) = self.vars.iter_mut().enumerate().find(|(_, v)| v.id == id) => { let sym = parser::find_symbol(&self.cf.symbols, id); - let loc = match parser::ident_flag_index(sym.flags) == index + let loc = match idfl::index(sym.flags) == index && !self.loops.last().is_some_and(|l| l.var_count > var_index) { true => std::mem::replace(&mut var.value.loc, Loc::Imm(0)), @@ -1064,31 +1368,16 @@ impl Codegen { loc, }) } - E::Ident { id, .. } => { - let id = match self.globals.iter().position(|g| g.id == id) { - Some(id) => id as GlobalId, - None => self.find_and_declare(0, Ok(id)), - }; - let ptr = self.alloc_reg(); - - let global = &mut self.globals[id as usize]; - if self.cur_global != GlobalId::MAX { - global.dep = global.dep.max(id); - } - - self.code.relocs.push(Reloc { - id: Err(id), - offset: self.code.code.len() as u32, - instr_offset: 3, - size: 4, - }); - self.code.encode(i::lra(ptr.0, 0, 0)); - - Some(Value { - ty: global.ty, - loc: Loc::Deref(ptr, None, 0), - }) - } + E::Ident { id, .. } => match self + .symbols + .get(&SymKey { id, file: 0 }) + .copied() + .map(TypeKind::from_ty) + .unwrap_or_else(|| self.find_and_declare(0, Ok(id))) + { + TypeKind::Global(id) => self.handle_global(id), + tk => Some(Value::ty(tk.encode())), + }, E::Return { val, .. } => { if let Some(val) = val { let size = self.size_of(self.ret); @@ -1130,29 +1419,27 @@ impl Codegen { let then_unreachable = self.expr(then).is_none(); let mut else_unreachable = false; - let mut jump = self.code.code.len() as i16 - jump_offset as i16; + let mut jump = self.code.code.len() as i64 - jump_offset as i64; if let Some(else_) = else_ { log::dbg!("if-else"); let else_jump_offset = self.code.code.len() as u32; if !then_unreachable { self.code.encode(i::jmp(0)); - jump = self.code.code.len() as i16 - jump_offset as i16; + jump = self.code.code.len() as i64 - jump_offset as i64; } else_unreachable = self.expr(else_).is_none(); if !then_unreachable { - let jump = self.code.code.len() as i32 - else_jump_offset as i32; + let jump = self.code.code.len() as i64 - else_jump_offset as i64; log::dbg!("if-else-jump: {}", jump); - self.code.code[else_jump_offset as usize + 1..][..4] - .copy_from_slice(&jump.to_ne_bytes()); + write_reloc(&mut self.code.code, else_jump_offset as usize + 1, jump, 4); } } log::dbg!("if-then-jump: {}", jump); - self.code.code[jump_offset as usize + 3..][..2] - .copy_from_slice(&jump.to_ne_bytes()); + write_reloc(&mut self.code.code, jump_offset as usize + 3, jump, 2); if then_unreachable && else_unreachable { break 'b None; @@ -1224,7 +1511,7 @@ impl Codegen { } => { let lhs = self.expr_ctx(left, Ctx::Inferred(bt::BOOL))?; let lhs = self.loc_to_reg(lhs.loc, 1); - let jump_offset = self.code.code.len() as u32 + 3; + let jump_offset = self.code.code.len() + 3; let op = if op == T::And { i::jeq } else { i::jne }; self.code.encode(op(lhs.0, 0, 0)); @@ -1233,8 +1520,8 @@ impl Codegen { self.code.encode(i::cp(lhs.0, rhs.0)); } - let jump = self.code.code.len() as i16 - jump_offset as i16; - self.code.code[jump_offset as usize..][..2].copy_from_slice(&jump.to_ne_bytes()); + let jump = self.code.code.len() as i64 - jump_offset as i64; + write_reloc(&mut self.code.code, jump_offset, jump, 2); Some(Value { ty: bt::BOOL, @@ -1444,7 +1731,7 @@ impl Codegen { _ => Loc::Stack(self.alloc_stack(self.size_of(ty)), 0), }; let mut offset = 0; - for &(_, ty) in self.records[stuct as usize].fields.clone().iter() { + for &(_, ty) in self.structs[stuct as usize].fields.clone().iter() { let align = self.align_of(ty); offset = align_up(offset, align); let size = self.size_of(ty); @@ -1511,22 +1798,6 @@ impl Codegen { } } - fn ensure_sign_extended(&mut self, val: Value, ty: Type) -> Value { - let size = self.size_of(ty); - let lsize = self.size_of(val.ty); - if lsize < size { - let reg = self.loc_to_reg(val.loc, lsize); - let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32][lsize.ilog2() as usize]; - self.code.encode(op(reg.0, reg.0)); - Value { - ty, - loc: Loc::Reg(reg), - } - } else { - val - } - } - fn assign_opaque(&mut self, size: u64, right: Loc, left: Loc) -> Option { if left == right { return Some(Value::VOID); @@ -1589,41 +1860,27 @@ impl Codegen { } } - fn ensure_owned(&mut self, loc: Loc, ty: Type) -> Loc { - match loc { - Loc::RegRef(reg) => { - let new = self.alloc_reg(); - self.code.encode(instrs::cp(new.0, reg)); - Loc::Reg(new) - } - l => { - let size = self.size_of(ty); - let stack = self.alloc_stack(size); - self.assign(ty, Loc::DerefRef(STACK_PTR, None, stack.offset), l); - Loc::Stack(stack, 0) - } - } - } - - fn find_and_declare(&mut self, file: parser::FileId, name: Result) -> LabelId { + fn find_and_declare(&mut self, file: parser::FileId, name: Result) -> TypeKind { let f = self.files[file as usize].clone(); - let expr = f.find_decl(name).expect("TODO: error"); - match expr { + let (expr, id) = f.find_decl(name).expect("TODO: error"); + let sym = match expr { E::BinOp { - left: &E::Ident { id, .. }, + left: &E::Ident { .. }, op: T::Decl, right: E::Closure { args, ret, .. }, } => { let args = args.iter().map(|arg| self.ty(&arg.ty)).collect::>(); let ret = self.ty(ret); + let id = self.declare_fn_label(args.into(), ret); self.to_generate.push(ItemId { file, expr: ExprRef::new(expr), + id, }); - self.declare_fn_label(id, args.into(), ret) + TypeKind::Func(id) } E::BinOp { - left: &E::Ident { id, name, .. }, + left: &E::Ident { .. }, op: T::Decl, right: E::Struct { fields, .. }, } => { @@ -1631,31 +1888,26 @@ impl Codegen { .iter() .map(|&(name, ty)| (name.into(), self.ty(&ty))) .collect(); - self.records.push(Struct { - id, - fields, - name: name.into(), - }); - self.records.len() as u32 - 1 + self.structs.push(Struct { fields }); + TypeKind::Struct(self.structs.len() as u32 - 1) } E::BinOp { - left: &E::Ident { id, .. }, + left: &E::Ident { .. }, op: T::Decl, right, } => { let gid = self.globals.len() as GlobalId; + let prev_in_global = std::mem::replace(&mut self.cur_item, TypeKind::Global(gid)); - let prev_in_global = std::mem::replace(&mut self.cur_global, gid); let prev_gpa = std::mem::replace(&mut *self.gpa.borrow_mut(), Default::default()); let prev_sa = std::mem::replace(&mut *self.sa.borrow_mut(), Default::default()); let offset = self.code.code.len(); let reloc_count = self.code.relocs.len(); self.globals.push(Global { - id, - ty: bt::UNDECLARED, - code: 0, - dep: 0, + ty: bt::UNDECLARED, + code: 0, + dep: 0, offset: u32::MAX, }); @@ -1675,78 +1927,56 @@ impl Codegen { *self.sa.borrow_mut() = prev_sa; *self.gpa.borrow_mut() = prev_gpa; - self.cur_global = prev_in_global; + self.cur_item = prev_in_global; - gid + TypeKind::Global(gid) } e => unimplemented!("{e:#?}"), - } + }; + self.symbols.insert(SymKey { id, file }, sym.encode()); + sym } - fn declare_fn_label(&mut self, name: Ident, args: Rc<[Type]>, ret: Type) -> LabelId { - self.labels.push(FnLabel { + fn declare_fn_label(&mut self, args: Rc<[Type]>, ret: Type) -> FuncId { + self.funcs.push(Func { offset: 0, - name, + relocs: 0, args, ret, }); - self.labels.len() as u32 - 1 + self.funcs.len() as u32 - 1 } - fn define_fn_label(&mut self, name: Ident) -> Frame { - let offset = self.code.code.len() as u32; - let label = self.get_label(name).unwrap(); - self.labels[label as usize].offset = offset; - Frame { - label, - prev_relocs: self.code.relocs.len(), - offset, - } + fn gen_prelude(&mut self) { + self.code.encode(instrs::addi64(STACK_PTR, STACK_PTR, 0)); + self.code.encode(instrs::st(RET_ADDR, STACK_PTR, 0, 0)); } - fn get_label(&self, name: Ident) -> Option { - self.labels - .iter() - .position(|l| l.name == name) - .map(|l| l as _) - } + fn reloc_prelude(&mut self, id: FuncId) { + let mut cursor = self.funcs[id as usize].offset as usize; + let mut allocate = |size| (cursor += size, cursor).1; - fn write_fn_prelude(&mut self, frame: Frame) { - self.temp.push(RET_ADDR, self.gpa.borrow().pushed_size()); - self.temp - .subi64(STACK_PTR, STACK_PTR, self.sa.borrow().height); + let pushed = self.gpa.borrow().pushed_size() as i64; + let stack = self.sa.borrow().height as i64; - for reloc in &mut self.code.relocs[frame.prev_relocs..] { - reloc.offset += self.temp.code.len() as u32; - } - - for reloc in &mut self.ret_relocs { - reloc.offset += self.temp.code.len() as u32; - } - - self.code.code.splice( - frame.offset as usize..frame.offset as usize, - self.temp.code.drain(..), - ); + write_reloc(&mut self.code.code, allocate(3), -(pushed + stack), 8); + write_reloc(&mut self.code.code, allocate(8 + 3), stack, 8); + write_reloc(&mut self.code.code, allocate(8), pushed, 2); } fn ret(&mut self) { - self.code.encode(instrs::addi64( - STACK_PTR, - STACK_PTR, - self.sa.borrow().height, - )); - self.code.pop(RET_ADDR, self.gpa.borrow().pushed_size()); + let pushed = self.gpa.borrow().pushed_size() as u64; + let stack = self.sa.borrow().height as u64; + self.code + .encode(instrs::ld(RET_ADDR, STACK_PTR, stack, pushed as _)); + self.code + .encode(instrs::addi64(STACK_PTR, STACK_PTR, stack + pushed)); self.code.ret(); } - pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> { - self.temp.prelude(self.main.unwrap()); - self.temp - .relocate(&self.labels, &self.globals, self.temp.code.len() as i64); - + fn link(&mut self) { let mut globals = std::mem::take(&mut self.globals); - for global in globals.iter_mut() { + for global in globals.iter_mut().skip(self.linked.globals as usize) { let size = self.size_of(global.ty); global.offset = self.code.code.len() as u32; self.code.code.extend(std::iter::repeat(0).take(size as _)); @@ -1756,36 +1986,43 @@ impl Codegen { let prev_len = self.code.code.len(); self.code.append(&mut self.data, 0, 0); - self.code.relocate(&self.labels, &self.globals, 0); + self.code + .relocate(&self.funcs, &self.globals, 0, self.linked.relocs); - { - let mut var_order = self - .globals - .iter() - .map(|g| g.dep) - .zip(0u32..) - .collect::>(); - var_order.sort_unstable(); + let mut var_order = self + .globals + .iter() + .map(|g| g.dep) + .zip(0u32..) + .skip(self.linked.globals as usize) + .collect::>(); + var_order.sort_unstable(); - let stack_size = 1024 * 1024 * 2; - let mut stack = Vec::::with_capacity(stack_size); - for (_, glob_id) in var_order.into_iter().rev() { - let global = &self.globals[glob_id as usize]; - self.vm.pc = hbvm::mem::Address::new( - &mut self.code.code[global.code as usize + prev_len] as *mut _ as u64, - ); - self.vm - .write_reg(254, unsafe { stack.as_mut_ptr().add(stack_size) } as u64); - self.vm.write_reg( - 1, - &mut self.code.code[global.offset as usize] as *mut _ as u64, - ); - self.vm.run().unwrap(); - } + for (_, glob_id) in var_order.into_iter().rev() { + let global = &self.globals[glob_id as usize]; + self.vm.pc = hbvm::mem::Address::new( + &mut self.code.code[global.code as usize + prev_len] as *mut _ as u64, + ); + self.vm.write_reg( + 1, + &mut self.code.code[global.offset as usize] as *mut _ as u64, + ); + self.vm.run().unwrap(); } self.code.code.truncate(prev_len); - out.write_all(&self.temp.code)?; + self.linked.globals = self.globals.len(); + self.linked.relocs = self.code.relocs.len(); + } + + pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> { + self.code.relocs.push(Reloc { + offset: 0, + size: 4, + instr_offset: 3, + id: TypeKind::Func(0).encode() as _, + }); + self.link(); out.write_all(&self.code.code) } @@ -1853,7 +2090,7 @@ impl Codegen { let size = self.size_of(ty); match size { 0 => Loc::Imm(0), - ..=8 if flags & parser::REFERENCED == 0 => { + ..=8 if flags & idfl::REFERENCED == 0 => { let reg = self.alloc_reg(); self.code.encode(instrs::cp(reg.0, parama.next().unwrap())); Loc::Reg(reg) @@ -1869,7 +2106,7 @@ impl Codegen { parama.next().unwrap(); Loc::Stack(stack, 0) } - _ if flags & (parser::MUTABLE | parser::REFERENCED) == 0 => { + _ if flags & (idfl::MUTABLE | idfl::REFERENCED) == 0 => { let ptr = parama.next().unwrap(); let reg = self.alloc_reg(); self.code.encode(instrs::cp(reg.0, ptr)); @@ -1963,6 +2200,20 @@ impl Value { fn new(ty: Type, loc: Loc) -> Self { Self { ty, loc } } + + fn ty(ty: Type) -> Self { + Self { + ty: bt::TYPE, + loc: Loc::Imm(ty as _), + } + } + + fn imm(imm: u64) -> Value { + Self { + ty: bt::UINT, + loc: Loc::Imm(imm), + } + } } #[derive(Debug, PartialEq, Eq)] @@ -2065,6 +2316,8 @@ impl hbvm::mem::Memory for LoggedMem { #[cfg(test)] mod tests { + use crate::codegen::LoggedMem; + use super::parser; fn generate(input: &'static str, output: &mut String) { @@ -2081,7 +2334,7 @@ mod tests { let mut vm = unsafe { hbvm::Vm::<_, 0>::new( - hbvm::mem::HostMemory, + LoggedMem::default(), hbvm::mem::Address::new(out.as_ptr() as u64), ) }; @@ -2118,5 +2371,6 @@ mod tests { struct_operators => include_str!("../examples/struct_operators.hb"); directives => include_str!("../examples/directives.hb"); global_variables => include_str!("../examples/global_variables.hb"); + geneic_types => include_str!("../examples/generic_types.hb"); } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index a81a299..b6aaf4a 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -11,10 +11,6 @@ impl Token { pub fn range(&self) -> std::ops::Range { self.start as usize..self.end as usize } - - pub fn len(&self) -> u32 { - self.end - self.start - } } macro_rules! gen_token_kind { diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 3cb9b32..351428f 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -1,4 +1,5 @@ #![feature(if_let_guard)] +#![feature(slice_partition_dedup)] #![feature(noop_waker)] #![feature(portable_simd)] #![feature(iter_collect_into)] @@ -7,9 +8,17 @@ #![feature(ptr_metadata)] #![feature(const_mut_refs)] #![feature(slice_ptr_get)] -#![allow(dead_code)] -use std::{collections::VecDeque, sync::Mutex}; +use std::{ + collections::VecDeque, + io::{self, Read}, + path::{Path, PathBuf}, + sync::Mutex, +}; + +use parser::Ast; + +use crate::parser::FileId; #[macro_export] macro_rules! run_tests { @@ -128,6 +137,288 @@ impl TaskQueueInner { } } +pub fn parse_all(threads: usize) -> io::Result> { + const GIT_DEPS_DIR: &str = "git-deps"; + + enum ImportPath<'a> { + Root { + path: &'a str, + }, + Rel { + path: &'a str, + }, + Git { + link: &'a str, + path: &'a str, + branch: Option<&'a str>, + tag: Option<&'a str>, + rev: Option<&'a str>, + }, + } + + impl<'a> TryFrom<&'a str> for ImportPath<'a> { + type Error = ParseImportError; + + fn try_from(value: &'a str) -> Result { + let (prefix, path) = value.split_once(':').unwrap_or(("", value)); + + match prefix { + "" => Ok(Self::Root { path }), + "rel" => Ok(Self::Rel { path }), + "git" => { + let (link, path) = + path.split_once(':').ok_or(ParseImportError::ExpectedPath)?; + let (link, params) = link.split_once('?').unwrap_or((link, "")); + let [mut branch, mut tag, mut rev] = [None; 3]; + for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) { + match key { + "branch" => branch = Some(value), + "tag" => tag = Some(value), + "rev" => rev = Some(value), + _ => return Err(ParseImportError::UnexpectedParam), + } + } + Ok(Self::Git { + link, + path, + branch, + tag, + rev, + }) + } + _ => Err(ParseImportError::InvalidPrefix), + } + } + } + + fn preprocess_git(link: &str) -> &str { + let link = link.strip_prefix("https://").unwrap_or(link); + link.strip_suffix(".git").unwrap_or(link) + } + + impl<'a> ImportPath<'a> { + fn resolve(&self, from: &str) -> Result { + match self { + Self::Root { path } => Ok(Path::new(path).to_owned()), + Self::Rel { path } => { + let path = PathBuf::from_iter([from, path]); + match path.canonicalize() { + Ok(path) => Ok(path), + Err(e) => Err(CantLoadFile(path, e)), + } + } + Self::Git { path, link, .. } => { + let link = preprocess_git(link); + Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path])) + } + } + } + } + + #[derive(Debug)] + enum ParseImportError { + ExpectedPath, + InvalidPrefix, + UnexpectedParam, + } + + impl std::fmt::Display for ParseImportError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::ExpectedPath => write!(f, "expected path"), + Self::InvalidPrefix => write!( + f, + "invalid prefix, expected one of rel, \ + git or none followed by colon" + ), + Self::UnexpectedParam => { + write!(f, "unexpected git param, expected branch, tag or rev") + } + } + } + } + + impl std::error::Error for ParseImportError {} + + impl From for io::Error { + fn from(e: ParseImportError) -> Self { + io::Error::new(io::ErrorKind::InvalidInput, e) + } + } + + #[derive(Debug)] + struct CantLoadFile(PathBuf, io::Error); + + impl std::fmt::Display for CantLoadFile { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "can't load file: {}", self.0.display()) + } + } + + impl std::error::Error for CantLoadFile { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.1) + } + } + + impl From for io::Error { + fn from(e: CantLoadFile) -> Self { + io::Error::new(io::ErrorKind::InvalidData, e) + } + } + + #[derive(Debug)] + struct InvalidFileData(std::str::Utf8Error); + + impl std::fmt::Display for InvalidFileData { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "invalid file data") + } + } + + impl std::error::Error for InvalidFileData { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.0) + } + } + + impl From for io::Error { + fn from(e: InvalidFileData) -> Self { + io::Error::new(io::ErrorKind::InvalidData, e) + } + } + + type Task = (FileId, PathBuf, Option); + + let seen = Mutex::new(HashMap::::default()); + let tasks = TaskQueue::::new(threads); + let ast = Mutex::new(Vec::>::new()); + + let loader = |path: &str, from: &str| { + let path = ImportPath::try_from(path)?; + + let physiscal_path = path.resolve(from)?; + + let id = { + let mut seen = seen.lock().unwrap(); + let len = seen.len(); + match seen.entry(physiscal_path.clone()) { + std::collections::hash_map::Entry::Occupied(entry) => { + return Ok(*entry.get()); + } + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(len as _); + len as FileId + } + } + }; + + let command = if !physiscal_path.exists() { + let ImportPath::Git { + link, + branch, + rev, + tag, + .. + } = path + else { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("can't find file: {}", physiscal_path.display()), + )); + }; + + let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]); + + let mut command = std::process::Command::new("git"); + command + .args(["clone", "--depth", "1"]) + .args(branch.map(|b| ["--branch", b]).into_iter().flatten()) + .args(tag.map(|t| ["--tag", t]).into_iter().flatten()) + .args(rev.map(|r| ["--rev", r]).into_iter().flatten()) + .arg(link) + .arg(root); + Some(command) + } else { + None + }; + + tasks.push((id, physiscal_path, command)); + Ok(id) + }; + + let execute_task = |(_, path, command): Task, buffer: &mut Vec| { + if let Some(mut command) = command { + let output = command.output()?; + if !output.status.success() { + let msg = format!( + "git command failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + } + } + + let path = path.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidData, + format!("path contains invalid characters: {}", path.display()), + ) + })?; + let mut file = std::fs::File::open(path)?; + file.read_to_end(buffer)?; + let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?; + Ok(Ast::new(path, src, &loader)) + }; + + let thread = || { + let mut buffer = Vec::new(); + while let Some(task @ (indx, ..)) = tasks.pop() { + let res = execute_task(task, &mut buffer); + buffer.clear(); + + let mut ast = ast.lock().unwrap(); + let len = ast.len().max(indx as usize + 1); + ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into())); + ast[indx as usize] = res; + } + }; + + std::thread::scope(|s| (0..threads).for_each(|_| _ = s.spawn(thread))); + + ast.into_inner() + .unwrap() + .into_iter() + .collect::>>() +} + +type HashMap = std::collections::HashMap; + +type FnvBuildHash = std::hash::BuildHasherDefault; + +struct FnvHasher(u64); + +impl std::hash::Hasher for FnvHasher { + fn finish(&self) -> u64 { + self.0 + } + + fn write(&mut self, bytes: &[u8]) { + self.0 = bytes.iter().fold(self.0, |hash, &byte| { + let mut hash = hash; + hash ^= byte as u64; + hash = hash.wrapping_mul(0x100000001B3); + hash + }); + } +} + +impl Default for FnvHasher { + fn default() -> Self { + Self(0xCBF29CE484222325) + } +} + #[cfg(test)] mod test { use std::sync::Arc; diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 30134db..629caf5 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -1,18 +1,15 @@ use std::{ cell::{Cell, UnsafeCell}, - collections::HashMap, - io::{self, Read}, + io, ops::{Deref, Not}, - path::{Path, PathBuf}, ptr::NonNull, - sync::{atomic::AtomicUsize, Mutex}, + sync::atomic::AtomicUsize, }; use crate::{ codegen::bt, ident::{self, Ident}, lexer::{Lexer, LineMap, Token, TokenKind}, - TaskQueue, }; pub type Pos = u32; @@ -21,267 +18,25 @@ pub type Symbols = Vec; pub type FileId = u32; pub type Loader<'a> = &'a (dyn Fn(&str, &str) -> io::Result + 'a); -pub const MUTABLE: IdentFlags = 1 << (std::mem::size_of::() * 8 - 1); -pub const REFERENCED: IdentFlags = 1 << (std::mem::size_of::() * 8 - 2); -const GIT_DEPS_DIR: &str = "git-deps"; +pub mod idfl { + use super::*; -pub fn parse_all(threads: usize) -> io::Result> { - enum ImportPath<'a> { - Root { - path: &'a str, - }, - Rel { - path: &'a str, - }, - Git { - link: &'a str, - path: &'a str, - branch: Option<&'a str>, - tag: Option<&'a str>, - rev: Option<&'a str>, - }, - } - - impl<'a> TryFrom<&'a str> for ImportPath<'a> { - type Error = ParseImportError; - - fn try_from(value: &'a str) -> Result { - let (prefix, path) = value.split_once(':').unwrap_or(("", value)); - - match prefix { - "" => Ok(Self::Root { path }), - "rel" => Ok(Self::Rel { path }), - "git" => { - let (link, path) = - path.split_once(':').ok_or(ParseImportError::ExpectedPath)?; - let (link, params) = link.split_once('?').unwrap_or((link, "")); - let [mut branch, mut tag, mut rev] = [None; 3]; - for (key, value) in params.split('&').filter_map(|s| s.split_once('=')) { - match key { - "branch" => branch = Some(value), - "tag" => tag = Some(value), - "rev" => rev = Some(value), - _ => return Err(ParseImportError::UnexpectedParam), - } - } - Ok(Self::Git { - link, - path, - branch, - tag, - rev, - }) - } - _ => Err(ParseImportError::InvalidPrefix), - } - } - } - - fn preprocess_git(link: &str) -> &str { - let link = link.strip_prefix("https://").unwrap_or(link); - link.strip_suffix(".git").unwrap_or(link) - } - - impl<'a> ImportPath<'a> { - fn resolve(&self, from: &str) -> Result { - match self { - Self::Root { path } => Ok(Path::new(path).to_owned()), - Self::Rel { path } => { - let path = PathBuf::from_iter([from, path]); - match path.canonicalize() { - Ok(path) => Ok(path), - Err(e) => Err(CantLoadFile(path, e)), - } - } - Self::Git { path, link, .. } => { - let link = preprocess_git(link); - Ok(PathBuf::from_iter([GIT_DEPS_DIR, link, path])) - } - } - } - } - - #[derive(Debug)] - enum ParseImportError { - ExpectedPath, - InvalidPrefix, - ExpectedGitAlias, - UnexpectedParam, - } - - impl std::fmt::Display for ParseImportError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::ExpectedPath => write!(f, "expected path"), - Self::InvalidPrefix => write!( - f, - "invalid prefix, expected one of rel, \ - git or none followed by colon" - ), - Self::ExpectedGitAlias => write!(f, "expected git alias as ':$'"), - Self::UnexpectedParam => { - write!(f, "unexpected git param, expected branch, tag or rev") - } - } - } - } - - impl std::error::Error for ParseImportError {} - - impl From for io::Error { - fn from(e: ParseImportError) -> Self { - io::Error::new(io::ErrorKind::InvalidInput, e) - } - } - - #[derive(Debug)] - struct CantLoadFile(PathBuf, io::Error); - - impl std::fmt::Display for CantLoadFile { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "can't load file: {}", self.0.display()) - } - } - - impl std::error::Error for CantLoadFile { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.1) - } - } - - impl From for io::Error { - fn from(e: CantLoadFile) -> Self { - io::Error::new(io::ErrorKind::InvalidData, e) - } - } - - #[derive(Debug)] - struct InvalidFileData(std::str::Utf8Error); - - impl std::fmt::Display for InvalidFileData { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "invalid file data") - } - } - - impl std::error::Error for InvalidFileData { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(&self.0) - } - } - - impl From for io::Error { - fn from(e: InvalidFileData) -> Self { - io::Error::new(io::ErrorKind::InvalidData, e) - } - } - - type Task = (FileId, PathBuf, Option); - - let seen = Mutex::new(HashMap::::new()); - let tasks = TaskQueue::::new(threads); - let ast = Mutex::new(Vec::>::new()); - - let loader = |path: &str, from: &str| { - let path = ImportPath::try_from(path)?; - - let physiscal_path = path.resolve(from)?; - - let id = { - let mut seen = seen.lock().unwrap(); - let len = seen.len(); - match seen.entry(physiscal_path.clone()) { - std::collections::hash_map::Entry::Occupied(entry) => { - return Ok(*entry.get()); - } - std::collections::hash_map::Entry::Vacant(entry) => { - entry.insert(len as _); - len as FileId - } - } + macro_rules! flags { + ($($name:ident,)*) => { + $(pub const $name: IdentFlags = 1 << (std::mem::size_of::() * 8 - 1 - ${index(0)});)* + pub const ALL: IdentFlags = 0 $(| $name)*; }; + } - let command = if !physiscal_path.exists() { - let ImportPath::Git { - link, - branch, - rev, - tag, - .. - } = path - else { - return Err(io::Error::new( - io::ErrorKind::NotFound, - format!("can't find file: {}", physiscal_path.display()), - )); - }; + flags! { + MUTABLE, + REFERENCED, + CAPTURED, + } - let root = PathBuf::from_iter([GIT_DEPS_DIR, preprocess_git(link)]); - - let mut command = std::process::Command::new("git"); - command - .args(["clone", "--depth", "1"]) - .args(branch.map(|b| ["--branch", b]).into_iter().flatten()) - .args(tag.map(|t| ["--tag", t]).into_iter().flatten()) - .args(rev.map(|r| ["--rev", r]).into_iter().flatten()) - .arg(link) - .arg(root); - Some(command) - } else { - None - }; - - tasks.push((id, physiscal_path, command)); - Ok(id) - }; - - let execute_task = |(_, path, command): Task, buffer: &mut Vec| { - if let Some(mut command) = command { - let output = command.output()?; - if !output.status.success() { - let msg = format!( - "git command failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - } - - let path = path.to_str().ok_or_else(|| { - io::Error::new( - io::ErrorKind::InvalidData, - format!("path contains invalid characters: {}", path.display()), - ) - })?; - let mut file = std::fs::File::open(path)?; - file.read_to_end(buffer)?; - let src = std::str::from_utf8(buffer).map_err(InvalidFileData)?; - Ok(Ast::new(path, src, &loader)) - }; - - let thread = || { - let mut buffer = Vec::new(); - while let Some(task @ (indx, ..)) = tasks.pop() { - let res = execute_task(task, &mut buffer); - buffer.clear(); - - let mut ast = ast.lock().unwrap(); - let len = ast.len().max(indx as usize + 1); - ast.resize_with(len, || Err(io::ErrorKind::InvalidData.into())); - ast[indx as usize] = res; - } - }; - - std::thread::scope(|s| (0..threads).for_each(|_| _ = s.spawn(thread))); - - ast.into_inner() - .unwrap() - .into_iter() - .collect::>>() -} - -pub fn ident_flag_index(flag: IdentFlags) -> u32 { - flag & !(MUTABLE | REFERENCED) + pub fn index(i: IdentFlags) -> u32 { + i & !ALL + } } pub fn no_loader(_: &str, _: &str) -> io::Result { @@ -301,13 +56,15 @@ struct ScopeIdent { } pub struct Parser<'a, 'b> { - path: &'b str, - loader: Loader<'b>, - lexer: Lexer<'b>, - arena: &'b Arena<'a>, - token: Token, - idents: Vec, - symbols: &'b mut Symbols, + path: &'b str, + loader: Loader<'b>, + lexer: Lexer<'b>, + arena: &'b Arena<'a>, + token: Token, + idents: Vec, + symbols: &'b mut Symbols, + ns_bound: usize, + captured: Vec, } impl<'a, 'b> Parser<'a, 'b> { @@ -321,6 +78,8 @@ impl<'a, 'b> Parser<'a, 'b> { arena, idents: Vec::new(), symbols, + ns_bound: 0, + captured: Vec::new(), } } @@ -382,7 +141,7 @@ impl<'a, 'b> Parser<'a, 'b> { let left = &*self.arena.alloc(fold); if let Some(op) = op.assign_op() { - self.flag_idents(*left, MUTABLE); + self.flag_idents(*left, idfl::MUTABLE); let right = Expr::BinOp { left, op, right }; fold = Expr::BinOp { left, @@ -392,7 +151,7 @@ impl<'a, 'b> Parser<'a, 'b> { } else { fold = Expr::BinOp { left, right, op }; if op == TokenKind::Assign { - self.flag_idents(*left, MUTABLE); + self.flag_idents(*left, idfl::MUTABLE); } } } @@ -400,41 +159,25 @@ impl<'a, 'b> Parser<'a, 'b> { fold } - fn try_resolve_builtin(name: &str) -> Option { - // FIXME: we actually do this the second time in the codegen - Some(match name { - "int" | "i64" => bt::INT, - "i8" => bt::I8, - "i16" => bt::I16, - "i32" => bt::I32, - "u8" => bt::U8, - "u16" => bt::U16, - "uint" | "u32" => bt::U32, - "bool" => bt::BOOL, - "void" => bt::VOID, - "never" => bt::NEVER, - _ => return None, - }) - } - fn resolve_ident(&mut self, token: Token, decl: bool) -> (Ident, u32) { let name = self.lexer.slice(token.range()); - if let Some(builtin) = Self::try_resolve_builtin(name) { + if let Some(builtin) = bt::from_str(name) { return (builtin, 0); } - let id = match self + let (i, id) = match self .idents .iter_mut() - .rfind(|elem| self.lexer.slice(ident::range(elem.ident)) == name) + .enumerate() + .rfind(|(_, elem)| self.lexer.slice(ident::range(elem.ident)) == name) { - Some(elem) if decl && elem.declared => { + Some((_, elem)) if decl && elem.declared => { self.report(format_args!("redeclaration of identifier: {name}")) } - Some(elem) => { + Some((i, elem)) => { elem.flags += 1; - elem + (i, elem) } None => { let id = ident::new(token.start, name.len() as _); @@ -443,13 +186,17 @@ impl<'a, 'b> Parser<'a, 'b> { declared: false, flags: 0, }); - self.idents.last_mut().unwrap() + (self.idents.len() - 1, self.idents.last_mut().unwrap()) } }; id.declared |= decl; + if self.ns_bound > i && id.declared { + id.flags |= idfl::CAPTURED; + self.captured.push(id.ident); + } - (id.ident, ident_flag_index(id.flags)) + (id.ident, idfl::index(id.flags)) } fn move_str(&mut self, range: Token) -> &'a str { @@ -460,6 +207,8 @@ impl<'a, 'b> Parser<'a, 'b> { use {Expr as E, TokenKind as T}; let frame = self.idents.len(); let token = self.next(); + let prev_boundary = self.ns_bound; + let prev_captured = self.captured.len(); let mut expr = match token.kind { T::Driective if self.lexer.slice(token.range()) == "use" => { self.expect_advance(TokenKind::LParen); @@ -489,8 +238,8 @@ impl<'a, 'b> Parser<'a, 'b> { value: true, }, T::Struct => E::Struct { - pos: token.start, - fields: { + fields: { + self.ns_bound = self.idents.len(); self.expect_advance(T::LBrace); self.collect_list(T::Comma, T::RBrace, |s| { let name = s.expect_advance(T::Ident); @@ -499,6 +248,20 @@ impl<'a, 'b> Parser<'a, 'b> { (s.move_str(name), ty) }) }, + captured: { + self.ns_bound = prev_boundary; + self.captured[prev_captured..].sort_unstable(); + let preserved = self.captured[prev_captured..].partition_dedup().0.len(); + self.captured.truncate(prev_captured + preserved); + self.arena.alloc_slice(&self.captured[prev_captured..]) + }, + pos: { + if self.ns_bound == 0 { + // we might save some memory + self.captured.clear(); + } + token.start + }, }, T::Ident => { let (id, index) = self.resolve_ident(token, self.token.kind == T::Decl); @@ -543,13 +306,13 @@ impl<'a, 'b> Parser<'a, 'b> { }, body: self.ptr_expr(), }, - T::Band | T::Mul => E::UnOp { + T::Band | T::Mul | T::Xor => E::UnOp { pos: token.start, op: token.kind, val: { let expr = self.ptr_unit_expr(); if token.kind == T::Band { - self.flag_idents(*expr, REFERENCED); + self.flag_idents(*expr, idfl::REFERENCED); } expr }, @@ -775,8 +538,9 @@ pub enum Expr<'a> { val: &'a Self, }, Struct { - pos: Pos, - fields: &'a [(&'a str, Self)], + pos: Pos, + fields: &'a [(&'a str, Self)], + captured: &'a [Ident], }, Ctor { pos: Pos, @@ -1041,13 +805,13 @@ impl Ast { unsafe { self.0.as_ref() } } - pub fn find_decl(&self, id: Result) -> Option<&Expr> { + pub fn find_decl(&self, id: Result) -> Option<(&Expr, Ident)> { self.exprs().iter().find_map(|expr| match expr { Expr::BinOp { left: &Expr::Ident { id: iden, name, .. }, op: TokenKind::Decl, .. - } if Ok(iden) == id || Err(name) == id => Some(expr), + } if Ok(iden) == id || Err(name) == id => Some((expr, iden)), _ => None, }) } @@ -1207,17 +971,6 @@ impl ArenaChunk { unsafe { std::ptr::read(curr.add(Self::NEXT_OFFSET) as *mut _) } } - fn reset(prev: *mut u8) -> Option { - if prev.is_null() { - return None; - } - - Some(Self { - base: prev, - end: unsafe { prev.add(Self::CHUNK_SIZE) }, - }) - } - fn alloc(&mut self, layout: std::alloc::Layout) -> Option> { let padding = self.end as usize - (self.end as usize & !(layout.align() - 1)); let size = layout.size() + padding; diff --git a/hblang/src/tests.rs b/hblang/src/tests.rs index 474da7c..0efafbd 100644 --- a/hblang/src/tests.rs +++ b/hblang/src/tests.rs @@ -11,7 +11,10 @@ pub fn run_test(name: &'static str, input: &'static str, test: fn(&'static str, let mut output = String::new(); test(input, &mut output); - let mut root = PathBuf::from(std::env::var("PT_TEST_ROOT").unwrap_or("tests".to_string())); + let mut root = PathBuf::from( + std::env::var("PT_TEST_ROOT") + .unwrap_or(concat!(env!("CARGO_MANIFEST_DIR"), "/tests").to_string()), + ); root.push( name.replace("::", "_") .replace(concat!(env!("CARGO_PKG_NAME"), "_"), ""), diff --git a/hblang/tests/codegen_tests_arithmetic.txt b/hblang/tests/codegen_tests_arithmetic.txt index 45bf88a..de488f4 100644 --- a/hblang/tests/codegen_tests_arithmetic.txt +++ b/hblang/tests/codegen_tests_arithmetic.txt @@ -1,3 +1,3 @@ -code size: 200 +code size: 189 ret: 1 status: Ok(()) diff --git a/hblang/tests/codegen_tests_different_types.txt b/hblang/tests/codegen_tests_different_types.txt index c6c7348..d6f5ec3 100644 --- a/hblang/tests/codegen_tests_different_types.txt +++ b/hblang/tests/codegen_tests_different_types.txt @@ -1,3 +1,3 @@ -code size: 499 +code size: 477 ret: 512 status: Ok(()) diff --git a/hblang/tests/codegen_tests_directives.txt b/hblang/tests/codegen_tests_directives.txt index 544a085..0c04030 100644 --- a/hblang/tests/codegen_tests_directives.txt +++ b/hblang/tests/codegen_tests_directives.txt @@ -1,4 +1,4 @@ ev: Ecall -code size: 204 +code size: 182 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_example.txt b/hblang/tests/codegen_tests_example.txt index 4650898..93fa541 100644 --- a/hblang/tests/codegen_tests_example.txt +++ b/hblang/tests/codegen_tests_example.txt @@ -1,3 +1,3 @@ -code size: 96 +code size: 85 ret: 1 status: Ok(()) diff --git a/hblang/tests/codegen_tests_fb_driver.txt b/hblang/tests/codegen_tests_fb_driver.txt index 1722fde..07dad9f 100644 --- a/hblang/tests/codegen_tests_fb_driver.txt +++ b/hblang/tests/codegen_tests_fb_driver.txt @@ -1,3 +1,3 @@ -code size: 569 +code size: 525 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_functions.txt b/hblang/tests/codegen_tests_functions.txt index 5c50735..c8c04fe 100644 --- a/hblang/tests/codegen_tests_functions.txt +++ b/hblang/tests/codegen_tests_functions.txt @@ -1,3 +1,3 @@ -code size: 314 +code size: 281 ret: 33 status: Ok(()) diff --git a/hblang/tests/codegen_tests_geneic_types.txt b/hblang/tests/codegen_tests_geneic_types.txt new file mode 100644 index 0000000..9239c43 --- /dev/null +++ b/hblang/tests/codegen_tests_geneic_types.txt @@ -0,0 +1,3 @@ +code size: 359 +ret: 69 +status: Ok(()) diff --git a/hblang/tests/codegen_tests_global_variables.txt b/hblang/tests/codegen_tests_global_variables.txt index ec0ebe7..822caf9 100644 --- a/hblang/tests/codegen_tests_global_variables.txt +++ b/hblang/tests/codegen_tests_global_variables.txt @@ -1,3 +1,3 @@ -code size: 305 +code size: 283 ret: 50 status: Ok(()) diff --git a/hblang/tests/codegen_tests_if_statements.txt b/hblang/tests/codegen_tests_if_statements.txt index a26ad5e..f95d4bd 100644 --- a/hblang/tests/codegen_tests_if_statements.txt +++ b/hblang/tests/codegen_tests_if_statements.txt @@ -1,3 +1,3 @@ -code size: 287 +code size: 265 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_loops.txt b/hblang/tests/codegen_tests_loops.txt index a26ad5e..f95d4bd 100644 --- a/hblang/tests/codegen_tests_loops.txt +++ b/hblang/tests/codegen_tests_loops.txt @@ -1,3 +1,3 @@ -code size: 287 +code size: 265 ret: 55 status: Ok(()) diff --git a/hblang/tests/codegen_tests_pointers.txt b/hblang/tests/codegen_tests_pointers.txt index 337f642..e820d87 100644 --- a/hblang/tests/codegen_tests_pointers.txt +++ b/hblang/tests/codegen_tests_pointers.txt @@ -1,3 +1,3 @@ -code size: 366 +code size: 322 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_struct_operators.txt b/hblang/tests/codegen_tests_struct_operators.txt index 2f5b08f..198c636 100644 --- a/hblang/tests/codegen_tests_struct_operators.txt +++ b/hblang/tests/codegen_tests_struct_operators.txt @@ -1,3 +1,3 @@ -code size: 800 +code size: 778 ret: 10 status: Ok(()) diff --git a/hblang/tests/codegen_tests_structs.txt b/hblang/tests/codegen_tests_structs.txt index 1141089..5c3dce3 100644 --- a/hblang/tests/codegen_tests_structs.txt +++ b/hblang/tests/codegen_tests_structs.txt @@ -1,3 +1,3 @@ -code size: 464 +code size: 420 ret: 3 status: Ok(()) diff --git a/hblang/tests/codegen_tests_variables.txt b/hblang/tests/codegen_tests_variables.txt index e9bb4e8..670eceb 100644 --- a/hblang/tests/codegen_tests_variables.txt +++ b/hblang/tests/codegen_tests_variables.txt @@ -1,3 +1,3 @@ -code size: 121 +code size: 110 ret: 0 status: Ok(()) diff --git a/tests/codegen_tests_arithmetic.txt b/tests/codegen_tests_arithmetic.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tests/codegen_tests_directives.txt b/tests/codegen_tests_directives.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tests/codegen_tests_example.txt b/tests/codegen_tests_example.txt deleted file mode 100644 index e69de29..0000000