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

@ -9,11 +9,17 @@ pub struct JitManager {
hits: HashMap<String, u32>,
compiled: HashMap<String, u64>,
engine: crate::jit::engine::JitEngine,
exec_ok: u64,
exec_trap: u64,
// Per-function lowering stats (accumulated)
func_phi_total: HashMap<String, u64>,
func_phi_b1: HashMap<String, u64>,
func_ret_bool_hint: HashMap<String, u64>,
}
impl JitManager {
pub fn new(threshold: u32) -> Self {
Self { threshold, hits: HashMap::new(), compiled: HashMap::new(), engine: crate::jit::engine::JitEngine::new() }
Self { threshold, hits: HashMap::new(), compiled: HashMap::new(), engine: crate::jit::engine::JitEngine::new(), exec_ok: 0, exec_trap: 0, func_phi_total: HashMap::new(), func_phi_b1: HashMap::new(), func_ret_bool_hint: HashMap::new() }
}
pub fn record_entry(&mut self, func: &str) {
@ -35,6 +41,9 @@ impl JitManager {
if self.should_jit(func) {
if let Some(handle) = self.engine.compile_function(func, mir) {
self.mark_compiled(func, handle);
// Record per-function lower stats captured by engine
let (phi_t, phi_b1, ret_b) = self.engine.last_lower_stats();
self.record_lower_stats(func, phi_t, phi_b1, ret_b);
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
eprintln!("[JIT] compiled {} -> handle={}", func, handle);
}
@ -47,12 +56,59 @@ impl JitManager {
pub fn is_compiled(&self, func: &str) -> bool { self.compiled.contains_key(func) }
pub fn handle_of(&self, func: &str) -> Option<u64> { self.compiled.get(func).copied() }
// --- Stats accessors for unified reporting ---
pub fn sites(&self) -> usize { self.hits.len() }
pub fn compiled_count(&self) -> usize { self.compiled.len() }
pub fn total_hits(&self) -> u64 { self.hits.values().map(|v| *v as u64).sum() }
pub fn exec_ok_count(&self) -> u64 { self.exec_ok }
pub fn exec_trap_count(&self) -> u64 { self.exec_trap }
// --- Per-function stats ---
pub fn record_lower_stats(&mut self, func: &str, phi_total: u64, phi_b1: u64, ret_bool_hint: bool) {
if phi_total > 0 { *self.func_phi_total.entry(func.to_string()).or_insert(0) += phi_total; }
if phi_b1 > 0 { *self.func_phi_b1.entry(func.to_string()).or_insert(0) += phi_b1; }
if ret_bool_hint { *self.func_ret_bool_hint.entry(func.to_string()).or_insert(0) += 1; }
}
pub fn per_function_stats(&self) -> Vec<(String, u64, u64, u64, u32, bool, u64)> {
// name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle
let mut names: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
names.extend(self.hits.keys().cloned());
names.extend(self.func_phi_total.keys().cloned());
names.extend(self.func_phi_b1.keys().cloned());
names.extend(self.func_ret_bool_hint.keys().cloned());
let mut out = Vec::new();
for name in names {
let phi_t = self.func_phi_total.get(&name).copied().unwrap_or(0);
let phi_b1 = self.func_phi_b1.get(&name).copied().unwrap_or(0);
let rb = self.func_ret_bool_hint.get(&name).copied().unwrap_or(0);
let hits = self.hits.get(&name).copied().unwrap_or(0);
let compiled = self.compiled.contains_key(&name);
let handle = self.compiled.get(&name).copied().unwrap_or(0);
out.push((name, phi_t, phi_b1, rb, hits, compiled, handle));
}
out
}
/// Return top-N hot functions by hits, with compiled flag and handle
pub fn top_hits(&self, n: usize) -> Vec<(String, u32, bool, u64)> {
let mut v: Vec<(&String, &u32)> = self.hits.iter().collect();
v.sort_by(|a, b| b.1.cmp(a.1));
v.into_iter()
.take(n)
.map(|(k, h)| {
let compiled = self.compiled.contains_key(k);
let handle = self.compiled.get(k).copied().unwrap_or(0);
(k.clone(), *h, compiled, handle)
})
.collect()
}
pub fn print_summary(&self) {
if std::env::var("NYASH_JIT_STATS").ok().as_deref() != Some("1") { return; }
let sites = self.hits.len();
let total_hits: u64 = self.hits.values().map(|v| *v as u64).sum();
let compiled = self.compiled.len();
eprintln!("[JIT] sites={} compiled={} hits_total={}", sites, compiled, total_hits);
eprintln!("[JIT] sites={} compiled={} hits_total={} exec_ok={} exec_trap={}", sites, compiled, total_hits, self.exec_ok, self.exec_trap);
// Top 5 hot functions
let mut v: Vec<(&String, &u32)> = self.hits.iter().collect();
v.sort_by(|a, b| b.1.cmp(a.1));
@ -78,17 +134,27 @@ impl JitManager {
}
/// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken.
pub fn execute_compiled(&self, func: &str, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
pub fn execute_compiled(&mut self, func: &str, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
if let Some(h) = self.handle_of(func) {
// Expose current args to hostcall shims
crate::jit::rt::set_current_args(args);
// Expose args to both legacy VM hostcalls and new JIT ABI TLS
crate::jit::rt::set_legacy_vm_args(args);
let jit_args = crate::jit::abi::adapter::to_jit_values(args);
crate::jit::rt::set_current_jit_args(&jit_args);
let t0 = std::time::Instant::now();
let out = self.engine.execute_handle(h, args);
// Begin handle scope so temporary handles are reclaimed after the call
crate::jit::rt::handles::begin_scope();
let out = self.engine.execute_handle(h, &jit_args);
if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1") {
let dt = t0.elapsed();
eprintln!("[JIT] exec_time_ms={} for {}", dt.as_millis(), func);
}
return out;
let res = match out {
Some(v) => { self.exec_ok = self.exec_ok.saturating_add(1); Some(crate::jit::abi::adapter::from_jit_value(v)) }
None => { self.exec_trap = self.exec_trap.saturating_add(1); None }
};
// Clear handles created during this call
crate::jit::rt::handles::end_scope_clear();
return res;
}
None
}