Phase 10.7 - JIT統計とイベント機能の完成
主要な実装: - PHI(b1)統計追跡: phi_total_slots/phi_b1_slotsをJSON出力 - 関数単位統計API: JitStatsBox.perFunction()で詳細統計取得 - JITイベントシステム: compile/execute/fallback/trapをJSONL形式で記録 - Store/Load命令対応: ローカル変数を含む関数のJIT実行が可能に 新しいBox: - JitStatsBox: JIT統計の取得 - JitConfigBox: JIT設定の管理(将来用) - JitEventsBox: イベントのJSONL出力(将来用) - JitPolicyBox: 実行ポリシー管理(将来用) CLI拡張: - --jit-exec, --jit-stats, --jit-dump等のフラグ追加 - --jit-directモードでの独立JIT実行 - NYASH_JIT_*環境変数によるきめ細かい制御 ドキュメント: - Phase 10.7実装計画の詳細化 - Phase 10.9 (ビルトインBox JIT) の計画追加 - JIT統計JSONスキーマ v1の仕様化 ChatGPT5との共同開発により、JIT基盤が大幅に強化されました。 次はPhase 10.9でビルトインBoxのJIT対応を進め、 Python統合(Phase 10.1)への道を開きます。 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
227
src/runner.rs
227
src/runner.rs
@ -64,6 +64,32 @@ impl NyashRunner {
|
||||
// Prefer explicit JSON flag over any default
|
||||
std::env::set_var("NYASH_VM_STATS_JSON", "1");
|
||||
}
|
||||
// Optional: JIT controls via CLI flags (centralized)
|
||||
{
|
||||
let mut jc = nyash_rust::jit::config::JitConfig::from_env();
|
||||
jc.exec |= self.config.jit_exec;
|
||||
jc.stats |= self.config.jit_stats;
|
||||
jc.stats_json |= self.config.jit_stats_json;
|
||||
jc.dump |= self.config.jit_dump;
|
||||
if self.config.jit_threshold.is_some() { jc.threshold = self.config.jit_threshold; }
|
||||
jc.phi_min |= self.config.jit_phi_min;
|
||||
jc.hostcall |= self.config.jit_hostcall;
|
||||
jc.handle_debug |= self.config.jit_handle_debug;
|
||||
jc.native_f64 |= self.config.jit_native_f64;
|
||||
jc.native_bool |= self.config.jit_native_bool;
|
||||
if self.config.jit_only { std::env::set_var("NYASH_JIT_ONLY", "1"); }
|
||||
// Apply runtime capability probe (e.g., disable b1 ABI if unsupported)
|
||||
let caps = nyash_rust::jit::config::probe_capabilities();
|
||||
jc = nyash_rust::jit::config::apply_runtime_caps(jc, caps);
|
||||
// Optional DOT emit via CLI (ensures dump is on when path specified)
|
||||
if let Some(path) = &self.config.emit_cfg {
|
||||
std::env::set_var("NYASH_JIT_DOT", path);
|
||||
jc.dump = true;
|
||||
}
|
||||
// Persist to env (CLI parity) and set as current
|
||||
jc.apply_env();
|
||||
nyash_rust::jit::config::set_current(jc.clone());
|
||||
}
|
||||
// Benchmark mode - can run without a file
|
||||
if self.config.benchmark {
|
||||
println!("📊 Nyash Performance Benchmark Suite");
|
||||
@ -76,6 +102,11 @@ impl NyashRunner {
|
||||
}
|
||||
|
||||
if let Some(ref filename) = self.config.file {
|
||||
// Independent JIT direct mode (no VM execute path)
|
||||
if self.config.jit_direct {
|
||||
self.run_file_jit_direct(filename);
|
||||
return;
|
||||
}
|
||||
// Delegate file-mode execution to modes::common dispatcher
|
||||
self.run_file(filename);
|
||||
} else {
|
||||
@ -501,6 +532,202 @@ impl NyashRunner {
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashRunner {
|
||||
/// Run a file through independent JIT engine (no VM execute loop)
|
||||
fn run_file_jit_direct(&self, filename: &str) {
|
||||
use std::fs;
|
||||
use nyash_rust::{parser::NyashParser, mir::MirCompiler};
|
||||
// Small helper for unified error output (text or JSON)
|
||||
let emit_err = |phase: &str, code: &str, msg: &str| {
|
||||
if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1")
|
||||
|| std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1") {
|
||||
let payload = serde_json::json!({
|
||||
"kind": "jit_direct_error",
|
||||
"phase": phase,
|
||||
"code": code,
|
||||
"message": msg,
|
||||
"file": filename,
|
||||
});
|
||||
println!("{}", payload.to_string());
|
||||
} else {
|
||||
eprintln!("[JIT-direct][{}][{}] {}", phase, code, msg);
|
||||
}
|
||||
};
|
||||
// Require cranelift feature at runtime by attempting compile; if unavailable compile_function returns None
|
||||
let code = match fs::read_to_string(filename) {
|
||||
Ok(s) => s,
|
||||
Err(e) => { emit_err("read_file", "IO", &format!("{}", e)); std::process::exit(1); }
|
||||
};
|
||||
let ast = match NyashParser::parse_from_string(&code) {
|
||||
Ok(a) => a, Err(e) => { emit_err("parse", "SYNTAX", &format!("{}", e)); std::process::exit(1); }
|
||||
};
|
||||
let mut mc = MirCompiler::new();
|
||||
let cr = match mc.compile(ast) { Ok(m) => m, Err(e) => { emit_err("mir", "MIR_COMPILE", &format!("{}", e)); std::process::exit(1); } };
|
||||
let func = match cr.module.functions.get("main") { Some(f) => f, None => { emit_err("mir", "NO_MAIN", "No main function found"); std::process::exit(1); } };
|
||||
|
||||
// Guard: refuse write-effects in jit-direct when policy.read_only
|
||||
{
|
||||
use nyash_rust::mir::MirInstruction;
|
||||
use nyash_rust::mir::effect::Effect;
|
||||
let policy = nyash_rust::jit::policy::current();
|
||||
let mut writes = 0usize;
|
||||
for (_bbid, bb) in func.blocks.iter() {
|
||||
for inst in bb.instructions.iter() {
|
||||
let mask = inst.effects();
|
||||
if mask.contains(Effect::WriteHeap) {
|
||||
writes += 1;
|
||||
}
|
||||
}
|
||||
if let Some(term) = &bb.terminator {
|
||||
if term.effects().contains(Effect::WriteHeap) { writes += 1; }
|
||||
}
|
||||
}
|
||||
if policy.read_only && writes > 0 {
|
||||
emit_err("policy", "WRITE_EFFECTS", &format!("write-effects detected ({} ops). jit-direct is read-only at this stage.", writes));
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut engine = nyash_rust::jit::engine::JitEngine::new();
|
||||
match engine.compile_function("main", func) {
|
||||
Some(h) => {
|
||||
// Optional event: compile
|
||||
nyash_rust::jit::events::emit("compile", &func.signature.name, Some(h), None, serde_json::json!({}));
|
||||
// Parse JIT args from env: NYASH_JIT_ARGS (comma-separated), with optional type prefixes
|
||||
// Formats per arg: i:123, f:3.14, b:true/false, h:42 (handle), or bare numbers (int), true/false (bool)
|
||||
let mut jit_args: Vec<nyash_rust::jit::abi::JitValue> = Vec::new();
|
||||
if let Ok(s) = std::env::var("NYASH_JIT_ARGS") {
|
||||
for raw in s.split(',') {
|
||||
let t = raw.trim();
|
||||
if t.is_empty() { continue; }
|
||||
let v = if let Some(rest) = t.strip_prefix("i:") {
|
||||
rest.parse::<i64>().ok().map(nyash_rust::jit::abi::JitValue::I64)
|
||||
} else if let Some(rest) = t.strip_prefix("f:") {
|
||||
rest.parse::<f64>().ok().map(nyash_rust::jit::abi::JitValue::F64)
|
||||
} else if let Some(rest) = t.strip_prefix("b:") {
|
||||
let b = matches!(rest, "1"|"true"|"True"|"TRUE");
|
||||
Some(nyash_rust::jit::abi::JitValue::Bool(b))
|
||||
} else if let Some(rest) = t.strip_prefix("h:") {
|
||||
rest.parse::<u64>().ok().map(nyash_rust::jit::abi::JitValue::Handle)
|
||||
} else if t.eq_ignore_ascii_case("true") || t == "1" { Some(nyash_rust::jit::abi::JitValue::Bool(true)) }
|
||||
else if t.eq_ignore_ascii_case("false") || t == "0" { Some(nyash_rust::jit::abi::JitValue::Bool(false)) }
|
||||
else if let Ok(iv) = t.parse::<i64>() { Some(nyash_rust::jit::abi::JitValue::I64(iv)) }
|
||||
else if let Ok(fv) = t.parse::<f64>() { Some(nyash_rust::jit::abi::JitValue::F64(fv)) }
|
||||
else { None };
|
||||
if let Some(jv) = v { jit_args.push(jv); }
|
||||
}
|
||||
}
|
||||
// Coerce args to expected MIR types
|
||||
use nyash_rust::mir::MirType;
|
||||
let expected = &func.signature.params;
|
||||
if expected.len() != jit_args.len() {
|
||||
emit_err("args", "COUNT_MISMATCH", &format!("expected={}, passed={}", expected.len(), jit_args.len()));
|
||||
eprintln!("Hint: set NYASH_JIT_ARGS as comma-separated values, e.g., i:42,f:3.14,b:true");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let mut coerced: Vec<nyash_rust::jit::abi::JitValue> = Vec::with_capacity(jit_args.len());
|
||||
for (i, (exp, got)) in expected.iter().zip(jit_args.iter()).enumerate() {
|
||||
let cv = match exp {
|
||||
MirType::Integer => match got {
|
||||
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::I64(*v),
|
||||
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::I64(*f as i64),
|
||||
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::I64(if *b {1} else {0}),
|
||||
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Integer", i)); std::process::exit(1); }
|
||||
},
|
||||
MirType::Float => match got {
|
||||
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::F64(*f),
|
||||
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::F64(*v as f64),
|
||||
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::F64(if *b {1.0} else {0.0}),
|
||||
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Float", i)); std::process::exit(1); }
|
||||
},
|
||||
MirType::Bool => match got {
|
||||
nyash_rust::jit::abi::JitValue::Bool(b) => nyash_rust::jit::abi::JitValue::Bool(*b),
|
||||
nyash_rust::jit::abi::JitValue::I64(v) => nyash_rust::jit::abi::JitValue::Bool(*v != 0),
|
||||
nyash_rust::jit::abi::JitValue::F64(f) => nyash_rust::jit::abi::JitValue::Bool(*f != 0.0),
|
||||
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects Bool", i)); std::process::exit(1); }
|
||||
},
|
||||
MirType::String | MirType::Box(_) | MirType::Array(_) | MirType::Future(_) => match got {
|
||||
nyash_rust::jit::abi::JitValue::Handle(h) => nyash_rust::jit::abi::JitValue::Handle(*h),
|
||||
_ => { emit_err("args", "TYPE_MISMATCH", &format!("param#{} expects handle (h:<id>)", i)); std::process::exit(1); }
|
||||
},
|
||||
MirType::Void | MirType::Unknown => {
|
||||
// Keep as-is
|
||||
*got
|
||||
}
|
||||
};
|
||||
coerced.push(cv);
|
||||
}
|
||||
nyash_rust::jit::rt::set_current_jit_args(&coerced);
|
||||
let t0 = std::time::Instant::now();
|
||||
let out = engine.execute_handle(h, &coerced);
|
||||
match out {
|
||||
Some(v) => {
|
||||
let ms = t0.elapsed().as_millis();
|
||||
nyash_rust::jit::events::emit("execute", &func.signature.name, Some(h), Some(ms), serde_json::json!({}));
|
||||
// Normalize result according to MIR return type for friendly output
|
||||
use nyash_rust::mir::MirType;
|
||||
let ret_ty = &func.signature.return_type;
|
||||
let vmv = match (ret_ty, v) {
|
||||
(MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Bool(i != 0),
|
||||
(MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => nyash_rust::backend::vm::VMValue::Bool(b),
|
||||
(MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => nyash_rust::backend::vm::VMValue::Float(f),
|
||||
(MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => nyash_rust::backend::vm::VMValue::Float(i as f64),
|
||||
// Default adapter for other combos
|
||||
_ => nyash_rust::jit::abi::adapter::from_jit_value(v),
|
||||
};
|
||||
println!("✅ JIT-direct execution completed successfully!");
|
||||
// Pretty print with expected type tag
|
||||
let (ety, sval) = match (ret_ty, &vmv) {
|
||||
(MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()),
|
||||
(MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)),
|
||||
(MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()),
|
||||
// Fallbacks
|
||||
(_, nyash_rust::backend::vm::VMValue::Integer(i)) => ("Integer", i.to_string()),
|
||||
(_, nyash_rust::backend::vm::VMValue::Float(f)) => ("Float", format!("{}", f)),
|
||||
(_, nyash_rust::backend::vm::VMValue::Bool(b)) => ("Bool", b.to_string()),
|
||||
(_, nyash_rust::backend::vm::VMValue::String(s)) => ("String", s.clone()),
|
||||
(_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => ("BoxRef", arc.type_name().to_string()),
|
||||
(_, nyash_rust::backend::vm::VMValue::Future(_)) => ("Future", "<future>".to_string()),
|
||||
(_, nyash_rust::backend::vm::VMValue::Void) => ("Void", "void".to_string()),
|
||||
};
|
||||
println!("ResultType(MIR): {}", ety);
|
||||
println!("Result: {}", sval);
|
||||
// Optional JSON stats
|
||||
if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1") {
|
||||
let cfg = nyash_rust::jit::config::current();
|
||||
let caps = nyash_rust::jit::config::probe_capabilities();
|
||||
let (phi_t, phi_b1, ret_b) = engine.last_lower_stats();
|
||||
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
|
||||
let payload = serde_json::json!({
|
||||
"version": 1,
|
||||
"function": func.signature.name,
|
||||
"abi_mode": abi_mode,
|
||||
"abi_b1_enabled": cfg.native_bool_abi,
|
||||
"abi_b1_supported": caps.supports_b1_sig,
|
||||
"b1_norm_count": nyash_rust::jit::rt::b1_norm_get(),
|
||||
"ret_bool_hint_count": nyash_rust::jit::rt::ret_bool_hint_get(),
|
||||
"phi_total_slots": phi_t,
|
||||
"phi_b1_slots": phi_b1,
|
||||
"ret_bool_hint_used": ret_b,
|
||||
});
|
||||
println!("{}", payload.to_string());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
nyash_rust::jit::events::emit("fallback", &func.signature.name, Some(h), None, serde_json::json!({"reason":"trap_or_missing"}));
|
||||
emit_err("execute", "TRAP_OR_MISSING", "execution failed (trap or missing handle)");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
emit_err("compile", "UNAVAILABLE", "Build with --features cranelift-jit");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Demo functions (moved from main.rs)
|
||||
fn demo_basic_boxes() {
|
||||
println!("\n📦 1. Basic Box Creation:");
|
||||
|
||||
Reference in New Issue
Block a user