diff --git a/hbbytecode/build.rs b/hbbytecode/build.rs index 6b549c6..9b6ee59 100644 --- a/hbbytecode/build.rs +++ b/hbbytecode/build.rs @@ -85,11 +85,7 @@ fn gen_instrs(generated: &mut String) -> Result<(), Box> } '_name_list: { - writeln!(generated, "pub const NAMES: [&str; {}] = [", instructions().count())?; - for [_, name, _, _] in instructions() { - writeln!(generated, " \"{}\",", name.to_lowercase())?; - } - writeln!(generated, "];")?; + writeln!(generated, "pub const COUNT: u8 = {};", instructions().count())?; } let instr = "Instr"; diff --git a/hbbytecode/src/lib.rs b/hbbytecode/src/lib.rs index b93a0cd..cc4f1a3 100644 --- a/hbbytecode/src/lib.rs +++ b/hbbytecode/src/lib.rs @@ -34,7 +34,7 @@ impl TryFrom for Instr { Err(value) } - if value < NAMES.len() as u8 { + if value < COUNT { unsafe { Ok(core::mem::transmute::(value)) } } else { failed(value) @@ -99,10 +99,7 @@ pub fn disasm( }; fn instr_from_byte(b: u8) -> std::io::Result { - if b as usize >= instrs::NAMES.len() { - return Err(std::io::ErrorKind::InvalidData.into()); - } - Ok(unsafe { std::mem::transmute::(b) }) + b.try_into().map_err(|_| std::io::ErrorKind::InvalidData.into()) } let mut labels = HashMap::::default(); diff --git a/hblang/README.md b/hblang/README.md index 1ead58d..681727d 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -160,12 +160,15 @@ Ty2 := struct { c: int, } +useless := struct {} + main := fn(): int { + // `packed` structs have no padding (all fields are alighred to 1) if @sizeof(packed struct {a: u8, b: u16}) != 3 { return 9001 } - finst := Ty2.{ty: Ty.{a: 4, b: 1}, c: 3} + finst := Ty2.{ty: .{a: 4, b: 1}, c: 3} inst := odher_pass(finst) if inst.c == 3 { return pass(&inst.ty) diff --git a/hblang/command-help.txt b/hblang/command-help.txt index 0f9ddb3..57b4191 100644 --- a/hblang/command-help.txt +++ b/hblang/command-help.txt @@ -1,5 +1,4 @@ --fmt - format all source files ---fmt-current - format mentioned file - --fmt-stdout - dont write the formatted file but print it +--fmt-stdout - dont write the formatted file but print it --dump-asm - output assembly instead of raw code, (the assembly is more for debugging the compiler) --threads <1...> - number of threads compiler can use [default: 1] diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 02f4246..88c905b 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -8,8 +8,8 @@ use { parser::{ self, find_symbol, idfl, CommentOr, CtorField, Expr, ExprRef, FileId, Pos, StructField, }, - ty, Field, Func, Global, LoggedMem, ParamAlloc, Reloc, Sig, Struct, SymKey, TypedReloc, - Types, + ty, Field, Func, Global, LoggedMem, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey, + TypedReloc, Types, }, core::panic, std::fmt::Display, @@ -483,13 +483,6 @@ impl ItemCtx { } fn emit(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) { - let name = instrs::NAMES[instr[0] as usize]; - log::trc!( - "{:08x}: {}: {}", - self.code.len(), - name, - instr.iter().take(len).skip(1).map(|b| format!("{:02x}", b)).collect::() - ); self.code.extend_from_slice(&instr[..len]); } @@ -729,7 +722,7 @@ impl Codegen { .filter_map(CommentOr::or) .map(|sf| Field { name: sf.name.into(), ty: self.ty(&sf.ty) }) .collect(); - self.tys.structs.push(Struct { fields, explicit_alignment }); + self.tys.structs.push(Struct { name: 0, file: 0, fields, explicit_alignment }); self.tys.structs.len() as u32 - 1 } @@ -1122,15 +1115,13 @@ impl Codegen { match ty.expand() { ty::Kind::Struct(stru) => { - let mut offset = 0; - let sfields = self.tys.structs[stru as usize].fields.clone(); - for (sfield, field) in sfields.iter().zip(fields) { + let mut oiter = OffsetIter::new(stru); + for field in fields { + let (ty, offset) = oiter.next_ty(&self.tys).unwrap(); let loc = loc.as_ref().offset(offset); - let ctx = Ctx::default().with_loc(loc).with_ty(sfield.ty); + let ctx = Ctx::default().with_loc(loc).with_ty(ty); let value = self.expr_ctx(field, ctx)?; self.ci.free_loc(value.loc); - offset += self.tys.size_of(sfield.ty); - offset = Types::align_up(offset, self.tys.align_of(sfield.ty)); } } ty::Kind::Slice(arr) => { @@ -1944,21 +1935,14 @@ impl Codegen { .loc .or_else(|| right.take_owned()) .unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(self.tys.size_of(ty)))); - let mut offset = 0; - for &Field { ty, .. } in self.tys.structs[stuct as usize].fields.clone().iter() { - offset = Types::align_up( - offset, - self.tys.structs[stuct as usize] - .explicit_alignment - .unwrap_or(self.tys.align_of(ty)), - ); - let size = self.tys.size_of(ty); + + let mut oiter = OffsetIter::new(stuct); + while let Some((ty, offset)) = oiter.next_ty(&self.tys) { let ctx = Ctx::from(Value { ty, loc: loc.as_ref().offset(offset) }); let left = left.as_ref().offset(offset); let right = right.as_ref().offset(offset); let value = self.struct_op(op, ty, ctx, left, right)?; self.ci.free_loc(value.loc); - offset += size; } self.ci.free_loc(left); @@ -2317,9 +2301,10 @@ impl Codegen { Reloc::pack_srel(stack, off) } - // TODO: sometimes its better to do this in bulk fn ty(&mut self, expr: &Expr) -> ty::Id { - ty::Id::from(self.eval_const(expr, ty::TYPE)) + self.tys + .ty(self.ci.file, expr, &self.files) + .unwrap_or_else(|| ty::Id::from(self.eval_const(expr, ty::TYPE))) } fn read_trap(addr: u64) -> Option<&'static trap::Trap> { diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 9c1551d..a8a419a 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -272,6 +272,7 @@ impl TokenKind { Self::Div => a.wrapping_div(b), Self::Shl => a.wrapping_shl(b as _), Self::Eq => (a == b) as i64, + Self::Ne => (a != b) as i64, Self::Band => a & b, s => todo!("{s}"), } diff --git a/hblang/src/lib.rs b/hblang/src/lib.rs index 59fe947..854248b 100644 --- a/hblang/src/lib.rs +++ b/hblang/src/lib.rs @@ -19,12 +19,13 @@ extract_if, ptr_internals )] -#![allow(stable_features, internal_features, clippy::format_collect)] +#![allow(stable_features, internal_features)] use { self::{ ident::Ident, - parser::{Expr, ExprRef, FileId}, + lexer::TokenKind, + parser::{CommentOr, Expr, ExprRef, FileId}, son::reg, ty::ArrayLen, }, @@ -32,6 +33,7 @@ use { parser::Ast, std::{ collections::{hash_map, BTreeMap, VecDeque}, + fmt::Display, io, ops::Range, path::{Path, PathBuf}, @@ -58,6 +60,7 @@ pub mod parser; pub mod son; mod lexer; +mod vc; mod task { use super::Offset; @@ -164,8 +167,9 @@ mod log { mod ty { use { crate::{ + ident, lexer::TokenKind, - parser::{self, Expr}, + parser::{self}, }, std::{num::NonZeroU32, ops::Range}, }; @@ -427,34 +431,22 @@ mod ty { TK::Ptr(ty) => { write!(f, "^{}", self.rety(self.tys.ptrs[ty as usize].base)) } - _ if let Some((key, _)) = self - .tys - .syms - .iter() - .find(|(sym, &ty)| sym.file < self.files.len() as u32 && ty == self.ty) - && let Some(name) = self.files[key.file as usize].exprs().iter().find_map( - |expr| match expr { - Expr::BinOp { - left: &Expr::Ident { name, id, .. }, - op: TokenKind::Decl, - .. - } if id == key.ident => Some(name), - _ => None, - }, - ) => - { - write!(f, "{name}") - } TK::Struct(idx) => { let record = &self.tys.structs[idx as usize]; - write!(f, "[{idx}]{{")?; - for (i, &super::Field { ref name, ty }) in record.fields.iter().enumerate() { - if i != 0 { - write!(f, ", ")?; + if ident::is_null(record.name) { + write!(f, "[{idx}]{{")?; + for (i, &super::Field { ref name, ty }) in record.fields.iter().enumerate() + { + if i != 0 { + write!(f, ", ")?; + } + write!(f, "{name}: {}", self.rety(ty))?; } - write!(f, "{name}: {}", self.rety(ty))?; + write!(f, "}}") + } else { + let file = &self.files[record.file as usize]; + write!(f, "{}", file.ident_str(record.name)) } - write!(f, "}}") } TK::Func(idx) => write!(f, "fn{idx}"), TK::Global(idx) => write!(f, "global{idx}"), @@ -585,6 +577,8 @@ struct Field { } struct Struct { + name: Ident, + file: FileId, explicit_alignment: Option, fields: Rc<[Field]>, } @@ -639,6 +633,53 @@ struct Types { const HEADER_SIZE: usize = std::mem::size_of::(); impl Types { + /// returns none if comptime eval is required + fn ty(&mut self, file: FileId, expr: &Expr, files: &[parser::Ast]) -> Option { + Some(match *expr { + Expr::UnOp { op: TokenKind::Xor, val, .. } => { + let base = self.ty(file, val, files)?; + self.make_ptr(base) + } + Expr::Ident { id, .. } if ident::is_null(id) => id.into(), + Expr::Ident { id, .. } => { + let f = &files[file as usize]; + let (Expr::BinOp { right, .. }, name) = f.find_decl(Ok(id))? else { + unreachable!() + }; + let ty = self.ty(file, right, files)?; + if let ty::Kind::Struct(s) = ty.expand() { + self.structs[s as usize].name = name; + } + ty + } + Expr::Struct { pos, fields, packed, .. } => { + let sym = SymKey { file, ident: pos }; + if let Some(&ty) = self.syms.get(&sym) { + return Some(ty); + } + + let fields = fields + .iter() + .filter_map(CommentOr::or) + .map(|sf| { + Some(Field { name: sf.name.into(), ty: self.ty(file, &sf.ty, files)? }) + }) + .collect::>()?; + self.structs.push(Struct { + name: 0, + file, + fields, + explicit_alignment: packed.then_some(1), + }); + + let ty = ty::Kind::Struct(self.structs.len() as u32 - 1).compress(); + self.syms.insert(sym, ty); + ty + } + _ => return None, + }) + } + fn assemble(&mut self, to: &mut Vec) { to.extend([0u8; HEADER_SIZE]); @@ -762,14 +803,10 @@ impl Types { } fn offset_of(&self, idx: ty::Struct, field: &str) -> Option<(Offset, ty::Id)> { - let record = &self.structs[idx as usize]; - let until = record.fields.iter().position(|f| f.name.as_ref() == field)?; - let mut offset = 0; - for &Field { ty, .. } in &record.fields[..until] { - offset = Self::align_up(offset, record.explicit_alignment.unwrap_or(self.align_of(ty))); - offset += self.size_of(ty); - } - Some((offset, record.fields[until].ty)) + OffsetIter::new(idx) + .into_iter(self) + .find(|(f, _)| f.name.as_ref() == field) + .map(|(f, off)| (off, f.ty)) } fn make_ptr(&mut self, base: ty::Id) -> ty::Id { @@ -812,10 +849,6 @@ impl Types { .inner() } - fn align_up(value: Size, align: Size) -> Size { - (value + align - 1) & !(align - 1) - } - fn size_of(&self, ty: ty::Id) -> Size { match ty.expand() { ty::Kind::Ptr(_) => 8, @@ -834,14 +867,9 @@ impl Types { } } ty::Kind::Struct(stru) => { - let mut offset = 0u32; - let record = &self.structs[stru as usize]; - for &Field { ty, .. } in record.fields.iter() { - let align = record.explicit_alignment.unwrap_or(self.align_of(ty)); - offset = Self::align_up(offset, align); - offset += self.size_of(ty); - } - offset + let mut oiter = OffsetIter::new(stru); + while oiter.next(self).is_some() {} + oiter.offset } ty => unimplemented!("size_of: {:?}", ty), } @@ -856,7 +884,7 @@ impl Types { .iter() .map(|&Field { ty, .. }| self.align_of(ty)) .max() - .unwrap() + .unwrap_or(1) }) } ty::Kind::Slice(arr) => { @@ -878,6 +906,43 @@ impl Types { } } +struct OffsetIter { + strct: ty::Struct, + offset: Offset, + index: usize, +} + +fn align_up(value: Size, align: Size) -> Size { + (value + align - 1) & !(align - 1) +} + +impl OffsetIter { + fn new(strct: ty::Struct) -> Self { + Self { strct, offset: 0, index: 0 } + } + + fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> { + let stru = &tys.structs[self.strct as usize]; + let field = stru.fields.get(self.index)?; + self.index += 1; + let align = stru.explicit_alignment.unwrap_or_else(|| tys.align_of(field.ty)); + + self.offset = align_up(self.offset, align); + let off = self.offset; + self.offset += tys.size_of(field.ty); + Some((field, off)) + } + + fn next_ty(&mut self, tys: &Types) -> Option<(ty::Id, Offset)> { + let (field, off) = self.next(tys)?; + Some((field.ty, off)) + } + + fn into_iter(mut self, tys: &Types) -> impl Iterator { + std::iter::from_fn(move || self.next(tys)) + } +} + struct TaskQueue { inner: Mutex>, } @@ -1277,7 +1342,7 @@ fn test_run_vm(out: &[u8], output: &mut String) { #[derive(Default)] pub struct Options { pub fmt: bool, - pub fmt_current: bool, + pub fmt_stdout: bool, pub dump_asm: bool, pub extra_threads: usize, } @@ -1328,7 +1393,7 @@ pub fn run_compiler( for parsed in parsed { format_ast(parsed)?; } - } else if options.fmt_current { + } else if options.fmt_stdout { let ast = parsed.into_iter().next().unwrap(); let source = std::fs::read_to_string(&*ast.path)?; format_to(&ast, &source, out)?; @@ -1352,6 +1417,42 @@ pub fn run_compiler( #[derive(Default)] pub struct LoggedMem { pub mem: hbvm::mem::HostMemory, + op_buf: Vec, + disp_buf: String, + prev_instr: Option, +} + +impl LoggedMem { + unsafe fn display_instr(&mut self, instr: hbbytecode::Instr, addr: hbvm::mem::Address) { + let novm: *const hbvm::Vm = std::ptr::null(); + let offset = std::ptr::addr_of!((*novm).memory) as usize; + let regs = unsafe { + &*std::ptr::addr_of!( + (*(((self as *mut _ as *mut u8).sub(offset)) as *const hbvm::Vm)) + .registers + ) + }; + + let mut bytes = core::slice::from_raw_parts( + (addr.get() - 1) as *const u8, + std::mem::size_of::() + 1, + ); + use std::fmt::Write; + hbbytecode::parse_args(&mut bytes, instr, &mut self.op_buf).unwrap(); + debug_assert!(bytes.is_empty()); + self.disp_buf.clear(); + write!(self.disp_buf, "{:<10}", format!("{instr:?}")).unwrap(); + for (i, op) in self.op_buf.drain(..).enumerate() { + if i != 0 { + write!(self.disp_buf, ", ").unwrap(); + } + write!(self.disp_buf, "{op:?}").unwrap(); + if let hbbytecode::Oper::R(r) = op { + write!(self.disp_buf, "({})", regs[r as usize].0).unwrap() + } + } + log::trc!("read-typed: {:x}: {}", addr.get(), self.disp_buf); + } } impl hbvm::mem::Memory for LoggedMem { @@ -1362,13 +1463,9 @@ impl hbvm::mem::Memory for LoggedMem { count: usize, ) -> Result<(), hbvm::mem::LoadError> { log::trc!( - "load: {:x} {:?}", + "load: {:x} {}", addr.get(), - core::slice::from_raw_parts(addr.get() as *const u8, count) - .iter() - .rev() - .map(|&b| format!("{b:02x}")) - .collect::() + AsHex(core::slice::from_raw_parts(addr.get() as *const u8, count)) ); self.mem.load(addr, target, count) } @@ -1379,39 +1476,38 @@ impl hbvm::mem::Memory for LoggedMem { source: *const u8, count: usize, ) -> Result<(), hbvm::mem::StoreError> { - log::trc!( - "store: {:x} {:?}", - addr.get(), - core::slice::from_raw_parts(source, count) - .iter() - .rev() - .map(|&b| format!("{b:02x}")) - .collect::() - ); + log::trc!("store: {:x} {}", addr.get(), AsHex(core::slice::from_raw_parts(source, count))); self.mem.store(addr, source, count) } - unsafe fn prog_read(&mut self, addr: hbvm::mem::Address) -> T { - log::trc!( - "read-typed: {:x} {} {:?}", - addr.get(), - std::any::type_name::(), - if core::mem::size_of::() == 1 - && let Some(nm) = - instrs::NAMES.get(std::ptr::read(addr.get() as *const u8) as usize) - { - nm.to_string() + unsafe fn prog_read(&mut self, addr: hbvm::mem::Address) -> T { + if log::LOG_LEVEL == log::Level::Trc { + if std::any::TypeId::of::() == std::any::TypeId::of::() { + if let Some(instr) = self.prev_instr { + self.display_instr::<()>(instr, addr); + } + self.prev_instr = hbbytecode::Instr::try_from(*(addr.get() as *const u8)).ok(); } else { - core::slice::from_raw_parts(addr.get() as *const u8, core::mem::size_of::()) - .iter() - .map(|&b| format!("{:02x}", b)) - .collect::() + let instr = self.prev_instr.take().unwrap(); + self.display_instr::(instr, addr); } - ); + } + self.mem.prog_read(addr) } } +struct AsHex<'a>(&'a [u8]); + +impl Display for AsHex<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for &b in self.0 { + write!(f, "{b:02x}")?; + } + Ok(()) + } +} + #[cfg(test)] mod test { use std::sync::Arc; diff --git a/hblang/src/main.rs b/hblang/src/main.rs index 7eccd1f..17f719f 100644 --- a/hblang/src/main.rs +++ b/hblang/src/main.rs @@ -14,7 +14,7 @@ fn main() -> std::io::Result<()> { args.iter().filter(|a| !a.starts_with('-')).nth(1).copied().unwrap_or("main.hb"), hblang::Options { fmt: args.contains(&"--fmt"), - fmt_current: args.contains(&"--fmt-stdout"), + fmt_stdout: args.contains(&"--fmt-stdout"), dump_asm: args.contains(&"--dump-asm"), extra_threads: args .iter() diff --git a/hblang/src/son.rs b/hblang/src/son.rs index f0e22de..dc4ddff 100644 --- a/hblang/src/son.rs +++ b/hblang/src/son.rs @@ -1,20 +1,20 @@ -#![allow(dead_code)] use { crate::{ - ident::{self, Ident}, + ident::Ident, instrs, lexer::{self, TokenKind}, log, parser::{ self, idfl::{self}, - CommentOr, Expr, ExprRef, FileId, Pos, StructField, + Expr, ExprRef, FileId, Pos, }, task, ty::{self}, - Field, Func, HashMap, Offset, Reloc, Sig, Size, Struct, SymKey, TypedReloc, Types, + vc::{BitSet, Vc}, + Func, HashMap, Offset, Reloc, Sig, Size, SymKey, TypedReloc, Types, }, - core::fmt, + core::{fmt, format_args as fa}, regalloc2::VReg, std::{ assert_matches::debug_assert_matches, @@ -23,294 +23,15 @@ use { convert::identity, fmt::{Debug, Display, Write}, hash::{Hash as _, Hasher}, - mem::{self, MaybeUninit}, - ops::{self, Deref, DerefMut, Not}, - ptr::Unique, + mem, ops, }, }; -const VC_SIZE: usize = 16; -const INLINE_ELEMS: usize = VC_SIZE / 2 - 1; const VOID: Nid = 0; const NEVER: Nid = 1; const ENTRY: Nid = 2; const MEM: Nid = 3; -union Vc { - inline: InlineVc, - alloced: AllocedVc, -} - -impl Default for Vc { - fn default() -> Self { - Vc { inline: InlineVc { elems: MaybeUninit::uninit(), cap: 0 } } - } -} - -impl Debug for Vc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.as_slice().fmt(f) - } -} - -impl Vc { - fn is_inline(&self) -> bool { - unsafe { self.inline.cap <= INLINE_ELEMS as Nid } - } - - fn layout(&self) -> Option { - unsafe { - self.is_inline() - .not() - .then(|| std::alloc::Layout::array::(self.alloced.cap as _).unwrap_unchecked()) - } - } - - fn len(&self) -> usize { - unsafe { - if self.is_inline() { - self.inline.cap as _ - } else { - self.alloced.len as _ - } - } - } - - fn len_mut(&mut self) -> &mut Nid { - unsafe { - if self.is_inline() { - &mut self.inline.cap - } else { - &mut self.alloced.len - } - } - } - - fn as_ptr(&self) -> *const Nid { - unsafe { - match self.is_inline() { - true => self.inline.elems.as_ptr().cast(), - false => self.alloced.base.as_ptr(), - } - } - } - - fn as_mut_ptr(&mut self) -> *mut Nid { - unsafe { - match self.is_inline() { - true => self.inline.elems.as_mut_ptr().cast(), - false => self.alloced.base.as_ptr(), - } - } - } - - fn as_slice(&self) -> &[Nid] { - unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) } - } - - fn as_slice_mut(&mut self) -> &mut [Nid] { - unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) } - } - - fn push(&mut self, value: Nid) { - if let Some(layout) = self.layout() - && unsafe { self.alloced.len == self.alloced.cap } - { - unsafe { - self.alloced.cap *= 2; - self.alloced.base = Unique::new_unchecked( - std::alloc::realloc( - self.alloced.base.as_ptr().cast(), - layout, - self.alloced.cap as usize * std::mem::size_of::(), - ) - .cast(), - ); - } - } else if self.len() == INLINE_ELEMS { - unsafe { - let mut allcd = - Self::alloc((self.inline.cap + 1).next_power_of_two() as _, self.len()); - std::ptr::copy_nonoverlapping(self.as_ptr(), allcd.as_mut_ptr(), self.len()); - *self = allcd; - } - } - - unsafe { - *self.len_mut() += 1; - self.as_mut_ptr().add(self.len() - 1).write(value); - } - } - - unsafe fn alloc(cap: usize, len: usize) -> Self { - debug_assert!(cap > INLINE_ELEMS); - let layout = unsafe { std::alloc::Layout::array::(cap).unwrap_unchecked() }; - let alloc = unsafe { std::alloc::alloc(layout) }; - unsafe { - Vc { - alloced: AllocedVc { - base: Unique::new_unchecked(alloc.cast()), - len: len as _, - cap: cap as _, - }, - } - } - } - - fn swap_remove(&mut self, index: usize) { - let len = self.len() - 1; - self.as_slice_mut().swap(index, len); - *self.len_mut() -= 1; - } - - fn remove(&mut self, index: usize) { - self.as_slice_mut().copy_within(index + 1.., index); - *self.len_mut() -= 1; - } -} - -impl Drop for Vc { - fn drop(&mut self) { - if let Some(layout) = self.layout() { - unsafe { - std::alloc::dealloc(self.alloced.base.as_ptr().cast(), layout); - } - } - } -} - -impl Clone for Vc { - fn clone(&self) -> Self { - self.as_slice().into() - } -} - -impl IntoIterator for Vc { - type IntoIter = VcIntoIter; - type Item = Nid; - - fn into_iter(self) -> Self::IntoIter { - VcIntoIter { start: 0, end: self.len(), vc: self } - } -} - -struct VcIntoIter { - start: usize, - end: usize, - vc: Vc, -} - -impl Iterator for VcIntoIter { - type Item = Nid; - - fn next(&mut self) -> Option { - if self.start == self.end { - return None; - } - - let ret = unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.start)) }; - self.start += 1; - Some(ret) - } - - fn size_hint(&self) -> (usize, Option) { - let len = self.end - self.start; - (len, Some(len)) - } -} - -impl DoubleEndedIterator for VcIntoIter { - fn next_back(&mut self) -> Option { - if self.start == self.end { - return None; - } - - self.end -= 1; - Some(unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.end)) }) - } -} - -impl ExactSizeIterator for VcIntoIter {} - -impl From<[Nid; SIZE]> for Vc { - fn from(value: [Nid; SIZE]) -> Self { - value.as_slice().into() - } -} - -impl<'a> From<&'a [Nid]> for Vc { - fn from(value: &'a [Nid]) -> Self { - if value.len() <= INLINE_ELEMS { - let mut dflt = Self::default(); - unsafe { - std::ptr::copy_nonoverlapping(value.as_ptr(), dflt.as_mut_ptr(), value.len()) - }; - dflt.inline.cap = value.len() as _; - dflt - } else { - let mut allcd = unsafe { Self::alloc(value.len(), value.len()) }; - unsafe { - std::ptr::copy_nonoverlapping(value.as_ptr(), allcd.as_mut_ptr(), value.len()) - }; - allcd - } - } -} - -impl Deref for Vc { - type Target = [Nid]; - - fn deref(&self) -> &Self::Target { - self.as_slice() - } -} - -impl DerefMut for Vc { - fn deref_mut(&mut self) -> &mut Self::Target { - self.as_slice_mut() - } -} - -#[derive(Clone, Copy)] -#[repr(C)] -struct InlineVc { - cap: Nid, - elems: MaybeUninit<[Nid; INLINE_ELEMS]>, -} - -#[derive(Clone, Copy)] -#[repr(C)] -struct AllocedVc { - cap: Nid, - len: Nid, - base: Unique, -} - -#[derive(Default)] -struct BitSet { - data: Vec, -} - -impl BitSet { - const ELEM_SIZE: usize = std::mem::size_of::() * 8; - - pub fn clear(&mut self, bit_size: usize) { - let new_len = (bit_size + Self::ELEM_SIZE - 1) / Self::ELEM_SIZE; - self.data.clear(); - self.data.resize(new_len, 0); - } - - #[track_caller] - pub fn set(&mut self, idx: Nid) -> bool { - let idx = idx as usize; - let data_idx = idx / Self::ELEM_SIZE; - let sub_idx = idx % Self::ELEM_SIZE; - let prev = self.data[data_idx] & (1 << sub_idx); - self.data[data_idx] |= 1 << sub_idx; - prev == 0 - } -} - type Nid = u16; pub mod reg { @@ -861,6 +582,7 @@ impl Nodes { log::inf!("{out}"); } + #[allow(dead_code)] fn graphviz(&self) { let out = &mut String::new(); _ = self.graphviz_low(out); @@ -920,6 +642,7 @@ impl Nodes { //} } + #[expect(dead_code)] fn climb_expr(&mut self, from: Nid, mut for_each: impl FnMut(Nid, &Node) -> bool) -> bool { fn climb_impl( nodes: &mut Nodes, @@ -942,6 +665,7 @@ impl Nodes { climb_impl(self, from, &mut for_each) } + #[expect(dead_code)] fn late_peephole(&mut self, target: Nid) -> Nid { if let Some(id) = self.peephole(target) { self.replace(target, id); @@ -1012,6 +736,7 @@ impl Nodes { } } + #[expect(dead_code)] fn iter_mut(&mut self) -> impl Iterator { self.values.iter_mut().flat_map(Result::as_mut) } @@ -1106,19 +831,6 @@ impl Kind { fn ends_basic_block(&self) -> bool { matches!(self, Self::Return | Self::If | Self::End) } - - fn starts_basic_block(&self) -> bool { - matches!( - self, - Self::Start - | Self::End - | Self::Entry - | Self::Then - | Self::Else - | Self::Region - | Self::Loop - ) - } } impl fmt::Display for Kind { @@ -1165,7 +877,6 @@ impl Node { type RallocBRef = u16; type LoopDepth = u16; -type CallCount = u16; type LockRc = u16; type IDomDepth = u16; @@ -1182,12 +893,6 @@ struct Variable { value: Nid, } -struct ColorMeta { - rc: u32, - depth: LoopDepth, - loc: Loc, -} - #[derive(PartialEq, Eq, Clone, Copy, Debug)] struct MemKey { region: Nid, @@ -1198,6 +903,7 @@ struct MemKey { #[derive(Default)] struct ItemCtx { file: FileId, + #[expect(dead_code)] id: ty::Id, ret: Option, @@ -1206,10 +912,8 @@ struct ItemCtx { nodes: Nodes, ctrl: Nid, - loop_depth: LoopDepth, call_count: u16, filled: Vec, - delayed_frees: Vec, stack_size: Size, loops: Vec, @@ -1248,22 +952,6 @@ impl Ctx { } } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -struct Loc { - reg: reg::Reg, -} - -#[derive(Default, Debug, Clone, Copy)] -struct GenCtx { - loc: Option, -} - -impl GenCtx { - pub fn with_loc(self, loc: impl Into) -> Self { - Self { loc: Some(loc.into()) } - } -} - #[derive(Default)] struct Pool { cis: Vec, @@ -1389,23 +1077,10 @@ impl Codegen { self.expr_ctx(expr, Ctx::default()) } - fn build_struct( - &mut self, - explicit_alignment: Option, - fields: &[CommentOr], - ) -> ty::Struct { - let fields = fields - .iter() - .filter_map(CommentOr::or) - .map(|sf| Field { name: sf.name.into(), ty: self.ty(&sf.ty) }) - .collect(); - self.tys.structs.push(Struct { fields, explicit_alignment }); - self.tys.structs.len() as u32 - 1 - } - fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option { let msg = "i know nothing about this name gal which is vired \ because we parsed succesfully"; + // ordered by complexity of the expression match *expr { Expr::Comment { .. } => Some(VOID), Expr::Ident { pos, id, .. } => { @@ -1422,6 +1097,70 @@ impl Codegen { Some(self.ci.vars[index].value) } + Expr::Number { value, .. } => Some(self.ci.nodes.new_node( + ctx.ty.filter(|ty| ty.is_integer() || ty.is_pointer()).unwrap_or(ty::INT.into()), + Kind::CInt { value }, + [VOID], + )), + Expr::Return { pos, val } => { + let value = if let Some(val) = val { + self.expr_ctx(val, Ctx { ty: self.ci.ret })? + } else { + VOID + }; + + let mut inps = Vc::from([self.ci.ctrl, value]); + for m in self.ci.memories.iter() { + inps.push(m.node); + } + + let out = &mut String::new(); + self.report_log_to(pos, "returning here", out); + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps); + + self.ci.nodes[NEVER].inputs.push(self.ci.ctrl); + self.ci.nodes[self.ci.ctrl].outputs.push(NEVER); + + let expected = *self.ci.ret.get_or_insert(self.tof(value)); + _ = self.assert_ty(pos, self.tof(value), expected, true, "return value"); + + None + } + Expr::UnOp { op: TokenKind::Band, val, .. } => { + let ctx = Ctx { ty: ctx.ty.and_then(|ty| self.tys.base_of(ty)) }; + + let mut val = self.expr_ctx(val, ctx)?; + let ty = self.tof(val); + if !matches!(self.ci.nodes[val].kind, Kind::Stck) { + let ptr = self.tys.make_ptr(ty); + let stck = self.ci.nodes.new_node_nop(ptr, Kind::Stck, [VOID, MEM]); + self.ci.nodes[stck].offset = self.ci.stack_size; + self.ci.stack_size += self.tys.size_of(ty); + self.store_mem(stck, 0, val); + val = stck; + } + + Some(val) + } + Expr::UnOp { op: TokenKind::Mul, val, pos } => { + let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; + let val = self.expr_ctx(val, ctx)?; + let Some(base) = self.get_load_type(val) else { + self.report( + pos, + fa!("the '{}' can not be dereferneced", self.ty_display(self.tof(val))), + ); + return Some(NEVER); + }; + Some(self.load_mem(val, 0, base)) + } + Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => { + let val = self.expr_ctx(val, ctx)?; + if !self.tof(val).is_integer() { + self.report(pos, fa!("cant negate '{}'", self.ty_display(self.tof(val)))); + } + Some(self.ci.nodes.new_node(self.tof(val), Kind::UnOp { op }, [VOID, val])) + } Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => { let value = self.expr(right)?; self.ci.nodes.lock(value); @@ -1451,10 +1190,7 @@ impl Codegen { let base = self.get_load_type(val).unwrap_or_else(|| { self.report( pos, - format_args!( - "the '{}' can not be dereferneced", - self.ty_display(self.tof(val)) - ), + fa!("the '{}' can not be dereferneced", self.ty_display(self.tof(val))), ); ty::NEVER.into() }); @@ -1479,121 +1215,127 @@ impl Codegen { let inps = [VOID, lhs, rhs]; Some(self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, inps)) } - Expr::UnOp { op: TokenKind::Band, val, .. } => { - let ctx = Ctx { ty: ctx.ty.and_then(|ty| self.tys.base_of(ty)) }; - - let mut val = self.expr_ctx(val, ctx)?; - let ty = self.tof(val); - if !matches!(self.ci.nodes[val].kind, Kind::Stck) { - let ptr = self.tys.make_ptr(ty); - let stck = self.ci.nodes.new_node_nop(ptr, Kind::Stck, [VOID, MEM]); - self.ci.nodes[stck].offset = self.ci.stack_size; - self.ci.stack_size += self.tys.size_of(ty); - self.store_mem(stck, 0, val); - val = stck; - } - - Some(val) + Expr::Directive { name: "sizeof", args: [ty], .. } => { + let ty = self.ty(ty); + Some(self.ci.nodes.new_node_nop( + ty::INT, + Kind::CInt { value: self.tys.size_of(ty) as _ }, + [VOID], + )) } - Expr::UnOp { op: TokenKind::Mul, val, pos } => { - let ctx = Ctx { ty: ctx.ty.map(|ty| self.tys.make_ptr(ty)) }; - let val = self.expr_ctx(val, ctx)?; - let Some(base) = self.get_load_type(val) else { + Expr::Call { func: &Expr::Ident { pos, id, name, .. }, args, .. } => { + self.ci.call_count += 1; + let func = self.find_or_declare(pos, self.ci.file, Some(id), name); + let ty::Kind::Func(func) = func else { self.report( pos, - format_args!( - "the '{}' can not be dereferneced", - self.ty_display(self.tof(val)) + fa!("compiler cant (yet) call '{}'", self.ty_display(func.compress())), + ); + return Some(NEVER); + }; + + self.make_func_reachable(func); + + let fuc = &self.tys.funcs[func as usize]; + let sig = fuc.sig.expect("TODO: generic functions"); + let ast = self.files[fuc.file as usize].clone(); + let Expr::BinOp { right: &Expr::Closure { args: cargs, .. }, .. } = + fuc.expr.get(&ast).unwrap() + else { + unreachable!() + }; + + self.assert_report( + args.len() == cargs.len(), + pos, + fa!( + "expected {} function argumenr{}, got {}", + cargs.len(), + if cargs.len() == 1 { "" } else { "s" }, + args.len() + ), + ); + + let mut inps = Vc::from([self.ci.ctrl]); + for ((arg, carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) { + let ty = self.tys.args[tyx]; + if self.tys.size_of(ty) == 0 { + continue; + } + let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; + _ = self.assert_ty( + arg.pos(), + self.tof(value), + ty, + true, + fa!("argument {}", carg.name), + ); + if ty.is_pointer() { + value = self + .ci + .memories + .binary_search_by_key(&(value, 0), |k| (k.region, k.offset)) + .map_or(value, |i| self.ci.memories[i].node); + // mark the read as clobbed since function can store + self.ci.nodes[value].offset = u32::MAX; + } + inps.push(value); + } + self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func }, inps); + + Some(self.ci.ctrl) + } + Expr::Ctor { pos, ty, fields, .. } => { + let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else { + self.report( + pos, + "the type of struct cannot be inferred from context, \ + use an explicit type instead: .{ ... }", + ); + return Some(NEVER); + }; + + let ty::Kind::Struct(s) = sty.expand() else { + let inferred = if ty.is_some() { "" } else { "inferred " }; + self.report( + pos, + fa!( + "the {inferred}type of the constructor is `{}`, \ + but thats not a struct", + self.ty_display(sty) ), ); return Some(NEVER); }; - Some(self.load_mem(val, 0, base)) + + todo!() } - Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => { - let val = self.expr_ctx(val, ctx)?; - if !self.tof(val).is_integer() { - self.report( - pos, - format_args!("cant negate '{}'", self.ty_display(self.tof(val))), - ); - } - Some(self.ci.nodes.new_node(self.tof(val), Kind::UnOp { op }, [VOID, val])) - } - Expr::If { cond, then, else_, .. } => { - let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?; + Expr::Block { stmts, .. } => { + let base = self.ci.vars.len(); - let if_node = self.ci.nodes.new_node(ty::VOID, Kind::If, [self.ci.ctrl, cond]); - - 'b: { - let branch = match self.tof(if_node).expand().inner() { - ty::LEFT_UNREACHABLE => else_, - ty::RIGHT_UNREACHABLE => Some(then), - _ => break 'b, - }; - - self.ci.nodes.lock(self.ci.ctrl); - self.ci.nodes.remove(if_node); - self.ci.nodes.unlock(self.ci.ctrl); - - if let Some(branch) = branch { - return self.expr(branch); + let mut ret = Some(VOID); + for stmt in stmts { + ret = ret.and(self.expr(stmt)); + if let Some(id) = ret { + _ = self.assert_ty( + stmt.pos(), + self.tof(id), + ty::VOID.into(), + true, + "statement", + ); } else { - return Some(VOID); + break; } } - let mut else_scope = self.ci.vars.clone(); - for &el in &self.ci.vars { - self.ci.nodes.lock(el.value); + self.ci.nodes.lock(self.ci.ctrl); + for var in self.ci.vars.drain(base..) { + self.ci.nodes.unlock_remove(var.value); } + self.ci.nodes.unlock(self.ci.ctrl); - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Then, [if_node]); - let lcntrl = self.expr(then).map_or(Nid::MAX, |_| self.ci.ctrl); - - let mut then_scope = std::mem::replace(&mut self.ci.vars, else_scope); - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Else, [if_node]); - let rcntrl = if let Some(else_) = else_ { - self.expr(else_).map_or(Nid::MAX, |_| self.ci.ctrl) - } else { - self.ci.ctrl - }; - - if lcntrl == Nid::MAX && rcntrl == Nid::MAX { - for then_var in then_scope { - self.ci.nodes.unlock_remove(then_var.value); - } - return None; - } else if lcntrl == Nid::MAX { - for then_var in then_scope { - self.ci.nodes.unlock_remove(then_var.value); - } - return Some(VOID); - } else if rcntrl == Nid::MAX { - for else_var in &self.ci.vars { - self.ci.nodes.unlock_remove(else_var.value); - } - self.ci.vars = then_scope; - self.ci.ctrl = lcntrl; - return Some(VOID); - } - - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Region, [lcntrl, rcntrl]); - - else_scope = std::mem::take(&mut self.ci.vars); - - Self::merge_scopes( - &mut self.ci.nodes, - &mut self.ci.loops, - self.ci.ctrl, - &mut else_scope, - &mut then_scope, - true, - ); - - self.ci.vars = else_scope; - - Some(VOID) + ret } Expr::Loop { body, .. } => { self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Loop, [self.ci.ctrl; 2]); @@ -1693,127 +1435,81 @@ impl Codegen { } Expr::Break { pos } => self.jump_to(pos, 1), Expr::Continue { pos } => self.jump_to(pos, 0), - Expr::Call { func: &Expr::Ident { pos, id, name, .. }, args, .. } => { - self.ci.call_count += 1; - let func = self.find_or_declare(pos, self.ci.file, Some(id), name); - let ty::Kind::Func(func) = func else { - self.report( - pos, - format_args!( - "compiler cant (yet) call '{}'", - self.ty_display(func.compress()) - ), - ); - return Some(NEVER); + Expr::If { cond, then, else_, .. } => { + let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?; + + let if_node = self.ci.nodes.new_node(ty::VOID, Kind::If, [self.ci.ctrl, cond]); + + 'b: { + let branch = match self.tof(if_node).expand().inner() { + ty::LEFT_UNREACHABLE => else_, + ty::RIGHT_UNREACHABLE => Some(then), + _ => break 'b, + }; + + self.ci.nodes.lock(self.ci.ctrl); + self.ci.nodes.remove(if_node); + self.ci.nodes.unlock(self.ci.ctrl); + + if let Some(branch) = branch { + return self.expr(branch); + } else { + return Some(VOID); + } + } + + let mut else_scope = self.ci.vars.clone(); + for &el in &self.ci.vars { + self.ci.nodes.lock(el.value); + } + + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Then, [if_node]); + let lcntrl = self.expr(then).map_or(Nid::MAX, |_| self.ci.ctrl); + + let mut then_scope = std::mem::replace(&mut self.ci.vars, else_scope); + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Else, [if_node]); + let rcntrl = if let Some(else_) = else_ { + self.expr(else_).map_or(Nid::MAX, |_| self.ci.ctrl) + } else { + self.ci.ctrl }; - self.make_func_reachable(func); + if lcntrl == Nid::MAX && rcntrl == Nid::MAX { + for then_var in then_scope { + self.ci.nodes.unlock_remove(then_var.value); + } + return None; + } else if lcntrl == Nid::MAX { + for then_var in then_scope { + self.ci.nodes.unlock_remove(then_var.value); + } + return Some(VOID); + } else if rcntrl == Nid::MAX { + for else_var in &self.ci.vars { + self.ci.nodes.unlock_remove(else_var.value); + } + self.ci.vars = then_scope; + self.ci.ctrl = lcntrl; + return Some(VOID); + } - let fuc = &self.tys.funcs[func as usize]; - let sig = fuc.sig.expect("TODO: generic functions"); - let ast = self.files[fuc.file as usize].clone(); - let Expr::BinOp { right: &Expr::Closure { args: cargs, .. }, .. } = - fuc.expr.get(&ast).unwrap() - else { - unreachable!() - }; + self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Region, [lcntrl, rcntrl]); - self.assert_report( - args.len() == cargs.len(), - pos, - format_args!( - "expected {} function argumenr{}, got {}", - cargs.len(), - if cargs.len() == 1 { "" } else { "s" }, - args.len() - ), + else_scope = std::mem::take(&mut self.ci.vars); + + Self::merge_scopes( + &mut self.ci.nodes, + &mut self.ci.loops, + self.ci.ctrl, + &mut else_scope, + &mut then_scope, + true, ); - let mut inps = Vc::from([self.ci.ctrl]); - for ((arg, carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) { - let ty = self.tys.args[tyx]; - if self.tys.size_of(ty) == 0 { - continue; - } - let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; - _ = self.assert_ty( - arg.pos(), - self.tof(value), - ty, - true, - format_args!("argument {}", carg.name), - ); - if ty.is_pointer() { - value = self - .ci - .memories - .binary_search_by_key(&(value, 0), |k| (k.region, k.offset)) - .map_or(value, |i| self.ci.memories[i].node); - // mark the read as clobbed since function can store - self.ci.nodes[value].offset = u32::MAX; - } - inps.push(value); - } - self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func }, inps); + self.ci.vars = else_scope; - Some(self.ci.ctrl) + Some(VOID) } - Expr::Return { pos, val } => { - let value = if let Some(val) = val { - self.expr_ctx(val, Ctx { ty: self.ci.ret })? - } else { - VOID - }; - - let mut inps = Vc::from([self.ci.ctrl, value]); - for m in self.ci.memories.iter() { - inps.push(m.node); - } - - let out = &mut String::new(); - self.report_log_to(pos, "returning here", out); - self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps); - - self.ci.nodes[NEVER].inputs.push(self.ci.ctrl); - self.ci.nodes[self.ci.ctrl].outputs.push(NEVER); - - let expected = *self.ci.ret.get_or_insert(self.tof(value)); - _ = self.assert_ty(pos, self.tof(value), expected, true, "return value"); - - None - } - Expr::Block { stmts, .. } => { - let base = self.ci.vars.len(); - - let mut ret = Some(VOID); - for stmt in stmts { - ret = ret.and(self.expr(stmt)); - if let Some(id) = ret { - _ = self.assert_ty( - stmt.pos(), - self.tof(id), - ty::VOID.into(), - true, - "statement", - ); - } else { - break; - } - } - - self.ci.nodes.lock(self.ci.ctrl); - for var in self.ci.vars.drain(base..) { - self.ci.nodes.unlock_remove(var.value); - } - self.ci.nodes.unlock(self.ci.ctrl); - - ret - } - Expr::Number { value, .. } => Some(self.ci.nodes.new_node( - ctx.ty.filter(|ty| ty.is_integer() || ty.is_pointer()).unwrap_or(ty::INT.into()), - Kind::CInt { value }, - [VOID], - )), ref e => { self.report_unhandled_ast(e, "bruh"); Some(NEVER) @@ -2218,19 +1914,13 @@ impl Codegen { saved_regs.len() } - // TODO: sometimes its better to do this in bulk fn ty(&mut self, expr: &Expr) -> ty::Id { - match *expr { - Expr::UnOp { op: TokenKind::Xor, val, .. } => { - let base = self.ty(val); - self.tys.make_ptr(base) - } - Expr::Ident { id, .. } if ident::is_null(id) => id.into(), - ref e => { - self.report_unhandled_ast(e, "type"); - ty::NEVER.into() - } + if let Some(ty) = self.tys.ty(self.ci.file, expr, &self.files) { + return ty; } + + self.report_unhandled_ast(expr, "type"); + ty::NEVER.into() } fn find_or_declare( @@ -2247,17 +1937,17 @@ impl Codegen { match name.ok_or(lit_name) { Ok(name) => { let name = self.cfile().ident_str(name); - self.report(pos, format_args!("idk indentifier: {name}")) + self.report(pos, fa!("idk indentifier: {name}")) } Err("main") => self.report( pos, - format_args!( + fa!( "missing main function in '{}', compiler can't \ emmit libraries since such concept is not defined", f.path ), ), - Err(name) => self.report(pos, format_args!("idk indentifier: {name}")), + Err(name) => self.report(pos, fa!("idk indentifier: {name}")), } return ty::Kind::Builtin(ty::NEVER); }; @@ -2278,7 +1968,7 @@ impl Codegen { let prev_file = std::mem::replace(&mut self.ci.file, file); let sym = match expr { Expr::BinOp { - left: &Expr::Ident { .. }, + left: Expr::Ident { .. }, op: TokenKind::Decl, right: &Expr::Closure { pos, args, ret, .. }, } => { @@ -2317,13 +2007,10 @@ impl Codegen { ty::Kind::Func(id) } Expr::BinOp { - left: &Expr::Ident { .. }, + left: Expr::Ident { .. }, op: TokenKind::Decl, - right: Expr::Struct { fields, .. }, - } => ty::Kind::Struct(self.build_struct(None, fields)), - Expr::BinOp { .. } => { - todo!() - } + right: right @ Expr::Struct { .. }, + } => self.ty(right).expand(), e => unimplemented!("{e:#?}"), }; self.ci.file = prev_file; @@ -2352,7 +2039,7 @@ impl Codegen { } else { let ty = self.ty_display(ty); let expected = self.ty_display(expected); - self.report(pos, format_args!("expected {hint} to be of type {expected}, got {ty}")); + self.report(pos, fa!("expected {hint} to be of type {expected}, got {ty}")); ty::NEVER.into() } } @@ -2382,12 +2069,10 @@ impl Codegen { fn report_unhandled_ast(&self, ast: &Expr, hint: &str) { self.report( ast.pos(), - format_args!( - "compiler does not (yet) know how to handle ({hint}):\n\ + fa!("compiler does not (yet) know how to handle ({hint}):\n\ {ast:}\n\ info for weak people:\n\ - {ast:#?}" - ), + {ast:#?}"), ); } @@ -3117,7 +2802,7 @@ mod tests { loops; fb_driver; pointers; - //structs; + structs; //different_types; //struct_operators; //directives; diff --git a/hblang/src/vc.rs b/hblang/src/vc.rs new file mode 100644 index 0000000..13c0ad4 --- /dev/null +++ b/hblang/src/vc.rs @@ -0,0 +1,286 @@ +use std::{ + fmt::Debug, + mem::MaybeUninit, + ops::{Deref, DerefMut, Not}, + ptr::Unique, +}; + +type Nid = u16; + +const VC_SIZE: usize = 16; +const INLINE_ELEMS: usize = VC_SIZE / 2 - 1; + +pub union Vc { + inline: InlineVc, + alloced: AllocedVc, +} + +impl Default for Vc { + fn default() -> Self { + Vc { inline: InlineVc { elems: MaybeUninit::uninit(), cap: Default::default() } } + } +} + +impl Debug for Vc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_slice().fmt(f) + } +} + +impl Vc { + fn is_inline(&self) -> bool { + unsafe { self.inline.cap <= INLINE_ELEMS as Nid } + } + + fn layout(&self) -> Option { + unsafe { + self.is_inline() + .not() + .then(|| std::alloc::Layout::array::(self.alloced.cap as _).unwrap_unchecked()) + } + } + + fn len(&self) -> usize { + unsafe { + if self.is_inline() { + self.inline.cap as _ + } else { + self.alloced.len as _ + } + } + } + + fn len_mut(&mut self) -> &mut Nid { + unsafe { + if self.is_inline() { + &mut self.inline.cap + } else { + &mut self.alloced.len + } + } + } + + fn as_ptr(&self) -> *const Nid { + unsafe { + match self.is_inline() { + true => self.inline.elems.as_ptr().cast(), + false => self.alloced.base.as_ptr(), + } + } + } + + fn as_mut_ptr(&mut self) -> *mut Nid { + unsafe { + match self.is_inline() { + true => self.inline.elems.as_mut_ptr().cast(), + false => self.alloced.base.as_ptr(), + } + } + } + + pub fn as_slice(&self) -> &[Nid] { + unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) } + } + + fn as_slice_mut(&mut self) -> &mut [Nid] { + unsafe { std::slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()) } + } + + pub fn push(&mut self, value: Nid) { + if let Some(layout) = self.layout() + && unsafe { self.alloced.len == self.alloced.cap } + { + unsafe { + self.alloced.cap *= 2; + self.alloced.base = Unique::new_unchecked( + std::alloc::realloc( + self.alloced.base.as_ptr().cast(), + layout, + self.alloced.cap as usize * std::mem::size_of::(), + ) + .cast(), + ); + } + } else if self.len() == INLINE_ELEMS { + unsafe { + let mut allcd = + Self::alloc((self.inline.cap + 1).next_power_of_two() as _, self.len()); + std::ptr::copy_nonoverlapping(self.as_ptr(), allcd.as_mut_ptr(), self.len()); + *self = allcd; + } + } + + unsafe { + *self.len_mut() += 1; + self.as_mut_ptr().add(self.len() - 1).write(value); + } + } + + unsafe fn alloc(cap: usize, len: usize) -> Self { + debug_assert!(cap > INLINE_ELEMS); + let layout = unsafe { std::alloc::Layout::array::(cap).unwrap_unchecked() }; + let alloc = unsafe { std::alloc::alloc(layout) }; + unsafe { + Vc { + alloced: AllocedVc { + base: Unique::new_unchecked(alloc.cast()), + len: len as _, + cap: cap as _, + }, + } + } + } + + pub fn swap_remove(&mut self, index: usize) { + let len = self.len() - 1; + self.as_slice_mut().swap(index, len); + *self.len_mut() -= 1; + } + + pub fn remove(&mut self, index: usize) { + self.as_slice_mut().copy_within(index + 1.., index); + *self.len_mut() -= 1; + } +} + +impl Drop for Vc { + fn drop(&mut self) { + if let Some(layout) = self.layout() { + unsafe { + std::alloc::dealloc(self.alloced.base.as_ptr().cast(), layout); + } + } + } +} + +impl Clone for Vc { + fn clone(&self) -> Self { + self.as_slice().into() + } +} + +impl IntoIterator for Vc { + type IntoIter = VcIntoIter; + type Item = Nid; + + fn into_iter(self) -> Self::IntoIter { + VcIntoIter { start: 0, end: self.len(), vc: self } + } +} + +pub struct VcIntoIter { + start: usize, + end: usize, + vc: Vc, +} + +impl Iterator for VcIntoIter { + type Item = Nid; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + + let ret = unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.start)) }; + self.start += 1; + Some(ret) + } + + fn size_hint(&self) -> (usize, Option) { + let len = self.end - self.start; + (len, Some(len)) + } +} + +impl DoubleEndedIterator for VcIntoIter { + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None; + } + + self.end -= 1; + Some(unsafe { std::ptr::read(self.vc.as_slice().get_unchecked(self.end)) }) + } +} + +impl ExactSizeIterator for VcIntoIter {} + +impl From<[Nid; SIZE]> for Vc { + fn from(value: [Nid; SIZE]) -> Self { + value.as_slice().into() + } +} + +impl<'a> From<&'a [Nid]> for Vc { + fn from(value: &'a [Nid]) -> Self { + if value.len() <= INLINE_ELEMS { + let mut dflt = Self::default(); + unsafe { + std::ptr::copy_nonoverlapping(value.as_ptr(), dflt.as_mut_ptr(), value.len()) + }; + dflt.inline.cap = value.len() as _; + dflt + } else { + let mut allcd = unsafe { Self::alloc(value.len(), value.len()) }; + unsafe { + std::ptr::copy_nonoverlapping(value.as_ptr(), allcd.as_mut_ptr(), value.len()) + }; + allcd + } + } +} + +impl Deref for Vc { + type Target = [Nid]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl DerefMut for Vc { + fn deref_mut(&mut self) -> &mut Self::Target { + self.as_slice_mut() + } +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct InlineVc { + cap: Nid, + elems: MaybeUninit<[Nid; INLINE_ELEMS]>, +} + +#[derive(Clone, Copy)] +#[repr(C)] +struct AllocedVc { + cap: Nid, + len: Nid, + base: Unique, +} + +#[derive(Default)] +pub struct BitSet { + data: Vec, +} + +impl BitSet { + const ELEM_SIZE: usize = std::mem::size_of::() * 8; + + pub fn clear(&mut self, bit_size: usize) { + let new_len = (bit_size + Self::ELEM_SIZE - 1) / Self::ELEM_SIZE; + self.data.clear(); + self.data.resize(new_len, 0); + } + + #[track_caller] + pub fn set(&mut self, idx: Nid) -> bool { + let idx = idx as usize; + let data_idx = idx / Self::ELEM_SIZE; + let sub_idx = idx % Self::ELEM_SIZE; + let prev = self.data[data_idx] & (1 << sub_idx); + self.data[data_idx] |= 1 << sub_idx; + prev == 0 + } +} diff --git a/hblang/tests/codegen_tests_pointers.txt b/hblang/tests/codegen_tests_pointers.txt index 643ec20..d573cef 100644 --- a/hblang/tests/codegen_tests_pointers.txt +++ b/hblang/tests/codegen_tests_pointers.txt @@ -6,8 +6,8 @@ drop: ADDI64 r254, r254, 16d JALA r0, r31, 0a main: - ADDI64 r254, r254, -48d - ST r31, r254, 8a, 40h + ADDI64 r254, r254, -40d + ST r31, r254, 8a, 32h LI64 r32, 1d ST r32, r254, 0a, 8h ADDI64 r32, r254, 0d @@ -15,12 +15,11 @@ main: JAL r31, r0, :modify LD r2, r254, 0a, 8h JAL r31, r0, :drop - LI64 r33, 0d - CP r34, r32 - LD r35, r34, 0a, 8h - ADDI64 r1, r35, -2d - LD r31, r254, 8a, 40h - ADDI64 r254, r254, 48d + CP r33, r32 + LD r34, r33, 0a, 8h + ADDI64 r1, r34, -2d + LD r31, r254, 8a, 32h + ADDI64 r254, r254, 40d JALA r0, r31, 0a modify: ADDI64 r254, r254, -32d @@ -32,6 +31,6 @@ modify: LD r31, r254, 0a, 32h ADDI64 r254, r254, 32d JALA r0, r31, 0a -code size: 318 +code size: 308 ret: 0 status: Ok(()) diff --git a/hblang/tests/codegen_tests_structs.txt b/hblang/tests/codegen_tests_structs.txt index 7bc4e83..9c8eec0 100644 --- a/hblang/tests/codegen_tests_structs.txt +++ b/hblang/tests/codegen_tests_structs.txt @@ -1,23 +1,28 @@ main: ADDI64 r254, r254, -72d ST r31, r254, 48a, 24h - LI64 r32, 4d - ST r32, r254, 0a, 8h - LI64 r32, 1d - ST r32, r254, 8a, 8h LI64 r32, 3d - ST r32, r254, 16a, 8h + LI64 r33, 3d + JEQ r32, r33, :0 + LI64 r1, 9001d + JMP :1 + 0: LI64 r33, 4d + ST r33, r254, 0a, 8h + LI64 r33, 1d + ST r33, r254, 8a, 8h + LI64 r33, 3d + ST r33, r254, 16a, 8h ADDI64 r2, r254, 0d ADDI64 r1, r254, 24d JAL r31, r0, :odher_pass - LD r32, r254, 40a, 8h - LI64 r33, 3d - JNE r32, r33, :0 - ADDI64 r33, r254, 24d - CP r2, r33 + LD r33, r254, 40a, 8h + LI64 r32, 3d + JNE r33, r32, :2 + ADDI64 r32, r254, 24d + CP r2, r32 JAL r31, r0, :pass JMP :1 - 0: LI64 r1, 0d + 2: LI64 r1, 0d 1: LD r31, r254, 48a, 24h ADDI64 r254, r254, 72d JALA r0, r31, 0a @@ -43,6 +48,6 @@ pass: LD r31, r254, 0a, 40h ADDI64 r254, r254, 40d JALA r0, r31, 0a -code size: 400 +code size: 440 ret: 3 status: Ok(()) diff --git a/hblang/tests/son_tests_structs.txt b/hblang/tests/son_tests_structs.txt new file mode 100644 index 0000000..e69de29 diff --git a/hbvm/src/mem/mod.rs b/hbvm/src/mem/mod.rs index 0767614..535a30b 100644 --- a/hbvm/src/mem/mod.rs +++ b/hbvm/src/mem/mod.rs @@ -35,7 +35,7 @@ pub trait Memory { /// /// # Safety /// - Data read have to be valid - unsafe fn prog_read(&mut self, addr: Address) -> T; + unsafe fn prog_read(&mut self, addr: Address) -> T; } /// Unhandled load access trap diff --git a/hbvm/src/vmrun.rs b/hbvm/src/vmrun.rs index 8d302d4..711aa1e 100644 --- a/hbvm/src/vmrun.rs +++ b/hbvm/src/vmrun.rs @@ -382,7 +382,7 @@ where /// Decode instruction operands #[inline(always)] - unsafe fn decode(&mut self) -> T { + unsafe fn decode(&mut self) -> T { unsafe { self.memory.prog_read::(self.pc + 1_u64) } } @@ -446,7 +446,7 @@ where /// Perform binary operation over register and immediate #[inline(always)] - unsafe fn binary_op_imm(&mut self, op: impl Fn(T, T) -> T) { + unsafe fn binary_op_imm(&mut self, op: impl Fn(T, T) -> T) { #[derive(Clone, Copy)] #[repr(packed)] struct OpsRRImm(OpsRR, I);