diff --git a/docs/development/current/CURRENT_TASK.md b/docs/development/current/CURRENT_TASK.md index 96995eca..a5d9dbdb 100644 --- a/docs/development/current/CURRENT_TASK.md +++ b/docs/development/current/CURRENT_TASK.md @@ -13,7 +13,7 @@ 2) 9.79b.2: VM VTable Thunks + Mono-PIC - `execute_boxcall`をvtable+thunkの単一路線へ(ユニバーサル0..3のfast-path追加)✅ スケルトン - call-site単位のモノモーフィックPICを追加(Key設計とカウンタ導入・記録まで)✅ スケルトン - - 次: 安定閾値での直呼び最適化(後段) + - 次: 安定閾値での直呼び最適化(InstanceBox関数名キャッシュ)✅ 実装(PIC=8で昇格) ### すぐ試せるコマンド ```bash diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 56791969..6ceb4813 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -210,6 +210,8 @@ pub struct VM { pub(super) exec_start: Option, /// Mono-PIC skeleton: global hit counters keyed by (recv_type, method_id/name) pub(super) boxcall_pic_hits: std::collections::HashMap, + /// Mono-PIC: cached direct targets (currently InstanceBox function name) + pub(super) boxcall_pic_funcname: std::collections::HashMap, // Phase 9.78a: Add unified Box handling components // TODO: Re-enable when interpreter refactoring is complete // /// Box registry for creating all Box types @@ -271,6 +273,7 @@ impl VM { instr_counter: std::collections::HashMap::new(), exec_start: None, boxcall_pic_hits: std::collections::HashMap::new(), + boxcall_pic_funcname: std::collections::HashMap::new(), // TODO: Re-enable when interpreter refactoring is complete // box_registry: Arc::new(UnifiedBoxRegistry::new()), // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] @@ -297,6 +300,7 @@ impl VM { instr_counter: std::collections::HashMap::new(), exec_start: None, boxcall_pic_hits: std::collections::HashMap::new(), + boxcall_pic_funcname: std::collections::HashMap::new(), } } diff --git a/src/backend/vm_instructions.rs b/src/backend/vm_instructions.rs index d1a15bec..d8cea64c 100644 --- a/src/backend/vm_instructions.rs +++ b/src/backend/vm_instructions.rs @@ -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 pub(super) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result { let vm_value = VMValue::from(value); @@ -555,6 +560,21 @@ impl VM { let pic_key = self.build_pic_key(&recv, method, method_id); 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::().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) if let Some(mid) = method_id { 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 let Some(inst) = arc_box.as_any().downcast_ref::() { 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); } // Build VMValue args: receiver first, then original VMValue args let mut vm_args = Vec::with_capacity(1 + args.len());