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:
Moe Charm
2025-08-28 09:26:58 +09:00
parent 99e59e24e2
commit e54561e69f
64 changed files with 4311 additions and 189 deletions

View File

@ -12,11 +12,15 @@ pub struct JitEngine {
initialized: bool,
next_handle: u64,
/// Stub function table: handle -> callable closure
fntab: HashMap<u64, Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
fntab: HashMap<u64, Arc<dyn Fn(&[crate::jit::abi::JitValue]) -> crate::jit::abi::JitValue + Send + Sync>>,
/// Host externs by symbol name (Phase 10_d)
externs: HashMap<String, Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
#[cfg(feature = "cranelift-jit")]
isa: Option<cranelift_codegen::isa::OwnedTargetIsa>,
// Last lower stats (per function)
last_phi_total: u64,
last_phi_b1: u64,
last_ret_bool_hint: bool,
}
impl JitEngine {
@ -27,6 +31,9 @@ impl JitEngine {
fntab: HashMap::new(),
externs: HashMap::new(),
#[cfg(feature = "cranelift-jit")] isa: None,
last_phi_total: 0,
last_phi_b1: 0,
last_ret_bool_hint: false,
};
#[cfg(feature = "cranelift-jit")]
{ this.isa = None; }
@ -47,26 +54,46 @@ impl JitEngine {
eprintln!("[JIT] lower failed for {}: {}", func_name, e);
return None;
}
if std::env::var("NYASH_JIT_DUMP").ok().as_deref() == Some("1") {
// Capture per-function lower stats for manager to query later
let (phi_t, phi_b1, ret_b) = lower.last_stats();
self.last_phi_total = phi_t; self.last_phi_b1 = phi_b1; self.last_ret_bool_hint = ret_b;
// Record per-function stats into manager via callback if available (handled by caller)
let cfg_now = crate::jit::config::current();
if cfg_now.dump {
let phi_min = cfg_now.phi_min;
let native_f64 = cfg_now.native_f64;
let native_bool = cfg_now.native_bool;
#[cfg(feature = "cranelift-jit")]
{
let s = builder.stats;
eprintln!("[JIT] lower {}: covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, lower.covered, lower.unsupported,
eprintln!("[JIT] lower {}: argc={} phi_min={} f64={} bool={} covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, mir.params.len(), phi_min, native_f64, native_bool,
lower.covered, lower.unsupported,
s.0, s.1, s.2, s.3, s.4);
}
#[cfg(not(feature = "cranelift-jit"))]
{
eprintln!("[JIT] lower {}: covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, lower.covered, lower.unsupported,
eprintln!("[JIT] lower {}: argc={} phi_min={} f64={} bool={} covered={} unsupported={} (consts={}, binops={}, cmps={}, branches={}, rets={})",
func_name, mir.params.len(), phi_min, native_f64, native_bool,
lower.covered, lower.unsupported,
builder.consts, builder.binops, builder.cmps, builder.branches, builder.rets);
}
// Optional DOT export
if let Ok(path) = std::env::var("NYASH_JIT_DOT") {
if !path.is_empty() {
if let Err(e) = crate::jit::lower::core::dump_cfg_dot(mir, &path, phi_min) {
eprintln!("[JIT] DOT export failed: {}", e);
} else {
eprintln!("[JIT] DOT written to {}", path);
}
}
}
}
// Create a handle and register an executable closure
let h = self.next_handle;
self.next_handle = self.next_handle.saturating_add(1);
// Create a handle and register an executable closure if available
#[cfg(feature = "cranelift-jit")]
{
let h = self.next_handle;
self.next_handle = self.next_handle.saturating_add(1);
if let Some(closure) = builder.take_compiled_closure() {
self.fntab.insert(h, closure);
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
@ -75,21 +102,27 @@ impl JitEngine {
}
return Some(h);
}
// If Cranelift path did not produce a closure, treat as not compiled
return None;
}
// Fallback: insert a stub closure
self.fntab.insert(h, Arc::new(|_args: &[crate::backend::vm::VMValue]| {
crate::backend::vm::VMValue::Void
}));
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
let dt = t0.elapsed();
eprintln!("[JIT] compile_time_ms={} for {} (stub)", dt.as_millis(), func_name);
#[cfg(not(feature = "cranelift-jit"))]
{
// Without Cranelift, do not register a stub that alters program semantics.
// Report as not compiled so VM path remains authoritative.
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
let dt = t0.elapsed();
eprintln!("[JIT] compile skipped (no cranelift) for {} after {}ms", func_name, dt.as_millis());
}
return None;
}
Some(h)
}
/// Get statistics from the last lowered function
pub fn last_lower_stats(&self) -> (u64, u64, bool) { (self.last_phi_total, self.last_phi_b1, self.last_ret_bool_hint) }
/// Execute compiled function by handle with trap fallback.
/// Returns Some(VMValue) if executed successfully; None on missing handle or trap (panic).
pub fn execute_handle(&self, handle: u64, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
pub fn execute_handle(&self, handle: u64, args: &[crate::jit::abi::JitValue]) -> Option<crate::jit::abi::JitValue> {
let f = match self.fntab.get(&handle) { Some(f) => f, None => return None };
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (f)(args)));
match res {