unifiing the type resolution into a trait

This commit is contained in:
Jakub Doka 2024-10-20 15:16:55 +02:00
parent c3a6e62bf2
commit 44c4b71bb3
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
4 changed files with 441 additions and 467 deletions

View file

@ -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<ty::Id>,
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<Ident>,
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<ty::Id> {
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<Vec<u8>>) {
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<Sig> {
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<ty::Id>) -> u64 {
self.eval_const_low(expr, Some(ty.into())).0
fn eval_const(&mut self, file: FileId, expr: &Expr, ty: impl Into<ty::Id>) -> u64 {
self.eval_const_low(file, expr, Some(ty.into())).0
}
fn eval_const_low(&mut self, expr: &Expr, mut ty: Option<ty::Id>) -> (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<ty::Id>,
) -> (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<Ident, &str>,
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<T, E>(

View file

@ -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::<AbleOsExecutableHeader>();
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<ty::Id>;
fn report(&self, pos: Pos, msg: impl Display) -> ty::Id;
fn eval_ty(
&mut self,
file: FileId,
name: Option<Ident>,
expr: &Expr,
files: &[parser::Ast],
) -> ty::Id;
fn find_type(
&mut self,
pos: Pos,
file: FileId,
id: Result<Ident, &str>,
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<Ident>,
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<usize> {
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<Ident, &str>,
files: &[parser::Ast],
) -> Option<ty::Id> {
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<ty::Id> {
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<u8>) {
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"
};

View file

@ -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<String>,
}
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<Ident>,
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<ty::Id> {
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<Ident, &str>) -> 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 {

View file

@ -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(())