dev: add selfhosting/cranelift workspaces; jit: add return materialization, ret_slot, hostcall arity pad, and dbg imports for diagnosis

This commit is contained in:
Tomoaki
2025-09-06 10:59:33 +09:00
parent f6e0d5111e
commit d631beba37
17 changed files with 539 additions and 25 deletions

View File

@ -172,6 +172,21 @@ impl IRBuilder for CraneliftBuilder {
self.entry_block = Some(entry);
self.current_block_index = Some(0);
self.cur_needs_term = true;
// Force a dbg call at function entry to verify import linking works at runtime
{
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module
.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig)
.expect("declare dbg_i64 at entry");
let fref = self.module.declare_func_in_func(fid, fb.func);
let ttag = fb.ins().iconst(types::I64, 900);
let tval = fb.ins().iconst(types::I64, 123);
let _ = fb.ins().call(fref, &[ttag, tval]);
}
let rb = fb.create_block();
self.ret_block = Some(rb);
fb.append_block_param(rb, types::I64);
@ -200,9 +215,15 @@ impl IRBuilder for CraneliftBuilder {
if fb.func.signature.returns.is_empty() {
fb.ins().return_(&[]);
} else {
let params = fb.func.dfg.block_params(rb).to_vec();
let mut v = params.get(0).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0));
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
// Prefer the persisted return slot if available; fallback to block param 0
let mut v = if let Some(ss) = self.ret_slot {
fb.ins().stack_load(types::I64, ss, 0)
} else {
let params = fb.func.dfg.block_params(rb).to_vec();
params.get(0).copied().unwrap_or_else(|| fb.ins().iconst(types::I64, 0))
};
// Unconditional runtime debug call to observe return value just before final return (feed result back)
{
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
@ -210,8 +231,9 @@ impl IRBuilder for CraneliftBuilder {
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module.declare_function("nyash.jit.dbg_i64", Linkage::Import, &sig).expect("declare dbg_i64");
let fref = self.module.declare_func_in_func(fid, fb.func);
let tag = fb.ins().iconst(types::I64, 200);
let _ = fb.ins().call(fref, &[tag, v]);
let tag = fb.ins().iconst(types::I64, 210);
let call_inst = fb.ins().call(fref, &[tag, v]);
if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { v = rv; }
}
let ret_ty = fb.func.signature.returns.get(0).map(|p| p.value_type).unwrap_or(types::I64);
if ret_ty == types::F64 { v = fb.ins().fcvt_from_sint(types::F64, v); }
@ -409,7 +431,7 @@ impl IRBuilder for CraneliftBuilder {
let mut v = if let Some(x) = self.value_stack.pop() { x } else { fb.ins().iconst(types::I64, 0) };
let v_ty = fb.func.dfg.value_type(v);
if v_ty != types::I64 { v = if v_ty == types::F64 { fb.ins().fcvt_to_sint(types::I64, v) } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); fb.ins().select(v, one, zero) } }
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") {
if std::env::var("NYASH_JIT_TRACE_RET").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_FORCE_RET_DBG").ok().as_deref() == Some("1") {
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
@ -420,6 +442,26 @@ impl IRBuilder for CraneliftBuilder {
let tag = fb.ins().iconst(types::I64, 201);
let _ = fb.ins().call(fref, &[tag, v]);
}
// Persist return value in a dedicated stack slot to avoid SSA arg mishaps on ret block
if self.ret_slot.is_none() {
use cranelift_codegen::ir::StackSlotData;
let ss = fb.create_sized_stack_slot(StackSlotData::new(cranelift_codegen::ir::StackSlotKind::ExplicitSlot, 8));
self.ret_slot = Some(ss);
}
if let Some(ss) = self.ret_slot { fb.ins().stack_store(v, ss, 0); }
// Unconditional debug of return value just before ret block jump (feed result back to v)
{
use cranelift_codegen::ir::{AbiParam, Signature};
let mut sig = Signature::new(self.module.isa().default_call_conv());
sig.params.push(AbiParam::new(types::I64));
sig.params.push(AbiParam::new(types::I64));
sig.returns.push(AbiParam::new(types::I64));
let fid = self.module.declare_function("nyash.jit.dbg_i64", cranelift_module::Linkage::Import, &sig).expect("declare dbg_i64");
let fref = self.module.declare_func_in_func(fid, fb.func);
let tag = fb.ins().iconst(types::I64, 211);
let call_inst = fb.ins().call(fref, &[tag, v]);
if let Some(rv) = fb.inst_results(call_inst).get(0).copied() { v = rv; }
}
if let Some(rb) = self.ret_block { fb.ins().jump(rb, &[v]); }
});
self.cur_needs_term = false;
@ -444,12 +486,15 @@ impl IRBuilder for CraneliftBuilder {
}
let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv);
// Collect up to _argc i64 values from stack (right-to-left)
// Collect up to _argc i64 values from stack (right-to-left) and pad with zeros to match arity
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
let take_n = _argc.min(self.value_stack.len());
for _ in 0..take_n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
args.reverse();
for _ in 0..args.len() { sig.params.push(AbiParam::new(types::I64)); }
Self::with_fb(|fb| {
while args.len() < _argc { args.push(fb.ins().iconst(types::I64, 0)); }
});
for _ in 0.._argc { sig.params.push(AbiParam::new(types::I64)); }
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare import failed");
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
@ -488,6 +533,18 @@ impl IRBuilder for CraneliftBuilder {
let func_id = self.module.declare_function(symbol, cranelift_module::Linkage::Import, &sig).expect("declare typed import failed");
if let Some(v) = tls_call_import_ret(&mut self.module, func_id, &args, has_ret) { self.value_stack.push(v); }
}
fn emit_debug_i64_local(&mut self, tag: i64, slot: usize) {
if std::env::var("NYASH_JIT_TRACE_LEN").ok().as_deref() != Some("1") { return; }
use cranelift_codegen::ir::types;
// Push tag and value
let t = Self::with_fb(|fb| fb.ins().iconst(types::I64, tag));
self.value_stack.push(t);
self.load_local_i64(slot);
// Use existing typed hostcall helper to pass two I64 args
self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false);
// Drop the returned value to keep stack balanced
let _ = self.value_stack.pop();
}
fn emit_host_call_fixed3(&mut self, symbol: &str, has_ret: bool) {
use cranelift_codegen::ir::{AbiParam, Signature, types};
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
@ -721,6 +778,14 @@ impl IRBuilder for CraneliftBuilder {
eprintln!("[JIT-LOCAL] store idx={} (tracked_slots={})", index, self.local_slots.len());
}
});
if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") {
// Also emit value via dbg hook: tag = 1000 + index
let tag = Self::with_fb(|fb| fb.ins().iconst(types::I64, (1000 + index as i64)));
self.value_stack.push(tag);
self.value_stack.push(v);
self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false);
let _ = self.value_stack.pop();
}
}
}
fn load_local_i64(&mut self, index: usize) {
@ -732,6 +797,14 @@ impl IRBuilder for CraneliftBuilder {
eprintln!("[JIT-LOCAL] load idx={} (tracked_slots={})", index, self.local_slots.len());
}
self.value_stack.push(v); self.stats.0 += 1;
if std::env::var("NYASH_JIT_TRACE_LOCAL").ok().as_deref() == Some("1") {
// tag = 2000 + index
let tag = Self::with_fb(|fb| fb.ins().iconst(types::I64, (2000 + index as i64)));
self.value_stack.push(tag);
self.value_stack.push(v);
self.emit_host_call_typed("nyash.jit.dbg_i64", &[ParamKind::I64, ParamKind::I64], true, false);
let _ = self.value_stack.pop();
}
}
}
}