Compare commits

..

No commits in common. "6ad0b41759dacd5767b5c9cfbc1b3b11c025396a" and "58578dd4b2904a5b8df39c14ca6fc4bb519c1dec" have entirely different histories.

5 changed files with 206 additions and 343 deletions

View file

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

View file

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

View file

@ -9,7 +9,8 @@ use {
idfl::{self},
Expr, ExprRef, FileId, Pos,
},
reg, task, ty,
reg, task,
ty::{self},
vc::{BitSet, Vc},
Func, HashMap, Offset, OffsetIter, Reloc, Sig, SymKey, TypedReloc, Types,
},
@ -34,16 +35,6 @@ 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);
@ -423,9 +414,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: "),
Kind::Mem => write!(out, " mem: "),
_ => unreachable!(),
}?;
if self[node].kind != Kind::Loop && self[node].kind != Kind::Region {
@ -622,48 +613,26 @@ impl Nodes {
target
}
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 {
fn load_loop_value(&mut self, index: usize, value: &mut Value, loops: &mut [Loop]) {
if value.id != 0 {
debug_assert!(!value.var);
return;
}
let [loob, loops @ ..] = loops else { unreachable!() };
let node = loob.node;
let (ty, lvalue) = get_lvalue(loob);
let lvalue = &mut loob.scope.vars[index].value;
self.load_loop_value(get_lvalue, lvalue, loops);
self.load_loop_value(index, lvalue, loops);
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;
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;
} else {
self.lock(*lvalue);
self.unlock(*value);
self.lock(lvalue.id);
self.unlock(value.id);
}
*value = *lvalue;
}
@ -710,26 +679,26 @@ impl Nodes {
}
fn lock_scope(&mut self, scope: &Scope) {
if let Some(str) = scope.store.to_store() {
if let Some(str) = scope.store {
self.lock(str);
}
for &load in &scope.loads {
self.lock(load);
}
for var in &scope.vars {
self.lock(var.value);
self.lock(var.value.id);
}
}
fn unlock_remove_scope(&mut self, scope: &Scope) {
if let Some(str) = scope.store.to_store() {
if let Some(str) = scope.store {
self.unlock_remove(str);
}
for &load in &scope.loads {
self.unlock_remove(load);
}
for var in &scope.vars {
self.unlock_remove(var.value);
self.unlock_remove(var.value.id);
}
}
}
@ -799,7 +768,7 @@ pub enum Kind {
impl Kind {
fn is_pinned(&self) -> bool {
self.is_cfg() || matches!(self, Self::Phi | Self::Mem | Self::Arg { .. })
self.is_cfg() || matches!(self, Self::Phi | Self::Mem)
}
fn is_cfg(&self) -> bool {
@ -811,6 +780,7 @@ impl Kind {
| Self::Entry
| Self::Then
| Self::Else
| Self::Arg { .. }
| Self::Call { .. }
| Self::If
| Self::Region
@ -880,24 +850,14 @@ struct Loop {
#[derive(Clone, Copy)]
struct Variable {
id: Ident,
ty: ty::Id,
value: Nid,
value: Value,
}
#[derive(Default, Clone)]
struct Scope {
vars: Vec<Variable>,
loads: Vec<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)))
}
store: Option<Nid>,
}
#[derive(Default)]
@ -983,7 +943,7 @@ impl Default for Regalloc {
}
}
#[derive(Default, Clone, Copy, PartialEq, Eq)]
#[derive(Default, Clone, Copy)]
struct Value {
ty: ty::Id,
var: bool,
@ -1065,13 +1025,8 @@ 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]);
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() {
if let Some(str) = self.ci.scope.store {
self.ci.nodes.unlock(str);
vc.push(str);
}
@ -1086,15 +1041,13 @@ impl Codegen {
}
let store = self.ci.nodes.new_node(self.tof(value), Kind::Stre, vc);
self.ci.nodes.lock(store);
self.ci.scope.store = store;
self.ci.scope.store = Some(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]);
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() {
if let Some(str) = self.ci.scope.store {
vc.push(str);
}
let load = self.ci.nodes.new_node(ty, Kind::Load, vc);
@ -1133,13 +1086,14 @@ impl Codegen {
return Value::NEVER;
};
self.ci.nodes.load_loop_var(
self.ci.nodes.load_loop_value(
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].ty))
Some(Value::var(index).ty(self.ci.scope.vars[index].value.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),
@ -1154,9 +1108,8 @@ impl Codegen {
};
let mut inps = Vc::from([self.ci.ctrl, value.id]);
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);
for &m in self.ci.scope.store.iter() {
inps.push(m);
}
self.ci.ctrl = self.ci.nodes.new_node(ty::Id::VOID, Kind::Return, inps);
@ -1249,7 +1202,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: value.id, ty: value.ty });
self.ci.scope.vars.push(Variable { id, value });
Some(Value::VOID)
}
Expr::BinOp { left, op: TokenKind::Assign, right } => {
@ -1261,8 +1214,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.id);
self.ci.nodes.unlock_remove(prev);
let prev = core::mem::replace(&mut var.value, value);
self.ci.nodes.unlock_remove(prev.id);
} else if dest.ptr {
self.store_mem(dest.id, value.id);
} else {
@ -1289,10 +1242,6 @@ 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();
@ -1345,7 +1294,7 @@ impl Codegen {
inps.push(value.id);
}
if let Some(str) = self.ci.scope.store.to_store() {
if let Some(str) = self.ci.scope.store {
inps.push(str);
}
for load in self.ci.scope.loads.drain(..) {
@ -1368,59 +1317,6 @@ 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(
@ -1504,7 +1400,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);
self.ci.nodes.unlock_remove(var.value.id);
}
self.ci.nodes.unlock(self.ci.ctrl);
@ -1519,8 +1415,8 @@ impl Codegen {
scope: self.ci.scope.clone(),
});
for (_, var) in &mut self.ci.scope.iter_elems_mut() {
*var = VOID;
for var in &mut self.ci.scope.vars {
var.value = Value::VOID;
}
self.ci.nodes.lock_scope(&self.ci.scope);
@ -1565,50 +1461,65 @@ impl Codegen {
core::mem::swap(&mut self.ci.scope, &mut bres);
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())
for ((dest_var, mut scope_var), loop_var) in
self.ci.scope.vars.iter_mut().zip(scope.vars.drain(..)).zip(bres.vars.drain(..))
{
self.ci.nodes.unlock(loop_value);
self.ci.nodes.unlock(loop_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);
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);
} else {
if *dest_value == scope_value {
self.ci.nodes.unlock(*dest_value);
*dest_value = VOID;
self.ci.nodes.lock(*dest_value);
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);
}
let phi = &self.ci.nodes[scope_value];
let phi = &self.ci.nodes[scope_var.value.id];
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_value, prev);
scope_value = prev;
self.ci.nodes.replace(scope_var.value.id, prev);
scope_var.value.id = prev;
self.ci.nodes.lock(prev);
}
}
if *dest_value == VOID {
self.ci.nodes.unlock(*dest_value);
*dest_value = scope_value;
self.ci.nodes.lock(*dest_value);
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);
}
debug_assert!(
self.ci.nodes[*dest_value].kind != Kind::Phi
|| self.ci.nodes[*dest_value].inputs[2] != 0
self.ci.nodes[dest_var.value.id].kind != Kind::Phi
|| self.ci.nodes[dest_var.value.id].inputs[2] != 0
);
self.ci.nodes.unlock_remove(scope_value);
self.ci.nodes.unlock_remove(scope_var.value.id);
}
if bres.store != scope.store {
let (to_store, from_store) = (bres.store.unwrap(), scope.store.unwrap());
self.ci.nodes.unlock(to_store);
bres.store = Some(
self.ci
.nodes
.new_node(ty::Id::VOID, Kind::Phi, [node, from_store, to_store]),
);
self.ci.nodes.lock(bres.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)
@ -1639,9 +1550,8 @@ 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.to_store() {
if let Some(str) = orig_store {
self.ci.nodes.lock(str);
}
let else_scope = self.ci.scope.clone();
@ -1658,7 +1568,7 @@ impl Codegen {
self.ci.ctrl
};
if let Some(str) = orig_store.to_store() {
if let Some(str) = orig_store {
self.ci.nodes.unlock_remove(str);
}
@ -1717,8 +1627,12 @@ 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_var(id, &mut self.ci.scope.vars[id].value, &mut self.ci.loops);
n.id = self.ci.scope.vars[id].value;
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;
}
}
@ -1768,26 +1682,32 @@ impl Codegen {
from: &mut Scope,
drop_from: bool,
) {
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);
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 load in to.loads.drain(..) {
nodes.unlock_remove(load);
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 from.loads.drain(..) {
nodes.unlock_remove(load);
to.loads.extend(&from.loads);
for &load in &from.loads {
nodes.lock(load);
}
if drop_from {
@ -1835,8 +1755,6 @@ 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 { .. },
@ -1854,7 +1772,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, ty });
self.ci.scope.vars.push(Variable { id: arg.id, value: Value::new(value).ty(ty) });
}
let orig_vars = self.ci.scope.vars.clone();
@ -1863,7 +1781,6 @@ 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));
@ -2289,13 +2206,16 @@ impl Codegen {
#[track_caller]
fn report(&self, pos: Pos, msg: impl core::fmt::Display) {
let mut buf = self.errors.borrow_mut();
write!(buf, "{}", self.cfile().report(pos, msg)).unwrap();
writeln!(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})"));
self.fatal_report(
ast.pos(),
fa!("compiler does not (yet) know how to handle ({hint}):\n{}", self.ast_display(ast)),
);
}
fn cfile(&self) -> &parser::Ast {
@ -2309,7 +2229,7 @@ impl Codegen {
fn gcm(&mut self) {
self.ci.nodes.visited.clear(self.ci.nodes.values.len());
push_up(&mut self.ci.nodes);
push_up(&mut self.ci.nodes, NEVER);
// TODO: handle infinte loops
self.ci.nodes.visited.clear(self.ci.nodes.values.len());
push_down(&mut self.ci.nodes, VOID);
@ -2665,8 +2585,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])
@ -2830,12 +2750,18 @@ fn loop_depth(target: Nid, nodes: &mut Nodes) -> LoopDepth {
depth
}
Kind::Start | Kind::End => 1,
u => unreachable!("{u:?}"),
_ => unreachable!(),
};
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;
@ -2852,147 +2778,103 @@ fn idepth(nodes: &mut Nodes, target: Nid) -> IDomDepth {
nodes[target].depth
}
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);
fn push_up(nodes: &mut Nodes, node: Nid) {
if !nodes.visited.set(node) {
return;
}
fn push_up_impl(node: Nid, nodes: &mut Nodes) {
if !nodes.visited.set(node) {
return;
}
if nodes[node].kind.is_pinned() {
for i in 0..nodes[node].inputs.len() {
let inp = nodes[node].inputs[i];
if !nodes[inp].kind.is_pinned() {
push_up_impl(inp, nodes);
let i = nodes[node].inputs[i];
push_up(nodes, i);
}
} 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) };
}
}
if nodes[node].kind.is_pinned() {
return;
#[cfg(debug_assertions)]
{
nodes.check_dominance(node, max, false);
}
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 {
if max == VOID {
return;
}
let index = nodes[0].outputs.iter().position(|&p| p == node).unwrap();
nodes[0].outputs.remove(index);
nodes[node].inputs[0] = deepest;
nodes[node].inputs[0] = max;
debug_assert!(
!nodes[deepest].outputs.contains(&node)
|| matches!(nodes[deepest].kind, Kind::Call { .. }),
"{node} {:?} {deepest} {:?}",
!nodes[max].outputs.contains(&node) || matches!(nodes[max].kind, Kind::Call { .. }),
"{node} {:?} {max} {:?}",
nodes[node],
nodes[deepest]
nodes[max]
);
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);
}
}
}
nodes[max].outputs.push(node);
}
}
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;
}
for usage in nodes[node].outputs.clone() {
if is_forward_edge(usage, node, nodes) {
push_down(nodes, usage);
}
}
// TODO: handle memory nodes first
if nodes[node].kind.is_pinned() {
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;
// TODO: use buffer to avoid allocation or better yet queue the location changes
for i in nodes[node].outputs.clone() {
push_down(nodes, i);
}
if cursor == nodes[node].inputs[0] {
break;
} 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));
}
cursor = idom(nodes, cursor);
}
let mut min = min.unwrap();
if nodes[min].kind.ends_basic_block() {
min = idom(nodes, min);
}
debug_assert!(nodes.dominates(nodes[node].inputs[0], min));
#[cfg(debug_assertions)]
{
nodes.check_dominance(node, min, true);
}
let mut cursor = min;
loop {
if better(nodes, cursor, min) {
min = cursor;
}
if cursor == nodes[node].inputs[0] {
break;
}
cursor = idom(nodes, cursor);
}
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);
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 {
@ -3083,7 +2965,7 @@ mod tests {
//generic_functions;
//c_strings;
//struct_patterns;
arrays;
//arrays;
//struct_return_from_module_function;
////comptime_pointers;
//sort_something_viredly;

View file

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

View file

@ -1,17 +0,0 @@
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(())