fixing code scheduling bugs

This commit is contained in:
Jakub Doka 2024-10-19 10:17:36 +02:00
parent 89cc611f7a
commit 6ad0b41759
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
5 changed files with 346 additions and 210 deletions

View file

@ -452,6 +452,8 @@ mod ty {
builtin_type! { builtin_type! {
UNDECLARED; UNDECLARED;
LEFT_UNREACHABLE;
RIGHT_UNREACHABLE;
NEVER; NEVER;
VOID; VOID;
TYPE; TYPE;
@ -464,8 +466,7 @@ mod ty {
I16; I16;
I32; I32;
INT; INT;
LEFT_UNREACHABLE;
RIGHT_UNREACHABLE;
} }
macro_rules! type_kind { macro_rules! type_kind {

View file

@ -210,7 +210,9 @@ impl<'a, 'b> Parser<'a, 'b> {
); );
} }
let index = self.ctx.idents.binary_search_by_key(&id, |s| s.ident).expect("fck up"); let Ok(index) = self.ctx.idents.binary_search_by_key(&id, |s| s.ident) else {
self.report(pos, "the identifier is rezerved for a builtin (proably)");
};
if core::mem::replace(&mut self.ctx.idents[index].declared, true) { if core::mem::replace(&mut self.ctx.idents[index].declared, true) {
self.report( self.report(
pos, pos,

View file

@ -9,8 +9,7 @@ use {
idfl::{self}, idfl::{self},
Expr, ExprRef, FileId, Pos, Expr, ExprRef, FileId, Pos,
}, },
reg, task, reg, task, ty,
ty::{self},
vc::{BitSet, Vc}, vc::{BitSet, Vc},
Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types,
}, },
@ -35,6 +34,16 @@ type Nid = u16;
type Lookup = crate::ctx_map::CtxMap<Nid>; type Lookup = crate::ctx_map::CtxMap<Nid>;
trait StoreId: Sized {
fn to_store(self) -> Option<Self>;
}
impl StoreId for Nid {
fn to_store(self) -> Option<Self> {
(self != NEVER).then_some(self)
}
}
impl crate::ctx_map::CtxEntry for Nid { impl crate::ctx_map::CtxEntry for Nid {
type Ctx = [Result<Node, Nid>]; type Ctx = [Result<Node, Nid>];
type Key<'a> = (Kind, &'a [Nid], ty::Id); type Key<'a> = (Kind, &'a [Nid], ty::Id);
@ -414,9 +423,9 @@ impl Nodes {
Kind::Then => write!(out, "ctrl: {:<5}", "then"), Kind::Then => write!(out, "ctrl: {:<5}", "then"),
Kind::Else => write!(out, "ctrl: {:<5}", "else"), Kind::Else => write!(out, "ctrl: {:<5}", "else"),
Kind::Stck => write!(out, "stck: "), Kind::Stck => write!(out, "stck: "),
Kind::Load => write!(out, "load: "), Kind::Load => write!(out, "load: "),
Kind::Stre => write!(out, "stre: "), Kind::Stre => write!(out, "stre: "),
_ => unreachable!(), Kind::Mem => write!(out, " mem: "),
}?; }?;
if self[node].kind != Kind::Loop && self[node].kind != Kind::Region { if self[node].kind != Kind::Loop && self[node].kind != Kind::Region {
@ -613,26 +622,48 @@ impl Nodes {
target target
} }
fn load_loop_value(&mut self, index: usize, value: &mut Value, loops: &mut [Loop]) { fn load_loop_var(&mut self, index: usize, value: &mut Nid, loops: &mut [Loop]) {
if value.id != 0 { self.load_loop_value(
debug_assert!(!value.var); &mut |l| {
l.scope
.vars
.get_mut(index)
.map_or((ty::Id::VOID, &mut l.scope.store), |v| (v.ty, &mut v.value))
},
value,
loops,
);
}
fn load_loop_store(&mut self, value: &mut Nid, loops: &mut [Loop]) {
self.load_loop_value(&mut |l| (ty::Id::VOID, &mut l.scope.store), value, loops);
}
fn load_loop_value(
&mut self,
get_lvalue: &mut impl FnMut(&mut Loop) -> (ty::Id, &mut Nid),
value: &mut Nid,
loops: &mut [Loop],
) {
if *value != VOID {
return; return;
} }
let [loob, loops @ ..] = loops else { unreachable!() }; let [loob, loops @ ..] = loops else { unreachable!() };
let lvalue = &mut loob.scope.vars[index].value; let node = loob.node;
let (ty, lvalue) = get_lvalue(loob);
self.load_loop_value(index, lvalue, loops); self.load_loop_value(get_lvalue, lvalue, loops);
if !self[lvalue.id].is_lazy_phi() { if !self[*lvalue].is_lazy_phi() {
self.unlock(value.id); self.unlock(*value);
let inps = [loob.node, lvalue.id, VOID]; let inps = [node, *lvalue, VOID];
self.unlock(lvalue.id); self.unlock(*lvalue);
lvalue.id = self.new_node_nop(lvalue.ty, Kind::Phi, inps); *lvalue = self.new_node_nop(ty, Kind::Phi, inps);
self[lvalue.id].lock_rc += 2; self[*lvalue].lock_rc += 2;
} else { } else {
self.lock(lvalue.id); self.lock(*lvalue);
self.unlock(value.id); self.unlock(*value);
} }
*value = *lvalue; *value = *lvalue;
} }
@ -679,26 +710,26 @@ impl Nodes {
} }
fn lock_scope(&mut self, scope: &Scope) { fn lock_scope(&mut self, scope: &Scope) {
if let Some(str) = scope.store { if let Some(str) = scope.store.to_store() {
self.lock(str); self.lock(str);
} }
for &load in &scope.loads { for &load in &scope.loads {
self.lock(load); self.lock(load);
} }
for var in &scope.vars { for var in &scope.vars {
self.lock(var.value.id); self.lock(var.value);
} }
} }
fn unlock_remove_scope(&mut self, scope: &Scope) { fn unlock_remove_scope(&mut self, scope: &Scope) {
if let Some(str) = scope.store { if let Some(str) = scope.store.to_store() {
self.unlock_remove(str); self.unlock_remove(str);
} }
for &load in &scope.loads { for &load in &scope.loads {
self.unlock_remove(load); self.unlock_remove(load);
} }
for var in &scope.vars { for var in &scope.vars {
self.unlock_remove(var.value.id); self.unlock_remove(var.value);
} }
} }
} }
@ -768,7 +799,7 @@ pub enum Kind {
impl Kind { impl Kind {
fn is_pinned(&self) -> bool { fn is_pinned(&self) -> bool {
self.is_cfg() || matches!(self, Self::Phi | Self::Mem) self.is_cfg() || matches!(self, Self::Phi | Self::Mem | Self::Arg { .. })
} }
fn is_cfg(&self) -> bool { fn is_cfg(&self) -> bool {
@ -780,7 +811,6 @@ impl Kind {
| Self::Entry | Self::Entry
| Self::Then | Self::Then
| Self::Else | Self::Else
| Self::Arg { .. }
| Self::Call { .. } | Self::Call { .. }
| Self::If | Self::If
| Self::Region | Self::Region
@ -850,14 +880,24 @@ struct Loop {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct Variable { struct Variable {
id: Ident, id: Ident,
value: Value, ty: ty::Id,
value: Nid,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct Scope { struct Scope {
vars: Vec<Variable>, vars: Vec<Variable>,
loads: Vec<Nid>, loads: Vec<Nid>,
store: Option<Nid>, store: Nid,
}
impl Scope {
pub fn iter_elems_mut(&mut self) -> impl Iterator<Item = (ty::Id, &mut Nid)> {
self.vars
.iter_mut()
.map(|v| (v.ty, &mut v.value))
.chain(core::iter::once((ty::Id::VOID, &mut self.store)))
}
} }
#[derive(Default)] #[derive(Default)]
@ -943,7 +983,7 @@ impl Default for Regalloc {
} }
} }
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy, PartialEq, Eq)]
struct Value { struct Value {
ty: ty::Id, ty: ty::Id,
var: bool, var: bool,
@ -1025,8 +1065,13 @@ impl Codegen {
} }
fn store_mem(&mut self, region: Nid, value: Nid) -> Nid { fn store_mem(&mut self, region: Nid, value: Nid) -> Nid {
if value == NEVER {
return NEVER;
}
let mut vc = Vc::from([VOID, value, region]); let mut vc = Vc::from([VOID, value, region]);
if let Some(str) = self.ci.scope.store { self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
if let Some(str) = self.ci.scope.store.to_store() {
self.ci.nodes.unlock(str); self.ci.nodes.unlock(str);
vc.push(str); vc.push(str);
} }
@ -1041,13 +1086,15 @@ impl Codegen {
} }
let store = self.ci.nodes.new_node(self.tof(value), Kind::Stre, vc); let store = self.ci.nodes.new_node(self.tof(value), Kind::Stre, vc);
self.ci.nodes.lock(store); self.ci.nodes.lock(store);
self.ci.scope.store = Some(store); self.ci.scope.store = store;
store store
} }
fn load_mem(&mut self, region: Nid, ty: ty::Id) -> Nid { fn load_mem(&mut self, region: Nid, ty: ty::Id) -> Nid {
debug_assert_ne!(region, VOID);
let mut vc = Vc::from([VOID, region]); let mut vc = Vc::from([VOID, region]);
if let Some(str) = self.ci.scope.store { self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
if let Some(str) = self.ci.scope.store.to_store() {
vc.push(str); vc.push(str);
} }
let load = self.ci.nodes.new_node(ty, Kind::Load, vc); let load = self.ci.nodes.new_node(ty, Kind::Load, vc);
@ -1086,14 +1133,13 @@ impl Codegen {
return Value::NEVER; return Value::NEVER;
}; };
self.ci.nodes.load_loop_value( self.ci.nodes.load_loop_var(
index, index,
&mut self.ci.scope.vars[index].value, &mut self.ci.scope.vars[index].value,
&mut self.ci.loops, &mut self.ci.loops,
); );
debug_assert_ne!(self.ci.scope.vars[index].value.ty, ty::Id::VOID);
Some(Value::var(index).ty(self.ci.scope.vars[index].value.ty)) Some(Value::var(index).ty(self.ci.scope.vars[index].ty))
} }
Expr::Number { value, .. } => Some(self.ci.nodes.new_node_lit( Expr::Number { value, .. } => Some(self.ci.nodes.new_node_lit(
ctx.ty.filter(|ty| ty.is_integer() || ty.is_pointer()).unwrap_or(ty::Id::INT), ctx.ty.filter(|ty| ty.is_integer() || ty.is_pointer()).unwrap_or(ty::Id::INT),
@ -1108,8 +1154,9 @@ impl Codegen {
}; };
let mut inps = Vc::from([self.ci.ctrl, value.id]); let mut inps = Vc::from([self.ci.ctrl, value.id]);
for &m in self.ci.scope.store.iter() { self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
inps.push(m); if let Some(str) = self.ci.scope.store.to_store() {
inps.push(str);
} }
self.ci.ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::Return, inps); self.ci.ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::Return, inps);
@ -1202,7 +1249,7 @@ impl Codegen {
Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => { Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => {
let value = self.expr(right)?; let value = self.expr(right)?;
self.ci.nodes.lock(value.id); self.ci.nodes.lock(value.id);
self.ci.scope.vars.push(Variable { id, value }); self.ci.scope.vars.push(Variable { id, value: value.id, ty: value.ty });
Some(Value::VOID) Some(Value::VOID)
} }
Expr::BinOp { left, op: TokenKind::Assign, right } => { Expr::BinOp { left, op: TokenKind::Assign, right } => {
@ -1214,8 +1261,8 @@ impl Codegen {
if dest.var { if dest.var {
self.ci.nodes.lock(value.id); self.ci.nodes.lock(value.id);
let var = &mut self.ci.scope.vars[(u16::MAX - dest.id) as usize]; let var = &mut self.ci.scope.vars[(u16::MAX - dest.id) as usize];
let prev = core::mem::replace(&mut var.value, value); let prev = core::mem::replace(&mut var.value, value.id);
self.ci.nodes.unlock_remove(prev.id); self.ci.nodes.unlock_remove(prev);
} else if dest.ptr { } else if dest.ptr {
self.store_mem(dest.id, value.id); self.store_mem(dest.id, value.id);
} else { } else {
@ -1242,6 +1289,10 @@ impl Codegen {
[VOID], [VOID],
)) ))
} }
Expr::Directive { name: "as", args: [ty, expr], .. } => {
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: &Expr::Ident { pos, id, .. }, args, .. } => {
self.ci.call_count += 1; self.ci.call_count += 1;
let cfile = self.cfile().clone(); let cfile = self.cfile().clone();
@ -1294,7 +1345,7 @@ impl Codegen {
inps.push(value.id); inps.push(value.id);
} }
if let Some(str) = self.ci.scope.store { if let Some(str) = self.ci.scope.store.to_store() {
inps.push(str); inps.push(str);
} }
for load in self.ci.scope.loads.drain(..) { for load in self.ci.scope.loads.drain(..) {
@ -1317,6 +1368,59 @@ impl Codegen {
Some(Value::new(self.ci.ctrl).ty(sig.ret)) Some(Value::new(self.ci.ctrl).ty(sig.ret))
} }
Expr::Tupl { pos, ty, fields, .. } => {
let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else {
self.report(
pos,
"the type of struct cannot be inferred from context, \
use an explicit type instead: <type>.{ ... }",
);
return Value::NEVER;
};
let ty::Kind::Struct(s) = sty.expand() else {
let inferred = if ty.is_some() { "" } else { "inferred " };
self.report(
pos,
fa!(
"the {inferred}type of the constructor is `{}`, \
but thats not a struct",
self.ty_display(sty)
),
);
return Value::NEVER;
};
// TODO: dont allocate
let mut offs = OffsetIter::new(s, &self.tys);
let mem = self.ci.nodes.new_node(sty, Kind::Stck, [VOID, MEM]);
for field in fields {
let Some((ty, offset)) = offs.next_ty(&self.tys) else {
self.report(field.pos(), "this init argumen overflows the field count");
break;
};
let value = self.expr_ctx(field, Ctx::default().with_ty(ty))?;
let mem = self.offset(mem, ty, offset);
self.store_mem(mem, value.id);
}
let field_list = offs
.into_iter(&self.tys)
.map(|(f, ..)| self.tys.names.ident_str(f.name))
.intersperse(", ")
.collect::<String>();
if !field_list.is_empty() {
self.report(
pos,
fa!("the struct initializer is missing {field_list} \
(append them to the end of the constructor)"),
);
}
Some(Value::ptr(mem).ty(sty))
}
Expr::Ctor { pos, ty, fields, .. } => { Expr::Ctor { pos, ty, fields, .. } => {
let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else { let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else {
self.report( self.report(
@ -1400,7 +1504,7 @@ impl Codegen {
self.ci.nodes.lock(self.ci.ctrl); self.ci.nodes.lock(self.ci.ctrl);
for var in self.ci.scope.vars.drain(base..) { for var in self.ci.scope.vars.drain(base..) {
self.ci.nodes.unlock_remove(var.value.id); self.ci.nodes.unlock_remove(var.value);
} }
self.ci.nodes.unlock(self.ci.ctrl); self.ci.nodes.unlock(self.ci.ctrl);
@ -1415,8 +1519,8 @@ impl Codegen {
scope: self.ci.scope.clone(), scope: self.ci.scope.clone(),
}); });
for var in &mut self.ci.scope.vars { for (_, var) in &mut self.ci.scope.iter_elems_mut() {
var.value = Value::VOID; *var = VOID;
} }
self.ci.nodes.lock_scope(&self.ci.scope); self.ci.nodes.lock_scope(&self.ci.scope);
@ -1461,66 +1565,50 @@ impl Codegen {
core::mem::swap(&mut self.ci.scope, &mut bres); core::mem::swap(&mut self.ci.scope, &mut bres);
for ((dest_var, mut scope_var), loop_var) in for (((_, dest_value), (_, &mut mut scope_value)), (_, &mut loop_value)) in self
self.ci.scope.vars.iter_mut().zip(scope.vars.drain(..)).zip(bres.vars.drain(..)) .ci
.scope
.iter_elems_mut()
.zip(scope.iter_elems_mut())
.zip(bres.iter_elems_mut())
{ {
self.ci.nodes.unlock(loop_var.value.id); self.ci.nodes.unlock(loop_value);
if loop_var.value.id != VOID { if loop_value != VOID {
self.ci.nodes.unlock(scope_var.value.id); self.ci.nodes.unlock(scope_value);
if loop_var.value.id != scope_var.value.id { if loop_value != scope_value {
scope_var.value.id = self.ci.nodes.modify_input( scope_value = self.ci.nodes.modify_input(scope_value, 2, loop_value);
scope_var.value.id, self.ci.nodes.lock(scope_value);
2,
loop_var.value.id,
);
self.ci.nodes.lock(scope_var.value.id);
} else { } else {
if dest_var.value.id == scope_var.value.id { if *dest_value == scope_value {
self.ci.nodes.unlock(dest_var.value.id); self.ci.nodes.unlock(*dest_value);
dest_var.value.id = VOID; *dest_value = VOID;
self.ci.nodes.lock(dest_var.value.id); self.ci.nodes.lock(*dest_value);
} }
let phi = &self.ci.nodes[scope_var.value.id]; let phi = &self.ci.nodes[scope_value];
debug_assert_eq!(phi.kind, Kind::Phi); debug_assert_eq!(phi.kind, Kind::Phi);
debug_assert_eq!(phi.inputs[2], VOID); debug_assert_eq!(phi.inputs[2], VOID);
debug_assert_eq!(phi.ty, scope_var.value.ty);
let prev = phi.inputs[1]; let prev = phi.inputs[1];
self.ci.nodes.replace(scope_var.value.id, prev); self.ci.nodes.replace(scope_value, prev);
scope_var.value.id = prev; scope_value = prev;
self.ci.nodes.lock(prev); self.ci.nodes.lock(prev);
} }
} }
if dest_var.value.id == VOID { if *dest_value == VOID {
self.ci.nodes.unlock(dest_var.value.id); self.ci.nodes.unlock(*dest_value);
dest_var.value = scope_var.value; *dest_value = scope_value;
self.ci.nodes.lock(dest_var.value.id); self.ci.nodes.lock(*dest_value);
} }
debug_assert!( debug_assert!(
self.ci.nodes[dest_var.value.id].kind != Kind::Phi self.ci.nodes[*dest_value].kind != Kind::Phi
|| self.ci.nodes[dest_var.value.id].inputs[2] != 0 || self.ci.nodes[*dest_value].inputs[2] != 0
); );
self.ci.nodes.unlock_remove(scope_var.value.id); self.ci.nodes.unlock_remove(scope_value);
} }
if bres.store != scope.store {
let (to_store, from_store, prev) =
(bres.store.unwrap(), scope.store.unwrap(), self.ci.scope.store.unwrap());
self.ci.nodes.unlock_remove(prev);
self.ci.scope.store = Some(
self.ci
.nodes
.new_node(ty::Id::VOID, Kind::Phi, [node, from_store, to_store]),
);
self.ci.nodes.lock(self.ci.scope.store.unwrap());
}
self.ci.nodes.unlock_remove_scope(&scope);
self.ci.nodes.unlock_remove_scope(&bres);
self.ci.nodes.unlock(self.ci.ctrl); self.ci.nodes.unlock(self.ci.ctrl);
Some(Value::VOID) Some(Value::VOID)
@ -1551,8 +1639,9 @@ impl Codegen {
} }
} }
self.ci.nodes.load_loop_store(&mut self.ci.scope.store, &mut self.ci.loops);
let orig_store = self.ci.scope.store; let orig_store = self.ci.scope.store;
if let Some(str) = orig_store { if let Some(str) = orig_store.to_store() {
self.ci.nodes.lock(str); self.ci.nodes.lock(str);
} }
let else_scope = self.ci.scope.clone(); let else_scope = self.ci.scope.clone();
@ -1569,7 +1658,7 @@ impl Codegen {
self.ci.ctrl self.ci.ctrl
}; };
if let Some(str) = orig_store { if let Some(str) = orig_store.to_store() {
self.ci.nodes.unlock_remove(str); self.ci.nodes.unlock_remove(str);
} }
@ -1628,12 +1717,8 @@ impl Codegen {
fn strip_var(&mut self, n: &mut Value) { fn strip_var(&mut self, n: &mut Value) {
if core::mem::take(&mut n.var) { if core::mem::take(&mut n.var) {
let id = (u16::MAX - n.id) as usize; let id = (u16::MAX - n.id) as usize;
self.ci.nodes.load_loop_value( self.ci.nodes.load_loop_var(id, &mut self.ci.scope.vars[id].value, &mut self.ci.loops);
id, n.id = self.ci.scope.vars[id].value;
&mut self.ci.scope.vars[id].value,
&mut self.ci.loops,
);
*n = self.ci.scope.vars[id].value;
} }
} }
@ -1683,32 +1768,26 @@ impl Codegen {
from: &mut Scope, from: &mut Scope,
drop_from: bool, drop_from: bool,
) { ) {
for (i, (to_var, from_var)) in to.vars.iter_mut().zip(&mut from.vars).enumerate() { for (i, ((ty, to_value), (_, from_value))) in
if to_var.value.id != from_var.value.id { to.iter_elems_mut().zip(from.iter_elems_mut()).enumerate()
nodes.load_loop_value(i, &mut from_var.value, loops); {
nodes.load_loop_value(i, &mut to_var.value, loops); if to_value != from_value {
if to_var.value.id != from_var.value.id { nodes.load_loop_var(i, from_value, loops);
let ty = nodes[to_var.value.id].ty; nodes.load_loop_var(i, to_value, loops);
debug_assert_eq!(ty, nodes[from_var.value.id].ty, "TODO: typecheck properly"); if to_value != from_value {
let inps = [ctrl, *from_value, *to_value];
let inps = [ctrl, from_var.value.id, to_var.value.id]; nodes.unlock(*to_value);
nodes.unlock(to_var.value.id); *to_value = nodes.new_node(ty, Kind::Phi, inps);
to_var.value.id = nodes.new_node(ty, Kind::Phi, inps); nodes.lock(*to_value);
nodes.lock(to_var.value.id);
} }
} }
} }
if to.store != from.store { for load in to.loads.drain(..) {
let (to_store, from_store) = (to.store.unwrap(), from.store.unwrap()); nodes.unlock_remove(load);
nodes.unlock(to_store);
to.store = Some(nodes.new_node(ty::Id::VOID, Kind::Phi, [ctrl, from_store, to_store]));
nodes.lock(to.store.unwrap());
} }
for load in from.loads.drain(..) {
to.loads.extend(&from.loads); nodes.unlock_remove(load);
for &load in &from.loads {
nodes.lock(load);
} }
if drop_from { if drop_from {
@ -1756,6 +1835,8 @@ impl Codegen {
let mem = self.ci.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]); let mem = self.ci.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]);
debug_assert_eq!(mem, MEM); debug_assert_eq!(mem, MEM);
self.ci.nodes.lock(mem); self.ci.nodes.lock(mem);
self.ci.nodes.lock(end);
self.ci.scope.store = end;
let Expr::BinOp { let Expr::BinOp {
left: Expr::Ident { .. }, left: Expr::Ident { .. },
@ -1773,7 +1854,7 @@ impl Codegen {
self.ci.nodes.lock(value); self.ci.nodes.lock(value);
let sym = parser::find_symbol(&ast.symbols, arg.id); let sym = parser::find_symbol(&ast.symbols, arg.id);
assert!(sym.flags & idfl::COMPTIME == 0, "TODO"); assert!(sym.flags & idfl::COMPTIME == 0, "TODO");
self.ci.scope.vars.push(Variable { id: arg.id, value: Value::new(value).ty(ty) }); self.ci.scope.vars.push(Variable { id: arg.id, value, ty });
} }
let orig_vars = self.ci.scope.vars.clone(); let orig_vars = self.ci.scope.vars.clone();
@ -1782,6 +1863,7 @@ impl Codegen {
self.report(body.pos(), "expected all paths in the fucntion to return"); self.report(body.pos(), "expected all paths in the fucntion to return");
} }
self.ci.nodes.unlock(end);
self.ci.nodes.unlock(end); self.ci.nodes.unlock(end);
self.ci.nodes.unlock_remove_scope(&core::mem::take(&mut self.ci.scope)); self.ci.nodes.unlock_remove_scope(&core::mem::take(&mut self.ci.scope));
@ -2207,16 +2289,13 @@ impl Codegen {
#[track_caller] #[track_caller]
fn report(&self, pos: Pos, msg: impl core::fmt::Display) { fn report(&self, pos: Pos, msg: impl core::fmt::Display) {
let mut buf = self.errors.borrow_mut(); let mut buf = self.errors.borrow_mut();
writeln!(buf, "{}", self.cfile().report(pos, msg)).unwrap(); write!(buf, "{}", self.cfile().report(pos, msg)).unwrap();
} }
#[track_caller] #[track_caller]
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) { fn report_unhandled_ast(&self, ast: &Expr, hint: &str) {
log::debug!("{ast:#?}"); log::debug!("{ast:#?}");
self.fatal_report( self.fatal_report(ast.pos(), fa!("compiler does not (yet) know how to handle ({hint})"));
ast.pos(),
fa!("compiler does not (yet) know how to handle ({hint}):\n{}", self.ast_display(ast)),
);
} }
fn cfile(&self) -> &parser::Ast { fn cfile(&self) -> &parser::Ast {
@ -2230,7 +2309,7 @@ impl Codegen {
fn gcm(&mut self) { fn gcm(&mut self) {
self.ci.nodes.visited.clear(self.ci.nodes.values.len()); self.ci.nodes.visited.clear(self.ci.nodes.values.len());
push_up(&mut self.ci.nodes, NEVER); push_up(&mut self.ci.nodes);
// TODO: handle infinte loops // TODO: handle infinte loops
self.ci.nodes.visited.clear(self.ci.nodes.values.len()); self.ci.nodes.visited.clear(self.ci.nodes.values.len());
push_down(&mut self.ci.nodes, VOID); push_down(&mut self.ci.nodes, VOID);
@ -2586,8 +2665,8 @@ impl<'a> Function<'a> {
self.add_instr(nid, ops); self.add_instr(nid, ops);
} }
} }
Kind::Stre { .. } if node.inputs[2] == VOID => self.nodes.lock(nid), Kind::Stre if node.inputs[2] == VOID => self.nodes.lock(nid),
Kind::Stre { .. } => { Kind::Stre => {
let mut region = node.inputs[2]; let mut region = node.inputs[2];
if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& self.nodes.is_const(self.nodes[region].inputs[2]) && self.nodes.is_const(self.nodes[region].inputs[2])
@ -2751,18 +2830,12 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth {
depth depth
} }
Kind::Start | Kind::End => 1, Kind::Start | Kind::End => 1,
_ => unreachable!(), u => unreachable!("{u:?}"),
}; };
nodes[target].loop_depth nodes[target].loop_depth
} }
fn better(nodes: &mut Nodes, is: Nid, then: Nid) -> bool {
loop_depth(is, nodes) < loop_depth(then, nodes)
|| idepth(nodes, is) > idepth(nodes, then)
|| nodes[then].kind == Kind::If
}
fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth { fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth {
if target == VOID { if target == VOID {
return 0; return 0;
@ -2779,103 +2852,147 @@ fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth {
nodes[target].depth nodes[target].depth
} }
fn push_up(nodes: &mut Nodes, node: Nid) { fn push_up(nodes: &mut Nodes) {
if !nodes.visited.set(node) { fn collect_rpo(node: Nid, nodes: &mut Nodes, rpo: &mut Vec<Nid>) {
return; if !nodes.is_cfg(node) || !nodes.visited.set(node) {
return;
}
for i in 0..nodes[node].outputs.len() {
collect_rpo(nodes[node].outputs[i], nodes, rpo);
}
rpo.push(node);
} }
if nodes[node].kind.is_pinned() { fn push_up_impl(node: Nid, nodes: &mut Nodes) {
for i in 0..nodes[node].inputs.len() { if !nodes.visited.set(node) {
let i = nodes[node].inputs[i]; return;
push_up(nodes, i);
} }
} else {
let mut max = VOID;
for i in 0..nodes[node].inputs.len() { for i in 0..nodes[node].inputs.len() {
let i = nodes[node].inputs[i]; let inp = nodes[node].inputs[i];
let is_call = matches!(nodes[i].kind, Kind::Call { .. }); if !nodes[inp].kind.is_pinned() {
if nodes.is_cfg(i) && !is_call { push_up_impl(inp, nodes);
continue;
}
push_up(nodes, i);
if idepth(nodes, i) > idepth(nodes, max) {
max = if is_call { i } else { idom(nodes, i) };
} }
} }
#[cfg(debug_assertions)] if nodes[node].kind.is_pinned() {
{ return;
nodes.check_dominance(node, max, false);
} }
if max == VOID { let mut deepest = VOID;
for i in 1..nodes[node].inputs.len() {
let inp = nodes[node].inputs[i];
if idepth(nodes, inp) > idepth(nodes, deepest) {
deepest = idom(nodes, inp);
}
}
if deepest == VOID {
return; return;
} }
let index = nodes[0].outputs.iter().position(|&p| p == node).unwrap(); let index = nodes[0].outputs.iter().position(|&p| p == node).unwrap();
nodes[0].outputs.remove(index); nodes[0].outputs.remove(index);
nodes[node].inputs[0] = max; nodes[node].inputs[0] = deepest;
debug_assert!( debug_assert!(
!nodes[max].outputs.contains(&node) || matches!(nodes[max].kind, Kind::Call { .. }), !nodes[deepest].outputs.contains(&node)
"{node} {:?} {max} {:?}", || matches!(nodes[deepest].kind, Kind::Call { .. }),
"{node} {:?} {deepest} {:?}",
nodes[node], nodes[node],
nodes[max] nodes[deepest]
); );
nodes[max].outputs.push(node); nodes[deepest].outputs.push(node);
}
let mut rpo = vec![];
collect_rpo(VOID, nodes, &mut rpo);
for node in rpo.into_iter().rev() {
loop_depth(node, nodes);
for i in 0..nodes[node].inputs.len() {
push_up_impl(nodes[node].inputs[i], nodes);
}
if matches!(nodes[node].kind, Kind::Loop | Kind::Region) {
for i in 0..nodes[node].outputs.len() {
let usage = nodes[node].outputs[i];
if nodes[usage].kind == Kind::Phi {
push_up_impl(usage, nodes);
}
}
}
} }
} }
fn push_down(nodes: &mut Nodes, node: Nid) { fn push_down(nodes: &mut Nodes, node: Nid) {
fn is_forward_edge(usage: Nid, def: Nid, nodes: &mut Nodes) -> bool {
match nodes[usage].kind {
Kind::Phi => {
nodes[usage].inputs[2] != def || nodes[nodes[usage].inputs[0]].kind != Kind::Loop
}
Kind::Loop => nodes[usage].inputs[1] != def,
_ => true,
}
}
fn better(nodes: &mut Nodes, is: Nid, then: Nid) -> bool {
loop_depth(is, nodes) < loop_depth(then, nodes)
|| idepth(nodes, is) > idepth(nodes, then)
|| nodes[then].kind == Kind::If
}
if !nodes.visited.set(node) { if !nodes.visited.set(node) {
return; return;
} }
// TODO: handle memory nodes first for usage in nodes[node].outputs.clone() {
if is_forward_edge(usage, node, nodes) {
push_down(nodes, usage);
}
}
if nodes[node].kind.is_pinned() { if nodes[node].kind.is_pinned() {
// TODO: use buffer to avoid allocation or better yet queue the location changes return;
for i in nodes[node].outputs.clone() {
push_down(nodes, i);
}
} else {
let mut min = None::<Nid>;
for i in 0..nodes[node].outputs.len() {
let i = nodes[node].outputs[i];
push_down(nodes, i);
let i = use_block(node, i, nodes);
min = min.map(|m| common_dom(i, m, nodes)).or(Some(i));
}
let mut min = min.unwrap();
debug_assert!(nodes.dominates(nodes[node].inputs[0], min));
let mut cursor = min;
loop {
if better(nodes, cursor, min) {
min = cursor;
}
if cursor == nodes[node].inputs[0] {
break;
}
cursor = idom(nodes, cursor);
}
if nodes[min].kind.ends_basic_block() {
min = idom(nodes, min);
}
#[cfg(debug_assertions)]
{
nodes.check_dominance(node, min, true);
}
let prev = nodes[node].inputs[0];
debug_assert!(idepth(nodes, min) >= idepth(nodes, prev));
let index = nodes[prev].outputs.iter().position(|&p| p == node).unwrap();
nodes[prev].outputs.remove(index);
nodes[node].inputs[0] = min;
nodes[min].outputs.push(node);
} }
let mut min = None::<Nid>;
for i in 0..nodes[node].outputs.len() {
let usage = nodes[node].outputs[i];
let ub = use_block(node, usage, nodes);
min = min.map(|m| common_dom(ub, m, nodes)).or(Some(ub));
}
let mut min = min.unwrap();
debug_assert!(nodes.dominates(nodes[node].inputs[0], min));
let mut cursor = min;
loop {
if better(nodes, cursor, min) {
min = cursor;
}
if cursor == nodes[node].inputs[0] {
break;
}
cursor = idom(nodes, cursor);
}
if nodes[min].kind.ends_basic_block() {
min = idom(nodes, min);
}
#[cfg(debug_assertions)]
{
nodes.check_dominance(node, min, true);
}
let prev = nodes[node].inputs[0];
debug_assert!(idepth(nodes, min) >= idepth(nodes, prev));
let index = nodes[prev].outputs.iter().position(|&p| p == node).unwrap();
nodes[prev].outputs.remove(index);
nodes[node].inputs[0] = min;
nodes[min].outputs.push(node);
} }
fn use_block(target: Nid, from: Nid, nodes: &mut Nodes) -> Nid { fn use_block(target: Nid, from: Nid, nodes: &mut Nodes) -> Nid {
@ -2966,7 +3083,7 @@ mod tests {
//generic_functions; //generic_functions;
//c_strings; //c_strings;
//struct_patterns; //struct_patterns;
//arrays; arrays;
//struct_return_from_module_function; //struct_return_from_module_function;
////comptime_pointers; ////comptime_pointers;
//sort_something_viredly; //sort_something_viredly;

View file

@ -38,16 +38,15 @@ odher_pass:
ADDI64 r254, r254, 40d ADDI64 r254, r254, 40d
JALA r0, r31, 0a JALA r0, r31, 0a
pass: pass:
ADDI64 r254, r254, -40d ADDI64 r254, r254, -32d
ST r31, r254, 0a, 40h ST r31, r254, 0a, 32h
CP r32, r2 CP r32, r2
CP r33, r32 LD r33, r32, 0a, 8h
LD r34, r33, 0a, 8h LD r34, r32, 8a, 8h
LD r35, r33, 8a, 8h SUB64 r1, r33, r34
SUB64 r1, r34, r35 LD r31, r254, 0a, 32h
LD r31, r254, 0a, 40h ADDI64 r254, r254, 32d
ADDI64 r254, r254, 40d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 440 code size: 437
ret: 3 ret: 3
status: Ok(()) status: Ok(())

View file

@ -0,0 +1,17 @@
main:
ADDI64 r254, r254, -8d
LI64 r4, 0d
LI64 r2, 10d
ADDI64 r5, r254, 0d
ST r2, r254, 0a, 8h
2: LD r1, r254, 0a, 8h
JNE r1, r4, :0
JMP :1
0: ADDI64 r1, r1, -1d
ST r1, r254, 0a, 8h
JMP :2
1: ADDI64 r254, r254, 8d
JALA r0, r31, 0a
code size: 137
ret: 0
status: Ok(())