better error recovery

This commit is contained in:
mlokr 2024-09-06 18:50:28 +02:00
parent e1ea4a573c
commit 34b34ff926
7 changed files with 300 additions and 154 deletions

View file

@ -116,16 +116,11 @@ main := fn(): int {
fib := fn(n: int): int { fib := fn(n: int): int {
a := 0 a := 0
b := 1 b := 1
loop { loop if n == 0 break else {
if n == 0 break
c := a + b c := a + b
a = b a = b
b = c b = c
n -= 1 n -= 1
stack_reclamation_edge_case := 0
//continue
} }
return a return a
} }
@ -765,3 +760,69 @@ integer_range := fn(min: uint, max: int): uint {
return @eca(uint, 3, 4) % (@bitcast(max) - min + 1) + min return @eca(uint, 3, 4) % (@bitcast(max) - min + 1) + min
} }
``` ```
#### exhaustive_loop_testing
```hb
main := fn(): void {
if {
} != 3 {
return 1
}
if multiple_breaks(4) != 10 {
return 2
}
if state_change_in_break(0) != 0 {
return 3
}
if state_change_in_break(4) != 10 {
return 4
}
if continue_and_state_change(0) != 10 {
return 5
}
if continue_and_state_change(3) != 0 {
return 6
}
return 0
}
multiple_breaks := fn(arg: int): int {
loop if arg < 10 {
arg += 1
if arg == 3 break
} else break
return arg
}
state_change_in_break := fn(arg: int): int {
loop if arg < 10 {
if arg == 3 {
arg = 0
break
}
arg += 1
} else break
return arg
}
continue_and_state_change := fn(arg: int): int {
loop if arg < 10 {
if arg == 2 {
arg = 4
continue
}
if arg == 3 {
arg = 0
break
}
arg += 1
} else break
return arg
}
```

View file

@ -3429,7 +3429,6 @@ mod tests {
use { use {
super::parser, super::parser,
crate::{codegen::LoggedMem, log, parser::FileId}, crate::{codegen::LoggedMem, log, parser::FileId},
core::panic,
std::io, std::io,
}; };

View file

@ -182,6 +182,8 @@ impl TokenKind {
TokenKind::Mul => a.wrapping_mul(b), TokenKind::Mul => a.wrapping_mul(b),
TokenKind::Div => a.wrapping_div(b), TokenKind::Div => a.wrapping_div(b),
TokenKind::Shl => a.wrapping_shl(b as _), TokenKind::Shl => a.wrapping_shl(b as _),
TokenKind::Eq => (a == b) as i64,
TokenKind::Band => a & b,
s => todo!("{s}"), s => todo!("{s}"),
} }
} }

View file

@ -14,7 +14,9 @@ use {
}, },
core::fmt, core::fmt,
std::{ std::{
cell::RefCell,
collections::BTreeMap, collections::BTreeMap,
fmt::Display,
mem, mem,
ops::{self, Range}, ops::{self, Range},
rc::Rc, rc::Rc,
@ -28,7 +30,6 @@ macro_rules! node_loc {
} }
type Nid = u32; type Nid = u32;
const NILL: u32 = u32::MAX;
mod reg { mod reg {
@ -165,7 +166,7 @@ mod ty {
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Id(NonZeroU32); pub struct Id(NonZeroU32);
impl Default for Id { impl Default for Id {
@ -425,7 +426,7 @@ mod ty {
struct Nodes { struct Nodes {
values: Vec<PoolSlot>, values: Vec<PoolSlot>,
free: u32, free: u32,
lookup: HashMap<(Kind, [Nid; MAX_INPUTS]), Nid>, lookup: HashMap<(Kind, [Nid; MAX_INPUTS], ty::Id), Nid>,
} }
impl Default for Nodes { impl Default for Nodes {
@ -464,16 +465,17 @@ impl Nodes {
self.free = u32::MAX; self.free = u32::MAX;
} }
fn new_node<const SIZE: usize>( fn new_node_nop<const SIZE: usize>(
&mut self, &mut self,
ty: impl Into<ty::Id>, ty: impl Into<ty::Id>,
kind: Kind, kind: Kind,
inps: [Nid; SIZE], inps: [Nid; SIZE],
) -> Nid { ) -> Nid {
let mut inputs = [NILL; MAX_INPUTS]; let mut inputs = [Nid::MAX; MAX_INPUTS];
inputs[..inps.len()].copy_from_slice(&inps); inputs[..inps.len()].copy_from_slice(&inps);
let ty = ty.into();
if let Some(&id) = self.lookup.get(&(kind.clone(), inputs)) { if let Some(&id) = self.lookup.get(&(kind.clone(), inputs, ty)) {
debug_assert_eq!(&self[id].kind, &kind); debug_assert_eq!(&self[id].kind, &kind);
debug_assert_eq!(self[id].inputs, inputs); debug_assert_eq!(self[id].inputs, inputs);
return id; return id;
@ -485,14 +487,24 @@ impl Nodes {
color: 0, color: 0,
depth: u32::MAX, depth: u32::MAX,
lock_rc: 0, lock_rc: 0,
ty: ty.into(), ty,
outputs: vec![], outputs: vec![],
}); });
let prev = self.lookup.insert((kind, inputs), id); let prev = self.lookup.insert((kind, inputs, ty), id);
debug_assert_eq!(prev, None); debug_assert_eq!(prev, None);
self.add_deps(id, &inps); self.add_deps(id, &inps);
id
}
fn new_node<const SIZE: usize>(
&mut self,
ty: impl Into<ty::Id>,
kind: Kind,
inps: [Nid; SIZE],
) -> Nid {
let id = self.new_node_nop(ty, kind, inps);
if let Some(opt) = self.peephole(id) { if let Some(opt) = self.peephole(id) {
debug_assert_ne!(opt, id); debug_assert_ne!(opt, id);
self.lock(opt); self.lock(opt);
@ -505,17 +517,11 @@ impl Nodes {
} }
fn lock(&mut self, target: Nid) { fn lock(&mut self, target: Nid) {
if target == NILL {
return;
}
self[target].lock_rc += 1; self[target].lock_rc += 1;
} }
#[track_caller] #[track_caller]
fn unlock(&mut self, target: Nid) { fn unlock(&mut self, target: Nid) {
if target == NILL {
return;
}
self[target].lock_rc -= 1; self[target].lock_rc -= 1;
} }
@ -530,7 +536,9 @@ impl Nodes {
self[inp].outputs.swap_remove(index); self[inp].outputs.swap_remove(index);
self.remove(inp); self.remove(inp);
} }
let res = self.lookup.remove(&(self[target].kind.clone(), self[target].inputs)); let res =
self.lookup.remove(&(self[target].kind.clone(), self[target].inputs, self[target].ty));
debug_assert_eq!(res, Some(target)); debug_assert_eq!(res, Some(target));
self.remove_low(target); self.remove_low(target);
true true
@ -545,7 +553,7 @@ impl Nodes {
Kind::Tuple { .. } => {} Kind::Tuple { .. } => {}
Kind::ConstInt { .. } => {} Kind::ConstInt { .. } => {}
Kind::Call { .. } => {} Kind::Call { .. } => {}
Kind::If => {} Kind::If => return self.peephole_if(target),
Kind::Region => {} Kind::Region => {}
Kind::Phi => {} Kind::Phi => {}
Kind::Loop => {} Kind::Loop => {}
@ -553,6 +561,16 @@ impl Nodes {
None None
} }
fn peephole_if(&mut self, target: Nid) -> Option<Nid> {
let cond = self[target].inputs[1];
if let Kind::ConstInt { value } = self[cond].kind {
let ty = if value == 0 { ty::LEFT_UNREACHABLE } else { ty::RIGHT_UNREACHABLE };
return Some(self.new_node_nop(ty, Kind::If, [self[target].inputs[0], cond]));
}
None
}
fn peephole_binop(&mut self, target: Nid, op: TokenKind) -> Option<Nid> { fn peephole_binop(&mut self, target: Nid, op: TokenKind) -> Option<Nid> {
use TokenKind as T; use TokenKind as T;
let [mut lhs, mut rhs, ..] = self[target].inputs; let [mut lhs, mut rhs, ..] = self[target].inputs;
@ -660,14 +678,16 @@ impl Nodes {
} }
fn modify_input(&mut self, target: Nid, inp_index: usize, with: Nid) -> Nid { fn modify_input(&mut self, target: Nid, inp_index: usize, with: Nid) -> Nid {
let out = self.lookup.remove(&(self[target].kind.clone(), self[target].inputs)); let out =
self.lookup.remove(&(self[target].kind.clone(), self[target].inputs, self[target].ty));
debug_assert!(out == Some(target)); debug_assert!(out == Some(target));
debug_assert_ne!(self[target].inputs[inp_index], with); debug_assert_ne!(self[target].inputs[inp_index], with);
let prev = self[target].inputs[inp_index]; let prev = self[target].inputs[inp_index];
self[target].inputs[inp_index] = with; self[target].inputs[inp_index] = with;
if let Err(other) = if let Err(other) = self
self.lookup.try_insert((self[target].kind.clone(), self[target].inputs), target) .lookup
.try_insert((self[target].kind.clone(), self[target].inputs, self[target].ty), target)
{ {
let rpl = *other.entry.get(); let rpl = *other.entry.get();
self[target].inputs[inp_index] = prev; self[target].inputs[inp_index] = prev;
@ -691,9 +711,6 @@ impl Nodes {
#[track_caller] #[track_caller]
fn unlock_remove(&mut self, id: Nid) -> bool { fn unlock_remove(&mut self, id: Nid) -> bool {
if id == NILL {
return false;
}
self[id].lock_rc -= 1; self[id].lock_rc -= 1;
self.remove(id) self.remove(id)
} }
@ -717,7 +734,7 @@ impl Nodes {
} }
Kind::Return => { Kind::Return => {
write!(f, "{}: return [{:?}] ", node, self[node].inputs[0])?; write!(f, "{}: return [{:?}] ", node, self[node].inputs[0])?;
if self[node].inputs[2] != NILL { if self[node].inputs[2] != 0 {
self.fmt(f, self[node].inputs[2], rcs)?; self.fmt(f, self[node].inputs[2], rcs)?;
} }
writeln!(f)?; writeln!(f)?;
@ -788,6 +805,7 @@ impl Nodes {
| Kind::Return | Kind::Return
| Kind::Tuple { .. } | Kind::Tuple { .. }
| Kind::Call { .. } | Kind::Call { .. }
| Kind::If
| Kind::Region | Kind::Region
| Kind::Loop | Kind::Loop
) )
@ -907,14 +925,9 @@ impl Node {
} }
fn inputs(&self) -> &[Nid] { fn inputs(&self) -> &[Nid] {
let len = self.inputs.iter().position(|&n| n == NILL).unwrap_or(MAX_INPUTS); let len = self.inputs.iter().position(|&n| n == Nid::MAX).unwrap_or(MAX_INPUTS);
&self.inputs[..len] &self.inputs[..len]
} }
fn inputs_mut(&mut self) -> &mut [Nid] {
let len = self.inputs.iter().position(|&n| n == NILL).unwrap_or(MAX_INPUTS);
&mut self.inputs[..len]
}
} }
impl fmt::Display for Nodes { impl fmt::Display for Nodes {
@ -1377,6 +1390,7 @@ pub struct Codegen {
tys: Types, tys: Types,
ci: ItemCtx, ci: ItemCtx,
pool: Pool, pool: Pool,
errors: RefCell<String>,
} }
impl Codegen { impl Codegen {
@ -1488,16 +1502,15 @@ impl Codegen {
let msg = "i know nothing about this name gal which is vired \ let msg = "i know nothing about this name gal which is vired \
because we parsed succesfully"; because we parsed succesfully";
match *expr { match *expr {
Expr::Comment { .. } => Some(NILL), Expr::Comment { .. } => Some(0),
Expr::Ident { pos, id, .. } => { Expr::Ident { pos, id, .. } => {
let index = self let Some(index) = self.ci.vars.iter().position(|v| v.id == id) else {
.ci self.report(pos, msg);
.vars return Some(self.ci.end);
.iter() };
.position(|v| v.id == id)
.unwrap_or_else(|| self.report(pos, msg));
if self.ci.vars[index].value == NILL { if self.ci.vars[index].value == 0 {
self.ci.nodes.unlock(self.ci.vars[index].value);
let loob = self.ci.loops.last_mut().unwrap(); let loob = self.ci.loops.last_mut().unwrap();
let inps = [loob.node, loob.scope[index].value, loob.scope[index].value]; let inps = [loob.node, loob.scope[index].value, loob.scope[index].value];
self.ci.nodes.unlock(inps[1]); self.ci.nodes.unlock(inps[1]);
@ -1515,7 +1528,7 @@ impl Codegen {
let value = self.expr(right)?; let value = self.expr(right)?;
self.ci.nodes.lock(value); self.ci.nodes.lock(value);
self.ci.vars.push(Variable { id, value }); self.ci.vars.push(Variable { id, value });
Some(NILL) Some(0)
} }
Expr::BinOp { left: &Expr::Ident { id, pos, .. }, op: TokenKind::Assign, right } => { Expr::BinOp { left: &Expr::Ident { id, pos, .. }, op: TokenKind::Assign, right } => {
let value = self.expr(right)?; let value = self.expr(right)?;
@ -1523,11 +1536,12 @@ impl Codegen {
let Some(var) = self.ci.vars.iter_mut().find(|v| v.id == id) else { let Some(var) = self.ci.vars.iter_mut().find(|v| v.id == id) else {
self.report(pos, msg); self.report(pos, msg);
return Some(self.ci.end);
}; };
let prev = std::mem::replace(&mut var.value, value); let prev = std::mem::replace(&mut var.value, value);
self.ci.nodes.unlock_remove(prev); self.ci.nodes.unlock_remove(prev);
Some(NILL) Some(0)
} }
Expr::BinOp { left, op, right } => { Expr::BinOp { left, op, right } => {
let lhs = self.expr_ctx(left, ctx)?; let lhs = self.expr_ctx(left, ctx)?;
@ -1535,7 +1549,14 @@ impl Codegen {
let rhs = self.expr_ctx(right, Ctx::default().with_ty(self.tof(lhs))); let rhs = self.expr_ctx(right, Ctx::default().with_ty(self.tof(lhs)));
self.ci.nodes.unlock(lhs); self.ci.nodes.unlock(lhs);
let rhs = rhs?; let rhs = rhs?;
let ty = self.assert_ty(left.pos(), self.tof(rhs), self.tof(lhs), false); log::dbg!("{} {}", self.ty_display(self.tof(rhs)), self.ty_display(self.tof(lhs)));
let ty = self.assert_ty(
left.pos(),
self.tof(rhs),
self.tof(lhs),
false,
"right operand",
);
let id = let id =
self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, [lhs, rhs]); self.ci.nodes.new_node(ty::bin_ret(ty, op), Kind::BinOp { op }, [lhs, rhs]);
Some(id) Some(id)
@ -1559,7 +1580,7 @@ impl Codegen {
if let Some(branch) = branch { if let Some(branch) = branch {
return self.expr(branch); return self.expr(branch);
} else { } else {
return Some(NILL); return Some(0);
} }
} }
@ -1570,33 +1591,33 @@ impl Codegen {
self.ci.ctrl = self.ci.ctrl =
self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 0 }, [if_node]); self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 0 }, [if_node]);
let lcntrl = self.expr(then).map_or(NILL, |_| self.ci.ctrl); let lcntrl = self.expr(then).map_or(Nid::MAX, |_| self.ci.ctrl);
let then_scope = std::mem::replace(&mut self.ci.vars, else_scope); let then_scope = std::mem::replace(&mut self.ci.vars, else_scope);
self.ci.ctrl = self.ci.ctrl =
self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 1 }, [if_node]); self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 1 }, [if_node]);
let rcntrl = if let Some(else_) = else_ { let rcntrl = if let Some(else_) = else_ {
self.expr(else_).map_or(NILL, |_| self.ci.ctrl) self.expr(else_).map_or(Nid::MAX, |_| self.ci.ctrl)
} else { } else {
self.ci.ctrl self.ci.ctrl
}; };
if lcntrl == NILL && rcntrl == NILL { if lcntrl == Nid::MAX && rcntrl == Nid::MAX {
for then_var in then_scope { for then_var in then_scope {
self.ci.nodes.unlock_remove(then_var.value); self.ci.nodes.unlock_remove(then_var.value);
} }
return None; return None;
} else if lcntrl == NILL { } else if lcntrl == Nid::MAX {
for then_var in then_scope { for then_var in then_scope {
self.ci.nodes.unlock_remove(then_var.value); self.ci.nodes.unlock_remove(then_var.value);
} }
return Some(NILL); return Some(0);
} else if rcntrl == NILL { } else if rcntrl == Nid::MAX {
for else_var in &self.ci.vars { for else_var in &self.ci.vars {
self.ci.nodes.unlock_remove(else_var.value); self.ci.nodes.unlock_remove(else_var.value);
} }
self.ci.vars = then_scope; self.ci.vars = then_scope;
return Some(NILL); return Some(0);
} }
self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Region, [lcntrl, rcntrl]); self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Region, [lcntrl, rcntrl]);
@ -1620,39 +1641,41 @@ impl Codegen {
self.ci.nodes.lock(else_var.value); self.ci.nodes.lock(else_var.value);
} }
Some(NILL) Some(0)
} }
Expr::Loop { body, .. } => { Expr::Loop { body, .. } => {
self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Loop, [self.ci.ctrl; 2]); self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Loop, [self.ci.ctrl; 2]);
self.ci.loops.push(Loop { self.ci.loops.push(Loop {
node: self.ci.ctrl, node: self.ci.ctrl,
continue_: NILL, continue_: Nid::MAX,
continue_scope: vec![], continue_scope: vec![],
break_: NILL, break_: Nid::MAX,
break_scope: vec![], break_scope: vec![],
scope: self.ci.vars.clone(), scope: self.ci.vars.clone(),
}); });
for var in &mut self.ci.vars { for var in &mut self.ci.vars {
var.value = NILL; var.value = 0;
} }
self.ci.nodes[0].lock_rc += self.ci.vars.len() as u32;
self.expr(body); self.expr(body);
let Loop { node, continue_, continue_scope: _, break_, break_scope: _, scope } = let Loop { node, continue_, continue_scope: _, break_, break_scope: _, scope } =
self.ci.loops.pop().unwrap(); self.ci.loops.pop().unwrap();
assert!(continue_ == NILL, "TODO: handle continue"); assert!(continue_ == Nid::MAX, "TODO: handle continue");
self.ci.nodes.modify_input(node, 1, self.ci.ctrl); self.ci.nodes.modify_input(node, 1, self.ci.ctrl);
self.ci.ctrl = break_; self.ci.ctrl = break_;
if break_ == NILL { if break_ == Nid::MAX {
return None; return None;
} }
for (loop_var, scope_var) in self.ci.vars.iter_mut().zip(scope) { for (loop_var, scope_var) in self.ci.vars.iter_mut().zip(scope) {
if loop_var.value == NILL { if loop_var.value == 0 {
self.ci.nodes.unlock(loop_var.value);
loop_var.value = scope_var.value; loop_var.value = scope_var.value;
continue; continue;
} }
@ -1674,14 +1697,14 @@ impl Codegen {
self.ci.nodes.lock(loop_var.value); self.ci.nodes.lock(loop_var.value);
} }
Some(NILL) Some(0)
} }
Expr::Break { .. } => { Expr::Break { .. } => {
let loob = self.ci.loops.last_mut().unwrap(); let loob = self.ci.loops.last_mut().unwrap();
debug_assert_eq!(loob.break_, NILL, "TODO: multile breaks"); debug_assert_eq!(loob.break_, Nid::MAX, "TODO: multile breaks");
loob.break_ = self.ci.ctrl; loob.break_ = self.ci.ctrl;
self.ci.ctrl = NILL; self.ci.ctrl = self.ci.end;
None None
} }
Expr::Call { func: &Expr::Ident { pos, id, name, .. }, args, .. } => { Expr::Call { func: &Expr::Ident { pos, id, name, .. }, args, .. } => {
@ -1695,6 +1718,7 @@ impl Codegen {
self.ty_display(func.compress()) self.ty_display(func.compress())
), ),
); );
return Some(self.ci.end);
}; };
let fuc = &self.tys.funcs[func as usize]; let fuc = &self.tys.funcs[func as usize];
@ -1706,26 +1730,31 @@ impl Codegen {
unreachable!() unreachable!()
}; };
if args.len() != cargs.len() { self.assert_report(
let s = if cargs.len() == 1 { "" } else { "s" }; args.len() == cargs.len(),
self.report(
pos, pos,
format_args!( format_args!(
"expected {} function argumenr{s}, got {}", "expected {} function argumenr{}, got {}",
cargs.len(), cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len() args.len()
), ),
); );
}
let mut inps = vec![]; let mut inps = vec![];
for ((arg, _carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) { for ((arg, carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) {
let ty = self.tys.args[tyx]; let ty = self.tys.args[tyx];
if self.tys.size_of(ty) == 0 { if self.tys.size_of(ty) == 0 {
continue; continue;
} }
let value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?; let value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
_ = self.assert_ty(arg.pos(), self.tof(value), ty, true); _ = self.assert_ty(
arg.pos(),
self.tof(value),
ty,
true,
format_args!("argument {}", carg.name),
);
inps.push(value); inps.push(value);
} }
self.ci.ctrl = self.ci.ctrl =
@ -1742,30 +1771,31 @@ impl Codegen {
(self.tof(value), value) (self.tof(value), value)
} else { } else {
(ty::VOID.into(), NILL) (ty::VOID.into(), 0)
}; };
if value == NILL {
let inps = [self.ci.ctrl, self.ci.end];
self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps);
} else {
let inps = [self.ci.ctrl, self.ci.end, value]; let inps = [self.ci.ctrl, self.ci.end, value];
self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps); self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Return, inps);
}
let expected = *self.ci.ret.get_or_insert(ty); let expected = *self.ci.ret.get_or_insert(ty);
_ = self.assert_ty(pos, ty, expected, true); _ = self.assert_ty(pos, ty, expected, true, "return value");
None None
} }
Expr::Block { stmts, .. } => { Expr::Block { stmts, .. } => {
let base = self.ci.vars.len(); let base = self.ci.vars.len();
let mut ret = Some(NILL); let mut ret = Some(0);
for stmt in stmts { for stmt in stmts {
ret = ret.and(self.expr(stmt)); ret = ret.and(self.expr(stmt));
if let Some(id) = ret { if let Some(id) = ret {
_ = self.assert_ty(stmt.pos(), self.tof(id), ty::VOID.into(), true); _ = self.assert_ty(
stmt.pos(),
self.tof(id),
ty::VOID.into(),
true,
"statement",
);
} else { } else {
break; break;
} }
@ -1780,23 +1810,22 @@ impl Codegen {
ret ret
} }
Expr::Number { value, .. } => Some(self.ci.nodes.new_node( Expr::Number { value, .. } => Some(self.ci.nodes.new_node(
ctx.ty.unwrap_or(ty::INT.into()), ctx.ty.filter(|ty| ty.is_integer() || ty.is_pointer()).unwrap_or(ty::INT.into()),
Kind::ConstInt { value }, Kind::ConstInt { value },
[], [],
)), )),
ref e => self.report_unhandled_ast(e, "bruh"), ref e => {
self.report_unhandled_ast(e, "bruh");
Some(self.ci.end)
}
} }
} }
#[inline(always)] #[inline(always)]
fn tof(&self, id: Nid) -> ty::Id { fn tof(&self, id: Nid) -> ty::Id {
if id == NILL {
return ty::VOID.into();
}
self.ci.nodes[id].ty self.ci.nodes[id].ty
} }
//#[must_use]
fn complete_call_graph(&mut self) { fn complete_call_graph(&mut self) {
self.complete_call_graph_low(); self.complete_call_graph_low();
} }
@ -1827,7 +1856,7 @@ impl Codegen {
let prev_ci = std::mem::replace(&mut self.ci, repl); let prev_ci = std::mem::replace(&mut self.ci, repl);
self.ci.start = self.ci.nodes.new_node(ty::VOID, Kind::Start, []); self.ci.start = self.ci.nodes.new_node(ty::VOID, Kind::Start, []);
self.ci.end = self.ci.nodes.new_node(ty::VOID, Kind::End, []); self.ci.end = self.ci.nodes.new_node(ty::NEVER, Kind::End, []);
self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 0 }, [self.ci.start]); self.ci.ctrl = self.ci.nodes.new_node(ty::VOID, Kind::Tuple { index: 0 }, [self.ci.start]);
let Expr::BinOp { let Expr::BinOp {
@ -1864,6 +1893,7 @@ impl Codegen {
} }
} }
if self.errors.borrow().is_empty() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
self.ci.nodes.check_final_integrity(); self.ci.nodes.check_final_integrity();
@ -1919,6 +1949,7 @@ impl Codegen {
self.ci.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, (pushed + stack) as _)); self.ci.emit(instrs::addi64(reg::STACK_PTR, reg::STACK_PTR, (pushed + stack) as _));
self.ci.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0)); self.ci.emit(instrs::jala(reg::ZERO, reg::RET_ADDR, 0));
} }
}
self.tys.funcs[id as usize].code.append(&mut self.ci.code); self.tys.funcs[id as usize].code.append(&mut self.ci.code);
self.tys.funcs[id as usize].relocs.append(&mut self.ci.relocs); self.tys.funcs[id as usize].relocs.append(&mut self.ci.relocs);
@ -1934,7 +1965,8 @@ impl Codegen {
Kind::Start => unreachable!(), Kind::Start => unreachable!(),
Kind::End => unreachable!(), Kind::End => unreachable!(),
Kind::Return => { Kind::Return => {
if let Some(&ret) = self.ci.nodes[ctrl].inputs().get(2) { let ret = self.ci.nodes[ctrl].inputs[2];
if ret != 0 {
_ = self.color_expr_consume(ret); _ = self.color_expr_consume(ret);
node_loc!(self, ret) = match self.tys.size_of(self.ci.ret.expect("TODO")) { node_loc!(self, ret) = match self.tys.size_of(self.ci.ret.expect("TODO")) {
0 => Loc::default(), 0 => Loc::default(),
@ -1961,6 +1993,7 @@ impl Codegen {
ctrl = *self.ci.nodes[ctrl] ctrl = *self.ci.nodes[ctrl]
.outputs .outputs
.iter() .iter()
.inspect(|&&o| log::dbg!(self.ci.nodes[o].kind))
.find(|&&o| self.ci.nodes.is_cfg(o)) .find(|&&o| self.ci.nodes.is_cfg(o))
.unwrap(); .unwrap();
} }
@ -2114,7 +2147,8 @@ impl Codegen {
Kind::Start => unreachable!(), Kind::Start => unreachable!(),
Kind::End => unreachable!(), Kind::End => unreachable!(),
Kind::Return => { Kind::Return => {
if let Some(&ret) = self.ci.nodes[ctrl].inputs().get(2) { let ret = self.ci.nodes[ctrl].inputs[2];
if ret != 0 {
self.emit_expr_consume(ret); self.emit_expr_consume(ret);
} }
self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4)); self.ci.ret_relocs.push(Reloc::new(self.ci.code.len(), 1, 4));
@ -2157,6 +2191,7 @@ impl Codegen {
}; };
if self.ci.nodes[ctrl].outputs.len() == 1 { if self.ci.nodes[ctrl].outputs.len() == 1 {
log::dbg!("whata");
break 'b; break 'b;
} }
@ -2527,7 +2562,10 @@ impl Codegen {
fn ty(&mut self, expr: &Expr) -> ty::Id { fn ty(&mut self, expr: &Expr) -> ty::Id {
match *expr { match *expr {
Expr::Ident { id, .. } if ident::is_null(id) => id.into(), Expr::Ident { id, .. } if ident::is_null(id) => id.into(),
ref e => self.report_unhandled_ast(e, "type"), ref e => {
self.report_unhandled_ast(e, "type");
ty::NEVER.into()
}
} }
} }
@ -2557,6 +2595,7 @@ impl Codegen {
), ),
Err(name) => self.report(pos, format_args!("undefined indentifier: {name}")), Err(name) => self.report(pos, format_args!("undefined indentifier: {name}")),
} }
return ty::Kind::Builtin(ty::NEVER);
}; };
if let Some(existing) = self.tys.syms.get(&SymKey { file, ident }) { if let Some(existing) = self.tys.syms.get(&SymKey { file, ident }) {
@ -2590,7 +2629,12 @@ impl Codegen {
self.tys.args.push(ty); self.tys.args.push(ty);
} }
let args = self.pack_args(pos, arg_base); let Some(args) = self.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); let ret = self.ty(ret);
Some(Sig { args, ret }) Some(Sig { args, ret })
@ -2629,7 +2673,14 @@ impl Codegen {
#[must_use] #[must_use]
#[track_caller] #[track_caller]
fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id, preserve_expected: bool) -> ty::Id { fn assert_ty(
&self,
pos: Pos,
ty: ty::Id,
expected: ty::Id,
preserve_expected: bool,
hint: impl fmt::Display,
) -> ty::Id {
if let Some(res) = ty.try_upcast(expected) if let Some(res) = ty.try_upcast(expected)
&& (!preserve_expected || res == expected) && (!preserve_expected || res == expected)
{ {
@ -2637,31 +2688,40 @@ impl Codegen {
} else { } else {
let ty = self.ty_display(ty); let ty = self.ty_display(ty);
let expected = self.ty_display(expected); let expected = self.ty_display(expected);
self.report(pos, format_args!("expected {expected}, got {ty}")); self.report(pos, format_args!("expected {hint} to be of type {expected}, got {ty}"));
ty::NEVER.into()
} }
} }
fn report_log(&self, pos: Pos, msg: impl std::fmt::Display) { fn report_log(&self, pos: Pos, msg: impl std::fmt::Display) {
use std::fmt::Write;
let mut buf = self.errors.borrow_mut();
let str = &self.cfile().file; let str = &self.cfile().file;
let (line, mut col) = lexer::line_col(str.as_bytes(), pos); let (line, mut col) = lexer::line_col(str.as_bytes(), pos);
println!("{}:{}:{}: {}", self.cfile().path, line, col, msg); _ = writeln!(buf, "{}:{}:{}: {}", self.cfile().path, line, col, msg);
let line = &str[str[..pos as usize].rfind('\n').map_or(0, |i| i + 1) let line = &str[str[..pos as usize].rfind('\n').map_or(0, |i| i + 1)
..str[pos as usize..].find('\n').unwrap_or(str.len()) + pos as usize]; ..str[pos as usize..].find('\n').unwrap_or(str.len()) + pos as usize];
col += line.matches('\t').count() * 3; col += line.matches('\t').count() * 3;
println!("{}", line.replace("\t", " ")); _ = writeln!(buf, "{}", line.replace("\t", " "));
println!("{}^", " ".repeat(col - 1)) _ = writeln!(buf, "{}^", " ".repeat(col - 1));
} }
#[track_caller] #[track_caller]
fn report(&self, pos: Pos, msg: impl std::fmt::Display) -> ! { fn assert_report(&self, cond: bool, pos: Pos, msg: impl std::fmt::Display) {
if !cond {
self.report(pos, msg);
}
}
#[track_caller]
fn report(&self, pos: Pos, msg: impl std::fmt::Display) {
self.report_log(pos, msg); self.report_log(pos, msg);
unreachable!();
} }
#[track_caller] #[track_caller]
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! { fn report_unhandled_ast(&self, ast: &Expr, hint: &str) {
self.report( self.report(
ast.pos(), ast.pos(),
format_args!( format_args!(
@ -2670,17 +2730,17 @@ impl Codegen {
info for weak people:\n\ info for weak people:\n\
{ast:#?}" {ast:#?}"
), ),
) );
} }
fn cfile(&self) -> &parser::Ast { fn cfile(&self) -> &parser::Ast {
&self.files[self.ci.file as usize] &self.files[self.ci.file as usize]
} }
fn pack_args(&mut self, pos: Pos, arg_base: usize) -> ty::Tuple { fn pack_args(&mut self, arg_base: usize) -> Option<ty::Tuple> {
let needle = &self.tys.args[arg_base..]; let needle = &self.tys.args[arg_base..];
if needle.is_empty() { if needle.is_empty() {
return ty::Tuple::empty(); return Some(ty::Tuple::empty());
} }
let len = needle.len(); let len = needle.len();
// FIXME: maybe later when this becomes a bottleneck we use more // FIXME: maybe later when this becomes a bottleneck we use more
@ -2688,7 +2748,6 @@ impl Codegen {
let sp = self.tys.args.windows(needle.len()).position(|val| val == needle).unwrap(); let sp = self.tys.args.windows(needle.len()).position(|val| val == needle).unwrap();
self.tys.args.truncate((sp + needle.len()).max(arg_base)); self.tys.args.truncate((sp + needle.len()).max(arg_base));
ty::Tuple::new(sp, len) ty::Tuple::new(sp, len)
.unwrap_or_else(|| self.report(pos, "amount of arguments not supported"))
} }
fn emit_pass(&mut self, src: Nid, dst: Nid) { fn emit_pass(&mut self, src: Nid, dst: Nid) {
@ -2720,6 +2779,12 @@ impl Codegen {
} }
false false
} }
fn fatal_report(&self, pos: Pos, msg: impl Display) -> ! {
self.report(pos, msg);
eprintln!("{}", self.errors.borrow());
std::process::exit(1);
}
} }
#[derive(Default)] #[derive(Default)]
@ -2854,6 +2919,14 @@ mod tests {
codegen.generate(); codegen.generate();
{
let errors = codegen.errors.borrow();
if !errors.is_empty() {
output.push_str(&errors);
return;
}
}
let mut out = Vec::new(); let mut out = Vec::new();
codegen.assemble(&mut out); codegen.assemble(&mut out);
@ -2946,5 +3019,6 @@ mod tests {
const_folding_with_arg => README; const_folding_with_arg => README;
// FIXME: contains redundant copies // FIXME: contains redundant copies
branch_assignments => README; branch_assignments => README;
exhaustive_loop_testing => README;
} }
} }

View file

@ -0,0 +1,10 @@
main:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r1, 0d
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 77
ret: 0
status: Ok(())

0
loob.hb Normal file
View file