Phase 12: add wasm_v2 scaffold (unified vtable slots), expand host by-slot (Map/String), STRICT extern diagnostics, identical-exec console.log test, and CLAUDE_WASM_TASK handoff
This commit is contained in:
@ -20,6 +20,8 @@ pub mod mir_interpreter; // Lightweight MIR interpreter
|
||||
pub mod wasm;
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
pub mod aot;
|
||||
#[cfg(feature = "wasm-backend")]
|
||||
pub mod wasm_v2;
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
pub mod llvm;
|
||||
|
||||
@ -208,6 +208,14 @@ pub struct VM {
|
||||
pub(super) instr_counter: std::collections::HashMap<&'static str, usize>,
|
||||
/// Execution start time for optional stats
|
||||
pub(super) exec_start: Option<Instant>,
|
||||
/// Stats: number of BoxCall hits via VTable path
|
||||
pub(super) boxcall_hits_vtable: u64,
|
||||
/// Stats: number of BoxCall hits via Poly-PIC path
|
||||
pub(super) boxcall_hits_poly_pic: u64,
|
||||
/// Stats: number of BoxCall hits via Mono-PIC path
|
||||
pub(super) boxcall_hits_mono_pic: u64,
|
||||
/// Stats: number of BoxCall hits via generic fallback path
|
||||
pub(super) boxcall_hits_generic: u64,
|
||||
/// Mono-PIC skeleton: global hit counters keyed by (recv_type, method_id/name)
|
||||
pub(super) boxcall_pic_hits: std::collections::HashMap<String, u32>,
|
||||
/// Mono-PIC: cached direct targets (currently InstanceBox function name)
|
||||
@ -372,6 +380,10 @@ impl VM {
|
||||
module: None,
|
||||
instr_counter: std::collections::HashMap::new(),
|
||||
exec_start: None,
|
||||
boxcall_hits_vtable: 0,
|
||||
boxcall_hits_poly_pic: 0,
|
||||
boxcall_hits_mono_pic: 0,
|
||||
boxcall_hits_generic: 0,
|
||||
boxcall_pic_hits: std::collections::HashMap::new(),
|
||||
boxcall_pic_funcname: std::collections::HashMap::new(),
|
||||
boxcall_poly_pic: std::collections::HashMap::new(),
|
||||
@ -403,6 +415,10 @@ impl VM {
|
||||
module: None,
|
||||
instr_counter: std::collections::HashMap::new(),
|
||||
exec_start: None,
|
||||
boxcall_hits_vtable: 0,
|
||||
boxcall_hits_poly_pic: 0,
|
||||
boxcall_hits_mono_pic: 0,
|
||||
boxcall_hits_generic: 0,
|
||||
boxcall_pic_hits: std::collections::HashMap::new(),
|
||||
boxcall_pic_funcname: std::collections::HashMap::new(),
|
||||
boxcall_poly_pic: std::collections::HashMap::new(),
|
||||
@ -494,8 +510,9 @@ impl VM {
|
||||
let hits_total: u64 = self.boxcall_pic_hits.values().map(|v| *v as u64).sum();
|
||||
let vt_entries = self.boxcall_vtable_funcname.len();
|
||||
eprintln!(
|
||||
"[VM] PIC/VT summary: poly_sites={} avg_entries={:.2} mono_sites={} hits_total={} vt_entries={}",
|
||||
sites_poly, avg_entries, sites_mono, hits_total, vt_entries
|
||||
"[VM] PIC/VT summary: poly_sites={} avg_entries={:.2} mono_sites={} hits_total={} vt_entries={} | hits: vt={} poly={} mono={} generic={}",
|
||||
sites_poly, avg_entries, sites_mono, hits_total, vt_entries,
|
||||
self.boxcall_hits_vtable, self.boxcall_hits_poly_pic, self.boxcall_hits_mono_pic, self.boxcall_hits_generic
|
||||
);
|
||||
// Top sites by hits (up to 5)
|
||||
let mut hits: Vec<(&String, &u32)> = self.boxcall_pic_hits.iter().collect();
|
||||
|
||||
@ -645,7 +645,12 @@ impl VM {
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name)));
|
||||
let strict = crate::config::env::extern_strict() || crate::config::env::abi_strict();
|
||||
if strict {
|
||||
return Err(VMError::InvalidInstruction(format!("ExternCall STRICT: unregistered or unsupported call {}.{}", iface_name, method_name)));
|
||||
} else {
|
||||
return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ControlFlow::Continue)
|
||||
@ -883,6 +888,7 @@ impl VM {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "BoxCall.setField");
|
||||
}
|
||||
let cloned_box = arc_box.share_box();
|
||||
self.boxcall_hits_generic = self.boxcall_hits_generic.saturating_add(1);
|
||||
let out = self.call_box_method(cloned_box, m, nyash_args)?;
|
||||
let vm_out = VMValue::from_nyash_box(out);
|
||||
if let Some(dst_id) = dst { self.set_value(dst_id, vm_out); }
|
||||
@ -912,6 +918,7 @@ impl VM {
|
||||
if let VMValue::BoxRef(arc_box) = &recv {
|
||||
if arc_box.as_any().downcast_ref::<crate::instance_v2::InstanceBox>().is_some() {
|
||||
if let Some(func_name) = self.try_poly_pic(&pic_key, &recv) {
|
||||
self.boxcall_hits_poly_pic = self.boxcall_hits_poly_pic.saturating_add(1);
|
||||
let mut vm_args = Vec::with_capacity(1 + args.len());
|
||||
vm_args.push(recv.clone());
|
||||
for a in args { vm_args.push(self.get_value(*a)?); }
|
||||
@ -921,6 +928,7 @@ impl VM {
|
||||
}
|
||||
// Fallback to Mono-PIC (legacy) if present
|
||||
if let Some(func_name) = self.boxcall_pic_funcname.get(&pic_key).cloned() {
|
||||
self.boxcall_hits_mono_pic = self.boxcall_hits_mono_pic.saturating_add(1);
|
||||
// Build VM args: receiver first, then original args
|
||||
let mut vm_args = Vec::with_capacity(1 + args.len());
|
||||
vm_args.push(recv.clone());
|
||||
@ -1126,6 +1134,7 @@ impl VM {
|
||||
// MapBox: size/len/has/get
|
||||
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
if matches!(slot, Some(200|201)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = map.size();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1141,6 +1150,7 @@ impl VM {
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = map.has(key_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1157,6 +1167,7 @@ impl VM {
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = map.get(key_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1166,6 +1177,7 @@ impl VM {
|
||||
// ArrayBox: get/set/len
|
||||
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if matches!(slot, Some(102)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = arr.length();
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1179,6 +1191,7 @@ impl VM {
|
||||
VMValue::Float(f) => Box::new(crate::box_trait::IntegerBox::new(f as i64)),
|
||||
VMValue::BoxRef(_) | VMValue::Future(_) | VMValue::Void => Box::new(crate::box_trait::IntegerBox::new(0)),
|
||||
};
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = arr.get(idx_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1202,6 +1215,7 @@ impl VM {
|
||||
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = arr.set(idx_box, val_box);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
@ -1211,6 +1225,7 @@ impl VM {
|
||||
// StringBox: len
|
||||
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
if matches!(slot, Some(300)) {
|
||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||
let out = crate::box_trait::IntegerBox::new(sb.value.len() as i64);
|
||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); }
|
||||
return Some(Ok(ControlFlow::Continue));
|
||||
|
||||
17
src/backend/wasm_v2/mod.rs
Normal file
17
src/backend/wasm_v2/mod.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! WASM Backend v2 (Phase 12 scaffolding)
|
||||
//!
|
||||
//! 目的:
|
||||
//! - vtable/スロット解決に基づく統一ディスパッチ経路の雛形
|
||||
//! - 既存ビルドに影響を与えない最小構成(feature/target gate)
|
||||
|
||||
#![cfg(feature = "wasm-backend")]
|
||||
|
||||
pub mod unified_dispatch;
|
||||
pub mod vtable_codegen;
|
||||
|
||||
/// エントリポイントの雛形
|
||||
pub fn compile_and_execute_v2(_module: &crate::mir::MirModule, _temp_name: &str) -> Result<Box<dyn crate::box_trait::NyashBox>, String> {
|
||||
// まだ未実装: vtable_codegenで生成したスロット表を unified_dispatch 経由で実行
|
||||
Err("wasm_v2: not implemented (scaffold)".to_string())
|
||||
}
|
||||
|
||||
25
src/backend/wasm_v2/unified_dispatch.rs
Normal file
25
src/backend/wasm_v2/unified_dispatch.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! Unified dispatch (WASM v2)
|
||||
//!
|
||||
//! - TypeRegistryのスロット表と一致させた呼び出し分岐の雛形
|
||||
//! - ここではあくまで「どのスロットに行くか」の判定のみ提供
|
||||
|
||||
#![cfg(feature = "wasm-backend")]
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// 受信ボックス/メソッド名/アリティからスロットを解決し、識別子を返す。
|
||||
pub fn resolve_slot(recv: &dyn NyashBox, method: &str, arity: usize) -> Option<u16> {
|
||||
let ty = recv.type_name();
|
||||
crate::runtime::type_registry::resolve_slot_by_name(ty, method, arity)
|
||||
}
|
||||
|
||||
/// 実際の呼び出し分岐は、将来的にここから生成済みのstubsに委譲する予定。
|
||||
pub fn dispatch_by_slot(
|
||||
_slot: u16,
|
||||
_recv: &dyn NyashBox,
|
||||
_args: &[Box<dyn NyashBox>],
|
||||
) -> Option<Box<dyn NyashBox>> {
|
||||
// 未実装: wasm_v2ではJS/hostへのブリッジや、Wasm内の簡易実装に委譲
|
||||
None
|
||||
}
|
||||
|
||||
18
src/backend/wasm_v2/vtable_codegen.rs
Normal file
18
src/backend/wasm_v2/vtable_codegen.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! VTable codegen stubs (WASM v2)
|
||||
//!
|
||||
//! - 将来的にTypeRegistryから静的テーブル/インライン分岐を生成
|
||||
//! - 現段階はプレースホルダ
|
||||
|
||||
#![cfg(feature = "wasm-backend")]
|
||||
|
||||
/// 生成結果のメタ情報(雛形)
|
||||
pub struct GeneratedVTableInfo {
|
||||
pub types: usize,
|
||||
pub methods: usize,
|
||||
}
|
||||
|
||||
pub fn generate_tables() -> GeneratedVTableInfo {
|
||||
// 未実装: TypeRegistry::resolve_typebox_by_name()/methods を走査して集計
|
||||
GeneratedVTableInfo { types: 0, methods: 0 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user