From 44c4b71bb308ab6aa62aff5d50487884089c68b1 Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Sun, 20 Oct 2024 15:16:55 +0200 Subject: [PATCH] unifiing the type resolution into a trait --- lang/src/codegen.rs | 283 ++++++--------- lang/src/lib.rs | 291 ++++++++++------ lang/src/son.rs | 327 +++++++----------- .../tests/son_tests_comptime_min_reg_leak.txt | 7 + 4 files changed, 441 insertions(+), 467 deletions(-) create mode 100644 lang/tests/son_tests_comptime_min_reg_leak.txt diff --git a/lang/src/codegen.rs b/lang/src/codegen.rs index 3abe42b1..ad9b24eb 100644 --- a/lang/src/codegen.rs +++ b/lang/src/codegen.rs @@ -9,7 +9,7 @@ use { reg, ty::{self, TyCheck}, Comptime, Field, Func, Global, OffsetIter, ParamAlloc, Reloc, Sig, Struct, SymKey, - TypedReloc, Types, + TypeParser, TypedReloc, Types, }, alloc::{string::String, vec::Vec}, core::{assert_matches::debug_assert_matches, fmt::Display}, @@ -443,7 +443,6 @@ struct ItemCtxSnap { #[derive(Default)] struct ItemCtx { file: FileId, - id: ty::Kind, ret: Option, ret_reg: rall::Id, inline_ret_loc: Loc, @@ -704,6 +703,72 @@ pub struct Codegen { ct: Comptime, } +impl TypeParser for Codegen { + fn tys(&mut self) -> &mut Types { + &mut self.tys + } + + fn on_reuse(&mut self, existing: ty::Id) { + if let ty::Kind::Func(id) = existing.expand() + && let func = &mut self.tys.ins.funcs[id as usize] + && let Err(idx) = task::unpack(func.offset) + && idx < self.tasks.len() + { + func.offset = task::id(self.tasks.len()); + let task = self.tasks[idx].take(); + self.tasks.push(task); + } + } + + fn eval_ty( + &mut self, + file: FileId, + name: Option, + expr: &Expr, + _: &[parser::Ast], + ) -> ty::Id { + let prev_file = core::mem::replace(&mut self.ci.file, file); + let sym = match *expr { + Expr::Slice { size, item, .. } => { + let ty = self.ty(item); + let len = size.map_or(ArrayLen::MAX, |expr| { + self.eval_const(self.ci.file, expr, ty::U32) as _ + }); + self.tys.make_array(ty, len).expand() + } + Expr::Directive { name: "TypeOf", args: [expr], .. } => self.infer_type(expr).expand(), + _ if let Some(name) = name => { + let gid = self.tys.ins.globals.len() as ty::Global; + self.tys.ins.globals.push(Global { file, name, ..Default::default() }); + + let ci = ItemCtx { file, ..self.pool.cis.pop().unwrap_or_default() }; + + self.tys.ins.globals[gid as usize] = self + .ct_eval(ci, |s, _| Ok::<_, !>(s.generate_global(expr, file, name))) + .into_ok(); + + ty::Kind::Global(gid) + } + _ => ty::Id::from(self.eval_const(file, expr, ty::Id::TYPE)).expand(), + }; + self.ci.file = prev_file; + sym.compress() + } + + fn report(&self, pos: Pos, msg: impl Display) -> ty::Id { + self.report(pos, msg) + } + + fn find_local_ty(&mut self, name: Ident) -> Option { + self.ci.vars.iter().rfind(|v| v.id == name && v.value.ty == ty::Id::TYPE).map(|v| { + match v.value.loc { + Loc::Rt { .. } => unreachable!(), + Loc::Ct { derefed, value } => ty::Id::from(ensure_loaded(value, derefed, 4)), + } + }) + } +} + impl Codegen { pub fn push_embeds(&mut self, embeds: Vec>) { self.tys.ins.globals = embeds @@ -719,7 +784,7 @@ impl Codegen { pub fn generate(&mut self, root: FileId) { self.ci.emit_entry_prelude(); self.ci.file = root; - self.find_or_declare(0, root, Err("main"), ""); + self.find_type(0, root, Err("main"), &self.files.clone()); self.make_func_reachable(0); self.complete_call_graph(); } @@ -816,7 +881,9 @@ impl Codegen { } E::Slice { size, item, .. } => { let ty = self.ty(item); - let len = size.map_or(ArrayLen::MAX, |expr| self.eval_const(expr, ty::U32) as _); + let len = size.map_or(ArrayLen::MAX, |expr| { + self.eval_const(self.ci.file, expr, ty::U32) as _ + }); Some(Value::ty(self.tys.make_array(ty, len))) } E::Index { base, index } => { @@ -879,15 +946,20 @@ impl Codegen { } } E::Directive { name: "inline", args: [func_ast, args @ ..], .. } => { - let ty::Kind::Func(mut func) = self.ty(func_ast).expand() else { - self.report(func_ast.pos(), "first argument of inline needs to be a function"); + let ty = self.ty(func_ast); + let ty::Kind::Func(mut func) = ty.expand() else { + self.report( + func_ast.pos(), + format_args!( + "first argument of inline needs to be a function, but its '{}'", + self.ty_display(ty) + ), + ); }; let fuc = &self.tys.ins.funcs[func as usize]; let ast = self.files[fuc.file as usize].clone(); - let E::BinOp { right: &E::Closure { args: cargs, body, .. }, .. } = - fuc.expr.get(&ast).unwrap() - else { + let &E::Closure { args: cargs, body, .. } = fuc.expr.get(&ast).unwrap() else { unreachable!(); }; @@ -1183,7 +1255,10 @@ impl Codegen { self.ci.revert(checkpoint); match self.ty(target).expand() { ty::Kind::Module(idx) => { - match self.find_or_declare(pos, idx, Err(field), "") { + match self + .find_type(pos, idx, Err(field), &self.files.clone()) + .expand() + { ty::Kind::Global(idx) => self.handle_global(idx), e => Some(Value::ty(e.compress())), } @@ -1282,7 +1357,7 @@ 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); + let (cnt, ty) = self.eval_const_low(self.ci.file, right, None); if self.assign_ct_pattern(left, ty, cnt as _) { self.ct.vm.write_reg(reg::STACK_PTR, slot_base); } @@ -1296,7 +1371,13 @@ impl Codegen { log::trace!("call {}", self.ast_display(fast)); let func_ty = self.ty(fast); let ty::Kind::Func(mut func) = func_ty.expand() else { - self.report(fast.pos(), "can't call this, maybe in the future"); + self.report( + fast.pos(), + format_args!( + "can't '{}' this, maybe in the future", + self.ty_display(func_ty) + ), + ); }; // TODO: this will be usefull but not now @@ -1306,9 +1387,7 @@ impl Codegen { let fuc = &self.tys.ins.funcs[func as usize]; let ast = self.files[fuc.file as usize].clone(); - let E::BinOp { right: &E::Closure { args: cargs, .. }, .. } = - fuc.expr.get(&ast).unwrap() - else { + let &E::Closure { args: cargs, .. } = fuc.expr.get(&ast).unwrap() else { unreachable!(); }; @@ -1371,14 +1450,8 @@ impl Codegen { let loc = var.value.loc.as_ref(); Some(Value { ty: self.ci.vars[var_index].value.ty, loc }) } - E::Ident { id, .. } => { - let cfile = self.cfile().clone(); - match self.find_or_declare( - ident::pos(id), - self.ci.file, - Ok(id), - cfile.ident_str(id), - ) { + E::Ident { id, pos, .. } => { + match self.find_type(pos, self.ci.file, Ok(id), &self.files.clone()).expand() { ty::Kind::Global(id) => self.handle_global(id), tk => Some(Value::ty(tk.compress())), } @@ -1719,9 +1792,7 @@ impl Codegen { fn compute_signature(&mut self, func: &mut ty::Func, pos: Pos, args: &[Expr]) -> Option { let fuc = &self.tys.ins.funcs[*func as usize]; let fast = self.files[fuc.file as usize].clone(); - let Expr::BinOp { right: &Expr::Closure { args: cargs, ret, .. }, .. } = - fuc.expr.get(&fast).unwrap() - else { + let &Expr::Closure { args: cargs, ret, .. } = fuc.expr.get(&fast).unwrap() else { unreachable!(); }; @@ -1786,7 +1857,6 @@ impl Codegen { // FIXME: very inneficient let mut ci = ItemCtx { file: self.ci.file, - id: self.ci.id, ret: self.ci.ret, task_base: self.ci.task_base, ..self.pool.cis.pop().unwrap_or_default() @@ -1818,17 +1888,17 @@ impl Codegen { value.ty } - fn eval_const(&mut self, expr: &Expr, ty: impl Into) -> u64 { - self.eval_const_low(expr, Some(ty.into())).0 + fn eval_const(&mut self, file: FileId, expr: &Expr, ty: impl Into) -> u64 { + self.eval_const_low(file, 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: ty::Kind::Builtin(u32::MAX), - ret: ty, - ..self.pool.cis.pop().unwrap_or_default() - }; + fn eval_const_low( + &mut self, + file: FileId, + expr: &Expr, + mut ty: Option, + ) -> (u64, ty::Id) { + let mut ci = ItemCtx { file, ret: ty, ..self.pool.cis.pop().unwrap_or_default() }; ci.vars.append(&mut self.ci.vars); let loc = self.ct_eval(ci, |s, prev| { @@ -2083,21 +2153,11 @@ impl Codegen { 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: Some(sig.ret), - ..self.pool.cis.pop().unwrap_or_default() - }; + let repl = ItemCtx { file, ret: Some(sig.ret), ..self.pool.cis.pop().unwrap_or_default() }; let prev_ci = core::mem::replace(&mut self.ci, repl); self.ci.regs.init(); - let Expr::BinOp { - left: Expr::Ident { .. }, - op: TokenKind::Decl, - right: &Expr::Closure { body, args, .. }, - } = expr - else { + let Expr::Closure { body, args, .. } = expr else { unreachable!("{}", self.ast_display(expr)) }; @@ -2375,18 +2435,7 @@ impl Codegen { } fn ty(&mut self, expr: &Expr) -> ty::Id { - let ty = self.tys.ty(self.ci.file, expr, &self.files); - let evaled_ty = ty::Id::from(self.eval_const(expr, ty::TYPE)); - if let Some(ty) = ty { - debug_assert_eq!( - ty, - evaled_ty, - "{} {}", - self.ty_display(ty), - self.ty_display(evaled_ty) - ); - } - evaled_ty + self.parse_ty(self.ci.file, expr, None, &self.files.clone()) } fn read_trap(addr: u64) -> Option<&'static trap::Trap> { @@ -2455,118 +2504,6 @@ impl Codegen { self.ct.vm.pc = hbvm::mem::Address::new(offset as _); } - fn find_or_declare( - &mut self, - pos: Pos, - file: FileId, - name: Result, - lit_name: &str, - ) -> ty::Kind { - log::trace!("find_or_declare: {lit_name} {file}"); - if let Some(ty) = self.tys.find_type(file, name, &self.files) { - return ty.expand(); - } - - let f = self.files[file as usize].clone(); - let Some((expr, ident)) = f.find_decl(name) else { - match name { - Ok(_) => self.report(pos, format_args!("undefined identifier: {lit_name}")), - Err("main") => self.report(pos, format_args!("missing main function")), - Err(name) => self.report(pos, format_args!("undefined indentifier: {name}")), - } - }; - - let key = SymKey::Decl(file, ident); - if let Some(existing) = self.tys.syms.get(key, &self.tys.ins) { - if let ty::Kind::Func(id) = existing.expand() - && let func = &mut self.tys.ins.funcs[id as usize] - && let Err(idx) = task::unpack(func.offset) - && idx < self.tasks.len() - { - func.offset = task::id(self.tasks.len()); - let task = self.tasks[idx].take(); - self.tasks.push(task); - } - return existing.expand(); - } - - let prev_file = core::mem::replace(&mut self.ci.file, file); - let sym = match expr { - Expr::BinOp { - left: &Expr::Ident { id, .. }, - op: TokenKind::Decl, - right: &Expr::Closure { pos, args, ret, .. }, - } => { - let func = Func { - file, - name: id, - sig: 'b: { - let arg_base = self.tys.tmp.args.len(); - for arg in args { - let sym = find_symbol(&self.files[file as usize].symbols, arg.id); - if sym.flags & idfl::COMPTIME != 0 { - self.tys.tmp.args.truncate(arg_base); - break 'b None; - } - let ty = self.ty(&arg.ty); - self.tys.tmp.args.push(ty); - } - - let args = self - .tys - .pack_args(arg_base) - .unwrap_or_else(|| self.report(pos, "function has too many argumnets")); - let ret = self.ty(ret); - - Some(Sig { args, ret }) - }, - expr: { - let refr = ExprRef::new(expr); - debug_assert!(refr.get(&f).is_some()); - refr - }, - ..Default::default() - }; - - let id = self.tys.ins.funcs.len() as _; - self.tys.ins.funcs.push(func); - - ty::Kind::Func(id) - } - Expr::BinOp { - left: &Expr::Ident { id, .. }, - op: TokenKind::Decl, - right: stru @ Expr::Struct { .. }, - } => { - let str = self.ty(stru).expand().inner(); - self.tys.ins.structs[str as usize].name = id; - ty::Kind::Struct(str) - } - Expr::BinOp { left, op: TokenKind::Decl, right } => { - let gid = self.tys.ins.globals.len() as ty::Global; - self.tys.ins.globals.push(Global { file, name: ident, ..Default::default() }); - - let ci = ItemCtx { - file, - id: ty::Kind::Builtin(u32::MAX), - ..self.pool.cis.pop().unwrap_or_default() - }; - - _ = left.find_pattern_path(ident, right, |expr| { - self.tys.ins.globals[gid as usize] = self - .ct_eval(ci, |s, _| Ok::<_, !>(s.generate_global(expr, file, ident))) - .into_ok(); - }); - - ty::Kind::Global(gid) - } - e => unimplemented!("{e:#?}"), - }; - self.ci.file = prev_file; - self.tys.syms.insert(key, sym.compress(), &self.tys.ins); - sym - } - fn make_func_reachable(&mut self, func: ty::Func) { let fuc = &mut self.tys.ins.funcs[func as usize]; if fuc.offset == u32::MAX { @@ -2598,7 +2535,7 @@ impl Codegen { self.ci.free_loc(ret.loc); - Global { ty: ret.ty, file, name, data, ..Default::default() } + Global { ty: ret.ty, file, data, name, ..Default::default() } } fn ct_eval( diff --git a/lang/src/lib.rs b/lang/src/lib.rs index bb98b8f2..647b0cb7 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -34,11 +34,11 @@ use { self::{ ident::Ident, lexer::TokenKind, - parser::{CommentOr, Expr, ExprRef, FileId, Pos}, + parser::{idfl, CommentOr, Expr, ExprRef, FileId, Pos}, ty::ArrayLen, }, alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}, - core::{cell::Cell, ops::Range}, + core::{cell::Cell, fmt::Display, ops::Range}, hashbrown::hash_map, hbbytecode as instrs, }; @@ -683,7 +683,7 @@ impl Default for Global { offset: u32::MAX, data: Default::default(), file: u32::MAX, - name: u32::MAX, + name: 0, } } } @@ -840,6 +840,192 @@ struct Types { const HEADER_SIZE: usize = core::mem::size_of::(); +trait TypeParser { + fn tys(&mut self) -> &mut Types; + fn on_reuse(&mut self, existing: ty::Id); + fn find_local_ty(&mut self, name: Ident) -> Option; + fn report(&self, pos: Pos, msg: impl Display) -> ty::Id; + fn eval_ty( + &mut self, + file: FileId, + name: Option, + expr: &Expr, + files: &[parser::Ast], + ) -> ty::Id; + + fn find_type( + &mut self, + pos: Pos, + file: FileId, + id: Result, + files: &[parser::Ast], + ) -> ty::Id { + let ty = if let Ok(id) = id + && let Some(ty) = self.find_local_ty(id) + { + ty + } else if let Ok(id) = id + && let tys = self.tys() + && let Some(&ty) = tys.syms.get(SymKey::Decl(file, id), &tys.ins) + { + self.on_reuse(ty); + ty + } else { + let f = &files[file as usize]; + + let Some((Expr::BinOp { left, right, .. }, name)) = f.find_decl(id) else { + return match id { + Ok(name) => { + let name = f.ident_str(name); + self.report(pos, format_args!("undefined indentifier: {name}")) + } + Err("main") => self.report( + pos, + format_args!( + "missing main function in '{}', compiler can't \ + emmit libraries since such concept is not defined", + f.path + ), + ), + Err(name) => self.report(pos, format_args!("undefined indentifier: {name}")), + }; + }; + + let tys = self.tys(); + if let Some(&ty) = tys.syms.get(SymKey::Decl(file, name), &tys.ins) { + ty + } else { + let ty = left + .find_pattern_path(name, right, |right| { + self.parse_ty(file, right, Some(name), files) + }) + .unwrap_or_else(|_| unreachable!()); + let tys = self.tys(); + let nm = match ty.expand() { + ty::Kind::Struct(s) => &mut tys.ins.structs[s as usize].name, + ty::Kind::Func(s) => &mut tys.ins.funcs[s as usize].name, + ty::Kind::Global(s) => &mut tys.ins.globals[s as usize].name, + _ => &mut 0, + }; + if *nm == 0 { + *nm = name; + } + tys.syms.insert(SymKey::Decl(file, name), ty, &tys.ins); + ty + } + }; + + let tys = self.tys(); + if let ty::Kind::Global(g) = ty.expand() { + let g = &tys.ins.globals[g as usize]; + if g.ty == ty::Id::TYPE { + return ty::Id::from( + u32::from_ne_bytes(g.data.as_slice().try_into().unwrap()) as u64 + ); + } + } + ty + } + + /// returns none if comptime eval is required + fn parse_ty( + &mut self, + file: FileId, + expr: &Expr, + name: Option, + files: &[parser::Ast], + ) -> ty::Id { + match *expr { + Expr::Mod { id, .. } => ty::Kind::Module(id).compress(), + Expr::UnOp { op: TokenKind::Xor, val, .. } => { + let base = self.parse_ty(file, val, None, files); + self.tys().make_ptr(base) + } + Expr::Ident { id, .. } if ident::is_null(id) => id.into(), + Expr::Ident { id, pos, .. } => self.find_type(pos, file, Ok(id), files), + Expr::Field { target, pos, name } => { + let ty::Kind::Module(file) = self.parse_ty(file, target, None, files).expand() + else { + return self.eval_ty(file, None, expr, files); + }; + + self.find_type(pos, file, Err(name), files) + } + Expr::Slice { size: None, item, .. } => { + let ty = self.parse_ty(file, item, None, files); + self.tys().make_array(ty, ArrayLen::MAX) + } + Expr::Slice { size: Some(&Expr::Number { value, .. }), item, .. } => { + let ty = self.parse_ty(file, item, None, files); + self.tys().make_array(ty, value as _) + } + Expr::Struct { pos, fields, packed, .. } => { + let sym = SymKey::Struct(file, pos); + let tys = self.tys(); + if let Some(&ty) = tys.syms.get(sym, &tys.ins) { + return ty; + } + + let prev_tmp = self.tys().tmp.fields.len(); + for field in fields.iter().filter_map(CommentOr::or) { + let ty = self.parse_ty(file, &field.ty, None, files); + let field = Field { name: self.tys().names.intern(field.name), ty }; + self.tys().tmp.fields.push(field); + } + + let tys = self.tys(); + tys.ins.structs.push(Struct { + file, + pos, + name: name.unwrap_or(0), + field_start: tys.ins.fields.len() as _, + explicit_alignment: packed.then_some(1), + ..Default::default() + }); + + tys.ins.fields.extend(tys.tmp.fields.drain(prev_tmp..)); + + let ty = ty::Kind::Struct(tys.ins.structs.len() as u32 - 1).compress(); + tys.syms.insert(sym, ty, &tys.ins); + ty + } + Expr::Closure { pos, args, ret, .. } if let Some(name) = name => { + let func = Func { + file, + name, + sig: 'b: { + let arg_base = self.tys().tmp.args.len(); + for arg in args { + let sym = parser::find_symbol(&files[file as usize].symbols, arg.id); + if sym.flags & idfl::COMPTIME != 0 { + self.tys().tmp.args.truncate(arg_base); + break 'b None; + } + let ty = self.parse_ty(file, &arg.ty, None, files); + self.tys().tmp.args.push(ty); + } + + let Some(args) = self.tys().pack_args(arg_base) else { + return self.report(pos, "function has too many argumnets"); + }; + let ret = self.parse_ty(file, ret, None, files); + + Some(Sig { args, ret }) + }, + expr: ExprRef::new(expr), + ..Default::default() + }; + + let id = self.tys().ins.funcs.len() as _; + self.tys().ins.funcs.push(func); + + ty::Kind::Func(id).compress() + } + _ => self.eval_ty(file, name, expr, files), + } + } +} + impl Types { fn struct_field_range(&self, strct: ty::Struct) -> Range { let start = self.ins.structs[strct as usize].field_start as usize; @@ -870,98 +1056,6 @@ impl Types { &self.ins.fields[self.struct_field_range(strct)] } - fn find_type( - &mut self, - file: FileId, - id: Result, - files: &[parser::Ast], - ) -> Option { - if let Ok(id) = id - && let Some(&ty) = self.syms.get(SymKey::Decl(file, id), &self.ins) - { - if let ty::Kind::Global(g) = ty.expand() { - let g = &self.ins.globals[g as usize]; - if g.ty == ty::Id::TYPE { - return Some(ty::Id::from( - u32::from_ne_bytes(*g.data.first_chunk().unwrap()) as u64 - )); - } - } - - return Some(ty); - } - - let f = &files[file as usize]; - let (Expr::BinOp { left, right, .. }, name) = f.find_decl(id)? else { unreachable!() }; - - let ty = left - .find_pattern_path(name, right, |right| self.ty(file, right, files)) - .unwrap_or_else(|_| unreachable!())?; - if let ty::Kind::Struct(s) = ty.expand() { - self.ins.structs[s as usize].name = name; - } - - self.syms.insert(SymKey::Decl(file, name), ty, &self.ins); - Some(ty) - } - - /// returns none if comptime eval is required - fn ty(&mut self, file: FileId, expr: &Expr, files: &[parser::Ast]) -> Option { - Some(match *expr { - Expr::Mod { id, .. } => ty::Kind::Module(id).compress(), - 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, .. } => self.find_type(file, Ok(id), files)?, - Expr::Field { target, name, .. } => { - let ty::Kind::Module(file) = self.ty(file, target, files)?.expand() else { - return None; - }; - - self.find_type(file, Err(name), files)? - } - Expr::Slice { size: None, item, .. } => { - let ty = self.ty(file, item, files)?; - self.make_array(ty, ArrayLen::MAX) - } - Expr::Slice { size: Some(&Expr::Number { value, .. }), item, .. } => { - let ty = self.ty(file, item, files)?; - self.make_array(ty, value as _) - } - Expr::Struct { pos, fields, packed, .. } => { - let sym = SymKey::Struct(file, pos); - if let Some(&ty) = self.syms.get(sym, &self.ins) { - return Some(ty); - } - - let prev_tmp = self.tmp.fields.len(); - for field in fields.iter().filter_map(CommentOr::or) { - let Some(ty) = self.ty(file, &field.ty, files) else { - self.tmp.fields.truncate(prev_tmp); - return None; - }; - self.tmp.fields.push(Field { name: self.names.intern(field.name), ty }); - } - - self.ins.structs.push(Struct { - file, - pos, - field_start: self.ins.fields.len() as _, - explicit_alignment: packed.then_some(1), - ..Default::default() - }); - self.ins.fields.extend(self.tmp.fields.drain(prev_tmp..)); - - let ty = ty::Kind::Struct(self.ins.structs.len() as u32 - 1).compress(); - self.syms.insert(sym, ty, &self.ins); - ty - } - _ => return None, - }) - } - fn reassemble(&mut self, buf: &mut Vec) { self.ins.funcs.iter_mut().for_each(|f| f.offset = u32::MAX); self.ins.globals.iter_mut().for_each(|g| g.offset = u32::MAX); @@ -1063,12 +1157,7 @@ impl Types { .map(|f| { let name = if f.file != u32::MAX { let file = &files[f.file as usize]; - let Expr::BinOp { left: &Expr::Ident { id, .. }, .. } = - f.expr.get(file).unwrap() - else { - unreachable!() - }; - file.ident_str(id) + file.ident_str(f.name) } else { "target_fn" }; diff --git a/lang/src/son.rs b/lang/src/son.rs index 4a704ddb..e9eebad8 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -12,7 +12,8 @@ use { reg, task, ty::{self, ArrayLen, Tuple}, vc::{BitSet, Vc}, - Comptime, Func, Global, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, + Comptime, Func, Global, HashMap, Offset, OffsetIter, Reloc, Sig, TypeParser, TypedReloc, + Types, }, alloc::{string::String, vec::Vec}, core::{ @@ -1416,6 +1417,123 @@ pub struct Codegen<'a> { errors: RefCell, } +impl TypeParser for Codegen<'_> { + fn tys(&mut self) -> &mut Types { + &mut self.tys + } + + fn on_reuse(&mut self, existing: ty::Id) { + if let ty::Kind::Func(id) = existing.expand() + && let func = &mut self.tys.ins.funcs[id as usize] + && let Err(idx) = task::unpack(func.offset) + && idx < self.tasks.len() + { + func.offset = task::id(self.tasks.len()); + let task = self.tasks[idx].take(); + self.tasks.push(task); + } + } + + #[expect(unused)] + fn eval_ty( + &mut self, + file: FileId, + name: Option, + expr: &Expr, + files: &[parser::Ast], + ) -> ty::Id { + let sym = match expr { + right if let Some(name) = name => { + let gid = self.tys.ins.globals.len() as ty::Global; + self.tys.ins.globals.push(Global { file, name, ..Default::default() }); + + let ty = ty::Kind::Global(gid); + self.pool.push_ci(file, None, self.tasks.len(), &mut self.ci); + + let ret = Expr::Return { pos: right.pos(), val: Some(right) }; + self.expr(&ret); + + self.ci.finalize(); + + let ret = self.ci.ret.expect("for return type to be infered"); + if self.errors.borrow().is_empty() { + self.ci.emit_body(&mut self.tys, self.files, Sig { args: Tuple::empty(), ret }); + self.ci.code.truncate(self.ci.code.len() - instrs::jala(0, 0, 0).0); + self.ci.emit(instrs::tx()); + + let func = Func { + file, + name, + expr: ExprRef::new(expr), + relocs: core::mem::take(&mut self.ci.relocs), + code: core::mem::take(&mut self.ci.code), + ..Default::default() + }; + self.pool.pop_ci(&mut self.ci); + self.complete_call_graph(); + + let mut mem = vec![0u8; self.tys.size_of(ret) as usize]; + + // TODO: return them back + let fuc = self.tys.ins.funcs.len() as ty::Func; + self.tys.ins.funcs.push(func); + + self.tys.dump_reachable(fuc, &mut self.ct.code); + + #[cfg(debug_assertions)] + { + let mut vc = String::new(); + if let Err(e) = self.tys.disasm(&self.ct.code, self.files, &mut vc, |_| {}) + { + panic!("{e} {}", vc); + } else { + log::trace!("{}", vc); + } + } + + self.ct.vm.write_reg(reg::RET, mem.as_mut_ptr() as u64); + let prev_pc = self.ct.push_pc(self.tys.ins.funcs[fuc as usize].offset); + loop { + match self.ct.vm.run().expect("TODO") { + hbvm::VmRunOk::End => break, + hbvm::VmRunOk::Timer => todo!(), + hbvm::VmRunOk::Ecall => todo!(), + hbvm::VmRunOk::Breakpoint => todo!(), + } + } + self.ct.pop_pc(prev_pc); + + match mem.len() { + 0 => unreachable!(), + len @ 1..=8 => mem + .copy_from_slice(&self.ct.vm.read_reg(reg::RET).0.to_ne_bytes()[..len]), + 9..=16 => todo!(), + _ => {} + } + + self.tys.ins.globals[gid as usize].data = mem; + } else { + self.pool.pop_ci(&mut self.ci); + } + self.tys.ins.globals[gid as usize].ty = ret; + + ty + } + e => self.report_unhandled_ast(expr, "type"), + }; + sym.compress() + } + + fn report(&self, pos: Pos, msg: impl Display) -> ty::Id { + self.report(pos, msg); + ty::Id::NEVER + } + + fn find_local_ty(&mut self, _: Ident) -> Option { + None + } +} + impl<'a> Codegen<'a> { fn store_mem(&mut self, region: Nid, value: Nid) -> Nid { if value == NEVER { @@ -1457,7 +1575,7 @@ impl<'a> Codegen<'a> { } pub fn generate(&mut self) { - self.find_or_declare(0, 0, Err("main")); + self.find_type(0, 0, Err("main"), self.files); self.make_func_reachable(0); self.complete_call_graph(); } @@ -1491,8 +1609,8 @@ impl<'a> Codegen<'a> { Some(Value::var(index).ty(var.ty)) } Expr::Ident { id, pos, .. } => { - let decl = self.find_or_declare(pos, self.ci.file, Ok(id)); - match decl { + let decl = self.find_type(pos, self.ci.file, Ok(id), self.files); + match decl.expand() { ty::Kind::Global(global) => { let gl = &self.tys.ins.globals[global as usize]; let value = self.ci.nodes.new_node(gl.ty, Kind::Global { global }, [VOID]); @@ -1744,31 +1862,29 @@ impl<'a> Codegen<'a> { let ctx = Ctx::default().with_ty(self.ty(ty)); self.expr_ctx(expr, ctx) } - Expr::Call { func: &Expr::Ident { pos, id, .. }, args, .. } => { + Expr::Call { func, args, .. } => { self.ci.call_count += 1; - let func = self.find_or_declare(pos, self.ci.file, Ok(id)); - let ty::Kind::Func(func) = func else { + let ty = self.ty(func); + let ty::Kind::Func(fnc) = ty.expand() else { self.report( - pos, - fa!("compiler cant (yet) call '{}'", self.ty_display(func.compress())), + func.pos(), + fa!("compiler cant (yet) call '{}'", self.ty_display(ty)), ); return Value::NEVER; }; - self.make_func_reachable(func); + self.make_func_reachable(fnc); - let fuc = &self.tys.ins.funcs[func as usize]; + let fuc = &self.tys.ins.funcs[fnc as usize]; let sig = fuc.sig.expect("TODO: generic functions"); let ast = &self.files[fuc.file as usize]; - let Expr::BinOp { right: &Expr::Closure { args: cargs, .. }, .. } = - fuc.expr.get(ast).unwrap() - else { + let &Expr::Closure { args: cargs, .. } = fuc.expr.get(ast).unwrap() else { unreachable!() }; self.assert_report( args.len() == cargs.len(), - pos, + func.pos(), fa!( "expected {} function argumenr{}, got {}", cargs.len(), @@ -1805,7 +1921,7 @@ impl<'a> Codegen<'a> { false }); - self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func }, inps); + self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func: fnc }, inps); self.store_mem(VOID, VOID); @@ -2301,7 +2417,7 @@ impl<'a> Codegen<'a> { self.pool.push_ci(file, Some(sig.ret), 0, &mut self.ci); - let Expr::BinOp { right: &Expr::Closure { body, args, .. }, .. } = expr else { + let &Expr::Closure { body, args, .. } = expr else { unreachable!("{}", self.ast_display(expr)) }; @@ -2335,182 +2451,7 @@ impl<'a> Codegen<'a> { } fn ty(&mut self, expr: &Expr) -> ty::Id { - if let Some(ty) = self.tys.ty(self.ci.file, expr, self.files) { - return ty; - } - - self.report_unhandled_ast(expr, "type") - } - - fn find_or_declare(&mut self, pos: Pos, file: FileId, name: Result) -> ty::Kind { - let f = &self.files[file as usize]; - let lit_name = name.map(|e| f.ident_str(e)).unwrap_or_else(core::convert::identity); - log::trace!("find_or_declare: {lit_name} {file}"); - if let Some(ty) = self.tys.find_type(file, name, self.files) { - return ty.expand(); - } - - let Some((expr, ident)) = f.find_decl(name) else { - match name { - Ok(name) => { - let name = self.cfile().ident_str(name); - self.report(pos, fa!("idk indentifier: {name}")) - } - Err("main") => self.report( - pos, - fa!( - "missing main function in '{}', compiler can't \ - emmit libraries since such concept is not defined", - f.path - ), - ), - Err(name) => self.report(pos, fa!("idk indentifier: {name}")), - } - return ty::Id::NEVER.expand(); - }; - - let key = SymKey::Decl(file, ident); - if let Some(existing) = self.tys.syms.get(key, &self.tys.ins) { - if let ty::Kind::Func(id) = existing.expand() - && let func = &mut self.tys.ins.funcs[id as usize] - && let Err(idx) = task::unpack(func.offset) - && idx < self.tasks.len() - { - func.offset = task::id(self.tasks.len()); - let task = self.tasks[idx].take(); - self.tasks.push(task); - } - return existing.expand(); - } - - let prev_file = core::mem::replace(&mut self.ci.file, file); - let sym = match expr { - Expr::BinOp { - left: &Expr::Ident { id, .. }, - op: TokenKind::Decl, - right: &Expr::Closure { pos, args, ret, .. }, - } => { - let func = Func { - file, - name: id, - sig: '_b: { - let arg_base = self.tys.tmp.args.len(); - for arg in args { - let sym = parser::find_symbol(&f.symbols, arg.id); - assert!(sym.flags & idfl::COMPTIME == 0, "TODO"); - let ty = self.ty(&arg.ty); - self.tys.tmp.args.push(ty); - } - - let Some(args) = self.tys.pack_args(arg_base) else { - self.fatal_report( - pos, - "you cant be serious, using more the 31 arguments in a function", - ); - }; - let ret = self.ty(ret); - - Some(Sig { args, ret }) - }, - expr: { - let refr = ExprRef::new(expr); - debug_assert!(refr.get(f).is_some()); - refr - }, - ..Default::default() - }; - - let id = self.tys.ins.funcs.len() as _; - self.tys.ins.funcs.push(func); - - ty::Kind::Func(id) - } - Expr::BinOp { - left: Expr::Ident { .. }, - op: TokenKind::Decl, - right: right @ (Expr::Struct { .. } | Expr::Mod { .. }), - } => self.ty(right).expand(), - Expr::BinOp { left: &Expr::Ident { id: name, .. }, op: TokenKind::Decl, right } => { - let gid = self.tys.ins.globals.len() as ty::Global; - self.tys.ins.globals.push(Global { file, name, ..Default::default() }); - - let ty = ty::Kind::Global(gid); - self.pool.push_ci(file, None, self.tasks.len(), &mut self.ci); - - let ret = Expr::Return { pos: right.pos(), val: Some(right) }; - self.expr(&ret); - - self.ci.finalize(); - - let ret = self.ci.ret.expect("for return type to be infered"); - if self.errors.borrow().is_empty() { - self.ci.emit_body(&mut self.tys, self.files, Sig { args: Tuple::empty(), ret }); - self.ci.code.truncate(self.ci.code.len() - instrs::jala(0, 0, 0).0); - self.ci.emit(instrs::tx()); - - let func = Func { - file, - name, - expr: ExprRef::new(expr), - relocs: core::mem::take(&mut self.ci.relocs), - code: core::mem::take(&mut self.ci.code), - ..Default::default() - }; - self.pool.pop_ci(&mut self.ci); - self.complete_call_graph(); - - let mut mem = vec![0u8; self.tys.size_of(ret) as usize]; - - // TODO: return them back - let fuc = self.tys.ins.funcs.len() as ty::Func; - self.tys.ins.funcs.push(func); - - self.tys.dump_reachable(fuc, &mut self.ct.code); - - #[cfg(debug_assertions)] - { - let mut vc = String::new(); - if let Err(e) = self.tys.disasm(&self.ct.code, self.files, &mut vc, |_| {}) - { - panic!("{e} {}", vc); - } else { - log::trace!("{}", vc); - } - } - - self.ct.vm.write_reg(reg::RET, mem.as_mut_ptr() as u64); - let prev_pc = self.ct.push_pc(self.tys.ins.funcs[fuc as usize].offset); - loop { - match self.ct.vm.run().expect("TODO") { - hbvm::VmRunOk::End => break, - hbvm::VmRunOk::Timer => todo!(), - hbvm::VmRunOk::Ecall => todo!(), - hbvm::VmRunOk::Breakpoint => todo!(), - } - } - self.ct.pop_pc(prev_pc); - - match mem.len() { - 0 => unreachable!(), - len @ 1..=8 => mem - .copy_from_slice(&self.ct.vm.read_reg(reg::RET).0.to_ne_bytes()[..len]), - 9..=16 => todo!(), - _ => {} - } - - self.tys.ins.globals[gid as usize].data = mem; - } else { - self.pool.pop_ci(&mut self.ci); - } - self.tys.ins.globals[gid as usize].ty = ret; - - ty - } - e => unimplemented!("{e:#?}"), - }; - self.ci.file = prev_file; - self.tys.syms.insert(key, sym.compress(), &self.tys.ins); - sym + self.parse_ty(self.ci.file, expr, None, self.files) } fn ty_display(&self, ty: ty::Id) -> ty::Display { diff --git a/lang/tests/son_tests_comptime_min_reg_leak.txt b/lang/tests/son_tests_comptime_min_reg_leak.txt new file mode 100644 index 00000000..a9b8fb97 --- /dev/null +++ b/lang/tests/son_tests_comptime_min_reg_leak.txt @@ -0,0 +1,7 @@ +main: + LRA r1, r0, :a + LD r1, r1, 0a, 8h + JALA r0, r31, 0a +code size: 47 +ret: 50 +status: Ok(())