fixing loop bugs and some optimization edgecases

This commit is contained in:
Jakub Doka 2024-10-21 15:12:37 +02:00
parent 8528bef8cf
commit ad4aed9c98
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
8 changed files with 441 additions and 138 deletions

View file

@ -375,7 +375,10 @@ main := fn(): int {
return @inline(foo, 1, 2, 3) - 6
}
gb := 0
foo := fn(a: int, b: int, c: int): int {
if gb != 0 return 1
return a + b + c
}
```
@ -777,12 +780,13 @@ screenidx := fn(orange: int): int {
// in module: random.hb
integer := fn(min: int, max: int): int {
rng := @as(int, @eca(3, 4))
rng := @as(int, 4)
if min != 0 | max != 0 {
if max == 0 {
return rng % (max - min + 1) + min
}
} else {
return rng
}
}
```

View file

@ -974,7 +974,7 @@ impl Codegen {
let fuc = &self.tys.ins.funcs[func as usize];
let ast = self.files[fuc.file as usize].clone();
let &E::Closure { args: cargs, body, .. } = fuc.expr.get(&ast).unwrap() else {
let &E::Closure { args: cargs, body, .. } = fuc.expr.get(&ast) else {
unreachable!();
};
@ -1398,7 +1398,7 @@ impl Codegen {
let fuc = &self.tys.ins.funcs[func as usize];
let ast = self.files[fuc.file as usize].clone();
let &E::Closure { args: cargs, .. } = fuc.expr.get(&ast).unwrap() else {
let &E::Closure { args: cargs, .. } = fuc.expr.get(&ast) else {
unreachable!();
};
@ -1803,7 +1803,7 @@ impl Codegen {
fn compute_signature(&mut self, func: &mut ty::Func, pos: Pos, args: &[Expr]) -> Option<Sig> {
let fuc = &self.tys.ins.funcs[*func as usize];
let fast = self.files[fuc.file as usize].clone();
let &Expr::Closure { args: cargs, ret, .. } = fuc.expr.get(&fast).unwrap() else {
let &Expr::Closure { args: cargs, ret, .. } = fuc.expr.get(&fast) else {
unreachable!();
};
@ -2122,7 +2122,7 @@ impl Codegen {
debug_assert!(func.file == file);
let sig = func.sig.unwrap();
let ast = self.files[file as usize].clone();
let expr = func.expr.get(&ast).unwrap();
let expr = func.expr.get(&ast);
let ct_stack_base = self.ct.vm.read_reg(reg::STACK_PTR).0;
let repl = ItemCtx { file, ret: Some(sig.ret), ..self.pool.cis.pop().unwrap_or_default() };
@ -2428,9 +2428,7 @@ impl Codegen {
match *trap {
trap::Trap::MakeStruct(trap::MakeStruct { file, struct_expr }) => {
let cfile = self.files[file as usize].clone();
let &Expr::Struct { fields, captured, packed, .. } =
struct_expr.get(&cfile).unwrap()
else {
let &Expr::Struct { fields, captured, packed, .. } = struct_expr.get(&cfile) else {
unreachable!()
};

View file

@ -280,11 +280,22 @@ impl TokenKind {
Self::Eq => (a == b) as i64,
Self::Ne => (a != b) as i64,
Self::Band => a & b,
Self::Bor => a | b,
Self::Mod => a % b,
Self::Shr => a >> b,
s => todo!("{s}"),
}
}
pub fn cmp_against(self) -> Option<u64> {
Some(match self {
TokenKind::Le | TokenKind::Gt => 1,
TokenKind::Ne | TokenKind::Eq => 0,
TokenKind::Ge | TokenKind::Lt => (-1i64) as _,
_ => return None,
})
}
pub fn is_homogenous(&self) -> bool {
self.precedence() != Self::Eq.precedence()
&& self.precedence() != Self::Gt.precedence()

View file

@ -395,6 +395,16 @@ mod ty {
_ => return None,
})
}
pub(crate) fn extend(self) -> Self {
if self.is_signed() {
Self::INT
} else if self.is_pointer() {
self
} else {
Self::UINT
}
}
}
#[derive(PartialEq, Eq, Default, Debug, Clone, Copy)]

View file

@ -1125,12 +1125,12 @@ impl ExprRef {
Self(NonNull::from(expr).cast())
}
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> {
from.mem.contains(self.0.as_ptr() as _).then_some(())?;
pub fn get<'a>(&self, from: &'a Ast) -> &'a Expr<'a> {
assert!(from.mem.contains(self.0.as_ptr() as _));
// SAFETY: the pointer is or was a valid reference in the past, if it points within one of
// arenas regions, it muts be walid, since arena does not give invalid pointers to its
// allocations
Some(unsafe { { self.0 }.as_ref() })
unsafe { { self.0 }.as_ref() }
}
pub fn dangling() -> Self {

View file

@ -394,6 +394,16 @@ impl Nodes {
return Some(self[target].inputs[1]);
}
}
K::Loop => {
if self[target].inputs[1] == NEVER {
for o in self[target].outputs.clone() {
if self[o].kind == Kind::Phi {
self.replace(o, self[o].inputs[1]);
}
}
return Some(self[target].inputs[0]);
}
}
_ => {}
}
@ -430,6 +440,7 @@ impl Nodes {
hash_map::RawEntryMut::Occupied(other) => {
let rpl = other.get_key_value().0.value;
self[target].inputs[inp_index] = prev;
self.lookup.insert(target.key(&self.values), target, &self.values);
self.replace(target, rpl);
rpl
}
@ -680,7 +691,6 @@ impl Nodes {
climb_impl(self, from, &mut for_each)
}
#[expect(dead_code)]
fn late_peephole(&mut self, target: Nid) -> Nid {
if let Some(id) = self.peephole(target) {
self.replace(target, id);
@ -695,7 +705,7 @@ impl Nodes {
l.scope
.vars
.get_mut(index)
.map_or((ty::Id::VOID, &mut l.scope.store), |v| (v.ty, &mut v.value))
.map_or((0, ty::Id::VOID, &mut l.scope.store), |v| (v.id, v.ty, &mut v.value))
},
value,
loops,
@ -703,12 +713,12 @@ impl Nodes {
}
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);
self.load_loop_value(&mut |l| (0, 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),
get_lvalue: &mut impl FnMut(&mut Loop) -> (Ident, ty::Id, &mut Nid),
value: &mut Nid,
loops: &mut [Loop],
) {
@ -716,13 +726,13 @@ impl Nodes {
return;
}
let [loob, loops @ ..] = loops else { unreachable!() };
let [loops @ .., loob] = loops else { unreachable!() };
let node = loob.node;
let (ty, lvalue) = get_lvalue(loob);
let (_id, ty, lvalue) = get_lvalue(loob);
self.load_loop_value(get_lvalue, lvalue, loops);
if !self[*lvalue].is_lazy_phi() {
if !self[*lvalue].is_lazy_phi(node) {
self.unlock(*value);
let inps = [node, *lvalue, VOID];
self.unlock(*lvalue);
@ -796,9 +806,13 @@ impl Nodes {
self.unlock_remove(load);
}
for var in &scope.vars {
if self[var.value].kind == Kind::Arg {
self.unlock(var.value);
} else {
self.unlock_remove(var.value);
}
}
}
}
impl ops::Index<Nid> for Nodes {
@ -933,12 +947,13 @@ impl Node {
(self.kind, &self.inputs, self.ty)
}
fn is_lazy_phi(&self) -> bool {
self.kind == Kind::Phi && self.inputs[2] == 0
fn is_lazy_phi(&self, loob: Nid) -> bool {
self.kind == Kind::Phi && self.inputs[2] == 0 && self.inputs[0] == loob
}
fn is_not_gvnd(&self) -> bool {
self.is_lazy_phi() || matches!(self.kind, Kind::Arg | Kind::Stck)
(self.kind == Kind::Phi && self.inputs[2] == 0)
|| matches!(self.kind, Kind::Arg | Kind::Stck)
}
}
@ -970,11 +985,12 @@ struct Scope {
}
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)))
pub fn iter_elems_mut(&mut self) -> impl Iterator<Item = (Ident, ty::Id, &mut Nid)> {
self.vars.iter_mut().map(|v| (v.id, v.ty, &mut v.value)).chain(core::iter::once((
0,
ty::Id::VOID,
&mut self.store,
)))
}
}
@ -983,6 +999,8 @@ struct ItemCtx {
file: FileId,
ret: Option<ty::Id>,
task_base: usize,
inline_depth: u16,
inline_ret: Option<(Value, Nid)>,
nodes: Nodes,
ctrl: Nid,
call_count: u16,
@ -1075,6 +1093,22 @@ impl ItemCtx {
*saved_regs.entry(hvenc).or_insert(would_insert)
};
let mut parama = tys.parama(sig.ret);
for (ti, &arg) in sig.args.range().zip(fuc.nodes[VOID].outputs.iter().skip(2)) {
let ty = tys.ins.args[ti];
if tys.size_of(ty) == 0 {
continue;
}
if let size @ 9..=16 = tys.size_of(ty) {
let rg = parama.next_wide();
self.emit(instrs::st(rg, reg::STACK_PTR, fuc.nodes[arg].offset as _, size as _));
self.emit(instrs::addi64(rg, reg::STACK_PTR, fuc.nodes[arg].offset as _));
} else {
parama.next();
}
}
for (i, block) in fuc.blocks.iter().enumerate() {
let blk = regalloc2::Block(i as _);
fuc.nodes[block.nid].offset = self.code.len() as _;
@ -1093,31 +1127,46 @@ impl ItemCtx {
};
let allocs = ralloc.ctx.output.inst_allocs(inst);
let node = &fuc.nodes[nid];
let mut extend = |base: ty::Id, dest: ty::Id, from: usize, to: usize| {
if base.simple_size() == dest.simple_size() {
return Default::default();
}
match (base.is_signed(), dest.is_signed()) {
(true, true) => {
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
[base.simple_size().unwrap().ilog2() as usize];
op(atr(allocs[to]), atr(allocs[from]))
}
_ => {
let mask = (1u64 << (base.simple_size().unwrap() * 8)) - 1;
instrs::andi(atr(allocs[to]), atr(allocs[from]), mask)
}
}
};
match node.kind {
Kind::If => {
let &[_, cond] = node.inputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = fuc.nodes[cond].kind
let &[_, cnd] = node.inputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = fuc.nodes[cnd].kind
&& let Some((op, swapped)) = op.cond_op(node.ty.is_signed())
{
let &[lhs, rhs] = allocs else { unreachable!() };
let &[_, lhsn, rhsn] = fuc.nodes[cond].inputs.as_slice() else {
let &[_, lh, rh] = fuc.nodes[cnd].inputs.as_slice() else {
unreachable!()
};
let lhsn_size = fuc.nodes[lhsn].ty.simple_size().unwrap() as u64;
if lhsn_size < 8 {
let mask = (1u64 << (lhsn_size * 8)) - 1;
self.emit(instrs::andi(atr(lhs), atr(lhs), mask));
}
let rhsn_size = fuc.nodes[rhsn].ty.simple_size().unwrap() as u64;
if rhsn_size < 8 {
let mask = (1u64 << (rhsn_size * 8)) - 1;
self.emit(instrs::andi(atr(rhs), atr(rhs), mask));
}
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[!swapped as usize], rel));
self.emit(op(atr(lhs), atr(rhs), 0));
} else {
todo!()
self.emit(extend(fuc.nodes[cnd].ty, fuc.nodes[cnd].ty.extend(), 0, 0));
let rel = Reloc::new(self.code.len(), 3, 2);
self.jump_relocs.push((node.outputs[1], rel));
self.emit(instrs::jne(atr(allocs[0]), reg::ZERO, 0));
}
}
Kind::Loop | Kind::Region => {
@ -1155,23 +1204,14 @@ impl ItemCtx {
let base = fuc.nodes[node.inputs[1]].ty;
let dest = node.ty;
match (base.is_signed(), dest.is_signed()) {
(true, true) => {
let op = [instrs::sxt8, instrs::sxt16, instrs::sxt32]
[tys.size_of(base).ilog2() as usize];
self.emit(op(atr(allocs[0]), atr(allocs[1])))
}
_ => {
let mask = (1u64 << (tys.size_of(base) * 8)) - 1;
self.emit(instrs::andi(atr(allocs[0]), atr(allocs[1]), mask));
}
}
self.emit(extend(base, dest, 1, 0))
}
Kind::UnOp { op } => {
let op = op.unop().expect("TODO: unary operator not supported");
let &[dst, oper] = allocs else { unreachable!() };
self.emit(op(atr(dst), atr(oper)));
}
Kind::BinOp { .. } if node.lock_rc != 0 => {}
Kind::BinOp { op } => {
let &[.., rhs] = node.inputs.as_slice() else { unreachable!() };
@ -1187,9 +1227,19 @@ impl ItemCtx {
{
let &[dst, lhs, rhs] = allocs else { unreachable!() };
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
} else if op.cond_op(node.ty.is_signed()).is_some() {
} else {
todo!()
} else if let Some(against) = op.cmp_against() {
let &[_, lh, rh] = node.inputs.as_slice() else { unreachable!() };
self.emit(extend(fuc.nodes[lh].ty, fuc.nodes[lh].ty.extend(), 0, 0));
self.emit(extend(fuc.nodes[rh].ty, fuc.nodes[rh].ty.extend(), 1, 1));
let signed = fuc.nodes[lh].ty.is_signed();
let op_fn = if signed { instrs::cmps } else { instrs::cmpu };
let &[dst, lhs, rhs] = allocs else { unreachable!() };
self.emit(op_fn(atr(dst), atr(lhs), atr(rhs)));
self.emit(instrs::cmpui(atr(dst), atr(dst), against));
if matches!(op, TokenKind::Eq | TokenKind::Lt | TokenKind::Gt) {
self.emit(instrs::not(atr(dst), atr(dst)));
}
}
}
Kind::Call { func } => {
@ -1280,9 +1330,9 @@ impl ItemCtx {
}
fn emit_body(&mut self, tys: &mut Types, files: &[parser::Ast], sig: Sig) {
self.nodes.check_final_integrity();
self.nodes.graphviz(tys, files);
self.nodes.gcm();
self.nodes.check_final_integrity();
self.nodes.basic_blocks();
self.nodes.graphviz(tys, files);
@ -1308,6 +1358,10 @@ impl ItemCtx {
if let Some(last_ret) = self.ret_relocs.last()
&& last_ret.offset as usize == self.code.len() - 5
&& self
.jump_relocs
.last()
.map_or(true, |&(r, _)| self.nodes[r].offset as usize != self.code.len())
{
self.code.truncate(self.code.len() - 5);
self.ret_relocs.pop();
@ -1316,6 +1370,7 @@ impl ItemCtx {
// FIXME: maybe do this incrementally
for (nd, rel) in self.jump_relocs.drain(..) {
let offset = self.nodes[nd].offset;
//debug_assert!(offset < self.code.len() as u32 - 1);
rel.apply_jump(&mut self.code, offset, 0);
}
@ -1673,6 +1728,11 @@ impl<'a> Codegen<'a> {
Some(self.ci.nodes.new_node_lit(ty, Kind::Idk, [VOID]))
}
}
Expr::Bool { value, .. } => Some(self.ci.nodes.new_node_lit(
ty::Id::BOOL,
Kind::CInt { value: value as i64 },
[VOID],
)),
Expr::Number { value, .. } => Some(self.ci.nodes.new_node_lit(
ctx.ty.filter(|ty| ty.is_integer()).unwrap_or(ty::Id::INT),
Kind::CInt { value },
@ -1728,6 +1788,7 @@ impl<'a> Codegen<'a> {
let expected = *self.ci.ret.get_or_insert(value.ty);
self.assert_ty(pos, &mut value, expected, "return value");
if self.ci.inline_depth == 0 {
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() {
@ -1738,6 +1799,17 @@ impl<'a> Codegen<'a> {
self.ci.nodes[NEVER].inputs.push(self.ci.ctrl);
self.ci.nodes[self.ci.ctrl].outputs.push(NEVER);
} else if let Some((pv, ctrl)) = &mut self.ci.inline_ret {
*ctrl =
self.ci.nodes.new_node(ty::Id::VOID, Kind::Region, [self.ci.ctrl, *ctrl]);
self.ci.ctrl = *ctrl;
self.ci.nodes.unlock(pv.id);
pv.id = self.ci.nodes.new_node(value.ty, Kind::Phi, [*ctrl, value.id, pv.id]);
self.ci.nodes.lock(pv.id);
} else {
self.ci.nodes.lock(value.id);
self.ci.inline_ret = Some((value, self.ci.ctrl));
}
None
}
@ -1819,7 +1891,8 @@ impl<'a> Codegen<'a> {
Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id]))
}
Expr::BinOp { left, op: TokenKind::Decl, right } => {
let right = self.raw_expr(right)?;
let mut right = self.raw_expr(right)?;
self.strip_var(&mut right);
self.assign_pattern(left, right);
Some(Value::VOID)
}
@ -1953,12 +2026,10 @@ impl<'a> Codegen<'a> {
let fuc = &self.tys.ins.funcs[fu as usize];
let sig = fuc.sig.expect("TODO: generic functions");
let ast = &self.files[fuc.file as usize];
let &Expr::Closure { args: cargs, .. } = fuc.expr.get(ast).unwrap() else {
unreachable!()
};
let &Expr::Closure { args: cargs, .. } = fuc.expr.get(ast) else { unreachable!() };
self.assert_report(
args.len() == cargs.len(),
if args.len() != cargs.len() {
self.report(
func.pos(),
fa!(
"expected {} function argumenr{}, got {}",
@ -1967,6 +2038,7 @@ impl<'a> Codegen<'a> {
args.len()
),
);
}
let mut inps = Vc::from([self.ci.ctrl]);
for ((arg, carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) {
@ -1979,9 +2051,14 @@ impl<'a> Codegen<'a> {
debug_assert_ne!(value.id, 0);
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
self.ci.nodes.lock(value.id);
inps.push(value.id);
}
for &n in inps.iter().skip(1) {
self.ci.nodes.unlock(n);
}
if let Some(str) = self.ci.scope.store.to_store() {
inps.push(str);
}
@ -2012,6 +2089,82 @@ impl<'a> Codegen<'a> {
alt_value.or(Some(Value::new(self.ci.ctrl).ty(sig.ret)))
}
Expr::Directive { name: "inline", args: [func, args @ ..], .. } => {
let ty = self.ty(func);
let ty::Kind::Func(fu) = ty.expand() else {
self.report(
func.pos(),
fa!(
"first argument to @inline should be a function,
but here its '{}'",
self.ty_display(ty)
),
);
return Value::NEVER;
};
let fuc = &self.tys.ins.funcs[fu as usize];
let sig = fuc.sig.expect("TODO: generic functions");
let ast = &self.files[fuc.file as usize];
let &Expr::Closure { args: cargs, body, .. } = fuc.expr.get(ast) else {
unreachable!()
};
if args.len() != cargs.len() {
self.report(
func.pos(),
fa!(
"expected {} inline function argumenr{}, got {}",
cargs.len(),
if cargs.len() == 1 { "" } else { "s" },
args.len()
),
);
}
let arg_base = self.ci.scope.vars.len();
for ((arg, carg), tyx) in args.iter().zip(cargs).zip(sig.args.range()) {
let ty = self.tys.ins.args[tyx];
if self.tys.size_of(ty) == 0 {
continue;
}
let mut value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
debug_assert_ne!(self.ci.nodes[value.id].kind, Kind::Stre);
debug_assert_ne!(value.id, 0);
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
self.ci.scope.vars.push(Variable {
id: carg.id,
ty,
ptr: value.ptr,
value: value.id,
});
self.ci.nodes.lock(value.id);
}
let prev_ret = self.ci.ret.replace(sig.ret);
let prev_inline_ret = self.ci.inline_ret.take();
self.ci.inline_depth += 1;
if self.expr(body).is_some() && sig.ret == ty::Id::VOID {
self.report(
body.pos(),
"expected all paths in the fucntion to return \
or the return type to be 'void'",
);
}
self.ci.ret = prev_ret;
self.ci.inline_depth -= 1;
for var in self.ci.scope.vars.drain(arg_base..) {
self.ci.nodes.unlock_remove(var.value);
}
core::mem::replace(&mut self.ci.inline_ret, prev_inline_ret).map(|(v, _)| {
self.ci.nodes.unlock(v.id);
v
})
}
Expr::Tupl { pos, ty, fields, .. } => {
let Some(sty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else {
self.report(
@ -2037,6 +2190,7 @@ impl<'a> Codegen<'a> {
let value = self.expr_ctx(field, Ctx::default().with_ty(ty))?;
let mem = self.offset(mem, offset);
self.store_mem(mem, value.id);
}
@ -2201,21 +2355,20 @@ impl<'a> Codegen<'a> {
scope: self.ci.scope.clone(),
});
for (_, var) in &mut self.ci.scope.iter_elems_mut() {
for (.., var) in &mut self.ci.scope.iter_elems_mut() {
*var = VOID;
}
self.ci.nodes.lock_scope(&self.ci.scope);
self.expr(body);
let Loop {
node,
ctrl: [mut con, bre],
ctrl_scope: [mut cons, mut bres],
mut scope,
} = self.ci.loops.pop().unwrap();
let Loop { ctrl: [con, ..], ctrl_scope: [cons, ..], .. } =
self.ci.loops.last_mut().unwrap();
let mut cons = core::mem::take(cons);
let mut con = *con;
if con != Nid::MAX {
self.ci.nodes.unlock(con);
con = self.ci.nodes.new_node(ty::Id::VOID, Kind::Region, [con, self.ci.ctrl]);
Self::merge_scopes(
&mut self.ci.nodes,
@ -2228,6 +2381,9 @@ impl<'a> Codegen<'a> {
self.ci.ctrl = con;
}
let Loop { node, ctrl: [.., bre], ctrl_scope: [.., mut bres], mut scope } =
self.ci.loops.pop().unwrap();
self.ci.nodes.modify_input(node, 1, self.ci.ctrl);
let idx = self.ci.nodes[node]
@ -2243,11 +2399,12 @@ impl<'a> Codegen<'a> {
}
self.ci.ctrl = bre;
self.ci.nodes.lock(self.ci.ctrl);
core::mem::swap(&mut self.ci.scope, &mut bres);
for (((_, dest_value), (_, &mut mut scope_value)), (_, &mut loop_value)) in self
debug_assert_eq!(self.ci.scope.vars.len(), scope.vars.len());
debug_assert_eq!(self.ci.scope.vars.len(), bres.vars.len());
for (((.., dest_value), (.., &mut mut scope_value)), (.., &mut loop_value)) in self
.ci
.scope
.iter_elems_mut()
@ -2256,7 +2413,7 @@ impl<'a> Codegen<'a> {
{
self.ci.nodes.unlock(loop_value);
if loop_value != VOID {
if self.ci.nodes[scope_value].is_lazy_phi(node) {
self.ci.nodes.unlock(scope_value);
if loop_value != scope_value {
scope_value = self.ci.nodes.modify_input(scope_value, 2, loop_value);
@ -2268,8 +2425,6 @@ impl<'a> Codegen<'a> {
self.ci.nodes.lock(*dest_value);
}
let phi = &self.ci.nodes[scope_value];
debug_assert_eq!(phi.kind, Kind::Phi);
debug_assert_eq!(phi.inputs[2], VOID);
let prev = phi.inputs[1];
self.ci.nodes.replace(scope_value, prev);
scope_value = prev;
@ -2283,10 +2438,7 @@ impl<'a> Codegen<'a> {
self.ci.nodes.lock(*dest_value);
}
debug_assert!(
self.ci.nodes[*dest_value].kind != Kind::Phi
|| self.ci.nodes[*dest_value].inputs[2] != 0
);
debug_assert!(!self.ci.nodes[*dest_value].is_lazy_phi(node));
self.ci.nodes.unlock_remove(scope_value);
}
@ -2296,6 +2448,8 @@ impl<'a> Codegen<'a> {
self.ci.nodes.unlock(self.ci.ctrl);
self.ci.nodes.late_peephole(node);
Some(Value::VOID)
}
Expr::Break { pos } => self.jump_to(pos, 1),
@ -2373,10 +2527,9 @@ impl<'a> Codegen<'a> {
}
}
fn assign_pattern(&mut self, pat: &Expr, mut right: Value) {
fn assign_pattern(&mut self, pat: &Expr, right: Value) {
match *pat {
Expr::Ident { id, .. } => {
self.strip_var(&mut right);
self.ci.nodes.lock(right.id);
self.ci.scope.vars.push(Variable {
id,
@ -2426,7 +2579,6 @@ impl<'a> Codegen<'a> {
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.ptr = self.ci.scope.vars[id].ptr;
n.id = self.ci.scope.vars[id].value;
}
@ -2443,6 +2595,7 @@ impl<'a> Codegen<'a> {
};
if loob.ctrl[id] == Nid::MAX {
self.ci.nodes.lock(self.ci.ctrl);
loob.ctrl[id] = self.ci.ctrl;
loob.ctrl_scope[id] = self.ci.scope.clone();
loob.ctrl_scope[id].vars.truncate(loob.scope.vars.len());
@ -2463,7 +2616,9 @@ impl<'a> Codegen<'a> {
loob = self.ci.loops.last_mut().unwrap();
loob.ctrl_scope[id] = scope;
self.ci.nodes.unlock(loob.ctrl[id]);
loob.ctrl[id] = reg;
self.ci.nodes.lock(loob.ctrl[id]);
}
self.ci.ctrl = NEVER;
@ -2478,7 +2633,7 @@ impl<'a> Codegen<'a> {
from: &mut Scope,
drop_from: bool,
) {
for (i, ((ty, to_value), (_, from_value))) in
for (i, ((.., ty, to_value), (.., from_value))) in
to.iter_elems_mut().zip(from.iter_elems_mut()).enumerate()
{
if to_value != from_value {
@ -2525,7 +2680,7 @@ impl<'a> Codegen<'a> {
func.offset = u32::MAX - 1;
let sig = func.sig.expect("to emmit only concrete functions");
let ast = &self.files[file as usize];
let expr = func.expr.get(ast).unwrap();
let expr = func.expr.get(ast);
self.pool.push_ci(file, Some(sig.ret), 0, &mut self.ci);
@ -2536,11 +2691,16 @@ impl<'a> Codegen<'a> {
let mut sig_args = sig.args.range();
for arg in args.iter() {
let ty = self.tys.ins.args[sig_args.next().unwrap()];
let mut deps = Vc::from([VOID]);
if matches!(self.tys.size_of(ty), 9..=16) {
deps.push(MEM);
}
let value = self.ci.nodes.new_node_nop(ty, Kind::Arg, [VOID]);
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, ptr: false });
let ptr = self.tys.size_of(ty) > 8;
self.ci.scope.vars.push(Variable { id: arg.id, value, ty, ptr });
}
if self.expr(body).is_some() && sig.ret == ty::Id::VOID {
@ -2578,12 +2738,6 @@ impl<'a> Codegen<'a> {
#[track_caller]
fn binop_ty(&mut self, pos: Pos, lhs: &mut Value, rhs: &mut Value, op: TokenKind) -> ty::Id {
if let Some(upcasted) = lhs.ty.try_upcast(rhs.ty, ty::TyCheck::BinOp) {
log::info!(
"{} {} {}",
self.ty_display(lhs.ty),
self.ty_display(rhs.ty),
self.ty_display(upcasted)
);
let to_correct = if lhs.ty != upcasted {
Some(lhs)
} else if rhs.ty != upcasted {
@ -2643,13 +2797,6 @@ impl<'a> Codegen<'a> {
}
}
#[track_caller]
fn assert_report(&self, cond: bool, pos: Pos, msg: impl core::fmt::Display) {
if !cond {
self.report(pos, msg);
}
}
#[track_caller]
fn report(&self, pos: Pos, msg: impl core::fmt::Display) {
let mut buf = self.errors.borrow_mut();
@ -2755,10 +2902,7 @@ impl<'a> Function<'a> {
regalloc2::Operand::reg_use(self.rg(nid))
}
fn def_nid(&mut self, _nid: Nid) {}
fn drg(&mut self, nid: Nid) -> regalloc2::Operand {
self.def_nid(nid);
regalloc2::Operand::reg_def(self.rg(nid))
}
@ -2822,6 +2966,9 @@ impl<'a> Function<'a> {
self.add_instr(nid, ops);
} else {
todo!()
//core::mem::swap(&mut then, &mut else_);
//let ops = vec![self.urg(cond)];
//self.add_instr(nid, ops);
}
self.emit_node(then, nid);
@ -2839,7 +2986,6 @@ impl<'a> Function<'a> {
if self.nodes[ph].kind != Kind::Phi || self.nodes[ph].ty == ty::Id::VOID {
continue;
}
self.def_nid(ph);
block.push(self.rg(ph));
}
self.blocks[self.nodes[nid].ralloc_backref as usize].params = block;
@ -2860,7 +3006,7 @@ impl<'a> Function<'a> {
vec![self.urg(self.nodes[node.inputs[1]].inputs[1])]
}
17.. => {
vec![self.urg(node.inputs[1])]
vec![self.urg(self.nodes[node.inputs[1]].inputs[1])]
}
};
@ -2890,22 +3036,28 @@ impl<'a> Function<'a> {
self.nodes[nid].ralloc_backref = self.add_block(nid);
let mut parama = self.tys.parama(self.sig.ret);
for (arg, ti) in
self.nodes[VOID].clone().outputs.into_iter().skip(2).zip(self.sig.args.range())
for (ti, arg) in
self.sig.args.range().zip(self.nodes[VOID].clone().outputs.into_iter().skip(2))
{
let ty = self.tys.ins.args[ti];
match self.tys.size_of(ty) {
0 => continue,
1..=8 => {
self.def_nid(arg);
self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def(
self.rg(arg),
regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int),
)]);
}
9..=16 => todo!(),
9..=16 => {
self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def(
self.rg(arg),
regalloc2::PReg::new(
parama.next_wide() as _,
regalloc2::RegClass::Int,
),
)]);
}
_ => {
self.def_nid(arg);
self.add_instr(NEVER, vec![regalloc2::Operand::reg_fixed_def(
self.rg(arg),
regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int),
@ -2934,19 +3086,21 @@ impl<'a> Function<'a> {
{
self.nodes.lock(nid)
}
Kind::BinOp { op } => {
Kind::BinOp { op }
if op.cond_op(node.ty.is_signed()).is_some()
&& node.outputs.iter().all(|&n| self.nodes[n].kind == Kind::If) =>
{
self.nodes.lock(nid)
}
Kind::BinOp { .. } => {
let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() };
let ops = if let Kind::CInt { .. } = self.nodes[rhs].kind
&& self.nodes[rhs].lock_rc != 0
{
vec![self.drg(nid), self.urg(lhs)]
} else if op.binop(node.ty.is_signed(), 8).is_some() {
vec![self.drg(nid), self.urg(lhs), self.urg(rhs)]
} else if op.cond_op(node.ty.is_signed()).is_some() {
return;
} else {
todo!("{op}")
vec![self.drg(nid), self.urg(lhs), self.urg(rhs)]
};
self.add_instr(nid, ops);
}
@ -2960,7 +3114,6 @@ impl<'a> Function<'a> {
let fuc = self.tys.ins.funcs[func as usize].sig.unwrap();
if self.tys.size_of(fuc.ret) != 0 {
self.def_nid(nid);
ops.push(regalloc2::Operand::reg_fixed_def(
self.rg(nid),
regalloc2::PReg::new(1, regalloc2::RegClass::Int),
@ -2968,18 +3121,39 @@ impl<'a> Function<'a> {
}
let mut parama = self.tys.parama(fuc.ret);
for (&(mut i), ti) in node.inputs[1..].iter().zip(fuc.args.range()) {
let mut inps = node.inputs[1..].iter();
for ti in fuc.args.range() {
let ty = self.tys.ins.args[ti];
match self.tys.size_of(ty) {
0 => continue,
1..=8 => {
ops.push(regalloc2::Operand::reg_fixed_use(
self.rg(*inps.next().unwrap()),
regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int),
));
}
9..=16 => {
let mut i = *inps.next().unwrap();
loop {
match self.nodes[i].kind {
Kind::Stre { .. } => i = self.nodes[i].inputs[2],
Kind::Load { .. } => i = self.nodes[i].inputs[1],
_ => break,
}
debug_assert_ne!(i, 0);
}
debug_assert!(i != 0);
ops.push(regalloc2::Operand::reg_fixed_use(
self.rg(i),
regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int),
));
ops.push(regalloc2::Operand::reg_fixed_use(
self.rg(i),
regalloc2::PReg::new(parama.next() as _, regalloc2::RegClass::Int),
));
}
9..=16 => todo!("pass in two register"),
_ => {
let mut i = *inps.next().unwrap();
loop {
match self.nodes[i].kind {
Kind::Stre { .. } => i = self.nodes[i].inputs[2],
@ -3468,7 +3642,7 @@ mod tests {
c_strings;
struct_patterns;
arrays;
//inline;
inline;
idk;
// Incomplete Examples;
@ -3485,7 +3659,7 @@ mod tests {
sort_something_viredly;
//structs_in_registers;
comptime_function_from_another_file;
//inline_test;
inline_test;
//inlined_generic_functions;
//some_generic_code;
//integer_inference_issues;

View file

@ -0,0 +1,14 @@
main:
LI64 r5, 0d
LRA r4, r0, :gb
LD r6, r4, 0a, 8h
LI64 r9, 6d
JEQ r6, r5, :0
LI64 r12, 1d
JMP :1
0: CP r12, r9
1: SUB64 r1, r12, r9
JALA r0, r31, 0a
code size: 94
ret: 0
status: Ok(())

View file

@ -0,0 +1,92 @@
example:
ADDI64 r254, r254, -8d
ST r31, r254, 0a, 8h
LI64 r3, 768d
LI64 r2, 0d
JAL r31, r0, :integer
LD r31, r254, 0a, 8h
ADDI64 r254, r254, 8d
JALA r0, r31, 0a
integer:
LI64 r6, 0d
LI64 r1, 4d
JNE r3, r6, :0
SUB64 r10, r3, r2
ADDI64 r12, r10, 1d
DIRS64 r0, r3, r1, r12
ADD64 r1, r3, r2
JMP :0
0: JALA r0, r31, 0a
line:
ST r2, r254, 0a, 16h
ADDI64 r2, r254, 0d
ST r4, r254, 0a, 16h
ADDI64 r4, r254, 0d
ST r6, r254, 0a, 16h
ADDI64 r6, r254, 0d
LD r9, r4, 0a, 8h
LD r11, r2, 0a, 8h
JGTU r11, r9, :0
JMP :0
0: JALA r0, r31, 0a
main:
ADDI64 r254, r254, -112d
ST r31, r254, 96a, 16h
LI64 r32, 10d
LI64 r1, 0d
ADDI64 r7, r254, 48d
ADDI64 r5, r254, 64d
ADDI64 r3, r254, 80d
ST r1, r254, 80a, 8h
ST r1, r254, 88a, 8h
ST r1, r254, 64a, 8h
ST r1, r254, 72a, 8h
ST r1, r254, 48a, 8h
ST r1, r254, 56a, 8h
CP r8, r32
CP r2, r3
CP r4, r5
CP r6, r7
JAL r31, r0, :line
ADDI64 r7, r254, 0d
ADDI64 r5, r254, 16d
ADDI64 r3, r254, 32d
ST r1, r254, 32a, 8h
ST r1, r254, 40a, 8h
ST r1, r254, 16a, 8h
ST r1, r254, 24a, 8h
ST r1, r254, 0a, 8h
ST r1, r254, 8a, 8h
CP r8, r32
CP r2, r3
CP r4, r5
CP r6, r7
JAL r31, r0, :rect_line
JAL r31, r0, :example
LD r31, r254, 96a, 16h
ADDI64 r254, r254, 112d
JALA r0, r31, 0a
rect_line:
ST r2, r254, 0a, 16h
ADDI64 r2, r254, 0d
ST r4, r254, 0a, 16h
ADDI64 r4, r254, 0d
ST r6, r254, 0a, 16h
ADDI64 r6, r254, 0d
LI64 r10, 0d
LD r12, r2, 8a, 8h
LD r4, r4, 0a, 8h
ADD64 r6, r4, r12
3: JNE r10, r8, :0
JMP :1
0: CP r9, r12
4: JNE r6, r9, :2
ADDI64 r10, r10, 1d
JMP :3
2: ADDI64 r9, r9, 1d
JMP :4
1: JALA r0, r31, 0a
timed out
code size: 797
ret: 0
status: Ok(())