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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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