implementing wide returns and adding integer upcast ops

This commit is contained in:
Jakub Doka 2024-10-20 21:00:56 +02:00
parent d5c90b95a7
commit 11c8755b18
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
4 changed files with 299 additions and 100 deletions

View file

@ -395,29 +395,6 @@ main := fn(): int {
}
```
#### wide_ret
```hb
OemIdent := struct {
dos_version: [u8; 8],
dos_version_name: [u8; 8],
}
Stru := struct {
a: u16,
b: u16,
}
small_struct := fn(): Stru {
return .{a: 0, b: 0}
}
main := fn(major: int, minor: int): OemIdent {
small_struct()
ver := [u8].(0, 0, 0, 0, 0, 0, 0, 0)
return OemIdent.(ver, ver)
}
```
### Incomplete Examples
#### comptime_pointers
@ -548,6 +525,34 @@ main := fn(): int {
### Purely Testing Examples
#### wide_ret
```hb
OemIdent := struct {
dos_version: [u8; 8],
dos_version_name: [u8; 8],
}
Stru := struct {
a: u16,
b: u16,
}
small_struct := fn(): Stru {
return .{a: 0, b: 0}
}
maina := fn(major: int, minor: int): OemIdent {
_f := small_struct()
ver := [u8].(0, 0, 0, 3, 1, 0, 0, 0)
return OemIdent.(ver, ver)
}
main := fn(): int {
m := maina(0, 0)
return m.dos_version[3] - m.dos_version_name[4]
}
```
#### comptime_min_reg_leak
```hb
a := @use("math.hb").min(100, 50)

View file

@ -7,7 +7,7 @@ use {
parser::{
self,
idfl::{self},
Expr, ExprRef, FileId, Pos,
CtorField, Expr, ExprRef, FileId, Pos,
},
reg, task,
ty::{self, ArrayLen, Tuple},
@ -483,6 +483,7 @@ impl Nodes {
Kind::Stre => write!(out, "stre: "),
Kind::Mem => write!(out, " mem: "),
Kind::Idk => write!(out, " idk: "),
Kind::Extend => write!(out, " ext: "),
}?;
if self[node].kind != Kind::Loop && self[node].kind != Kind::Region {
@ -837,6 +838,8 @@ pub enum Kind {
Phi,
Arg,
// [ctrl, oper]
Extend,
// [ctrl, oper]
UnOp {
op: lexer::TokenKind,
},
@ -1031,9 +1034,9 @@ impl ItemCtx {
let mut nodes = core::mem::take(&mut self.nodes);
nodes.visited.clear(nodes.values.len());
let func = Function::new(&mut nodes, tys, sig);
let fuc = Function::new(&mut nodes, tys, sig);
let mut ralloc = Regalloc::default(); // TODO: reuse
log::info!("{:?}", func);
log::info!("{:?}", fuc);
if self.call_count != 0 {
core::mem::swap(
&mut ralloc.env.preferred_regs_by_class,
@ -1046,7 +1049,7 @@ impl ItemCtx {
validate_ssa: false,
algorithm: regalloc2::Algorithm::Ion,
};
regalloc2::run_with_ctx(&func, &ralloc.env, &options, &mut ralloc.ctx)
regalloc2::run_with_ctx(&fuc, &ralloc.env, &options, &mut ralloc.ctx)
.unwrap_or_else(|err| panic!("{err}"));
if self.call_count != 0 {
@ -1067,10 +1070,10 @@ impl ItemCtx {
*saved_regs.entry(hvenc).or_insert(would_insert)
};
for (i, block) in func.blocks.iter().enumerate() {
for (i, block) in fuc.blocks.iter().enumerate() {
let blk = regalloc2::Block(i as _);
func.nodes[block.nid].offset = self.code.len() as _;
for instr_or_edit in ralloc.ctx.output.block_insts_and_edits(&func, blk) {
fuc.nodes[block.nid].offset = self.code.len() as _;
for instr_or_edit in ralloc.ctx.output.block_insts_and_edits(&fuc, blk) {
let inst = match instr_or_edit {
regalloc2::InstOrEdit::Inst(inst) => inst,
regalloc2::InstOrEdit::Edit(&regalloc2::Edit::Move { from, to }) => {
@ -1079,16 +1082,16 @@ impl ItemCtx {
}
};
let nid = func.instrs[inst.index()].nid;
let nid = fuc.instrs[inst.index()].nid;
if nid == NEVER {
continue;
};
let allocs = ralloc.ctx.output.inst_allocs(inst);
let node = &func.nodes[nid];
let node = &fuc.nodes[nid];
match node.kind {
Kind::If => {
let &[_, cond] = node.inputs.as_slice() else { unreachable!() };
if let Kind::BinOp { op } = func.nodes[cond].kind
if let Kind::BinOp { op } = fuc.nodes[cond].kind
&& let Some((op, swapped)) = op.cond_op(node.ty.is_signed())
{
let rel = Reloc::new(self.code.len(), 3, 2);
@ -1109,13 +1112,19 @@ impl ItemCtx {
Kind::Return => {
match tys.size_of(sig.ret) {
0..=8 => {}
9..=16 => todo!(),
size @ 9..=16 => {
self.emit(instrs::ld(reg::RET, atr(allocs[0]), 0, size as _));
}
size @ 17.. => {
self.emit(instrs::bmc(atr(allocs[0]), 1, size.try_into().unwrap()));
self.emit(instrs::bmc(
atr(allocs[0]),
reg::RET,
size.try_into().unwrap(),
));
}
}
if i != func.blocks.len() - 1 {
if i != fuc.blocks.len() - 1 {
let rel = Reloc::new(self.code.len(), 1, 4);
self.ret_relocs.push(rel);
self.emit(instrs::jmp(0));
@ -1124,6 +1133,22 @@ impl ItemCtx {
Kind::CInt { value } => {
self.emit(instrs::li64(atr(allocs[0]), value as _));
}
Kind::Extend => {
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));
}
}
}
Kind::UnOp { op } => {
let op = op.unop().expect("TODO: unary operator not supported");
let &[dst, oper] = allocs else { unreachable!() };
@ -1132,15 +1157,15 @@ impl ItemCtx {
Kind::BinOp { op } => {
let &[.., rhs] = node.inputs.as_slice() else { unreachable!() };
if let Kind::CInt { value } = func.nodes[rhs].kind
&& func.nodes[rhs].lock_rc != 0
if let Kind::CInt { value } = fuc.nodes[rhs].kind
&& fuc.nodes[rhs].lock_rc != 0
&& let Some(op) =
op.imm_binop(node.ty.is_signed(), func.tys.size_of(node.ty))
op.imm_binop(node.ty.is_signed(), fuc.tys.size_of(node.ty))
{
let &[dst, lhs] = allocs else { unreachable!() };
self.emit(op(atr(dst), atr(lhs), value as _));
} else if let Some(op) =
op.binop(node.ty.is_signed(), func.tys.size_of(node.ty))
op.binop(node.ty.is_signed(), fuc.tys.size_of(node.ty))
{
let &[dst, lhs, rhs] = allocs else { unreachable!() };
self.emit(op(atr(dst), atr(lhs), atr(rhs)));
@ -1155,6 +1180,10 @@ impl ItemCtx {
reloc: Reloc::new(self.code.len(), 3, 4),
});
self.emit(instrs::jal(reg::RET_ADDR, reg::ZERO, 0));
if let size @ 9..=16 = tys.size_of(node.ty) {
let stck = fuc.nodes[*node.inputs.last().unwrap()].offset;
self.emit(instrs::st(reg::RET, reg::STACK_PTR, stck as _, size as _));
}
}
Kind::Global { global } => {
let reloc = Reloc::new(self.code.len(), 3, 4);
@ -1166,24 +1195,24 @@ impl ItemCtx {
}
Kind::Stck => {
let base = reg::STACK_PTR;
let offset = func.nodes[nid].offset;
let offset = fuc.nodes[nid].offset;
self.emit(instrs::addi64(atr(allocs[0]), base, offset as _));
}
Kind::Idk => {}
Kind::Load => {
let mut region = node.inputs[1];
let mut offset = 0;
if func.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
func.nodes[func.nodes[region].inputs[2]].kind
fuc.nodes[fuc.nodes[region].inputs[2]].kind
{
region = func.nodes[region].inputs[1];
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let size = tys.size_of(node.ty);
if size <= 8 {
let (base, offset) = match func.nodes[region].kind {
Kind::Stck => (reg::STACK_PTR, func.nodes[region].offset + offset),
let (base, offset) = match fuc.nodes[region].kind {
Kind::Stck => (reg::STACK_PTR, fuc.nodes[region].offset + offset),
_ => (atr(allocs[1]), offset),
};
self.emit(instrs::ld(atr(allocs[0]), base, offset as _, size as _));
@ -1194,15 +1223,15 @@ impl ItemCtx {
let mut region = node.inputs[2];
let mut offset = 0;
let size = u16::try_from(tys.size_of(node.ty)).expect("TODO");
if func.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
if fuc.nodes[region].kind == (Kind::BinOp { op: TokenKind::Add })
&& let Kind::CInt { value } =
func.nodes[func.nodes[region].inputs[2]].kind
fuc.nodes[fuc.nodes[region].inputs[2]].kind
&& size <= 8
{
region = func.nodes[region].inputs[1];
region = fuc.nodes[region].inputs[1];
offset = value as Offset;
}
let nd = &func.nodes[region];
let nd = &fuc.nodes[region];
let (base, offset, src) = match nd.kind {
Kind::Stck if size <= 8 => {
(reg::STACK_PTR, nd.offset + offset, allocs[0])
@ -1215,7 +1244,14 @@ impl ItemCtx {
self.emit(instrs::st(atr(src), base, offset as _, size));
}
}
_ => unreachable!(),
Kind::Start
| Kind::Entry
| Kind::Mem
| Kind::End
| Kind::Then
| Kind::Else
| Kind::Phi
| Kind::Arg => unreachable!(),
}
}
}
@ -1635,12 +1671,16 @@ impl<'a> Codegen<'a> {
Expr::Ident { id, pos, .. } => {
let decl = self.find_type(pos, self.ci.file, Ok(id), self.files);
match decl.expand() {
ty::Kind::Builtin(ty::NEVER) => Value::NEVER,
ty::Kind::Global(global) => {
let gl = &self.tys.ins.globals[global as usize];
let value = self.ci.nodes.new_node(gl.ty, Kind::Global { global }, [VOID]);
Some(Value::ptr(value).ty(gl.ty))
}
_ => todo!("{decl:?}"),
_ => self.report_unhandled_ast(
expr,
format_args!("identifier evaluated to '{}'", self.ty_display(decl)),
),
}
}
Expr::Comment { .. } => Some(Value::VOID),
@ -1661,12 +1701,15 @@ impl<'a> Codegen<'a> {
Some(Value::new(global).ty(ty))
}
Expr::Return { pos, val } => {
let value = if let Some(val) = val {
let mut value = if let Some(val) = val {
self.expr_ctx(val, Ctx { ty: self.ci.ret })?
} else {
Value { ty: ty::Id::VOID, ..Default::default() }
};
let expected = *self.ci.ret.get_or_insert(value.ty);
self.assert_ty(pos, &mut value, expected, "return value");
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() {
@ -1678,9 +1721,6 @@ impl<'a> Codegen<'a> {
self.ci.nodes[NEVER].inputs.push(self.ci.ctrl);
self.ci.nodes[self.ci.ctrl].outputs.push(NEVER);
let expected = *self.ci.ret.get_or_insert(value.ty);
self.assert_ty(pos, value.ty, expected, "return value");
None
}
Expr::Field { target, name, pos } => {
@ -1760,23 +1800,16 @@ impl<'a> Codegen<'a> {
}
Some(self.ci.nodes.new_node_lit(val.ty, Kind::UnOp { op }, [VOID, val.id]))
}
Expr::BinOp { left: &Expr::Ident { id, .. }, op: TokenKind::Decl, right } => {
let mut value = self.raw_expr(right)?;
self.strip_var(&mut value);
self.ci.nodes.lock(value.id);
self.ci.scope.vars.push(Variable {
id,
value: value.id,
ptr: value.ptr,
ty: value.ty,
});
Expr::BinOp { left, op: TokenKind::Decl, right } => {
let right = self.raw_expr(right)?;
self.assign_pattern(left, right);
Some(Value::VOID)
}
Expr::BinOp { left, op: TokenKind::Assign, right } => {
let dest = self.raw_expr(left)?;
let value = self.expr_ctx(right, Ctx::default().with_ty(dest.ty))?;
let mut value = self.expr_ctx(right, Ctx::default().with_ty(dest.ty))?;
self.assert_ty(left.pos(), value.ty, dest.ty, "assignment source");
self.assert_ty(left.pos(), &mut value, dest.ty, "assignment source");
if dest.var {
self.ci.nodes.lock(value.id);
@ -1791,13 +1824,16 @@ impl<'a> Codegen<'a> {
Some(Value::VOID)
}
Expr::BinOp { left, op, right } if op != TokenKind::Assign => {
let lhs = self.expr_ctx(left, ctx)?;
Expr::BinOp { left, op, right }
if !matches!(op, TokenKind::Assign | TokenKind::Decl) =>
{
let mut lhs = self.expr_ctx(left, ctx)?;
self.ci.nodes.lock(lhs.id);
let rhs = self.expr_ctx(right, Ctx::default().with_ty(lhs.ty));
self.ci.nodes.unlock(lhs.id);
let ty = self.binop_ty(left.pos(), rhs?.ty, lhs.ty, op);
let inps = [VOID, lhs.id, rhs?.id];
let mut rhs = rhs?;
let ty = self.binop_ty(left.pos(), &mut rhs, &mut lhs, op);
let inps = [VOID, lhs.id, rhs.id];
Some(self.ci.nodes.new_node_lit(ty::bin_ret(ty, op), Kind::BinOp { op }, inps))
}
Expr::Index { base, index } => {
@ -1886,7 +1922,7 @@ impl<'a> Codegen<'a> {
Expr::Call { func, args, .. } => {
self.ci.call_count += 1;
let ty = self.ty(func);
let ty::Kind::Func(fnc) = ty.expand() else {
let ty::Kind::Func(fu) = ty.expand() else {
self.report(
func.pos(),
fa!("compiler cant (yet) call '{}'", self.ty_display(ty)),
@ -1894,9 +1930,9 @@ impl<'a> Codegen<'a> {
return Value::NEVER;
};
self.make_func_reachable(fnc);
self.make_func_reachable(fu);
let fuc = &self.tys.ins.funcs[fnc as usize];
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 {
@ -1920,10 +1956,10 @@ impl<'a> Codegen<'a> {
if self.tys.size_of(ty) == 0 {
continue;
}
let value = self.expr_ctx(arg, Ctx::default().with_ty(ty))?;
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(), value.ty, ty, fa!("argument {}", carg.name));
self.assert_ty(arg.pos(), &mut value, ty, fa!("argument {}", carg.name));
inps.push(value.id);
}
@ -1945,15 +1981,14 @@ impl<'a> Codegen<'a> {
let alt_value = match self.tys.size_of(sig.ret) {
0..=8 => None,
9..=16 => todo!(),
17.. => {
9.. => {
let stck = self.ci.nodes.new_node_nop(sig.ret, Kind::Stck, [VOID, MEM]);
inps.push(stck);
Some(Value::ptr(stck).ty(sig.ret))
}
};
self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func: fnc }, inps);
self.ci.ctrl = self.ci.nodes.new_node(sig.ret, Kind::Call { func: fu }, inps);
self.store_mem(VOID, VOID);
@ -2028,8 +2063,8 @@ impl<'a> Codegen<'a> {
for (field, offset) in
fields.iter().zip((0u32..).step_by(elem_size as usize))
{
let value = self.expr_ctx(field, Ctx::default().with_ty(elem))?;
_ = self.assert_ty(field.pos(), value.ty, elem, "array value");
let mut value = self.expr_ctx(field, Ctx::default().with_ty(elem))?;
_ = self.assert_ty(field.pos(), &mut value, elem, "array value");
let mem = self.offset(mem, offset);
self.store_mem(mem, value.id);
}
@ -2124,8 +2159,8 @@ impl<'a> Codegen<'a> {
let mut ret = Some(Value::VOID);
for stmt in stmts {
ret = ret.and(self.expr(stmt));
if let Some(id) = ret {
self.assert_ty(stmt.pos(), id.ty, ty::Id::VOID, "statement");
if let Some(mut id) = ret {
self.assert_ty(stmt.pos(), &mut id, ty::Id::VOID, "statement");
} else {
break;
}
@ -2320,6 +2355,37 @@ impl<'a> Codegen<'a> {
}
}
fn assign_pattern(&mut self, pat: &Expr, mut 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,
value: right.id,
ptr: right.ptr,
ty: right.ty,
});
}
Expr::Ctor { pos, fields, .. } => {
let ty::Kind::Struct(idx) = right.ty.expand() else {
self.report(pos, "can't use struct destruct on non struct value (TODO: shold work with modules)");
return;
};
for &CtorField { pos, name, ref value } in fields {
let Some((offset, ty)) = OffsetIter::offset_of(&self.tys, idx, name) else {
self.report(pos, format_args!("field not found: {name:?}"));
continue;
};
let off = self.offset(right.id, offset);
self.assign_pattern(value, Value::ptr(off).ty(ty));
}
}
ref pat => self.report_unhandled_ast(pat, "pattern"),
}
}
fn expr_ctx(&mut self, expr: &Expr, ctx: Ctx) -> Option<Value> {
let mut n = self.raw_expr_ctx(expr, ctx)?;
self.strip_var(&mut n);
@ -2492,26 +2558,46 @@ impl<'a> Codegen<'a> {
#[must_use]
#[track_caller]
fn binop_ty(&self, pos: Pos, lhs: ty::Id, rhs: ty::Id, op: TokenKind) -> ty::Id {
if let Some(res) = lhs.try_upcast(rhs, ty::TyCheck::BinOp) {
res
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 lhs.ty != upcasted {
lhs.ty = upcasted;
lhs.id = self.ci.nodes.new_node(upcasted, Kind::Extend, [VOID, lhs.id]);
} else if rhs.ty != upcasted {
rhs.ty = upcasted;
rhs.id = self.ci.nodes.new_node(upcasted, Kind::Extend, [VOID, rhs.id]);
}
upcasted
} else {
let ty = self.ty_display(lhs);
let expected = self.ty_display(rhs);
let ty = self.ty_display(lhs.ty);
let expected = self.ty_display(rhs.ty);
self.report(pos, fa!("'{ty} {op} {expected}' is not supported"));
ty::Id::NEVER
}
}
#[track_caller]
fn assert_ty(&self, pos: Pos, ty: ty::Id, expected: ty::Id, hint: impl fmt::Display) -> bool {
if ty.try_upcast(expected, ty::TyCheck::BinOp) != Some(expected) {
let ty = self.ty_display(ty);
fn assert_ty(
&mut self,
pos: Pos,
src: &mut Value,
expected: ty::Id,
hint: impl fmt::Display,
) -> bool {
if let Some(upcasted) = src.ty.try_upcast(expected, ty::TyCheck::BinOp)
&& upcasted == expected
{
if src.ty != upcasted {
src.ty = upcasted;
src.id = self.ci.nodes.new_node(upcasted, Kind::Extend, [VOID, src.id]);
}
true
} else {
let ty = self.ty_display(src.ty);
let expected = self.ty_display(expected);
self.report(pos, fa!("expected {hint} to be of type {expected}, got {ty}"));
return false;
false
}
true
}
#[track_caller]
@ -2528,7 +2614,7 @@ impl<'a> Codegen<'a> {
}
#[track_caller]
fn report_unhandled_ast(&self, ast: &Expr, hint: &str) -> ! {
fn report_unhandled_ast(&self, ast: &Expr, hint: impl Display) -> ! {
log::info!("{ast:#?}");
self.fatal_report(ast.pos(), fa!("compiler does not (yet) know how to handle ({hint})"));
}
@ -2727,7 +2813,9 @@ impl<'a> Function<'a> {
regalloc2::PReg::new(1, regalloc2::RegClass::Int),
)]
}
9..=16 => todo!(),
9..=16 => {
vec![self.urg(self.nodes[node.inputs[1]].inputs[1])]
}
17.. => {
vec![self.urg(node.inputs[1])]
}
@ -2751,6 +2839,10 @@ impl<'a> Function<'a> {
let ops = vec![self.drg(nid)];
self.add_instr(nid, ops);
}
Kind::Extend => {
let ops = vec![self.drg(nid), self.urg(node.inputs[1])];
self.add_instr(nid, ops);
}
Kind::Entry => {
self.nodes[nid].ralloc_backref = self.add_block(nid);
@ -2863,8 +2955,7 @@ impl<'a> Function<'a> {
}
match self.tys.size_of(fuc.ret) {
0..=8 => {}
9..=16 => todo!(),
0..=16 => {}
17.. => {
ops.push(regalloc2::Operand::reg_fixed_use(
self.rg(*node.inputs.last().unwrap()),
@ -3332,11 +3423,10 @@ mod tests {
global_variables;
//directives;
c_strings;
//struct_patterns;
struct_patterns;
arrays;
//inline;
idk;
//wide_ret;
// Incomplete Examples;
//comptime_pointers;
@ -3345,6 +3435,7 @@ mod tests {
fb_driver;
// Purely Testing Examples;
wide_ret;
comptime_min_reg_leak;
//different_types;
//struct_return_from_module_function;

View file

@ -0,0 +1,51 @@
fib:
ADDI64 r254, r254, -32d
ST r31, r254, 0a, 32h
CP r32, r2
LI64 r33, 2d
JLTU r2, r33, :0
CP r34, r32
ADDI64 r2, r34, -1d
JAL r31, r0, :fib
CP r2, r32
CP r34, r1
SUB64 r2, r2, r33
JAL r31, r0, :fib
ADD64 r1, r1, r34
JMP :1
0: CP r1, r32
1: LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d
JALA r0, r31, 0a
fib_iter:
LI64 r4, 1d
LI64 r5, 0d
CP r1, r5
CP r10, r4
2: JNE r2, r5, :0
JMP :1
0: ADD64 r3, r10, r1
SUB64 r2, r2, r4
CP r1, r10
CP r10, r3
JMP :2
1: JALA r0, r31, 0a
main:
ADDI64 r254, r254, -34d
ST r31, r254, 2a, 32h
LI64 r32, 10d
ADDI64 r33, r254, 0d
ST r32, r254, 0a, 1h
ST r32, r254, 1a, 1h
LD r2, r254, 0a, 1h
JAL r31, r0, :fib
CP r34, r1
LD r2, r254, 1a, 1h
JAL r31, r0, :fib_iter
SUB64 r1, r34, r1
LD r31, r254, 2a, 32h
ADDI64 r254, r254, 34d
JALA r0, r31, 0a
code size: 354
ret: 0
status: Ok(())

View file

@ -0,0 +1,52 @@
main:
ADDI64 r254, r254, -56d
ST r31, r254, 16a, 40h
LI64 r4, 0d
ADDI64 r32, r254, 0d
CP r3, r4
JAL r31, r0, :maina
ST r1, r254, 0a, 16h
LD r33, r254, 12a, 1h
LD r34, r254, 3a, 1h
SUB8 r35, r34, r33
ANDI r1, r35, 255d
LD r31, r254, 16a, 40h
ADDI64 r254, r254, 56d
JALA r0, r31, 0a
maina:
ADDI64 r254, r254, -88d
ST r31, r254, 24a, 64h
JAL r31, r0, :small_struct
LI64 r32, 1d
LI64 r33, 3d
LI64 r34, 0d
ADDI64 r35, r254, 0d
ADDI64 r36, r254, 16d
ST r34, r254, 16a, 1h
ST r34, r254, 17a, 1h
ST r34, r254, 18a, 1h
ST r33, r254, 19a, 1h
ST r32, r254, 20a, 1h
ST r34, r254, 21a, 1h
ST r34, r254, 22a, 1h
ST r34, r254, 23a, 1h
LD r37, r254, 16a, 8h
ST r37, r254, 0a, 8h
LD r38, r254, 16a, 8h
ST r38, r254, 8a, 8h
LD r1, r35, 0a, 16h
LD r31, r254, 24a, 64h
ADDI64 r254, r254, 88d
JALA r0, r31, 0a
small_struct:
ADDI64 r254, r254, -4d
LI64 r2, 0d
ADDI64 r3, r254, 0d
ST r2, r254, 0a, 2h
ST r2, r254, 2a, 2h
LD r1, r254, 0a, 4h
ADDI64 r254, r254, 4d
JALA r0, r31, 0a
code size: 532
ret: 2
status: Ok(())