@@ -12,9 +12,9 @@ use core::fmt;
1212// ── A04:2021 — runtime limits ──
1313
1414const MAX_STACK : usize = 1_024 ;
15- const MAX_CALLS : usize = 256 ;
15+ const MAX_CALLS : usize = 512 ;
1616const MAX_HEAP : usize = 100_000 ;
17- const MAX_OPS : usize = 10_000_000 ;
17+ const MAX_OPS : usize = 100_000_000 ;
1818const CACHE_THRESHOLD : u8 = 8 ;
1919const HOTSPOT_THRESHOLD : u32 = 1_000 ;
2020const TEMPLATE_THRESHOLD : u32 = 4 ;
@@ -75,9 +75,22 @@ impl Obj {
7575
7676 pub fn display ( & self ) -> String {
7777 match self {
78- Self :: Int ( i) => i. to_string ( ) ,
79- Self :: Float ( f) if * f == ( * f as i64 ) as f64 && f. is_finite ( ) => format ! ( "{:.1}" , f) ,
80- Self :: Float ( f) => f. to_string ( ) ,
78+ Self :: Int ( i) => {
79+ let mut buf = itoa:: Buffer :: new ( ) ;
80+ buf. format ( * i) . into ( )
81+ }
82+ Self :: Float ( f) if * f == ( * f as i64 ) as f64 && f. is_finite ( ) => {
83+ let mut s = itoa:: Buffer :: new ( ) ;
84+ let i = * f as i64 ;
85+ let mut out = String :: with_capacity ( 20 ) ;
86+ out. push_str ( s. format ( i) ) ;
87+ out. push_str ( ".0" ) ;
88+ out
89+ }
90+ Self :: Float ( f) => {
91+ let mut buf = ryu:: Buffer :: new ( ) ;
92+ buf. format ( * f) . into ( )
93+ }
8194 Self :: Str ( s) => s. clone ( ) ,
8295 Self :: Bool ( b) => if * b { "True" } else { "False" } . into ( ) ,
8396 Self :: None => "None" . into ( ) ,
@@ -175,26 +188,44 @@ impl InlineCache {
175188
176189// ── Template table — memoized function results ──
177190
178- struct TemplateEntry { result : Obj , hits : u32 }
191+ struct TemplateEntry { args : Vec < Obj > , result : Obj , hits : u32 }
179192
180- struct TemplateTable { entries : HashMap < ( usize , Vec < u8 > ) , TemplateEntry > }
193+ struct TemplateTable { entries : HashMap < usize , Vec < TemplateEntry > > }
181194
182195impl TemplateTable {
183196 fn new ( ) -> Self { Self { entries : HashMap :: new ( ) } }
184197
185198 fn lookup ( & self , fi : usize , args : & [ Obj ] ) -> Option < & Obj > {
186- let e = self . entries . get ( & ( fi, args. iter ( ) . map ( type_tag) . collect ( ) ) ) ?;
187- if e. hits >= TEMPLATE_THRESHOLD { Some ( & e. result ) } else { None }
199+ let entries = self . entries . get ( & fi) ?;
200+ entries. iter ( )
201+ . find ( |e| e. hits >= TEMPLATE_THRESHOLD && Self :: args_eq ( & e. args , args) )
202+ . map ( |e| & e. result )
188203 }
189204
190205 fn record ( & mut self , fi : usize , args : & [ Obj ] , result : & Obj ) {
191- let key = ( fi, args. iter ( ) . map ( type_tag) . collect ( ) ) ;
192- let e = self . entries . entry ( key) . or_insert ( TemplateEntry { result : result. clone ( ) , hits : 0 } ) ;
193- e. hits += 1 ;
194- e. result = result. clone ( ) ;
206+ let entries = self . entries . entry ( fi) . or_insert_with ( Vec :: new) ;
207+ if let Some ( e) = entries. iter_mut ( ) . find ( |e| Self :: args_eq ( & e. args , args) ) {
208+ e. hits += 1 ;
209+ e. result = result. clone ( ) ;
210+ } else if entries. len ( ) < 256 {
211+ entries. push ( TemplateEntry { args : args. to_vec ( ) , result : result. clone ( ) , hits : 1 } ) ;
212+ }
213+ }
214+
215+ fn args_eq ( a : & [ Obj ] , b : & [ Obj ] ) -> bool {
216+ a. len ( ) == b. len ( ) && a. iter ( ) . zip ( b) . all ( |( x, y) | match ( x, y) {
217+ ( Obj :: Int ( a) , Obj :: Int ( b) ) => a == b,
218+ ( Obj :: Float ( a) , Obj :: Float ( b) ) => a == b,
219+ ( Obj :: Str ( a) , Obj :: Str ( b) ) => a == b,
220+ ( Obj :: Bool ( a) , Obj :: Bool ( b) ) => a == b,
221+ ( Obj :: None , Obj :: None ) => true ,
222+ _ => false ,
223+ } )
195224 }
196225
197- fn cached_count ( & self ) -> usize { self . entries . values ( ) . filter ( |e| e. hits >= TEMPLATE_THRESHOLD ) . count ( ) }
226+ fn cached_count ( & self ) -> usize {
227+ self . entries . values ( ) . map ( |v| v. iter ( ) . filter ( |e| e. hits >= TEMPLATE_THRESHOLD ) . count ( ) ) . sum ( )
228+ }
198229}
199230
200231// ── Adaptive engine — hotspot detection + bytecode overlay ──
@@ -240,15 +271,26 @@ pub struct VM<'a> {
240271 budget : usize ,
241272 depth : usize ,
242273 pub output : Vec < String > ,
274+ versions : HashMap < String , String > , // "x_2" -> "x_1", precomputed prev version map
243275}
244276
245277impl < ' a > VM < ' a > {
246278 pub fn new ( chunk : & ' a SSAChunk ) -> Self {
247279 let n = chunk. instructions . len ( ) ;
280+ let mut versions = HashMap :: new ( ) ;
281+ for name in & chunk. names {
282+ if let Some ( pos) = name. rfind ( '_' ) {
283+ if let Ok ( ver) = name[ pos + 1 ..] . parse :: < u32 > ( ) {
284+ if ver > 0 {
285+ versions. insert ( name. clone ( ) , format ! ( "{}_{}" , & name[ ..pos] , ver - 1 ) ) ;
286+ }
287+ }
288+ }
289+ }
248290 Self {
249291 stack : Vec :: with_capacity ( 256 ) , chunk, pool : Pool :: new ( ) ,
250292 cache : InlineCache :: new ( n) , templates : TemplateTable :: new ( ) , adaptive : AdaptiveEngine :: new ( n) ,
251- budget : MAX_OPS , depth : 0 , output : Vec :: new ( ) ,
293+ budget : MAX_OPS , depth : 0 , output : Vec :: new ( ) , versions ,
252294 }
253295 }
254296
@@ -333,14 +375,12 @@ impl<'a> VM<'a> {
333375
334376 OpCode :: LoadConst => self . push ( self . to_obj ( & chunk. constants [ op as usize ] ) ) ?,
335377 OpCode :: LoadName => { let n = & chunk. names [ op as usize ] ; self . push ( names. get ( n) . cloned ( ) . ok_or_else ( || VmErr :: Name ( n. clone ( ) ) ) ?) ?; }
336- OpCode :: StoreName => {
378+ OpCode :: StoreName => {
337379 let v = self . pop ( ) ?;
338380 let full = & chunk. names [ op as usize ] ;
339381 names. insert ( full. clone ( ) , v. clone ( ) ) ;
340- if let Some ( pos) = full. rfind ( '_' ) {
341- if let Ok ( ver) = full[ pos + 1 ..] . parse :: < u32 > ( ) {
342- if ver > 0 { names. insert ( format ! ( "{}_{}" , & full[ ..pos] , ver - 1 ) , v) ; }
343- }
382+ if let Some ( prev) = self . versions . get ( full) {
383+ names. insert ( prev. clone ( ) , v) ;
344384 }
345385 }
346386 OpCode :: LoadTrue => self . push ( Obj :: Bool ( true ) ) ?,
@@ -433,24 +473,43 @@ impl<'a> VM<'a> {
433473 OpCode :: MakeFunction | OpCode :: MakeCoroutine => self . push ( Obj :: Func ( op as usize ) ) ?,
434474 OpCode :: Call => {
435475 let argc = op as usize ;
436- let args = self . pop_n ( argc) ?;
437- let func = self . pop ( ) ?;
438- match func {
476+ if self . depth >= MAX_CALLS { return Err ( VmErr :: CallDepth ) ; }
477+
478+ // Extraer argumentos en orden de llamada (sin reverse)
479+ let mut args = Vec :: with_capacity ( argc) ;
480+ for _ in 0 ..argc {
481+ args. push ( self . pop ( ) ?) ;
482+ }
483+ args. reverse ( ) ;
484+ let callable = self . pop ( ) ?;
485+
486+ match callable {
439487 Obj :: Func ( fi) => {
440- if self . depth >= MAX_CALLS { return Err ( VmErr :: CallDepth ) ; }
441- if let Some ( cached) = self . templates . lookup ( fi, & args) { self . push ( cached. clone ( ) ) ?; continue ; }
488+ if let Some ( cached) = self . templates . lookup ( fi, & args) {
489+ self . push ( cached. clone ( ) ) ?;
490+ continue ;
491+ }
442492 self . depth += 1 ;
443493 let ( params, body, _) = & self . chunk . functions [ fi] ;
444- let mut fn_ns = names. clone ( ) ;
494+ // Scope chain: only bind params, pass outer scope by ref
495+ let mut fn_ns: HashMap < String , Obj > = HashMap :: with_capacity ( params. len ( ) + 4 ) ;
445496 for ( i, p) in params. iter ( ) . enumerate ( ) {
446- if i < args. len ( ) { fn_ns. insert ( format ! ( "{}_0" , p. trim_start_matches( '*' ) ) , args[ i] . clone ( ) ) ; }
497+ if i < args. len ( ) {
498+ fn_ns. insert ( format ! ( "{}_0" , p. trim_start_matches( '*' ) ) , args[ i] . clone ( ) ) ;
499+ }
500+ }
501+ // Copy only function definitions from outer scope (for recursion)
502+ for ( k, v) in names. iter ( ) {
503+ if matches ! ( v, Obj :: Func ( _) ) {
504+ fn_ns. insert ( k. clone ( ) , v. clone ( ) ) ;
505+ }
447506 }
448507 let result = self . exec ( body, & mut fn_ns) ?;
449508 self . depth -= 1 ;
450509 self . templates . record ( fi, & args, & result) ;
451510 self . push ( result) ?;
452511 }
453- _ => return Err ( VmErr :: Type ( format ! ( "'{}' not callable" , func . ty ( ) ) ) ) ,
512+ _ => return Err ( VmErr :: Type ( "call non‑function" . into ( ) ) ) ,
454513 }
455514 }
456515
0 commit comments