11// vm/cache.rs
22
33use super :: types:: { Val , eq_vals_with_heap} ;
4- use super :: threaded;
5-
64use crate :: modules:: parser:: { OpCode , SSAChunk , Instruction } ;
75use crate :: modules:: fx:: FxHashMap as HashMap ;
86
@@ -48,7 +46,7 @@ impl OpcodeCache {
4846 /// Compile fused instruction stream on first access; reuse afterward.
4947 pub fn ensure_fused ( & mut self , chunk : & SSAChunk ) -> & [ Instruction ] {
5048 if self . fused . is_none ( ) {
51- self . fused = Some ( threaded :: compile ( chunk) ) ;
49+ self . fused = Some ( fuse_method_calls ( chunk) ) ;
5250 }
5351 self . fused . as_ref ( ) . unwrap ( )
5452 }
@@ -140,4 +138,25 @@ impl Templates {
140138 pub fn count ( & self ) -> usize {
141139 self . map . values ( ) . flat_map ( |v| v. iter ( ) ) . filter ( |e| e. hits >= TPL_THRESH ) . count ( )
142140 }
141+ }
142+
143+ /// Fuses LoadAttr+Call into CallMethod+CallMethodArgs in-place. Replaces
144+ /// the two opcodes but keeps both operands and instruction count, so jump
145+ /// targets stay valid. Compiled once per chunk; cached above.
146+ fn fuse_method_calls ( chunk : & SSAChunk ) -> Vec < Instruction > {
147+ let src = & chunk. instructions ;
148+ let n = src. len ( ) ;
149+ let mut out = src. clone ( ) ;
150+
151+ let mut i = 0 ;
152+ while i + 1 < n {
153+ if src[ i] . opcode == OpCode :: LoadAttr && src[ i + 1 ] . opcode == OpCode :: Call {
154+ out[ i] . opcode = OpCode :: CallMethod ;
155+ out[ i + 1 ] . opcode = OpCode :: CallMethodArgs ;
156+ i += 2 ;
157+ continue ;
158+ }
159+ i += 1 ;
160+ }
161+ out
143162}
0 commit comments