diff --git a/docs/quick-reference/code-map.md b/docs/quick-reference/code-map.md new file mode 100644 index 00000000..42d1ec7c --- /dev/null +++ b/docs/quick-reference/code-map.md @@ -0,0 +1,25 @@ +# Code Map (Backend VM) — Quick Guide + +Purpose: Make it obvious where to look without reading everything. + +## Backend Modules (VM) +- `src/backend/vm.rs`: Core VM struct, execute loop, storage, control-flow. +- `src/backend/vm_instructions.rs`: Instruction handlers called from `execute_instruction`. +- `src/backend/vm_values.rs`: Value-level ops (binary/unary/compare), boolean coercions. +- `src/backend/vm_boxcall.rs`: Box method dispatch (`call_box_method_impl`), BoxCall debug logger。 +- `src/backend/vm_phi.rs`: Loop/phi utilities (LoopExecutor)。 +- `src/backend/vm_stats.rs`: Stats/diagnostics printing(JSON/Text, envで制御)。 + +## MIR Pipeline (where to check next) +- `src/mir/printer.rs`: `--mir-verbose(--effects)`の出力。 +- `src/mir/verification.rs`: SSA/支配/CFG/merge-phi + WeakRef/Barrier 最小検証+Strict Barrier診断。 +- `src/mir/optimizer.rs`: DCE/CSE/順序調整 + 未lowering検知(is/as 系)。 + +## Useful env flags +- `NYASH_VM_DEBUG_BOXCALL=1`: BoxCallの受け手/引数/結果型をstderrに出力。 +- `NYASH_VM_STATS=1` (`NYASH_VM_STATS_JSON=1`): 実行統計を表示(JSON可)。 +- `NYASH_VERIFY_BARRIER_STRICT=1`: Barrierの軽い文脈診断を有効化。 +- `NYASH_OPT_DIAG_FAIL=1`: Optimizerの未lowering検知でエラー終了(CI向け)。 + +Last updated: 2025-08-25 + diff --git a/src/backend/mod.rs b/src/backend/mod.rs index a5dc1ab0..9bd52239 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -7,6 +7,7 @@ pub mod vm_phi; pub mod vm_instructions; pub mod vm_values; pub mod vm_boxcall; +pub mod vm_stats; #[cfg(feature = "wasm-backend")] pub mod wasm; diff --git a/src/backend/vm.rs b/src/backend/vm.rs index e7180dd7..61ef5dc0 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -672,76 +672,6 @@ impl VM { *self.instr_counter.entry(key).or_insert(0) += 1; } - pub(super) fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box], stage: &str, result: Option<&VMValue>) { - if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") { - let recv_ty = match recv { - VMValue::BoxRef(arc) => arc.type_name().to_string(), - VMValue::Integer(_) => "Integer".to_string(), - VMValue::Float(_) => "Float".to_string(), - VMValue::Bool(_) => "Bool".to_string(), - VMValue::String(_) => "String".to_string(), - VMValue::Future(_) => "Future".to_string(), - VMValue::Void => "Void".to_string(), - }; - let args_desc: Vec = args.iter().map(|a| a.type_name().to_string()).collect(); - if let Some(res) = result { - let res_ty = match res { - VMValue::BoxRef(arc) => format!("BoxRef({})", arc.type_name()), - VMValue::Integer(_) => "Integer".to_string(), - VMValue::Float(_) => "Float".to_string(), - VMValue::Bool(_) => "Bool".to_string(), - VMValue::String(_) => "String".to_string(), - VMValue::Future(_) => "Future".to_string(), - VMValue::Void => "Void".to_string(), - }; - eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty); - } else { - eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc); - } - } - } - - /// Print simple VM execution statistics when enabled via env var - fn maybe_print_stats(&mut self) { - let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); - if !enabled { return; } - - let elapsed_ms = self.exec_start.map(|t| t.elapsed().as_secs_f64() * 1000.0).unwrap_or(0.0); - let mut items: Vec<(&str, usize)> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); - items.sort_by(|a,b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0))); - let total: usize = items.iter().map(|(_,v)| *v).sum(); - - let json_enabled = std::env::var("NYASH_VM_STATS_JSON").ok().map(|v| v != "0").unwrap_or(false) - || std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false); - - if json_enabled { - // Build JSON structure: { total, elapsed_ms, counts: {op: n, ...}, top20: [{op, count}], timestamp_ms } - let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); - let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| { - serde_json::json!({ "op": op, "count": cnt }) - }).collect(); - let now_ms = { - use std::time::{SystemTime, UNIX_EPOCH}; - SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) - }; - let payload = serde_json::json!({ - "total": total, - "elapsed_ms": elapsed_ms, - "counts": counts_obj, - "top20": top20, - "timestamp_ms": now_ms - }); - match serde_json::to_string_pretty(&payload) { - Ok(s) => println!("{}", s), - Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms), - } - } else { - println!("\n🧮 VM Stats: {} instructions in {:.3} ms", total, elapsed_ms); - for (k, v) in items.into_iter().take(20) { - println!(" {:>10}: {:>8}", k, v); - } - } - } /// Phase 9.78a: Unified method dispatch for all Box types fn call_unified_method(&self, box_value: Box, method: &str, args: Vec>) -> Result, VMError> { diff --git a/src/backend/vm_boxcall.rs b/src/backend/vm_boxcall.rs index 30f0ca0e..fdd326a9 100644 --- a/src/backend/vm_boxcall.rs +++ b/src/backend/vm_boxcall.rs @@ -3,7 +3,7 @@ */ use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; -use super::vm::{VM, VMError}; +use super::vm::{VM, VMError, VMValue}; impl VM { /// Call a method on a Box - simplified version of interpreter method dispatch @@ -107,4 +107,34 @@ impl VM { // Default: return void for any unrecognized box type or method Ok(Box::new(VoidBox::new())) } + + /// Debug helper for BoxCall tracing (enabled via NYASH_VM_DEBUG_BOXCALL=1) + pub(super) fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box], stage: &str, result: Option<&VMValue>) { + if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") { + let recv_ty = match recv { + VMValue::BoxRef(arc) => arc.type_name().to_string(), + VMValue::Integer(_) => "Integer".to_string(), + VMValue::Float(_) => "Float".to_string(), + VMValue::Bool(_) => "Bool".to_string(), + VMValue::String(_) => "String".to_string(), + VMValue::Future(_) => "Future".to_string(), + VMValue::Void => "Void".to_string(), + }; + let args_desc: Vec = args.iter().map(|a| a.type_name().to_string()).collect(); + if let Some(res) = result { + let res_ty = match res { + VMValue::BoxRef(arc) => format!("BoxRef({})", arc.type_name()), + VMValue::Integer(_) => "Integer".to_string(), + VMValue::Float(_) => "Float".to_string(), + VMValue::Bool(_) => "Bool".to_string(), + VMValue::String(_) => "String".to_string(), + VMValue::Future(_) => "Future".to_string(), + VMValue::Void => "Void".to_string(), + }; + eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty); + } else { + eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc); + } + } + } } diff --git a/src/backend/vm_stats.rs b/src/backend/vm_stats.rs new file mode 100644 index 00000000..45bd4bc6 --- /dev/null +++ b/src/backend/vm_stats.rs @@ -0,0 +1,54 @@ +/*! + * VM Stats & Diagnostics - prints per-instruction counters and timing + * + * Responsibility: + * - Aggregate instruction counts (by MIR opcode) + * - Print text or JSON summary based on env vars + * - Stay side-effect free w.r.t. VM execution semantics + */ + +use super::vm::VM; + +impl VM { + /// Print simple VM execution statistics when enabled via env var + pub(super) fn maybe_print_stats(&mut self) { + let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); + if !enabled { return; } + + let elapsed_ms = self.exec_start.map(|t| t.elapsed().as_secs_f64() * 1000.0).unwrap_or(0.0); + let mut items: Vec<(&str, usize)> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); + items.sort_by(|a,b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0))); + let total: usize = items.iter().map(|(_,v)| *v).sum(); + + let json_enabled = std::env::var("NYASH_VM_STATS_JSON").ok().map(|v| v != "0").unwrap_or(false) + || std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false); + + if json_enabled { + let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); + let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| { + serde_json::json!({ "op": op, "count": cnt }) + }).collect(); + let now_ms = { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) + }; + let payload = serde_json::json!({ + "total": total, + "elapsed_ms": elapsed_ms, + "counts": counts_obj, + "top20": top20, + "timestamp_ms": now_ms + }); + match serde_json::to_string_pretty(&payload) { + Ok(s) => println!("{}", s), + Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms), + } + } else { + println!("\n🧮 VM Stats: {} instructions in {:.3} ms", total, elapsed_ms); + for (k, v) in items.into_iter().take(20) { + println!(" {:>10}: {:>8}", k, v); + } + } + } +} +