diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 327fc35..0ffefd0 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -452,6 +452,8 @@ mod ty { builtin_type! { UNDECLARED; + LEFT_UNREACHABLE; + RIGHT_UNREACHABLE; NEVER; VOID; TYPE; @@ -464,8 +466,7 @@ mod ty { I16; I32; INT; - LEFT_UNREACHABLE; - RIGHT_UNREACHABLE; + } macro_rules! type_kind { diff --git a/lang/src/parser.rs b/lang/src/parser.rs index f786dcd..6d78460 100644 --- a/lang/src/parser.rs +++ b/lang/src/parser.rs @@ -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) { self.report( pos, diff --git a/lang/src/son.rs b/lang/src/son.rs index bf5bf98..7ecfe48 100644 --- a/lang/src/son.rs +++ b/lang/src/son.rs @@ -9,8 +9,7 @@ use { idfl::{self}, Expr, ExprRef, FileId, Pos, }, - reg, task, - ty::{self}, + reg, task, ty, vc::{BitSet, Vc}, Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types, }, @@ -35,6 +34,16 @@ type Nid = u16; type Lookup = crate::ctx_map::CtxMap; +trait StoreId: Sized { + fn to_store(self) -> Option; +} + +impl StoreId for Nid { + fn to_store(self) -> Option { + (self != NEVER).then_some(self) + } +} + impl crate::ctx_map::CtxEntry for Nid { type Ctx = [Result]; type Key<'a> = (Kind, &'a [Nid], ty::Id); @@ -414,9 +423,9 @@ impl Nodes { Kind::Then => write!(out, "ctrl: {:<5}", "then"), Kind::Else => write!(out, "ctrl: {:<5}", "else"), Kind::Stck => write!(out, "stck: "), - Kind::Load => write!(out, "load: "), + Kind::Load => write!(out, "load: "), Kind::Stre => write!(out, "stre: "), - _ => unreachable!(), + Kind::Mem => write!(out, " mem: "), }?; if self[node].kind != Kind::Loop && self[node].kind != Kind::Region { @@ -613,26 +622,48 @@ impl Nodes { target } - fn load_loop_value(&mut self, index: usize, value: &mut Value, loops: &mut [Loop]) { - if value.id != 0 { - debug_assert!(!value.var); + fn load_loop_var(&mut self, index: usize, value: &mut Nid, loops: &mut [Loop]) { + self.load_loop_value( + &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; } 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() { - self.unlock(value.id); - let inps = [loob.node, lvalue.id, VOID]; - self.unlock(lvalue.id); - lvalue.id = self.new_node_nop(lvalue.ty, Kind::Phi, inps); - self[lvalue.id].lock_rc += 2; + if !self[*lvalue].is_lazy_phi() { + self.unlock(*value); + let inps = [node, *lvalue, VOID]; + self.unlock(*lvalue); + *lvalue = self.new_node_nop(ty, Kind::Phi, inps); + self[*lvalue].lock_rc += 2; } else { - self.lock(lvalue.id); - self.unlock(value.id); + self.lock(*lvalue); + self.unlock(*value); } *value = *lvalue; } @@ -679,26 +710,26 @@ impl Nodes { } fn lock_scope(&mut self, scope: &Scope) { - if let Some(str) = scope.store { + if let Some(str) = scope.store.to_store() { self.lock(str); } for &load in &scope.loads { self.lock(load); } for var in &scope.vars { - self.lock(var.value.id); + self.lock(var.value); } } 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); } for &load in &scope.loads { self.unlock_remove(load); } 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 { 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 { @@ -780,7 +811,6 @@ impl Kind { | Self::Entry | Self::Then | Self::Else - | Self::Arg { .. } | Self::Call { .. } | Self::If | Self::Region @@ -850,14 +880,24 @@ struct Loop { #[derive(Clone, Copy)] struct Variable { id: Ident, - value: Value, + ty: ty::Id, + value: Nid, } #[derive(Default, Clone)] struct Scope { vars: Vec, loads: Vec, - store: Option, + store: Nid, +} + +impl Scope { + pub fn iter_elems_mut(&mut self) -> impl Iterator { + self.vars + .iter_mut() + .map(|v| (v.ty, &mut v.value)) + .chain(core::iter::once((ty::Id::VOID, &mut self.store))) + } } #[derive(Default)] @@ -943,7 +983,7 @@ impl Default for Regalloc { } } -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, PartialEq, Eq)] struct Value { ty: ty::Id, var: bool, @@ -1025,8 +1065,13 @@ impl Codegen { } fn store_mem(&mut self, region: Nid, value: Nid) -> Nid { + if value == NEVER { + return NEVER; + } + 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); vc.push(str); } @@ -1041,13 +1086,15 @@ impl Codegen { } let store = self.ci.nodes.new_node(self.tof(value), Kind::Stre, vc); self.ci.nodes.lock(store); - self.ci.scope.store = Some(store); + self.ci.scope.store = store; store } fn load_mem(&mut self, region: Nid, ty: ty::Id) -> Nid { + debug_assert_ne!(region, VOID); 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); } let load = self.ci.nodes.new_node(ty, Kind::Load, vc); @@ -1086,14 +1133,13 @@ impl Codegen { return Value::NEVER; }; - self.ci.nodes.load_loop_value( + self.ci.nodes.load_loop_var( index, &mut self.ci.scope.vars[index].value, &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( 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]); - for &m in self.ci.scope.store.iter() { - inps.push(m); + 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() { + inps.push(str); } 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 } => { let value = self.expr(right)?; 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) } Expr::BinOp { left, op: TokenKind::Assign, right } => { @@ -1214,8 +1261,8 @@ impl Codegen { if dest.var { self.ci.nodes.lock(value.id); let var = &mut self.ci.scope.vars[(u16::MAX - dest.id) as usize]; - let prev = core::mem::replace(&mut var.value, value); - self.ci.nodes.unlock_remove(prev.id); + let prev = core::mem::replace(&mut var.value, value.id); + self.ci.nodes.unlock_remove(prev); } else if dest.ptr { self.store_mem(dest.id, value.id); } else { @@ -1242,6 +1289,10 @@ impl Codegen { [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, .. } => { self.ci.call_count += 1; let cfile = self.cfile().clone(); @@ -1294,7 +1345,7 @@ impl Codegen { 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); } for load in self.ci.scope.loads.drain(..) { @@ -1317,6 +1368,59 @@ impl Codegen { 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: .{ ... }", + ); + 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::(); + + 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, .. } => { let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else { self.report( @@ -1400,7 +1504,7 @@ impl Codegen { self.ci.nodes.lock(self.ci.ctrl); 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); @@ -1415,8 +1519,8 @@ impl Codegen { scope: self.ci.scope.clone(), }); - for var in &mut self.ci.scope.vars { - var.value = Value::VOID; + for (_, var) in &mut self.ci.scope.iter_elems_mut() { + *var = VOID; } self.ci.nodes.lock_scope(&self.ci.scope); @@ -1461,66 +1565,50 @@ impl Codegen { core::mem::swap(&mut self.ci.scope, &mut bres); - for ((dest_var, mut scope_var), loop_var) in - self.ci.scope.vars.iter_mut().zip(scope.vars.drain(..)).zip(bres.vars.drain(..)) + for (((_, dest_value), (_, &mut mut scope_value)), (_, &mut loop_value)) in self + .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 { - self.ci.nodes.unlock(scope_var.value.id); - if loop_var.value.id != scope_var.value.id { - scope_var.value.id = self.ci.nodes.modify_input( - scope_var.value.id, - 2, - loop_var.value.id, - ); - self.ci.nodes.lock(scope_var.value.id); + if loop_value != VOID { + self.ci.nodes.unlock(scope_value); + if loop_value != scope_value { + scope_value = self.ci.nodes.modify_input(scope_value, 2, loop_value); + self.ci.nodes.lock(scope_value); } else { - if dest_var.value.id == scope_var.value.id { - self.ci.nodes.unlock(dest_var.value.id); - dest_var.value.id = VOID; - self.ci.nodes.lock(dest_var.value.id); + if *dest_value == scope_value { + self.ci.nodes.unlock(*dest_value); + *dest_value = VOID; + 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.inputs[2], VOID); - debug_assert_eq!(phi.ty, scope_var.value.ty); let prev = phi.inputs[1]; - self.ci.nodes.replace(scope_var.value.id, prev); - scope_var.value.id = prev; + self.ci.nodes.replace(scope_value, prev); + scope_value = prev; self.ci.nodes.lock(prev); } } - if dest_var.value.id == VOID { - self.ci.nodes.unlock(dest_var.value.id); - dest_var.value = scope_var.value; - self.ci.nodes.lock(dest_var.value.id); + if *dest_value == VOID { + self.ci.nodes.unlock(*dest_value); + *dest_value = scope_value; + self.ci.nodes.lock(*dest_value); } debug_assert!( - self.ci.nodes[dest_var.value.id].kind != Kind::Phi - || self.ci.nodes[dest_var.value.id].inputs[2] != 0 + self.ci.nodes[*dest_value].kind != Kind::Phi + || 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); 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; - if let Some(str) = orig_store { + if let Some(str) = orig_store.to_store() { self.ci.nodes.lock(str); } let else_scope = self.ci.scope.clone(); @@ -1569,7 +1658,7 @@ impl Codegen { self.ci.ctrl }; - if let Some(str) = orig_store { + if let Some(str) = orig_store.to_store() { self.ci.nodes.unlock_remove(str); } @@ -1628,12 +1717,8 @@ impl Codegen { fn strip_var(&mut self, n: &mut Value) { if core::mem::take(&mut n.var) { let id = (u16::MAX - n.id) as usize; - self.ci.nodes.load_loop_value( - id, - &mut self.ci.scope.vars[id].value, - &mut self.ci.loops, - ); - *n = self.ci.scope.vars[id].value; + self.ci.nodes.load_loop_var(id, &mut self.ci.scope.vars[id].value, &mut self.ci.loops); + n.id = self.ci.scope.vars[id].value; } } @@ -1683,32 +1768,26 @@ impl Codegen { from: &mut Scope, drop_from: bool, ) { - for (i, (to_var, from_var)) in to.vars.iter_mut().zip(&mut from.vars).enumerate() { - if to_var.value.id != from_var.value.id { - nodes.load_loop_value(i, &mut from_var.value, loops); - nodes.load_loop_value(i, &mut to_var.value, loops); - if to_var.value.id != from_var.value.id { - let ty = nodes[to_var.value.id].ty; - debug_assert_eq!(ty, nodes[from_var.value.id].ty, "TODO: typecheck properly"); - - let inps = [ctrl, from_var.value.id, to_var.value.id]; - nodes.unlock(to_var.value.id); - to_var.value.id = nodes.new_node(ty, Kind::Phi, inps); - nodes.lock(to_var.value.id); + for (i, ((ty, to_value), (_, from_value))) in + to.iter_elems_mut().zip(from.iter_elems_mut()).enumerate() + { + if to_value != from_value { + nodes.load_loop_var(i, from_value, loops); + nodes.load_loop_var(i, to_value, loops); + if to_value != from_value { + let inps = [ctrl, *from_value, *to_value]; + nodes.unlock(*to_value); + *to_value = nodes.new_node(ty, Kind::Phi, inps); + nodes.lock(*to_value); } } } - if to.store != from.store { - let (to_store, from_store) = (to.store.unwrap(), from.store.unwrap()); - 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 to.loads.drain(..) { + nodes.unlock_remove(load); } - - to.loads.extend(&from.loads); - for &load in &from.loads { - nodes.lock(load); + for load in from.loads.drain(..) { + nodes.unlock_remove(load); } if drop_from { @@ -1756,6 +1835,8 @@ impl Codegen { let mem = self.ci.nodes.new_node(ty::Id::VOID, Kind::Mem, [VOID]); debug_assert_eq!(mem, MEM); self.ci.nodes.lock(mem); + self.ci.nodes.lock(end); + self.ci.scope.store = end; let Expr::BinOp { left: Expr::Ident { .. }, @@ -1773,7 +1854,7 @@ impl Codegen { self.ci.nodes.lock(value); let sym = parser::find_symbol(&ast.symbols, arg.id); 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(); @@ -1782,6 +1863,7 @@ impl Codegen { 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_remove_scope(&core::mem::take(&mut self.ci.scope)); @@ -2207,16 +2289,13 @@ impl Codegen { #[track_caller] fn report(&self, pos: Pos, msg: impl core::fmt::Display) { 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] fn report_unhandled_ast(&self, ast: &Expr, hint: &str) { log::debug!("{ast:#?}"); - self.fatal_report( - ast.pos(), - fa!("compiler does not (yet) know how to handle ({hint}):\n{}", self.ast_display(ast)), - ); + self.fatal_report(ast.pos(), fa!("compiler does not (yet) know how to handle ({hint})")); } fn cfile(&self) -> &parser::Ast { @@ -2230,7 +2309,7 @@ impl Codegen { fn gcm(&mut self) { 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 self.ci.nodes.visited.clear(self.ci.nodes.values.len()); push_down(&mut self.ci.nodes, VOID); @@ -2586,8 +2665,8 @@ impl<'a> Function<'a> { self.add_instr(nid, ops); } } - Kind::Stre { .. } if node.inputs[2] == VOID => self.nodes.lock(nid), - Kind::Stre { .. } => { + Kind::Stre if node.inputs[2] == VOID => self.nodes.lock(nid), + Kind::Stre => { let mut region = node.inputs[2]; if self.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add }) && self.nodes.is_const(self.nodes[region].inputs[2]) @@ -2751,18 +2830,12 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth { depth } Kind::Start | Kind::End => 1, - _ => unreachable!(), + u => unreachable!("{u:?}"), }; 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 { if target == VOID { return 0; @@ -2779,103 +2852,147 @@ fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth { nodes[target].depth } -fn push_up(nodes: &mut Nodes, node: Nid) { - if !nodes.visited.set(node) { - return; +fn push_up(nodes: &mut Nodes) { + fn collect_rpo(node: Nid, nodes: &mut Nodes, rpo: &mut Vec) { + 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() { - for i in 0..nodes[node].inputs.len() { - let i = nodes[node].inputs[i]; - push_up(nodes, i); + fn push_up_impl(node: Nid, nodes: &mut Nodes) { + if !nodes.visited.set(node) { + return; } - } else { - let mut max = VOID; + for i in 0..nodes[node].inputs.len() { - let i = nodes[node].inputs[i]; - let is_call = matches!(nodes[i].kind, Kind::Call { .. }); - if nodes.is_cfg(i) && !is_call { - continue; - } - push_up(nodes, i); - if idepth(nodes, i) > idepth(nodes, max) { - max = if is_call { i } else { idom(nodes, i) }; + let inp = nodes[node].inputs[i]; + if !nodes[inp].kind.is_pinned() { + push_up_impl(inp, nodes); } } - #[cfg(debug_assertions)] - { - nodes.check_dominance(node, max, false); + if nodes[node].kind.is_pinned() { + return; } - 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; } let index = nodes[0].outputs.iter().position(|&p| p == node).unwrap(); nodes[0].outputs.remove(index); - nodes[node].inputs[0] = max; + nodes[node].inputs[0] = deepest; debug_assert!( - !nodes[max].outputs.contains(&node) || matches!(nodes[max].kind, Kind::Call { .. }), - "{node} {:?} {max} {:?}", + !nodes[deepest].outputs.contains(&node) + || matches!(nodes[deepest].kind, Kind::Call { .. }), + "{node} {:?} {deepest} {:?}", 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 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) { 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() { - // TODO: use buffer to avoid allocation or better yet queue the location changes - for i in nodes[node].outputs.clone() { - push_down(nodes, i); - } - } else { - let mut min = None::; - 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); + return; } + + let mut min = None::; + 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 { @@ -2966,7 +3083,7 @@ mod tests { //generic_functions; //c_strings; //struct_patterns; - //arrays; + arrays; //struct_return_from_module_function; ////comptime_pointers; //sort_something_viredly; diff --git a/lang/tests/codegen_tests_structs.txt b/lang/tests/codegen_tests_structs.txt index 9c8eec0..bb31b51 100644 --- a/lang/tests/codegen_tests_structs.txt +++ b/lang/tests/codegen_tests_structs.txt @@ -38,16 +38,15 @@ odher_pass: ADDI64 r254, r254, 40d JALA r0, r31, 0a pass: - ADDI64 r254, r254, -40d - ST r31, r254, 0a, 40h + ADDI64 r254, r254, -32d + ST r31, r254, 0a, 32h CP r32, r2 - CP r33, r32 - LD r34, r33, 0a, 8h - LD r35, r33, 8a, 8h - SUB64 r1, r34, r35 - LD r31, r254, 0a, 40h - ADDI64 r254, r254, 40d + LD r33, r32, 0a, 8h + LD r34, r32, 8a, 8h + SUB64 r1, r33, r34 + LD r31, r254, 0a, 32h + ADDI64 r254, r254, 32d JALA r0, r31, 0a -code size: 440 +code size: 437 ret: 3 status: Ok(()) diff --git a/lang/tests/son_tests_loop_stores.txt b/lang/tests/son_tests_loop_stores.txt index e69de29..18e8ece 100644 --- a/lang/tests/son_tests_loop_stores.txt +++ b/lang/tests/son_tests_loop_stores.txt @@ -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(())