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_instructions;
|
||||||
pub mod vm_values;
|
pub mod vm_values;
|
||||||
pub mod vm_boxcall;
|
pub mod vm_boxcall;
|
||||||
|
pub mod vm_stats;
|
||||||
|
|
||||||
#[cfg(feature = "wasm-backend")]
|
#[cfg(feature = "wasm-backend")]
|
||||||
pub mod wasm;
|
pub mod wasm;
|
||||||
|
|||||||
@ -672,76 +672,6 @@ impl VM {
|
|||||||
*self.instr_counter.entry(key).or_insert(0) += 1;
|
*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
|
/// 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> {
|
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 crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
|
||||||
use super::vm::{VM, VMError};
|
use super::vm::{VM, VMError, VMValue};
|
||||||
|
|
||||||
impl VM {
|
impl VM {
|
||||||
/// Call a method on a Box - simplified version of interpreter method dispatch
|
/// 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
|
// Default: return void for any unrecognized box type or method
|
||||||
Ok(Box::new(VoidBox::new()))
|
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