diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 6ceb4813..96622a61 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -212,6 +212,8 @@ pub struct VM { 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, + /// VTable-like cache: (type, method_id, arity) → direct target (InstanceBox method) + pub(super) boxcall_vtable_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 @@ -274,6 +276,7 @@ impl VM { exec_start: None, boxcall_pic_hits: std::collections::HashMap::new(), boxcall_pic_funcname: std::collections::HashMap::new(), + boxcall_vtable_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")))] @@ -301,6 +304,7 @@ impl VM { exec_start: None, boxcall_pic_hits: std::collections::HashMap::new(), boxcall_pic_funcname: std::collections::HashMap::new(), + boxcall_vtable_funcname: std::collections::HashMap::new(), } } diff --git a/src/backend/vm_instructions.rs b/src/backend/vm_instructions.rs index d8cea64c..a9ba1507 100644 --- a/src/backend/vm_instructions.rs +++ b/src/backend/vm_instructions.rs @@ -63,6 +63,11 @@ impl VM { fn pic_hits(&self, key: &str) -> u32 { *self.boxcall_pic_hits.get(key).unwrap_or(&0) } + + /// Build vtable cache key for InstanceBox: TypeName#slot/arity + fn build_vtable_key(&self, class_name: &str, method_id: u16, arity: usize) -> String { + format!("VT:{}#{}{}", class_name, method_id, format!("/{}", arity)) + } /// Execute a constant instruction pub(super) fn execute_const(&mut self, dst: ValueId, value: &ConstValue) -> Result { let vm_value = VMValue::from(value); @@ -560,6 +565,21 @@ impl VM { let pic_key = self.build_pic_key(&recv, method, method_id); self.pic_record_hit(&pic_key); + // VTable-like direct call using method_id for InstanceBox (no threshold) + if let (Some(mid), VMValue::BoxRef(arc_box)) = (method_id, &recv) { + if let Some(inst) = arc_box.as_any().downcast_ref::() { + let vkey = self.build_vtable_key(&inst.class_name, mid, args.len()); + if let Some(func_name) = self.boxcall_vtable_funcname.get(&vkey).cloned() { + 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); + } + } + } + // 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() { @@ -601,6 +621,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())); + // Populate vtable cache if method_id is known + if let Some(mid) = method_id { + let vkey = self.build_vtable_key(&inst.class_name, mid, args.len()); + self.boxcall_vtable_funcname.entry(vkey).or_insert(func_name.clone()); + } // 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 {