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 return @inline(foo, 1, 2, 3) - 6
} }
gb := 0
foo := fn(a: int, b: int, c: int): int { foo := fn(a: int, b: int, c: int): int {
if gb != 0 return 1
return a + b + c return a + b + c
} }
``` ```
@ -777,13 +780,14 @@ screenidx := fn(orange: int): int {
// in module: random.hb // in module: random.hb
integer := fn(min: int, max: int): int { 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 return rng % (max - min + 1) + min
} } else {
return rng return rng
} }
}
``` ```
#### inlined_generic_functions #### inlined_generic_functions

View file

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

View file

@ -280,11 +280,22 @@ impl TokenKind {
Self::Eq => (a == b) as i64, Self::Eq => (a == b) as i64,
Self::Ne => (a != b) as i64, Self::Ne => (a != b) as i64,
Self::Band => a & b, Self::Band => a & b,
Self::Bor => a | b,
Self::Mod => a % b,
Self::Shr => a >> b, Self::Shr => a >> b,
s => todo!("{s}"), 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 { pub fn is_homogenous(&self) -> bool {
self.precedence() != Self::Eq.precedence() self.precedence() != Self::Eq.precedence()
&& self.precedence() != Self::Gt.precedence() && self.precedence() != Self::Gt.precedence()

View file

@ -395,6 +395,16 @@ mod ty {
_ => return None, _ => 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)] #[derive(PartialEq, Eq, Default, Debug, Clone, Copy)]

View file

@ -1125,12 +1125,12 @@ impl ExprRef {
Self(NonNull::from(expr).cast()) Self(NonNull::from(expr).cast())
} }
pub fn get<'a>(&self, from: &'a Ast) -> Option<&'a Expr<'a>> { pub fn get<'a>(&self, from: &'a Ast) -> &'a Expr<'a> {
from.mem.contains(self.0.as_ptr() as _).then_some(())?; 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 // 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 // arenas regions, it muts be walid, since arena does not give invalid pointers to its
// allocations // allocations
Some(unsafe { { self.0 }.as_ref() }) unsafe { { self.0 }.as_ref() }
} }
pub fn dangling() -> Self { pub fn dangling() -> Self {

View file

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