Refactor (structure): add vm_stats module; move BoxCall debug logger into vm_boxcall; add quick-reference/code-map.md for AI-friendly repo navigation. Keep behavior unchanged.
This commit is contained in:
25
docs/quick-reference/code-map.md
Normal file
25
docs/quick-reference/code-map.md
Normal file
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<dyn NyashBox>], 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<String> = 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<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
|
||||
@ -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<dyn NyashBox>], 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<String> = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
src/backend/vm_stats.rs
Normal file
54
src/backend/vm_stats.rs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user