From aeb3a37f7dc9eab8322dd00f71d22f89b93b84c7 Mon Sep 17 00:00:00 2001 From: mlokr Date: Fri, 19 Jul 2024 12:00:55 +0200 Subject: [PATCH] fixing typechecking issues, pointers now properly typecheck and generic types are properly cached --- hblang/README.md | 13 ++ hblang/src/codegen.rs | 387 +++++++++++++++++++++++++++++++----------- hblang/src/lexer.rs | 3 + hblang/src/parser.rs | 14 ++ 4 files changed, 315 insertions(+), 102 deletions(-) diff --git a/hblang/README.md b/hblang/README.md index 7aca49d..721c1bf 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -326,6 +326,19 @@ pass := fn(arr: ^[int; 3]): int { ### Incomplete Examples +#### comptime_pointers +```hb +main := fn(): int { + $integer := 7; + modify(&integer); + return integer; +} + +modify := fn($num: ^int): void { + $: *num = 0; +} +``` + #### generic_types ```hb MALLOC_SYS_CALL := 69 diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 383a3a2..64e4fad 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -8,6 +8,7 @@ use { parser::{self, find_symbol, idfl, CtorField, Expr, ExprRef, FileId, Pos}, HashMap, }, + core::panic, std::{ops::Range, rc::Rc}, }; @@ -15,6 +16,20 @@ type Offset = u32; type Size = u32; type ArrayLen = u32; +fn load_value(ptr: *const u8, size: u32) -> u64 { + let mut dst = [0u8; 8]; + dst[..size as usize].copy_from_slice(unsafe { std::slice::from_raw_parts(ptr, size as usize) }); + u64::from_ne_bytes(dst) +} + +fn ensure_loaded(value: CtValue, derefed: bool, size: u32) -> u64 { + if derefed { + load_value(value.0 as *const u8, size) + } else { + value.0 + } +} + mod stack { use { super::{Offset, Size}, @@ -293,6 +308,7 @@ pub mod ty { let (a, b) = (oa.strip_pointer(), ob.strip_pointer()); Some(match () { _ if oa == ob => oa, + _ if oa.is_pointer() && ob.is_pointer() => return None, _ if a.is_signed() && b.is_signed() || a.is_unsigned() && b.is_unsigned() => ob, _ if a.is_unsigned() && b.is_signed() && a.repr() - U8 < b.repr() - I8 => ob, _ if oa.is_integer() && ob.is_pointer() => ob, @@ -309,9 +325,9 @@ pub mod ty { } } - impl From<[u8; 8]> for Id { - fn from(id: [u8; 8]) -> Self { - Self(unsafe { NonZeroU32::new_unchecked(u64::from_ne_bytes(id) as _) }) + impl From for Id { + fn from(id: u64) -> Self { + Self(unsafe { NonZeroU32::new_unchecked(id as _) }) } } @@ -566,15 +582,15 @@ impl Value { } fn void() -> Self { - Self { ty: ty::VOID.into(), loc: Loc::imm(0) } + Self { ty: ty::VOID.into(), loc: Loc::ct(0) } } fn imm(value: u64) -> Self { - Self { ty: ty::UINT.into(), loc: Loc::imm(value) } + Self { ty: ty::UINT.into(), loc: Loc::ct(value) } } fn ty(ty: ty::Id) -> Self { - Self { ty: ty::TYPE.into(), loc: Loc::ct((ty.repr() as u64).to_ne_bytes()) } + Self { ty: ty::TYPE.into(), loc: Loc::ct(ty.repr() as u64) } } } @@ -604,10 +620,14 @@ impl<'a> From for LocCow<'a> { } } +#[repr(packed)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +struct CtValue(u64); + #[derive(Debug, PartialEq, Eq)] -pub enum Loc { +enum Loc { Rt { derefed: bool, reg: reg::Id, stack: Option, offset: Offset }, - Ct { value: [u8; 8] }, + Ct { derefed: bool, value: CtValue }, } impl Loc { @@ -621,21 +641,22 @@ impl Loc { Self::Rt { derefed: false, reg, stack: None, offset: 0 } } - fn imm(value: u64) -> Self { - Self::ct(value.to_ne_bytes()) + fn ct(value: u64) -> Self { + Self::Ct { value: CtValue(value), derefed: false } } - fn ct(value: [u8; 8]) -> Self { - Self::Ct { value } + fn ct_ptr(value: u64) -> Self { + Self::Ct { value: CtValue(value), derefed: true } } fn ty(ty: ty::Id) -> Self { - Self::imm(ty.repr() as _) + Self::ct(ty.repr() as _) } fn offset(mut self, offset: u32) -> Self { match &mut self { Self::Rt { offset: off, .. } => *off += offset, + Self::Ct { derefed: false, value } => value.0 += offset as u64, _ => unreachable!("offseting constant"), } self @@ -649,7 +670,7 @@ impl Loc { stack: stack.as_ref().map(stack::Id::as_ref), offset, }, - Loc::Ct { value } => Self::Ct { value }, + Loc::Ct { value, derefed } => Self::Ct { derefed, value }, } } @@ -679,7 +700,10 @@ impl Loc { fn to_ty(&self) -> Option { match *self { - Self::Ct { value } => Some(ty::Id::from(value)), + Self::Ct { derefed: false, value } => Some(ty::Id::from(value.0)), + Self::Ct { derefed: true, value } => { + Some(unsafe { std::ptr::read(value.0 as *const u8 as _) }) + } Self::Rt { .. } => None, } } @@ -693,7 +717,7 @@ impl From for Loc { impl Default for Loc { fn default() -> Self { - Self::Ct { value: [0; 8] } + Self::ct(0) } } @@ -712,7 +736,7 @@ struct Variable { struct ItemCtx { file: FileId, id: ty::Kind, - ret: ty::Id, + ret: Option, ret_reg: reg::Id, task_base: usize, @@ -737,7 +761,7 @@ impl ItemCtx { stack: stack.as_ref().map(|s| self.stack.dup_id(s)), offset, }, - Loc::Ct { value } => Loc::Ct { value }, + ref loc => loc.as_ref(), } } @@ -1114,10 +1138,9 @@ impl Output { self.strings.truncate(snap.strings); } - fn write_trap(&mut self, trap: Trap) { - let len = self.code.len(); - self.code.resize(len + std::mem::size_of::(), 0); - unsafe { std::ptr::write_unaligned(self.code.as_mut_ptr().add(len) as _, trap) } + fn write_trap(&mut self, kind: trap::Trap) { + self.emit(eca()); + self.code.extend(kind.as_slice()); } fn snap(&mut self) -> Snapshot { @@ -1245,8 +1268,61 @@ impl Default for Comptime { } } -enum Trap { - MakeStruct { file: FileId, struct_expr: ExprRef }, +mod trap { + use { + super::ty, + crate::parser::{ExprRef, FileId}, + }; + + macro_rules! gen_trap { + ( + #[derive(Trap)] + $vis:vis enum $name:ident { + $($variant:ident { + $($fname:ident: $fty:ty,)* + },)* + } + ) => { + #[repr(u8)] + $vis enum $name { + $($variant($variant),)* + } + + impl $name { + $vis fn size(&self) -> usize { + 1 + match self { + $(Self::$variant(_) => std::mem::size_of::<$variant>(),)* + } + } + } + + $( + #[repr(packed)] + $vis struct $variant { + $($vis $fname: $fty,)* + } + )* + }; + } + + gen_trap! { + #[derive(Trap)] + pub enum Trap { + MakeStruct { + file: FileId, + struct_expr: ExprRef, + }, + MomizedCall { + func: ty::Func, + }, + } + } + + impl Trap { + pub fn as_slice(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self as *const _ as _, self.size()) } + } + } } struct StringReloc { @@ -1324,7 +1400,10 @@ impl Codegen { self.stack_offset(2, STACK_PTR, Some(&stack), 0); let val = self.eca( - Trap::MakeStruct { file: self.ci.file, struct_expr: ExprRef::new(expr) }, + trap::Trap::MakeStruct(trap::MakeStruct { + file: self.ci.file, + struct_expr: ExprRef::new(expr), + }), ty::TYPE, ); self.ci.free_loc(Loc::stack(stack)); @@ -1487,7 +1566,7 @@ impl Codegen { return self.expr_ctx(val, ctx); } E::Bool { value, .. } => { - Some(Value { ty: ty::BOOL.into(), loc: Loc::imm(value as u64) }) + Some(Value { ty: ty::BOOL.into(), loc: Loc::ct(value as u64) }) } E::String { pos, mut literal } => { literal = literal.trim_matches('"'); @@ -1634,6 +1713,7 @@ impl Codegen { ty::Kind::Builtin(ty::TYPE) => { self.ci.free_loc(tal.loc); self.pop_local_snap(checkpoint); + self.report_log(target.pos(), format_args!("field: {expr}")); match ty::Kind::from_ty(self.ty(target)) { ty::Kind::Module(idx) => { match self.find_or_declare(target.pos(), idx, Err(field), "") { @@ -1693,27 +1773,34 @@ impl Codegen { ), } } + E::BinOp { left, op: T::Decl, right } if self.has_ct(left) => { + let slot_base = self.ct.vm.read_reg(reg::STACK_PTR).0; + let (cnt, ty) = self.eval_const_low(right, None); + if self.assign_ct_pattern(left, ty, cnt as _) { + self.ct.vm.write_reg(reg::STACK_PTR, slot_base); + } + Some(Value::void()) + } E::BinOp { left, op: T::Decl, right } => { let value = self.expr(right)?; - self.assign_pattern(left, value) } E::Call { func: fast, args, .. } => { log::dbg!("call {fast}"); let func_ty = self.ty(fast); - let ty::Kind::Func(mut func_id) = func_ty.expand() else { + let ty::Kind::Func(mut func) = func_ty.expand() else { self.report(fast.pos(), "can't call this, maybe in the future"); }; - let func = self.tys.funcs[func_id as usize]; - let ast = self.files[func.file as usize].clone(); + let fuc = self.tys.funcs[func as usize]; + let ast = self.files[fuc.file as usize].clone(); let E::BinOp { right: &E::Closure { args: cargs, ret, .. }, .. } = - func.expr.get(&ast).unwrap() + fuc.expr.get(&ast).unwrap() else { unreachable!(); }; - let sig = if let Some(sig) = func.sig { + let sig = if let Some(sig) = fuc.sig { sig } else { let scope = self.ci.vars.len(); @@ -1745,14 +1832,17 @@ impl Codegen { let ret = self.ty(ret); self.ci.vars.truncate(scope); - let sym = SymKey { file: !args.repr(), ident: func_id }; + let sym = SymKey { + file: !args.repr(), + ident: ty::Kind::Func(func).compress().repr(), + }; let ct = || { let func_id = self.tys.funcs.len(); self.tys.funcs.push(Func { - file: func.file, + file: fuc.file, offset: task::id(func_id), sig: Some(Sig { args, ret }), - expr: func.expr, + expr: fuc.expr, }); self.tasks.push(Some(FTask { @@ -1763,7 +1853,7 @@ impl Codegen { ty::Kind::Func(func_id as _).compress() }; - func_id = self.tys.syms.entry(sym).or_insert_with(ct).expand().inner(); + func = self.tys.syms.entry(sym).or_insert_with(ct).expand().inner(); Sig { args, ret } }; @@ -1771,6 +1861,7 @@ impl Codegen { let mut parama = self.tys.parama(sig.ret); let mut values = Vec::with_capacity(args.len()); let mut sig_args = sig.args.range(); + let mut should_momize = !args.is_empty() && sig.ret == ty::Id::from(ty::TYPE); for (arg, carg) in args.iter().zip(cargs) { let ty = self.tys.args[sig_args.next().unwrap()]; let sym = parser::find_symbol(&ast.symbols, carg.id); @@ -1783,6 +1874,7 @@ impl Codegen { let varg = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; self.pass_arg(&varg, &mut parama); values.push(varg.loc); + should_momize = false; } for value in values { @@ -1793,10 +1885,19 @@ impl Codegen { let loc = self.alloc_ret(sig.ret, ctx); + if should_momize { + self.report_log(expr.pos(), format_args!("momized call: {expr}")); + self.output.write_trap(trap::Trap::MomizedCall(trap::MomizedCall { func })); + } + let reloc = Reloc::new(self.local_offset(), 3, 4); - self.output.funcs.push((func_id, reloc)); + self.output.funcs.push((func, reloc)); self.output.emit(jal(RET_ADDR, ZERO, 0)); + if should_momize { + self.output.emit(tx()); + } + self.load_ret(sig.ret, &loc); return Some(Value { ty: sig.ret, loc }); } @@ -1826,15 +1927,19 @@ impl Codegen { ty::Kind::Global(id) => self.handle_global(id), tk => Some(Value::ty(tk.compress())), }, - E::Return { val, .. } => { + E::Return { pos, val, .. } => { if let Some(val) = val { - let size = self.tys.size_of(self.ci.ret); + let size = self.ci.ret.map_or(17, |ty| self.tys.size_of(ty)); let loc = match size { 0 => Loc::default(), 1..=16 => Loc::reg(1), _ => Loc::reg(self.ci.ret_reg.as_ref()).into_derefed(), }; - self.expr_ctx(val, Ctx::default().with_ty(self.ci.ret).with_loc(loc))?; + let ty = self.expr_ctx(val, Ctx { ty: self.ci.ret, loc: Some(loc) })?.ty; + match self.ci.ret { + None => self.ci.ret = Some(ty), + Some(ret) => _ = self.assert_ty(pos, ty, ret), + } } self.ci.ret_relocs.push(Reloc::new(self.local_offset(), 1, 4)); self.output.emit(jmp(0)); @@ -1848,7 +1953,7 @@ impl Codegen { } E::Number { value, .. } => Some(Value { ty: ctx.ty.map(ty::Id::strip_pointer).unwrap_or(ty::INT.into()), - loc: Loc::imm(value), + loc: Loc::ct(value), }), E::If { cond, then, else_, .. } => { log::dbg!("if-cond"); @@ -1978,10 +2083,16 @@ impl Codegen { let size = self.tys.size_of(ty); let signed = ty.is_signed(); - if let Loc::Ct { value } = right.loc + if let Loc::Ct { value: CtValue(mut imm), derefed } = right.loc && let Some(oper) = Self::imm_math_op(op, signed, size) { - let mut imm = u64::from_ne_bytes(value); + if derefed { + let mut dst = [0u8; 8]; + dst[..size as usize].copy_from_slice(unsafe { + std::slice::from_raw_parts(imm as _, rsize as usize) + }); + imm = u64::from_ne_bytes(dst); + } if matches!(op, T::Add | T::Sub) && let ty::Kind::Ptr(ty) = ty::Kind::from_ty(ty) { @@ -2045,15 +2156,7 @@ impl Codegen { unimplemented!("{:#?}", op) } E::Comment { .. } => Some(Value::void()), - ast => self.report( - ast.pos(), - format_args!( - "compiler does not (yet) konw how to handle:\n\ - {ast:}\n\ - info for weak people:\n\ - {ast:#?}" - ), - ), + ref ast => self.report_unhandled_ast(ast, "expression"), }?; if let Some(ty) = ctx.ty { @@ -2064,17 +2167,25 @@ impl Codegen { Some(dest) => { let ty = ctx.ty.unwrap_or(value.ty); self.store_typed(value.loc, dest, ty); - Value { ty, loc: Loc::imm(0) } + Value { ty, loc: Loc::ct(0) } } None => value, }) } + fn has_ct(&self, expr: &Expr) -> bool { + expr.has_ct(&self.cfile().symbols) + } + fn eval_const(&mut self, expr: &Expr, ty: impl Into) -> u64 { + self.eval_const_low(expr, Some(ty.into())).0 + } + + fn eval_const_low(&mut self, expr: &Expr, mut ty: Option) -> (u64, ty::Id) { let mut ci = ItemCtx { file: self.ci.file, id: self.ci.id, - ret: ty.into(), + ret: ty, ..self.pool.cis.pop().unwrap_or_default() }; ci.vars.append(&mut self.ci.vars); @@ -2082,15 +2193,19 @@ impl Codegen { let loc = self.ct_eval(ci, |s, prev| { s.output.emit_prelude(); - if s.expr_ctx( - &Expr::Return { pos: 0, val: Some(expr) }, - Ctx::default().with_ty(s.ci.ret), - ) - .is_some() - { + if s.ci.ret.map_or(true, |r| s.tys.size_of(r) > 16) { + let reg = s.ci.regs.allocate(); + s.output.emit(instrs::cp(reg.get(), 1)); + s.ci.ret_reg = reg; + }; + + let ctx = Ctx { loc: None, ty: s.ci.ret }; + if s.expr_ctx(&Expr::Return { pos: 0, val: Some(expr) }, ctx).is_some() { s.report(expr.pos(), "we fucked up"); }; + ty = s.ci.ret; + let stash = s.complete_call_graph(); s.push_stash(stash); @@ -2105,7 +2220,29 @@ impl Codegen { }); match loc { - Ok(i) | Err(i) => self.ct.vm.read_reg(i).cast::(), + Ok(i) | Err(i) => { + (self.ct.vm.read_reg(i).cast::(), ty.expect("you have died (in brahmaputra)")) + } + } + } + + fn assign_ct_pattern(&mut self, pat: &Expr, ty: ty::Id, offset: *mut u8) -> bool { + let size = self.tys.size_of(ty); + match *pat { + Expr::Ident { id, .. } + if find_symbol(&self.cfile().symbols, id).flags & idfl::REFERENCED == 0 + && size <= 8 => + { + let loc = Loc::ct(load_value(offset, size)); + self.ci.vars.push(Variable { id, value: Value { ty, loc } }); + true + } + Expr::Ident { id, .. } => { + let var = Variable { id, value: Value { ty, loc: Loc::ct_ptr(offset as _) } }; + self.ci.vars.push(var); + false + } + ref pat => self.report_unhandled_ast(pat, "comptime pattern"), } } @@ -2134,15 +2271,7 @@ impl Codegen { self.ci.free_loc(right.loc); } - pat => self.report( - pat.pos(), - format_args!( - "compiler does not (yet) konw how to handle:\n\ - {pat:}\n\ - info for weak people:\n\ - {pat:#?}" - ), - ), + ref pat => self.report_unhandled_ast(pat, "pattern"), }; Some(Value::void()) @@ -2226,10 +2355,10 @@ impl Codegen { let signed = ty.is_signed(); let lhs = self.loc_to_reg(left, size); - if let Loc::Ct { value } = right + if let Loc::Ct { value, derefed: false } = right && let Some(op) = Self::imm_math_op(op, signed, size) { - self.output.emit(op(lhs.get(), lhs.get(), u64::from_ne_bytes(value))); + self.output.emit(op(lhs.get(), lhs.get(), value.0)); return Some(if let Some(value) = ctx.into_value() { self.store_typed(Loc::reg(lhs.as_ref()), value.loc, value.ty); Value::void() @@ -2395,11 +2524,12 @@ impl Codegen { let sig = func.sig.unwrap(); let ast = self.files[file as usize].clone(); let expr = func.expr.get(&ast).unwrap(); + let ct_stack_base = self.ct.vm.read_reg(reg::STACK_PTR).0; let repl = ItemCtx { file, id: ty::Kind::Func(id), - ret: sig.ret, + ret: Some(sig.ret), ..self.pool.cis.pop().unwrap_or_default() }; let prev_ci = std::mem::replace(&mut self.ci, repl); @@ -2420,7 +2550,7 @@ impl Codegen { self.output.emit_prelude(); log::dbg!("fn-args"); - let mut parama = self.tys.parama(self.ci.ret); + let mut parama = self.tys.parama(sig.ret); let mut sig_args = sig.args.range(); for arg in args.iter() { let ty = self.tys.args[sig_args.next().unwrap()]; @@ -2432,7 +2562,7 @@ impl Codegen { self.ci.vars.push(Variable { id: arg.id, value: Value { ty, loc } }); } - if self.tys.size_of(self.ci.ret) > 16 { + if self.tys.size_of(sig.ret) > 16 { let reg = self.ci.regs.allocate(); self.output.emit(instrs::cp(reg.get(), 1)); self.ci.ret_reg = reg; @@ -2457,6 +2587,7 @@ impl Codegen { self.ci.regs.free(std::mem::take(&mut self.ci.ret_reg)); self.tys.funcs[id as usize].offset = self.ci.snap.code as Offset; self.pool.cis.push(std::mem::replace(&mut self.ci, prev_ci)); + self.ct.vm.write_reg(reg::STACK_PTR, ct_stack_base); } fn load_arg(&mut self, flags: parser::IdentFlags, ty: ty::Id, parama: &mut ParamAlloc) -> Loc { @@ -2484,8 +2615,7 @@ impl Codegen { dst } - fn eca(&mut self, trap: Trap, ret: impl Into) -> Value { - self.output.emit(eca()); + fn eca(&mut self, trap: trap::Trap, ret: impl Into) -> Value { self.output.write_trap(trap); Value { ty: ret.into(), loc: Loc::reg(1) } } @@ -2582,24 +2712,26 @@ impl Codegen { dst.as_ref().assert_valid(); match (src.as_ref(), dst.as_ref()) { - (&Loc::Ct { value }, lpat!(true, reg, off, ref sta)) => { + (&Loc::Ct { value, derefed }, lpat!(true, reg, off, ref sta)) => { let ct = self.ci.regs.allocate(); - self.output.emit(li64(ct.get(), u64::from_ne_bytes(value))); + self.output.emit(li64(ct.get(), ensure_loaded(value, derefed, size))); let off = self.opt_stack_reloc(sta.as_ref(), off, 3); self.output.emit(st(ct.get(), reg.get(), off, size as _)); self.ci.regs.free(ct); } - (&Loc::Ct { value }, lpat!(false, reg, 0, None)) => { - self.output.emit(li64(reg.get(), u64::from_ne_bytes(value))) + (&Loc::Ct { value, derefed }, lpat!(false, reg, 0, None)) => { + self.output.emit(li64(reg.get(), ensure_loaded(value, derefed, size))) } - (&Loc::Ct { value }, lpat!(false, reg, 8, None)) if reg.get() == 1 && size == 8 => { - self.output.emit(li64(reg.get() + 1, u64::from_ne_bytes(value))); + (&Loc::Ct { value, derefed }, lpat!(false, reg, 8, None)) + if reg.get() == 1 && size == 8 => + { + self.output.emit(li64(reg.get() + 1, ensure_loaded(value, derefed, size))); } - (&Loc::Ct { value }, lpat!(false, reg, off, None)) if reg.get() == 1 => { + (&Loc::Ct { value, derefed }, lpat!(false, reg, off, None)) if reg.get() == 1 => { let freg = reg.get() + (off / 8) as u8; let mask = !(((1u64 << (8 * size)) - 1) << (8 * (off % 8))); self.output.emit(andi(freg, freg, mask)); - let value = u64::from_ne_bytes(value) << (8 * (off % 8)); + let value = ensure_loaded(value, derefed, size) << (8 * (off % 8)); self.output.emit(ori(freg, freg, value)); } (lpat!(true, src, soff, ref ssta), lpat!(true, dst, doff, ref dsta)) => { @@ -2710,19 +2842,19 @@ impl Codegen { // 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).to_ne_bytes()) + ty::Id::from(self.eval_const(expr, ty::TYPE)) } fn handle_ecall(&mut self) { - let arr = self.ct.vm.pc.get() as *const Trap; - let trap = unsafe { std::ptr::read_unaligned(arr) }; - self.ct.vm.pc = self.ct.vm.pc.wrapping_add(std::mem::size_of::()); + let trap = unsafe { &*(self.ct.vm.pc.get() as *const trap::Trap) }; + self.ct.vm.pc = self.ct.vm.pc.wrapping_add(trap.size()); - let local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize) + let mut extra_jump = 0; + let mut local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize) .checked_sub(self.ci.snap.code); - match trap { - Trap::MakeStruct { file, struct_expr } => { + match *trap { + trap::Trap::MakeStruct(trap::MakeStruct { file, struct_expr }) => { let cfile = self.files[file as usize].clone(); let &Expr::Struct { fields, captured, .. } = struct_expr.get(&cfile).unwrap() else { @@ -2741,7 +2873,7 @@ impl Codegen { unsafe { std::ptr::copy_nonoverlapping(values, imm.as_mut_ptr(), size) }; self.ci.vars.push(Variable { id, - value: Value::new(ty, Loc::imm(u64::from_ne_bytes(imm))), + value: Value::new(ty, Loc::ct(u64::from_ne_bytes(imm))), }); } @@ -2749,12 +2881,24 @@ impl Codegen { self.ci.vars.truncate(prev_len); self.ct.vm.write_reg(1, stru.repr() as u64); } + trap::Trap::MomizedCall(trap::MomizedCall { func }) => { + let sym = SymKey { file: u32::MAX, ident: ty::Kind::Func(func).compress().repr() }; + if let Some(&ty) = self.tys.syms.get(&sym) { + self.ct.vm.write_reg(1, ty.repr()); + extra_jump = jal(0, 0, 0).0 + tx().0; + } else { + local_pc = None; + self.run_vm(); + self.tys.syms.insert(sym, self.ct.vm.read_reg(1).0.into()); + } + } } if let Some(lpc) = local_pc { let offset = lpc + self.ci.snap.code + self.output.code.as_ptr() as usize; self.ct.vm.pc = hbvm::mem::Address::new(offset as _); } + self.ct.vm.pc += extra_jump; } fn find_or_declare( @@ -2912,21 +3056,29 @@ impl Codegen { self.ci.regs.init(); let ret = compile(self, &mut prev_ci); + let mut rr = std::mem::take(&mut self.ci.ret_reg); + let is_on_stack = !rr.is_ref(); + if !rr.is_ref() { + self.output.emit(instrs::cp(1, rr.get())); + let rref = rr.as_ref(); + self.ci.regs.free(std::mem::replace(&mut rr, rref)); + } if ret.is_ok() { + if is_on_stack { + let size = + self.tys.size_of(self.ci.ret.expect("you have died (colaterall fuck up)")); + let slot = self.ct.vm.read_reg(reg::STACK_PTR).0; + self.ct.vm.write_reg(reg::STACK_PTR, slot.wrapping_add(size as _)); + self.ct.vm.write_reg(1, slot); + } + self.link(); self.output.trunc(&Snapshot { code: self.output.code.len(), ..self.ci.snap }); log::dbg!("{} {}", self.output.code.len(), self.ci.snap.code); let entry = &mut self.output.code[self.ci.snap.code] as *mut _ as _; let prev_pc = std::mem::replace(&mut self.ct.vm.pc, hbvm::mem::Address::new(entry)); - loop { - match self.ct.vm.run().unwrap() { - hbvm::VmRunOk::End => break, - hbvm::VmRunOk::Timer => unreachable!(), - hbvm::VmRunOk::Ecall => self.handle_ecall(), - hbvm::VmRunOk::Breakpoint => unreachable!(), - } - } + self.run_vm(); self.ct.vm.pc = prev_pc; } @@ -2941,11 +3093,23 @@ impl Codegen { ret } + fn run_vm(&mut self) { + loop { + match self.ct.vm.run().unwrap() { + hbvm::VmRunOk::End => break, + hbvm::VmRunOk::Timer => unreachable!(), + hbvm::VmRunOk::Ecall => self.handle_ecall(), + hbvm::VmRunOk::Breakpoint => unreachable!(), + } + } + } + fn ty_display(&self, ty: ty::Id) -> ty::Display { ty::Display::new(&self.tys, &self.files, ty) } #[must_use] + #[track_caller] fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id) -> ty::Id { if let Some(res) = ty.try_upcast(expected) { res @@ -2956,12 +3120,30 @@ impl Codegen { } } - fn report(&self, pos: Pos, msg: impl std::fmt::Display) -> ! { + fn report_log(&self, pos: Pos, msg: impl std::fmt::Display) { let (line, col) = self.cfile().nlines.line_col(pos); println!("{}:{}:{}: {}", self.cfile().path, line, col, msg); + } + + #[track_caller] + fn report(&self, pos: Pos, msg: impl std::fmt::Display) -> ! { + self.report_log(pos, msg); unreachable!(); } + #[track_caller] + fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! { + self.report( + ast.pos(), + format_args!( + "compiler does not (yet) konw how to handle ({hint}):\n\ + {ast:}\n\ + info for weak people:\n\ + {ast:#?}" + ), + ) + } + fn cfile(&self) -> &parser::Ast { &self.files[self.ci.file as usize] } @@ -3142,5 +3324,6 @@ mod tests { struct_patterns => README; arrays => README; struct_return_from_module_function => README; + comptime_pointers => README; } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index cbcf1a3..543fdd2 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -115,6 +115,8 @@ pub enum TokenKind { Number, Eof, + Ct, + Return, If, Else, @@ -321,6 +323,7 @@ impl<'a> Lexer<'a> { b'.' if self.advance_if(b'(') => T::Tupl, b'&' if self.advance_if(b'&') => T::And, b'|' if self.advance_if(b'|') => T::Or, + b'$' if self.advance_if(b':') => T::Ct, b'@' | b'$' => { start += 1; advance_ident(self); diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index d1c2e18..81e4b32 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -266,6 +266,7 @@ impl<'a, 'b> Parser<'a, 'b> { let prev_boundary = self.ns_bound; let prev_captured = self.captured.len(); let mut expr = match token.kind { + T::Ct => E::Ct { pos: token.start, value: self.ptr_expr() }, T::Directive if self.lexer.slice(token.range()) == "use" => { self.expect_advance(TokenKind::LParen); let str = self.expect_advance(TokenKind::DQuote); @@ -613,6 +614,10 @@ macro_rules! generate_expr { generate_expr! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Expr<'a> { + Ct { + pos: Pos, + value: &'a Expr<'a>, + }, String { pos: Pos, literal: &'a str, @@ -732,6 +737,14 @@ impl<'a> Expr<'a> { } } + pub fn has_ct(&self, symbols: &[Symbol]) -> bool { + match *self { + Self::Ident { id, .. } => find_symbol(symbols, id).flags & idfl::COMPTIME != 0, + Self::Ctor { fields, .. } => fields.iter().any(|f| f.value.has_ct(symbols)), + _ => false, + } + } + pub fn find_pattern_path( &self, ident: Ident, @@ -875,6 +888,7 @@ impl<'a> std::fmt::Display for Expr<'a> { } match *self { + Self::Ct { value, .. } => write!(f, "$: {}", value), Self::String { literal, .. } => write!(f, "{}", literal), Self::Comment { literal, .. } => write!(f, "{}", literal.trim_end()), Self::Mod { path, .. } => write!(f, "@mod(\"{path}\")"),