making tests more robust for no reason

This commit is contained in:
mlokr 2024-09-04 02:35:09 +02:00
parent 9e0e0242aa
commit 00ad474881
No known key found for this signature in database
GPG key ID: DEA147DDEE644993
7 changed files with 274 additions and 237 deletions

View file

@ -8,7 +8,7 @@ use {
parser::{self, find_symbol, idfl, CtorField, Expr, ExprRef, FileId, Pos}, parser::{self, find_symbol, idfl, CtorField, Expr, ExprRef, FileId, Pos},
HashMap, HashMap,
}, },
std::{ops::Range, rc::Rc}, std::{ops::Range, rc::Rc, u32},
}; };
type Offset = u32; type Offset = u32;
@ -517,29 +517,11 @@ struct Reloc {
offset: Offset, offset: Offset,
sub_offset: u8, sub_offset: u8,
width: u8, width: u8,
#[cfg(debug_assertions)]
shifted: bool,
} }
impl Reloc { impl Reloc {
fn new(offset: u32, sub_offset: u8, width: u8) -> Self { fn new(offset: u32, sub_offset: u8, width: u8) -> Self {
Self { Self { offset, sub_offset, width }
offset,
sub_offset,
width,
#[cfg(debug_assertions)]
shifted: false,
}
}
fn shifted(offset: u32, sub_offset: u8, width: u8) -> Self {
Self {
offset,
sub_offset,
width,
#[cfg(debug_assertions)]
shifted: true,
}
} }
fn apply_stack_offset(&self, code: &mut [u8], stack: &stack::Alloc) { fn apply_stack_offset(&self, code: &mut [u8], stack: &stack::Alloc) {
@ -556,14 +538,14 @@ impl Reloc {
((id >> 32) as u32, id as u32) ((id >> 32) as u32, id as u32)
} }
fn apply_jump(&self, code: &mut [u8], to: u32) { fn apply_jump(mut self, code: &mut [u8], to: u32, from: u32) -> i64 {
self.offset += from;
let offset = to as i64 - self.offset as i64; let offset = to as i64 - self.offset as i64;
self.write_offset(code, offset); self.write_offset(code, offset);
offset
} }
fn write_offset(&self, code: &mut [u8], offset: i64) { fn write_offset(&self, code: &mut [u8], offset: i64) {
#[cfg(debug_assertions)]
assert!(self.shifted);
let bytes = offset.to_ne_bytes(); let bytes = offset.to_ne_bytes();
let slice = &mut code[self.offset as usize + self.sub_offset as usize..]; let slice = &mut code[self.offset as usize + self.sub_offset as usize..];
slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]); slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]);
@ -766,21 +748,10 @@ impl ItemCtx {
} }
fn finalize(&mut self, output: &mut Output) { fn finalize(&mut self, output: &mut Output) {
let len = output.code.len() as Offset;
let base = self.snap.code as Offset; let base = self.snap.code as Offset;
for reloc in output.reloc_iter_mut(&self.snap).chain(&mut self.ret_relocs) {
#[cfg(debug_assertions)]
{
if std::mem::replace(&mut reloc.shifted, true) {
panic!("reloc visited twice");
}
}
reloc.offset += base;
debug_assert!(reloc.offset < len);
}
if let Some(last_ret) = self.ret_relocs.last() if let Some(last_ret) = self.ret_relocs.last()
&& last_ret.offset as usize == output.code.len() - 5 && (last_ret.offset + base) as usize == output.code.len() - 5
{ {
output.code.truncate(output.code.len() - 5); output.code.truncate(output.code.len() - 5);
self.ret_relocs.pop(); self.ret_relocs.pop();
@ -794,7 +765,8 @@ impl ItemCtx {
} }
for rel in self.ret_relocs.drain(..) { for rel in self.ret_relocs.drain(..) {
rel.apply_jump(&mut output.code, len); let off = rel.apply_jump(&mut output.code, len, base);
debug_assert!(off > 0);
} }
self.finalize_frame(output); self.finalize_frame(output);
@ -872,6 +844,8 @@ struct Global {
offset: Offset, offset: Offset,
ty: ty::Id, ty: ty::Id,
runtime: bool, runtime: bool,
_file: FileId,
_name: Ident,
} }
struct Field { struct Field {
@ -918,6 +892,25 @@ struct Types {
} }
impl Types { impl Types {
pub fn offset_of_item(&self, item: ty::Kind, ct_hint: Option<u32>) -> Option<Offset> {
task::unpack(match item {
ty::Kind::Func(f) => self.funcs[f as usize].offset,
ty::Kind::Global(g) => self.globals[g as usize].offset,
ty::Kind::Builtin(u32::MAX) => ct_hint?,
_ => unreachable!(),
})
.ok()
}
pub fn is_runtime_item(&self, item: ty::Kind) -> bool {
match item {
ty::Kind::Func(f) => self.funcs[f as usize].runtime,
ty::Kind::Global(f) => self.globals[f as usize].runtime,
ty::Kind::Builtin(u32::MAX) => false,
_ => unreachable!(),
}
}
fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc { fn parama(&self, ret: impl Into<ty::Id>) -> ParamAlloc {
ParamAlloc(2 + (9..=16).contains(&self.size_of(ret.into())) as u8..12) ParamAlloc(2 + (9..=16).contains(&self.size_of(ret.into())) as u8..12)
} }
@ -1053,8 +1046,7 @@ struct FTask {
pub struct Snapshot { pub struct Snapshot {
code: usize, code: usize,
string_data: usize, string_data: usize,
funcs: usize, relocs: usize,
globals: usize,
strings: usize, strings: usize,
} }
@ -1062,26 +1054,29 @@ impl Snapshot {
fn _sub(&mut self, other: &Self) { fn _sub(&mut self, other: &Self) {
self.code -= other.code; self.code -= other.code;
self.string_data -= other.string_data; self.string_data -= other.string_data;
self.funcs -= other.funcs; self.relocs -= other.relocs;
self.globals -= other.globals;
self.strings -= other.strings; self.strings -= other.strings;
} }
fn _add(&mut self, other: &Self) { fn _add(&mut self, other: &Self) {
self.code += other.code; self.code += other.code;
self.string_data += other.string_data; self.string_data += other.string_data;
self.funcs += other.funcs; self.relocs += other.relocs;
self.globals += other.globals;
self.strings += other.strings; self.strings += other.strings;
} }
} }
struct OutReloc {
from: ty::Kind,
to: ty::Kind,
rel: Reloc,
}
#[derive(Default)] #[derive(Default)]
struct Output { struct Output {
code: Vec<u8>, code: Vec<u8>,
string_data: Vec<u8>, string_data: Vec<u8>,
funcs: Vec<(ty::Func, Reloc)>, relocs: Vec<OutReloc>,
globals: Vec<(ty::Global, Reloc)>,
strings: Vec<StringReloc>, strings: Vec<StringReloc>,
} }
@ -1118,14 +1113,6 @@ impl Output {
self.emit(tx()); self.emit(tx());
} }
fn reloc_iter_mut(&mut self, snap: &Snapshot) -> impl Iterator<Item = &mut Reloc> {
self.globals[snap.globals..]
.iter_mut()
.chain(&mut self.funcs[snap.funcs..])
.map(|(_, rel)| rel)
.chain(self.strings[snap.strings..].iter_mut().map(|rl| &mut rl.reloc))
}
fn append(&mut self, val: &mut Self) { fn append(&mut self, val: &mut Self) {
val.pop(self, &Snapshot::default()); val.pop(self, &Snapshot::default());
} }
@ -1135,26 +1122,7 @@ impl Output {
stash.code.extend(self.code.drain(snap.code..)); stash.code.extend(self.code.drain(snap.code..));
stash.string_data.extend(self.string_data.drain(snap.string_data..)); stash.string_data.extend(self.string_data.drain(snap.string_data..));
stash.funcs.extend(self.funcs.drain(snap.funcs..).inspect(|(_, rel)| { stash.relocs.extend(self.relocs.drain(snap.relocs..));
#[cfg(debug_assertions)]
assert!(!rel.shifted);
debug_assert!(
rel.offset as usize + init_code < stash.code.len(),
"{} {} {}",
rel.offset,
init_code,
stash.code.len()
)
}));
stash.globals.extend(self.globals.drain(snap.globals..).inspect(|(_, rel)| {
log::dbg!(
"reloc: {rel:?} {init_code} {} {} {}",
stash.code.len(),
self.code.len(),
snap.code
);
debug_assert!(rel.offset as usize + init_code < stash.code.len())
}));
stash.strings.extend(self.strings.drain(snap.strings..).inspect(|str| { stash.strings.extend(self.strings.drain(snap.strings..).inspect(|str| {
debug_assert!(str.reloc.offset as usize + init_code < stash.code.len()) debug_assert!(str.reloc.offset as usize + init_code < stash.code.len())
})); }));
@ -1163,13 +1131,13 @@ impl Output {
fn trunc(&mut self, snap: &Snapshot) { fn trunc(&mut self, snap: &Snapshot) {
self.code.truncate(snap.code); self.code.truncate(snap.code);
self.string_data.truncate(snap.string_data); self.string_data.truncate(snap.string_data);
self.globals.truncate(snap.globals); self.relocs.truncate(snap.relocs);
self.funcs.truncate(snap.funcs);
self.strings.truncate(snap.strings); self.strings.truncate(snap.strings);
} }
fn write_trap(&mut self, kind: trap::Trap) { fn write_trap(&mut self, kind: trap::Trap) {
self.emit(eca()); self.emit(eca());
self.code.push(255);
self.code.extend(kind.as_slice()); self.code.extend(kind.as_slice());
} }
@ -1177,8 +1145,7 @@ impl Output {
Snapshot { Snapshot {
code: self.code.len(), code: self.code.len(),
string_data: self.string_data.len(), string_data: self.string_data.len(),
funcs: self.funcs.len(), relocs: self.relocs.len(),
globals: self.globals.len(),
strings: self.strings.len(), strings: self.strings.len(),
} }
} }
@ -1283,7 +1250,7 @@ impl hbvm::mem::Memory for LoggedMem {
const VM_STACK_SIZE: usize = 1024 * 1024 * 2; const VM_STACK_SIZE: usize = 1024 * 1024 * 2;
struct Comptime { struct Comptime {
vm: hbvm::Vm<LoggedMem, 0>, vm: hbvm::Vm<LoggedMem, { 1024 * 10 }>,
depth: usize, depth: usize,
_stack: Box<[u8; VM_STACK_SIZE]>, _stack: Box<[u8; VM_STACK_SIZE]>,
} }
@ -1293,7 +1260,6 @@ impl Default for Comptime {
let mut stack = Box::<[u8; VM_STACK_SIZE]>::new_uninit(); let mut stack = Box::<[u8; VM_STACK_SIZE]>::new_uninit();
let mut vm = hbvm::Vm::default(); let mut vm = hbvm::Vm::default();
let ptr = unsafe { stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 }; let ptr = unsafe { stack.as_mut_ptr().cast::<u8>().add(VM_STACK_SIZE) as u64 };
log::dbg!("stack_ptr: {:x}", ptr);
vm.write_reg(STACK_PTR, ptr); vm.write_reg(STACK_PTR, ptr);
Self { vm, depth: 0, _stack: unsafe { stack.assume_init() } } Self { vm, depth: 0, _stack: unsafe { stack.assume_init() } }
} }
@ -1371,10 +1337,10 @@ mod trap {
} }
struct StringReloc { struct StringReloc {
// TODO: change to ty::Id
from: ty::Kind,
reloc: Reloc, reloc: Reloc,
range: std::ops::Range<u32>, range: std::ops::Range<u32>,
#[cfg(debug_assertions)]
shifted: bool,
} }
#[derive(Default)] #[derive(Default)]
@ -1399,8 +1365,7 @@ impl Codegen {
} }
pub fn dump(&mut self, out: &mut impl std::io::Write) -> std::io::Result<()> { pub fn dump(&mut self, out: &mut impl std::io::Write) -> std::io::Result<()> {
let reloc = Reloc::shifted(0, 3, 4); Reloc::new(0, 3, 4).apply_jump(&mut self.output.code, self.tys.funcs[0].offset, 0);
self.output.funcs.push((0, reloc));
self.link(); self.link();
out.write_all(&self.output.code) out.write_all(&self.output.code)
} }
@ -1567,13 +1532,8 @@ impl Codegen {
self.ci.ret_relocs.pop(); self.ci.ret_relocs.pop();
} }
let len = self.output.code.len() as u32; let len = self.output.code.len() as u32;
for mut rel in self.ci.ret_relocs.drain(ret_reloc_base..) { for rel in self.ci.ret_relocs.drain(ret_reloc_base..) {
rel.offset += self.ci.snap.code as u32; rel.apply_jump(&mut self.output.code, len, self.ci.snap.code as u32);
#[cfg(debug_assertions)]
{
rel.shifted = true;
}
rel.apply_jump(&mut self.output.code, len);
} }
return Some(Value { ty: sig.ret, loc }); return Some(Value { ty: sig.ret, loc });
@ -1736,12 +1696,7 @@ impl Codegen {
let range = start as _..self.output.string_data.len() as _; let range = start as _..self.output.string_data.len() as _;
let reloc = Reloc::new(self.local_offset() as _, 3, 4); let reloc = Reloc::new(self.local_offset() as _, 3, 4);
self.output.strings.push(StringReloc { self.output.strings.push(StringReloc { from: self.ci.id, reloc, range });
reloc,
range,
#[cfg(debug_assertions)]
shifted: false,
});
let reg = self.ci.regs.allocate(); let reg = self.ci.regs.allocate();
self.output.emit(instrs::lra(reg.get(), 0, 0)); self.output.emit(instrs::lra(reg.get(), 0, 0));
Some(Value::new(self.tys.make_ptr(ty::U8.into()), reg)) Some(Value::new(self.tys.make_ptr(ty::U8.into()), reg))
@ -1945,16 +1900,18 @@ impl Codegen {
self.ci.free_loc(value); self.ci.free_loc(value);
} }
log::dbg!("call ctx: {ctx:?}");
let loc = self.alloc_ret(sig.ret, ctx, false); let loc = self.alloc_ret(sig.ret, ctx, false);
if should_momize { if should_momize {
self.output.write_trap(trap::Trap::MomizedCall(trap::MomizedCall { func })); self.output.write_trap(trap::Trap::MomizedCall(trap::MomizedCall { func }));
} }
let reloc = Reloc::new(self.local_offset(), 3, 4); let rel = Reloc::new(self.local_offset(), 3, 4);
self.output.funcs.push((func, reloc)); self.output.relocs.push(OutReloc {
from: self.ci.id,
to: ty::Kind::Func(func),
rel,
});
self.output.emit(jal(RET_ADDR, ZERO, 0)); self.output.emit(jal(RET_ADDR, ZERO, 0));
self.make_func_reachable(func); self.make_func_reachable(func);
@ -2027,7 +1984,6 @@ impl Codegen {
loc: Loc::ct(value as u64), loc: Loc::ct(value as u64),
}), }),
E::If { cond, then, else_, .. } => { E::If { cond, then, else_, .. } => {
log::dbg!("if-cond");
let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?; let cond = self.expr_ctx(cond, Ctx::default().with_ty(ty::BOOL))?;
let reg = self.loc_to_reg(&cond.loc, 1); let reg = self.loc_to_reg(&cond.loc, 1);
let jump_offset = self.local_offset(); let jump_offset = self.local_offset();
@ -2035,14 +1991,12 @@ impl Codegen {
self.ci.free_loc(cond.loc); self.ci.free_loc(cond.loc);
self.ci.regs.free(reg); self.ci.regs.free(reg);
log::dbg!("if-then");
let then_unreachable = self.expr(then).is_none(); let then_unreachable = self.expr(then).is_none();
let mut else_unreachable = false; let mut else_unreachable = false;
let mut jump = self.local_offset() as i64 - jump_offset as i64; let mut jump = self.local_offset() as i64 - jump_offset as i64;
if let Some(else_) = else_ { if let Some(else_) = else_ {
log::dbg!("if-else");
let else_jump_offset = self.local_offset(); let else_jump_offset = self.local_offset();
if !then_unreachable { if !then_unreachable {
self.output.emit(jmp(0)); self.output.emit(jmp(0));
@ -2053,19 +2007,15 @@ impl Codegen {
if !then_unreachable { if !then_unreachable {
let jump = self.local_offset() as i64 - else_jump_offset as i64; let jump = self.local_offset() as i64 - else_jump_offset as i64;
log::dbg!("if-else-jump: {}", jump);
write_reloc(self.local_code(), else_jump_offset as usize + 1, jump, 4); write_reloc(self.local_code(), else_jump_offset as usize + 1, jump, 4);
} }
} }
log::dbg!("if-then-jump: {}", jump);
write_reloc(self.local_code(), jump_offset as usize + 3, jump, 2); write_reloc(self.local_code(), jump_offset as usize + 3, jump, 2);
(!then_unreachable || !else_unreachable).then_some(Value::void()) (!then_unreachable || !else_unreachable).then_some(Value::void())
} }
E::Loop { body, .. } => 'a: { E::Loop { body, .. } => 'a: {
log::dbg!("loop");
let loop_start = self.local_offset(); let loop_start = self.local_offset();
self.ci.loops.push(Loop { self.ci.loops.push(Loop {
var_count: self.ci.vars.len() as _, var_count: self.ci.vars.len() as _,
@ -2074,18 +2024,19 @@ impl Codegen {
}); });
let body_unreachable = self.expr(body).is_none(); let body_unreachable = self.expr(body).is_none();
log::dbg!("loop-end");
if !body_unreachable { if !body_unreachable {
let loop_end = self.local_offset(); let loop_end = self.local_offset();
self.output.emit(jmp(loop_start as i32 - loop_end as i32)); self.output.emit(jmp(loop_start as i32 - loop_end as i32));
} }
let loop_end = self.local_offset(); let loop_end = self.output.code.len() as u32;
let loopa = self.ci.loops.pop().unwrap(); let loopa = self.ci.loops.pop().unwrap();
let is_unreachable = loopa.reloc_base == self.ci.loop_relocs.len() as u32; let is_unreachable = loopa.reloc_base == self.ci.loop_relocs.len() as u32;
for reloc in self.ci.loop_relocs.drain(loopa.reloc_base as usize..) { for reloc in self.ci.loop_relocs.drain(loopa.reloc_base as usize..) {
reloc.apply_jump(&mut self.output.code[self.ci.snap.code..], loop_end); let off =
reloc.apply_jump(&mut self.output.code, loop_end, self.ci.snap.code as _);
debug_assert!(off > 0);
} }
let mut vars = std::mem::take(&mut self.ci.vars); let mut vars = std::mem::take(&mut self.ci.vars);
@ -2095,14 +2046,13 @@ impl Codegen {
self.ci.vars = vars; self.ci.vars = vars;
if is_unreachable { if is_unreachable {
log::dbg!("infinite loop");
break 'a None; break 'a None;
} }
Some(Value::void()) Some(Value::void())
} }
E::Break { .. } => { E::Break { .. } => {
self.ci.loop_relocs.push(Reloc::shifted(self.local_offset(), 1, 4)); self.ci.loop_relocs.push(Reloc::new(self.local_offset(), 1, 4));
self.output.emit(jmp(0)); self.output.emit(jmp(0));
None None
} }
@ -2147,7 +2097,6 @@ impl Codegen {
let lsize = self.tys.size_of(left.ty); let lsize = self.tys.size_of(left.ty);
let lhs = self.loc_to_reg(left.loc, lsize); let lhs = self.loc_to_reg(left.loc, lsize);
log::dbg!("{expr}");
let right = self.expr_ctx(right, Ctx::default().with_ty(left.ty))?; let right = self.expr_ctx(right, Ctx::default().with_ty(left.ty))?;
let rsize = self.tys.size_of(right.ty); let rsize = self.tys.size_of(right.ty);
@ -2261,7 +2210,6 @@ impl Codegen {
for (arg, carg) in args.iter().zip(cargs) { for (arg, carg) in args.iter().zip(cargs) {
let ty = self.ty(&carg.ty); let ty = self.ty(&carg.ty);
log::dbg!("arg: {}", self.ty_display(ty));
self.tys.args.push(ty); self.tys.args.push(ty);
let sym = parser::find_symbol(&fast.symbols, carg.id); let sym = parser::find_symbol(&fast.symbols, carg.id);
let loc = if sym.flags & idfl::COMPTIME == 0 { let loc = if sym.flags & idfl::COMPTIME == 0 {
@ -2315,7 +2263,7 @@ impl Codegen {
fn eval_const_low(&mut self, expr: &Expr, mut ty: Option<ty::Id>) -> (u64, ty::Id) { fn eval_const_low(&mut self, expr: &Expr, mut ty: Option<ty::Id>) -> (u64, ty::Id) {
let mut ci = ItemCtx { let mut ci = ItemCtx {
file: self.ci.file, file: self.ci.file,
id: self.ci.id, id: ty::Kind::Builtin(u32::MAX),
ret: ty, ret: ty,
..self.pool.cis.pop().unwrap_or_default() ..self.pool.cis.pop().unwrap_or_default()
}; };
@ -2341,8 +2289,6 @@ impl Codegen {
s.push_stash(stash); s.push_stash(stash);
s.dunp_imported_fns();
prev.vars.append(&mut s.ci.vars); prev.vars.append(&mut s.ci.vars);
s.ci.finalize(&mut s.output); s.ci.finalize(&mut s.output);
s.output.emit(tx()); s.output.emit(tx());
@ -2587,11 +2533,11 @@ impl Codegen {
fn handle_global(&mut self, id: ty::Global) -> Option<Value> { fn handle_global(&mut self, id: ty::Global) -> Option<Value> {
let ptr = self.ci.regs.allocate(); let ptr = self.ci.regs.allocate();
let reloc = Reloc::new(self.local_offset(), 3, 4); let rel = Reloc::new(self.local_offset(), 3, 4);
let global = &mut self.tys.globals[id as usize]; let global = &mut self.tys.globals[id as usize];
self.output.globals.push((id, reloc)); self.output.relocs.push(OutReloc { from: self.ci.id, to: ty::Kind::Global(id), rel });
log::dbg!("{}", self.output.globals.len() - self.ci.snap.globals);
self.output.emit(instrs::lra(ptr.get(), 0, 0)); self.output.emit(instrs::lra(ptr.get(), 0, 0));
global.runtime |= !self.ct.active();
Some(Value { ty: global.ty, loc: Loc::reg(ptr).into_derefed() }) Some(Value { ty: global.ty, loc: Loc::reg(ptr).into_derefed() })
} }
@ -2626,6 +2572,7 @@ impl Codegen {
} }
fn complete_call_graph_low(&mut self) { fn complete_call_graph_low(&mut self) {
let prev_strings = self.output.strings.len();
while self.ci.task_base < self.tasks.len() while self.ci.task_base < self.tasks.len()
&& let Some(task_slot) = self.tasks.pop() && let Some(task_slot) = self.tasks.pop()
{ {
@ -2636,13 +2583,8 @@ impl Codegen {
let base = self.output.code.len() as u32; let base = self.output.code.len() as u32;
let prev_data_len = self.output.string_data.len(); let prev_data_len = self.output.string_data.len();
self.output.code.append(&mut self.output.string_data); self.output.code.append(&mut self.output.string_data);
for srel in self.output.strings.iter_mut() { // we drain these when linking
#[cfg(debug_assertions)] for srel in self.output.strings.iter_mut().skip(prev_strings) {
{
if std::mem::replace(&mut srel.shifted, true) {
panic!("str reloc visited twice");
}
}
debug_assert!(srel.range.end <= prev_data_len as u32); debug_assert!(srel.range.end <= prev_data_len as u32);
debug_assert!(srel.range.start <= srel.range.end); debug_assert!(srel.range.start <= srel.range.end);
srel.range.start += base; srel.range.start += base;
@ -2669,7 +2611,7 @@ impl Codegen {
self.ci.snap = self.output.snap(); self.ci.snap = self.output.snap();
let Expr::BinOp { let Expr::BinOp {
left: Expr::Ident { name, .. }, left: Expr::Ident { .. },
op: TokenKind::Decl, op: TokenKind::Decl,
right: &Expr::Closure { body, args, .. }, right: &Expr::Closure { body, args, .. },
} = expr } = expr
@ -2677,11 +2619,8 @@ impl Codegen {
unreachable!("{expr}") unreachable!("{expr}")
}; };
log::dbg!("fn: {}", name);
self.output.emit_prelude(); self.output.emit_prelude();
log::dbg!("fn-args");
let mut parama = self.tys.parama(sig.ret); let mut parama = self.tys.parama(sig.ret);
let mut sig_args = sig.args.range(); let mut sig_args = sig.args.range();
for arg in args.iter() { for arg in args.iter() {
@ -2702,7 +2641,6 @@ impl Codegen {
self.ci.ret_reg = reg::Id::RET; self.ci.ret_reg = reg::Id::RET;
} }
log::dbg!("fn-body");
if self.expr(body).is_some() { if self.expr(body).is_some() {
self.report(body.pos(), "expected all paths in the fucntion to return"); self.report(body.pos(), "expected all paths in the fucntion to return");
} }
@ -2711,9 +2649,6 @@ impl Codegen {
self.ci.free_loc(vars.value.loc); self.ci.free_loc(vars.value.loc);
} }
log::dbg!("fn-prelude, stack: {:x}", self.ci.stack.max_height);
log::dbg!("fn-relocs");
self.ci.finalize(&mut self.output); self.ci.finalize(&mut self.output);
self.output.emit(jala(ZERO, RET_ADDR, 0)); self.output.emit(jala(ZERO, RET_ADDR, 0));
self.ci.regs.free(std::mem::take(&mut self.ci.ret_reg)); self.ci.regs.free(std::mem::take(&mut self.ci.ret_reg));
@ -2927,32 +2862,31 @@ impl Codegen {
} }
fn stack_reloc(&mut self, stack: &stack::Id, off: Offset, sub_offset: u8) -> u64 { fn stack_reloc(&mut self, stack: &stack::Id, off: Offset, sub_offset: u8) -> u64 {
log::dbg!("whaaaaatahack: {:b}", stack.repr());
let offset = self.local_offset(); let offset = self.local_offset();
self.ci.stack_relocs.push(Reloc::shifted(offset, sub_offset, 8)); self.ci.stack_relocs.push(Reloc::new(offset, sub_offset, 8));
Reloc::pack_srel(stack, off) Reloc::pack_srel(stack, off)
} }
fn link(&mut self) { fn link(&mut self) {
// FIXME: this will cause problems relating snapshots let ct_hint = self.ct.active().then_some(self.ci.snap.code as u32);
self.output.funcs.retain(|&(f, rel)| { for reloc in &self.output.relocs {
_ = task::unpack(self.tys.funcs[f as usize].offset) if !self.tys.is_runtime_item(reloc.from) && !self.ct.active() {
.map(|off| rel.apply_jump(&mut self.output.code, off)); continue;
true }
});
self.output.globals.retain(|&(g, rel)| { let Some(to_offset) = self.tys.offset_of_item(reloc.to, ct_hint) else {
_ = task::unpack(self.tys.globals[g as usize].offset) continue;
.map(|off| rel.apply_jump(&mut self.output.code, off)); };
true
}); let from_offset = self.tys.offset_of_item(reloc.from, ct_hint).unwrap();
reloc.rel.apply_jump(&mut self.output.code, to_offset, from_offset);
}
//self.compress_strings(); //self.compress_strings();
for srel in self.output.strings.drain(..) { for reloc in self.output.strings.iter() {
#[cfg(debug_assertions)] let from_offset = self.tys.offset_of_item(reloc.from, ct_hint).unwrap();
assert!(srel.shifted); reloc.reloc.apply_jump(&mut self.output.code, reloc.range.start, from_offset);
srel.reloc.apply_jump(&mut self.output.code, srel.range.start);
} }
} }
@ -2990,9 +2924,17 @@ impl Codegen {
ty::Id::from(self.eval_const(expr, ty::TYPE)) ty::Id::from(self.eval_const(expr, ty::TYPE))
} }
fn read_trap(addr: u64) -> Option<&'static trap::Trap> {
// TODO: make this debug only
if unsafe { *(addr as *const u8) } != 255 {
return None;
}
Some(unsafe { &*((addr + 1) as *const trap::Trap) })
}
fn handle_ecall(&mut self) { fn handle_ecall(&mut self) {
let trap = unsafe { &*(self.ct.vm.pc.get() as *const trap::Trap) }; let trap = Self::read_trap(self.ct.vm.pc.get()).unwrap();
self.ct.vm.pc = self.ct.vm.pc.wrapping_add(trap.size()); self.ct.vm.pc = self.ct.vm.pc.wrapping_add(trap.size() + 1);
let mut extra_jump = 0; let mut extra_jump = 0;
let mut local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize) let mut local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize)
@ -3063,8 +3005,6 @@ impl Codegen {
} }
}; };
log::dbg!("foo: {expr}");
if let Some(existing) = self.tys.syms.get(&SymKey { file, ident }) { if let Some(existing) = self.tys.syms.get(&SymKey { file, ident }) {
if let ty::Kind::Func(id) = existing.expand() if let ty::Kind::Func(id) = existing.expand()
&& let func = &mut self.tys.funcs[id as usize] && let func = &mut self.tys.funcs[id as usize]
@ -3100,7 +3040,6 @@ impl Codegen {
} }
let args = self.pack_args(pos, arg_base); let args = self.pack_args(pos, arg_base);
log::dbg!("eval ret");
let ret = self.ty(ret); let ret = self.ty(ret);
Some(Sig { args, ret }) Some(Sig { args, ret })
@ -3131,17 +3070,20 @@ impl Codegen {
offset: u32::MAX, offset: u32::MAX,
ty: Default::default(), ty: Default::default(),
runtime: false, runtime: false,
_file: file,
_name: ident,
}); });
let ci = ItemCtx { let ci = ItemCtx {
file, file,
id: ty::Kind::Global(gid), id: ty::Kind::Builtin(u32::MAX),
..self.pool.cis.pop().unwrap_or_default() ..self.pool.cis.pop().unwrap_or_default()
}; };
_ = left.find_pattern_path(ident, right, |expr| { _ = left.find_pattern_path(ident, right, |expr| {
self.tys.globals[gid as usize] = self.tys.globals[gid as usize] = self
self.ct_eval(ci, |s, _| Ok::<_, !>(s.generate_global(expr))).into_ok(); .ct_eval(ci, |s, _| Ok::<_, !>(s.generate_global(expr, file, ident)))
.into_ok();
}); });
ty::Kind::Global(gid) ty::Kind::Global(gid)
@ -3162,7 +3104,7 @@ impl Codegen {
} }
} }
fn generate_global(&mut self, expr: &Expr) -> Global { fn generate_global(&mut self, expr: &Expr, file: FileId, name: Ident) -> Global {
self.output.emit_prelude(); self.output.emit_prelude();
let ret = self.ci.regs.allocate(); let ret = self.ci.regs.allocate();
@ -3190,16 +3132,7 @@ impl Codegen {
self.ci.free_loc(ret.loc); self.ci.free_loc(ret.loc);
Global { ty: ret.ty, offset: offset as _, runtime: false } Global { ty: ret.ty, offset: offset as _, runtime: false, _file: file, _name: name }
}
fn dunp_imported_fns(&mut self) {
for &(f, _) in &self.output.funcs[self.ci.snap.funcs..] {
let fnball = self.tys.funcs[f as usize];
let file = self.files[fnball.file as usize].clone();
let expr = fnball.expr.get(&file).unwrap();
log::dbg!("{expr}");
}
} }
fn pop_stash(&mut self) -> Output { fn pop_stash(&mut self) -> Output {
@ -3248,9 +3181,14 @@ impl Codegen {
self.link(); self.link();
self.output.trunc(&Snapshot { code: self.output.code.len(), ..self.ci.snap }); self.output.trunc(&Snapshot { code: self.output.code.len(), ..self.ci.snap });
log::dbg!("{} {}", self.output.code.len(), self.ci.snap.code);
let entry = &mut self.output.code[self.ci.snap.code] as *mut _ as _; let entry = &mut self.output.code[self.ci.snap.code] as *mut _ as _;
let prev_pc = std::mem::replace(&mut self.ct.vm.pc, hbvm::mem::Address::new(entry)); let prev_pc = std::mem::replace(&mut self.ct.vm.pc, hbvm::mem::Address::new(entry));
#[cfg(test)]
{
self.disasm(&mut String::new());
}
self.run_vm(); self.run_vm();
self.ct.vm.pc = prev_pc; self.ct.vm.pc = prev_pc;
} }
@ -3266,6 +3204,85 @@ impl Codegen {
ret ret
} }
#[cfg(test)]
fn disasm(&mut self, output: &mut String) {
use crate::DisasmItem;
let mut sluce = self.output.code.as_slice();
let functions = self
.ct
.active()
.then_some((
self.ci.snap.code as u32,
(
"target_fn",
(self.output.code.len() - self.ci.snap.code) as u32,
DisasmItem::Func,
),
))
.into_iter()
.chain(
self.tys
.funcs
.iter()
.enumerate()
.filter(|&(i, f)| {
task::unpack(f.offset).is_ok()
&& (f.runtime || self.ct.active())
&& self.is_fully_linked(i as ty::Func)
})
.map(|(_, f)| {
let file = &self.files[f.file as usize];
let Expr::BinOp { left: &Expr::Ident { name, .. }, .. } =
f.expr.get(file).unwrap()
else {
unreachable!()
};
(f.offset, (name, f.size, DisasmItem::Func))
}),
)
.chain(
self.tys
.globals
.iter()
.filter(|g| task::unpack(g.offset).is_ok() && (g.runtime || self.ct.active()))
.map(|g| {
let file = &self.files[g._file as usize];
(
g.offset,
(file.ident_str(g._name), self.tys.size_of(g.ty), DisasmItem::Global),
)
}),
)
.chain(
self.output
.strings
.iter()
.map(|s| (s.range.start, ("string", s.range.len() as _, DisasmItem::Global))),
)
.collect::<HashMap<_, _>>();
if crate::disasm(&mut sluce, &functions, output, |bin| {
if self.ct.active()
&& let Some(trap) = Self::read_trap(bin.as_ptr() as u64)
{
bin.take(..trap.size() + 1).unwrap();
}
})
.is_err()
{
panic!("{} {:?}", output, sluce);
}
}
#[cfg(test)]
fn is_fully_linked(&self, func: ty::Func) -> bool {
self.output
.relocs
.iter()
.filter(|r| r.from == ty::Kind::Func(func))
.all(|r| self.tys.offset_of_item(r.to, None).is_some())
}
fn run_vm(&mut self) { fn run_vm(&mut self) {
loop { loop {
match self.ct.vm.run().unwrap() { match self.ct.vm.run().unwrap() {
@ -3333,8 +3350,7 @@ impl Codegen {
Snapshot { Snapshot {
code: self.output.code.len() - self.ci.snap.code, code: self.output.code.len() - self.ci.snap.code,
string_data: self.output.string_data.len() - self.ci.snap.string_data, string_data: self.output.string_data.len() - self.ci.snap.string_data,
funcs: self.output.funcs.len() - self.ci.snap.funcs, relocs: self.output.relocs.len() - self.ci.snap.relocs,
globals: self.output.globals.len() - self.ci.snap.globals,
strings: self.output.strings.len() - self.ci.snap.strings, strings: self.output.strings.len() - self.ci.snap.strings,
} }
} }
@ -3342,8 +3358,7 @@ impl Codegen {
fn pop_local_snap(&mut self, snap: Snapshot) { fn pop_local_snap(&mut self, snap: Snapshot) {
self.output.code.truncate(snap.code + self.ci.snap.code); self.output.code.truncate(snap.code + self.ci.snap.code);
self.output.string_data.truncate(snap.string_data + self.ci.snap.string_data); self.output.string_data.truncate(snap.string_data + self.ci.snap.string_data);
self.output.funcs.truncate(snap.funcs + self.ci.snap.funcs); self.output.relocs.truncate(snap.relocs + self.ci.snap.relocs);
self.output.globals.truncate(snap.globals + self.ci.snap.globals);
self.output.strings.truncate(snap.strings + self.ci.snap.strings); self.output.strings.truncate(snap.strings + self.ci.snap.strings);
} }
@ -3366,11 +3381,7 @@ impl Codegen {
mod tests { mod tests {
use { use {
super::parser, super::parser,
crate::{ crate::{codegen::LoggedMem, log, parser::FileId},
codegen::LoggedMem,
log,
parser::{Expr, FileId},
},
std::io, std::io,
}; };
@ -3434,24 +3445,7 @@ mod tests {
let mut out = Vec::new(); let mut out = Vec::new();
codegen.dump(&mut out).unwrap(); codegen.dump(&mut out).unwrap();
let mut sluce = out.as_slice(); codegen.disasm(output);
let functions = codegen
.tys
.funcs
.iter()
.filter(|f| f.offset != u32::MAX && f.runtime)
.map(|f| {
let file = &codegen.files[f.file as usize];
let Expr::BinOp { left: &Expr::Ident { name, .. }, .. } = f.expr.get(file).unwrap()
else {
unreachable!()
};
(f.offset, (name, f.size))
})
.collect::<crate::HashMap<_, _>>();
if crate::disasm(&mut sluce, &functions, output).is_err() {
panic!("{} {:?}", output, sluce);
}
use std::fmt::Write; use std::fmt::Write;

View file

@ -21,6 +21,7 @@ use {
parser::Ast, parser::Ast,
std::{ std::{
collections::{hash_map, VecDeque}, collections::{hash_map, VecDeque},
default,
io::{self, Read}, io::{self, Read},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Mutex, sync::Mutex,
@ -129,11 +130,20 @@ unsafe fn encode<T>(instr: T) -> (usize, [u8; instrs::MAX_SIZE]) {
std::ptr::write(buf.as_mut_ptr() as *mut T, instr); std::ptr::write(buf.as_mut_ptr() as *mut T, instr);
(std::mem::size_of::<T>(), buf) (std::mem::size_of::<T>(), buf)
} }
#[cfg(test)]
#[derive(Clone, Copy)]
enum DisasmItem {
Func,
Global,
}
#[cfg(test)] #[cfg(test)]
fn disasm( fn disasm(
binary: &mut &[u8], binary: &mut &[u8],
functions: &HashMap<u32, (&str, u32)>, functions: &HashMap<u32, (&str, u32, DisasmItem)>,
out: &mut String, out: &mut String,
mut eca_handler: impl FnMut(&mut &[u8]),
) -> std::fmt::Result { ) -> std::fmt::Result {
use self::instrs::Instr; use self::instrs::Instr;
@ -146,11 +156,18 @@ fn disasm(
let mut labels = HashMap::<u32, u32>::default(); let mut labels = HashMap::<u32, u32>::default();
let mut buf = Vec::<instrs::Oper>::new(); let mut buf = Vec::<instrs::Oper>::new();
let mut has_cycle = false;
let mut has_oob = false;
'_offset_pass: for (&off, &(_name, len, kind)) in functions.iter() {
if matches!(kind, DisasmItem::Global) {
continue;
}
'_offset_pass: for (&off, &(_name, len)) in functions.iter() {
let prev = *binary; let prev = *binary;
binary.take(..off as usize).unwrap(); binary.take(..off as usize).unwrap();
let mut label_count = 0; let mut label_count = 0;
while let Some(&byte) = binary.first() { while let Some(&byte) = binary.first() {
let inst = instr_from_byte(byte)?; let inst = instr_from_byte(byte)?;
@ -167,18 +184,27 @@ fn disasm(
_ => continue, _ => continue,
}; };
has_cycle |= rel == 0;
let global_offset: u32 = (offset + rel).try_into().unwrap(); let global_offset: u32 = (offset + rel).try_into().unwrap();
if functions.get(&global_offset).is_some() { if functions.get(&global_offset).is_some() {
continue; continue;
} }
label_count += labels.try_insert(global_offset, label_count).is_ok() as u32; label_count += labels.try_insert(global_offset, label_count).is_ok() as u32;
} }
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
} }
*binary = prev; *binary = prev;
} }
'_dump: for (&off, &(name, len)) in functions.iter() { '_dump: for (&off, &(name, len, kind)) in functions.iter() {
if matches!(kind, DisasmItem::Global) {
continue;
}
let prev = *binary; let prev = *binary;
use std::fmt::Write; use std::fmt::Write;
@ -224,20 +250,37 @@ fn disasm(
}; };
let global_offset: u32 = (offset + rel).try_into().unwrap(); let global_offset: u32 = (offset + rel).try_into().unwrap();
if let Some(&(name, _)) = functions.get(&global_offset) { if let Some(&(name, ..)) = functions.get(&global_offset) {
write!(out, ":{name}")?; write!(out, ":{name}")?;
} else { } else {
let local_has_oob = global_offset < off
|| global_offset > off + len
|| instr_from_byte(prev[global_offset as usize]).is_err()
|| prev[global_offset as usize] == 0;
has_oob |= local_has_oob;
let label = labels.get(&global_offset).unwrap(); let label = labels.get(&global_offset).unwrap();
if local_has_oob {
write!(out, "!!!!!!!!!{rel}")?;
} else {
write!(out, ":{label}")?; write!(out, ":{label}")?;
} }
} }
}
writeln!(out)?; writeln!(out)?;
if matches!(inst, Instr::ECA) {
eca_handler(binary);
}
} }
*binary = prev; *binary = prev;
} }
if has_oob || has_cycle {
return Err(std::fmt::Error);
}
Ok(()) Ok(())
} }

View file

@ -1,11 +1,11 @@
main: main:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
LRA r32, r0, :0 LRA r32, r0, :string
CP r2, r32 CP r2, r32
JAL r31, r0, :str_len JAL r31, r0, :str_len
CP r32, r1 CP r32, r1
LRA r33, r0, :1 LRA r33, r0, :string
CP r2, r33 CP r2, r33
JAL r31, r0, :str_len JAL r31, r0, :str_len
CP r33, r1 CP r33, r1

View file

@ -1,7 +1,7 @@
main: main:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h ST r31, r254, 0a, 16h
LRA r32, r0, :0 LRA r32, r0, :a
LD r1, r32, 0a, 8h LD r1, r32, 0a, 8h
LD r31, r254, 0a, 16h LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d ADDI64 r254, r254, 16d

View file

@ -1,7 +1,7 @@
main: main:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h ST r31, r254, 0a, 16h
LRA r32, r0, :0 LRA r32, r0, :a
LD r1, r32, 0a, 8h LD r1, r32, 0a, 8h
LD r31, r254, 0a, 16h LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d ADDI64 r254, r254, 16d

View file

@ -1,15 +1,17 @@
new: free:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -40d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 40h
CP r32, r1 CP r32, r2
LI64 r33, 0d CP r33, r3
ST r33, r32, 0a, 8h CP r34, r4
LI64 r33, 0d LRA r35, r0, :FREE_SYS_CALL
ST r33, r32, 8a, 8h LD r2, r35, 0a, 8h
LI64 r33, 0d CP r3, r32
ST r33, r32, 16a, 8h CP r4, r33
LD r31, r254, 0a, 24h CP r5, r34
ADDI64 r254, r254, 24d ECA
LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a JALA r0, r31, 0a
push: push:
ADDI64 r254, r254, -88d ADDI64 r254, r254, -88d
@ -98,6 +100,19 @@ push:
4: LD r31, r254, 0a, 88h 4: LD r31, r254, 0a, 88h
ADDI64 r254, r254, 88d ADDI64 r254, r254, 88d
JALA r0, r31, 0a JALA r0, r31, 0a
new:
ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h
CP r32, r1
LI64 r33, 0d
ST r33, r32, 0a, 8h
LI64 r33, 0d
ST r33, r32, 8a, 8h
LI64 r33, 0d
ST r33, r32, 16a, 8h
LD r31, r254, 0a, 24h
ADDI64 r254, r254, 24d
JALA r0, r31, 0a
deinit: deinit:
ADDI64 r254, r254, -24d ADDI64 r254, r254, -24d
ST r31, r254, 0a, 24h ST r31, r254, 0a, 24h
@ -118,7 +133,7 @@ malloc:
ST r31, r254, 0a, 32h ST r31, r254, 0a, 32h
CP r32, r2 CP r32, r2
CP r33, r3 CP r33, r3
LRA r34, r0, :0 LRA r34, r0, :MALLOC_SYS_CALL
LD r2, r34, 0a, 8h LD r2, r34, 0a, 8h
CP r3, r32 CP r3, r32
CP r4, r33 CP r4, r33
@ -126,21 +141,6 @@ malloc:
LD r31, r254, 0a, 32h LD r31, r254, 0a, 32h
ADDI64 r254, r254, 32d ADDI64 r254, r254, 32d
JALA r0, r31, 0a JALA r0, r31, 0a
free:
ADDI64 r254, r254, -40d
ST r31, r254, 0a, 40h
CP r32, r2
CP r33, r3
CP r34, r4
LRA r35, r0, :0
LD r2, r35, 0a, 8h
CP r3, r32
CP r4, r33
CP r5, r34
ECA
LD r31, r254, 0a, 40h
ADDI64 r254, r254, 40d
JALA r0, r31, 0a
main: main:
ADDI64 r254, r254, -72d ADDI64 r254, r254, -72d
ST r31, r254, 48a, 24h ST r31, r254, 48a, 24h
@ -162,6 +162,6 @@ main:
LD r31, r254, 48a, 24h LD r31, r254, 48a, 24h
ADDI64 r254, r254, 72d ADDI64 r254, r254, 72d
JALA r0, r31, 0a JALA r0, r31, 0a
code size: 1469 code size: 1470
ret: 69 ret: 69
status: Ok(()) status: Ok(())

View file

@ -1,7 +1,7 @@
main: main:
ADDI64 r254, r254, -16d ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h ST r31, r254, 0a, 16h
LRA r32, r0, :0 LRA r32, r0, :complex_global_var
LD r1, r32, 0a, 8h LD r1, r32, 0a, 8h
LD r31, r254, 0a, 16h LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d ADDI64 r254, r254, 16d