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! {
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 {

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) {
self.report(
pos,

View file

@ -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<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 {
type Ctx = [Result<Node, Nid>];
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<Variable>,
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)]
@ -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: <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, .. } => {
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<Nid>) {
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::<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);
return;
}
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 {
@ -2966,7 +3083,7 @@ mod tests {
//generic_functions;
//c_strings;
//struct_patterns;
//arrays;
arrays;
//struct_return_from_module_function;
////comptime_pointers;
//sort_something_viredly;

View file

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

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