PIC direct-call: cache InstanceBox function name after threshold\n- Add boxcall_pic_funcname cache in VM\n- execute_boxcall uses cache for direct function calls\n- Threshold=8 hits per site\n- Update CURRENT_TASK

This commit is contained in:
Moe Charm
2025-08-26 22:11:17 +09:00
parent b2b82c9e81
commit 2960a9b2f8
3 changed files with 30 additions and 1 deletions

View File

@ -13,7 +13,7 @@
2) 9.79b.2: VM VTable Thunks + Mono-PIC 2) 9.79b.2: VM VTable Thunks + Mono-PIC
- `execute_boxcall`をvtable+thunkの単一路線へユニバーサル0..3のfast-path追加✅ スケルトン - `execute_boxcall`をvtable+thunkの単一路線へユニバーサル0..3のfast-path追加✅ スケルトン
- call-site単位のモモーフィックPICを追加Key設計とカウンタ導入・記録まで✅ スケルトン - call-site単位のモモーフィックPICを追加Key設計とカウンタ導入・記録まで✅ スケルトン
- 次: 安定閾値での直呼び最適化(後段 - 次: 安定閾値での直呼び最適化(InstanceBox関数名キャッシュ✅ 実装PIC=8で昇格
### すぐ試せるコマンド ### すぐ試せるコマンド
```bash ```bash

View File

@ -210,6 +210,8 @@ pub struct VM {
pub(super) exec_start: Option<Instant>, pub(super) exec_start: Option<Instant>,
/// Mono-PIC skeleton: global hit counters keyed by (recv_type, method_id/name) /// Mono-PIC skeleton: global hit counters keyed by (recv_type, method_id/name)
pub(super) boxcall_pic_hits: std::collections::HashMap<String, u32>, pub(super) boxcall_pic_hits: std::collections::HashMap<String, u32>,
/// Mono-PIC: cached direct targets (currently InstanceBox function name)
pub(super) boxcall_pic_funcname: std::collections::HashMap<String, String>,
// Phase 9.78a: Add unified Box handling components // Phase 9.78a: Add unified Box handling components
// TODO: Re-enable when interpreter refactoring is complete // TODO: Re-enable when interpreter refactoring is complete
// /// Box registry for creating all Box types // /// Box registry for creating all Box types
@ -271,6 +273,7 @@ impl VM {
instr_counter: std::collections::HashMap::new(), instr_counter: std::collections::HashMap::new(),
exec_start: None, exec_start: None,
boxcall_pic_hits: std::collections::HashMap::new(), boxcall_pic_hits: std::collections::HashMap::new(),
boxcall_pic_funcname: std::collections::HashMap::new(),
// TODO: Re-enable when interpreter refactoring is complete // TODO: Re-enable when interpreter refactoring is complete
// box_registry: Arc::new(UnifiedBoxRegistry::new()), // box_registry: Arc::new(UnifiedBoxRegistry::new()),
// #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
@ -297,6 +300,7 @@ impl VM {
instr_counter: std::collections::HashMap::new(), instr_counter: std::collections::HashMap::new(),
exec_start: None, exec_start: None,
boxcall_pic_hits: std::collections::HashMap::new(), boxcall_pic_hits: std::collections::HashMap::new(),
boxcall_pic_funcname: std::collections::HashMap::new(),
} }
} }

View File

@ -58,6 +58,11 @@ impl VM {
} }
} }
} }
/// Read current PIC hit count for a key
fn pic_hits(&self, key: &str) -> u32 {
*self.boxcall_pic_hits.get(key).unwrap_or(&0)
}
/// Execute a constant instruction /// Execute a constant instruction
pub(super) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<ControlFlow, VMError> { pub(super) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result<ControlFlow, VMError> {
let vm_value = VMValue::from(value); let vm_value = VMValue::from(value);
@ -555,6 +560,21 @@ impl VM {
let pic_key = self.build_pic_key(&recv, method, method_id); let pic_key = self.build_pic_key(&recv, method, method_id);
self.pic_record_hit(&pic_key); self.pic_record_hit(&pic_key);
// Mono-PIC direct call: if we cached a target for this site and receiver is InstanceBox, use it
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.boxcall_pic_funcname.get(&pic_key).cloned() {
// Build VM args: receiver first, then original args
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)?); }
let res = self.call_function_by_name(&func_name, vm_args)?;
if let Some(dst_id) = dst { self.set_value(dst_id, res); }
return Ok(ControlFlow::Continue);
}
}
}
// Fast path: universal method slots via method_id (0..3) // Fast path: universal method slots via method_id (0..3)
if let Some(mid) = method_id { if let Some(mid) = method_id {
if let Some(fast_res) = self.try_fast_universal(mid, &recv, args)? { if let Some(fast_res) = self.try_fast_universal(mid, &recv, args)? {
@ -581,6 +601,11 @@ impl VM {
// If this is a user InstanceBox, redirect to lowered function: Class.method/arity // If this is a user InstanceBox, redirect to lowered function: Class.method/arity
if let Some(inst) = arc_box.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() { if let Some(inst) = arc_box.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
let func_name = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len())); let func_name = format!("{}.{}{}", inst.class_name, method, format!("/{}", args.len()));
// If this call-site is hot, cache the function name for direct calls next time
const PIC_THRESHOLD: u32 = 8;
if self.pic_hits(&pic_key) >= PIC_THRESHOLD {
self.boxcall_pic_funcname.insert(pic_key.clone(), func_name.clone());
}
if debug_boxcall { eprintln!("[BoxCall] InstanceBox -> call {}", func_name); } if debug_boxcall { eprintln!("[BoxCall] InstanceBox -> call {}", func_name); }
// Build VMValue args: receiver first, then original VMValue args // Build VMValue args: receiver first, then original VMValue args
let mut vm_args = Vec::with_capacity(1 + args.len()); let mut vm_args = Vec::with_capacity(1 + args.len());