fix(jit): NewBoxのJIT安全化とDebugBox Phase 1実装
- NewBoxのJIT扱いを安全化(src/jit/lower/core.rs) - NYASH_USE_PLUGIN_BUILTINS=1 && args.is_empty() かつ StringBox/IntegerBox のみJIT許可 - ArrayBox/MapBox等のプラグインBoxまたは引数ありはunsupportedとしてカウント - unsupported>0の関数はJIT対象外となりVM実行にフォールバック(Segfault回避) - DebugBox Phase 1実装(JITトレース機能) - tracePluginCalls(bool)でJITシムトレースON/OFF - getJitEvents()で直近のJITイベント取得 - src/jit/shim_trace.rs追加でトレース基盤実装 - Printのサポート - PrintはJIT非対応に戻しVM経路で確実に出力(出力消失解消) - テストとサンプル追加 - examples/jit_plugin_invoke_param_array.nyash: 最小JITスモークテスト - examples/py_result_*.nyash: Python plugin結果チェーン処理デモ - PyRuntimeBox拡張 - str()メソッドでPyObjectのstring表現を取得可能に - エラーハンドリング改善とResultチェーンサポート 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -143,6 +143,7 @@ extern "C" fn nyash_host_stub0() -> i64 { 0 }
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 {
|
||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
let trace = crate::jit::shim_trace::is_enabled();
|
||||
// Resolve receiver instance from legacy VM args (param index)
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
@ -157,19 +158,54 @@ extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64,
|
||||
}
|
||||
});
|
||||
}
|
||||
// If not resolved, scan all VM args for a matching PluginBoxV2 by type_id
|
||||
if invoke.is_none() {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
for v in args.iter() {
|
||||
if let crate::backend::vm::VMValue::BoxRef(b) = v {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
// type_id compatibility is best-effort; fall back to first PluginBoxV2
|
||||
if p.inner.type_id == (type_id as u32) || invoke.is_none() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
if p.inner.type_id == (type_id as u32) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() { return 0; }
|
||||
// Build TLV args from a1/a2 if present
|
||||
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16));
|
||||
let mut add_i64 = |v: i64| { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); };
|
||||
if argc >= 2 { add_i64(a1); }
|
||||
if argc >= 3 { add_i64(a2); }
|
||||
// Prepare output buffer
|
||||
let mut out: [u8; 32] = [0; 32];
|
||||
let mut out_len: usize = out.len();
|
||||
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
|
||||
// Prepare output buffer with canaries for overrun detection
|
||||
let mut out = vec![0xCDu8; 4096 + 32];
|
||||
let canary_val = 0xABu8;
|
||||
let canary_len = 16usize;
|
||||
for i in 0..canary_len { out[i] = canary_val; }
|
||||
for i in 0..canary_len { out[4096 + canary_len + i] = canary_val; }
|
||||
let mut out_len: usize = 4096;
|
||||
let out_ptr = unsafe { out.as_mut_ptr().add(canary_len) };
|
||||
if trace { eprintln!("[JIT-SHIM i64] invoke type={} method={} argc={} inst_id={} a1={} a2={} buf_len={}", type_id, method_id, argc, instance_id, a1, a2, buf.len()); }
|
||||
crate::jit::shim_trace::push(format!("i64.start type={} method={} argc={} inst={} a1={} a2={}", type_id, method_id, argc, instance_id, a1, a2));
|
||||
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out_ptr, &mut out_len) };
|
||||
// Canary check
|
||||
let pre_ok = out[..canary_len].iter().all(|&b| b==canary_val);
|
||||
let post_ok = out[canary_len + out_len .. canary_len + out_len + canary_len].iter().all(|&b| b==canary_val);
|
||||
if trace { eprintln!("[JIT-SHIM i64] rc={} out_len={} canary_pre={} canary_post={}", rc, out_len, pre_ok, post_ok); }
|
||||
crate::jit::shim_trace::push(format!("i64.end rc={} out_len={} pre_ok={} post_ok={}", rc, out_len, pre_ok, post_ok));
|
||||
if rc != 0 { return 0; }
|
||||
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
let out_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len) };
|
||||
if let Some((tag, sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
|
||||
if trace { eprintln!("[JIT-SHIM i64] TLV tag={} sz={}", tag, sz); }
|
||||
crate::jit::shim_trace::push(format!("i64.tlv tag={} sz={}", tag, sz));
|
||||
match tag {
|
||||
2 => { // I32
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; }
|
||||
}
|
||||
3 => { // I64
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; }
|
||||
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); }
|
||||
@ -177,11 +213,99 @@ extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64,
|
||||
1 => { // Bool
|
||||
return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 };
|
||||
}
|
||||
5 => { // F64 → optional conversion to i64 when enabled
|
||||
if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") {
|
||||
if sz == 8 {
|
||||
let mut b=[0u8;8]; b.copy_from_slice(payload);
|
||||
let f = f64::from_le_bytes(b);
|
||||
return f as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
// F64-typed shim: decodes TLV first entry and returns f64 when possible
|
||||
extern "C" fn nyash_plugin_invoke3_f64(type_id: i64, method_id: i64, argc: i64, a0: i64, a1: i64, a2: i64) -> f64 {
|
||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
let trace = crate::jit::shim_trace::is_enabled();
|
||||
// Resolve receiver + invoke_fn from legacy VM args
|
||||
let mut instance_id: u32 = 0;
|
||||
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
|
||||
if a0 >= 0 {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
let idx = a0 as usize;
|
||||
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() {
|
||||
crate::jit::rt::with_legacy_vm_args(|args| {
|
||||
for v in args.iter() {
|
||||
if let crate::backend::vm::VMValue::BoxRef(b) = v {
|
||||
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
|
||||
if p.inner.type_id == (type_id as u32) || invoke.is_none() {
|
||||
instance_id = p.instance_id();
|
||||
invoke = Some(p.inner.invoke_fn);
|
||||
if p.inner.type_id == (type_id as u32) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if invoke.is_none() { return 0.0; }
|
||||
// Build TLV args from a1/a2 if present (i64 only for now)
|
||||
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16));
|
||||
let mut add_i64 = |v: i64| { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, v); };
|
||||
if argc >= 2 { add_i64(a1); }
|
||||
if argc >= 3 { add_i64(a2); }
|
||||
// Prepare output buffer with canaries
|
||||
let mut out = vec![0xCDu8; 4096 + 32];
|
||||
let canary_val = 0xABu8;
|
||||
let canary_len = 16usize;
|
||||
for i in 0..canary_len { out[i] = canary_val; }
|
||||
for i in 0..canary_len { out[4096 + canary_len + i] = canary_val; }
|
||||
let mut out_len: usize = 4096;
|
||||
let out_ptr = unsafe { out.as_mut_ptr().add(canary_len) };
|
||||
if trace { eprintln!("[JIT-SHIM f64] invoke type={} method={} argc={} inst_id={} a1={} a2={} buf_len={}", type_id, method_id, argc, instance_id, a1, a2, buf.len()); }
|
||||
crate::jit::shim_trace::push(format!("f64.start type={} method={} argc={} inst={} a1={} a2={}", type_id, method_id, argc, instance_id, a1, a2));
|
||||
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id as u32, instance_id, buf.as_ptr(), buf.len(), out_ptr, &mut out_len) };
|
||||
let pre_ok = out[..canary_len].iter().all(|&b| b==canary_val);
|
||||
let post_ok = out[canary_len + out_len .. canary_len + out_len + canary_len].iter().all(|&b| b==canary_val);
|
||||
if trace { eprintln!("[JIT-SHIM f64] rc={} out_len={} canary_pre={} canary_post={}", rc, out_len, pre_ok, post_ok); }
|
||||
crate::jit::shim_trace::push(format!("f64.end rc={} out_len={} pre_ok={} post_ok={}", rc, out_len, pre_ok, post_ok));
|
||||
if rc != 0 { return 0.0; }
|
||||
let out_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len) };
|
||||
if let Some((tag, sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
|
||||
if trace { eprintln!("[JIT-SHIM f64] TLV tag={} sz={}", tag, sz); }
|
||||
crate::jit::shim_trace::push(format!("f64.tlv tag={} sz={}", tag, sz));
|
||||
match tag {
|
||||
5 => { // F64
|
||||
if sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return f64::from_le_bytes(b); }
|
||||
}
|
||||
3 => { // I64 → f64
|
||||
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return (i64::from_le_bytes(b)) as f64; }
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return (v as i64) as f64; }
|
||||
}
|
||||
2 => { // I32 → f64
|
||||
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return (v as i64) as f64; }
|
||||
}
|
||||
1 => { // Bool → 0.0/1.0
|
||||
return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1.0 } else { 0.0 };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
0.0
|
||||
}
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use super::extern_thunks::{
|
||||
nyash_math_sin_f64, nyash_math_cos_f64, nyash_math_abs_f64, nyash_math_min_f64, nyash_math_max_f64,
|
||||
@ -752,13 +876,22 @@ impl IRBuilder for CraneliftBuilder {
|
||||
z
|
||||
}); }
|
||||
|
||||
// Build signature: (i64 type_id, i64 method_id, i64 argc, i64 a0, i64 a1, i64 a2) -> i64
|
||||
// Choose f64 shim if allowlisted
|
||||
let use_f64 = if has_ret {
|
||||
if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") {
|
||||
list.split(',').any(|e| {
|
||||
let mut it = e.split(':');
|
||||
match (it.next(), it.next()) { (Some(t), Some(m)) => t.parse::<u32>().ok()==Some(type_id) && m.parse::<u32>().ok()==Some(method_id), _ => false }
|
||||
})
|
||||
} else { false }
|
||||
} else { false };
|
||||
// Build signature: (i64 type_id, i64 method_id, i64 argc, i64 a0, i64 a1, i64 a2) -> i64/f64
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); }
|
||||
if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
if has_ret { sig.returns.push(AbiParam::new(if use_f64 { types::F64 } else { types::I64 })); }
|
||||
|
||||
let symbol = "nyash_plugin_invoke3_i64";
|
||||
let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" };
|
||||
let func_id = self.module
|
||||
.declare_function(symbol, Linkage::Import, &sig)
|
||||
.expect("declare plugin shim failed");
|
||||
@ -770,6 +903,7 @@ impl IRBuilder for CraneliftBuilder {
|
||||
let c_type = fb.ins().iconst(types::I64, type_id as i64);
|
||||
let c_meth = fb.ins().iconst(types::I64, method_id as i64);
|
||||
let c_argc = fb.ins().iconst(types::I64, argc as i64);
|
||||
// Pass receiver param index (a0) when known; shim will fallback-scan if invalid (<0)
|
||||
let call_inst = fb.ins().call(fref, &[c_type, c_meth, c_argc, arg_vals[0], arg_vals[1], arg_vals[2]]);
|
||||
if has_ret {
|
||||
let results = fb.inst_results(call_inst).to_vec();
|
||||
@ -1235,8 +1369,18 @@ impl IRBuilder for ObjectBuilder {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types}; use cranelift_frontend::FunctionBuilder; use cranelift_module::{Linkage, Module};
|
||||
let mut arg_vals: 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() { arg_vals.push(v); } } arg_vals.reverse();
|
||||
while arg_vals.len() < 3 { let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } else if let Some(b) = self.entry_block { fb.switch_to_block(b); } let z = fb.ins().iconst(types::I64, 0); fb.finalize(); arg_vals.push(z); }
|
||||
let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); } if has_ret { sig.returns.push(AbiParam::new(types::I64)); }
|
||||
let symbol = "nyash_plugin_invoke3_i64"; let func_id = self.module.declare_function(symbol, Linkage::Import, &sig).expect("declare plugin shim");
|
||||
// Choose f64 or i64 shim based on env allowlist: NYASH_JIT_PLUGIN_F64="type:method,type:method"
|
||||
let use_f64 = if has_ret {
|
||||
if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") {
|
||||
list.split(',').any(|e| {
|
||||
let mut it = e.split(':');
|
||||
match (it.next(), it.next()) { (Some(t), Some(m)) => t.parse::<u32>().ok()==Some(type_id) && m.parse::<u32>().ok()==Some(method_id), _ => false }
|
||||
})
|
||||
} else { false }
|
||||
} else { false };
|
||||
let call_conv = self.module.isa().default_call_conv(); let mut sig = Signature::new(call_conv); for _ in 0..6 { sig.params.push(AbiParam::new(types::I64)); } if has_ret { if use_f64 { sig.returns.push(AbiParam::new(types::F64)); } else { sig.returns.push(AbiParam::new(types::I64)); } }
|
||||
let symbol = if use_f64 { "nyash_plugin_invoke3_f64" } else { "nyash_plugin_invoke3_i64" };
|
||||
let func_id = self.module.declare_function(symbol, Linkage::Import, &sig).expect("declare plugin shim");
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); } else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let c_type = fb.ins().iconst(types::I64, type_id as i64); let c_meth = fb.ins().iconst(types::I64, method_id as i64); let c_argc = fb.ins().iconst(types::I64, argc as i64);
|
||||
|
||||
Reference in New Issue
Block a user