JIT Tiered — Compilación en Múltiples Niveles
Archivo: src/jit_tiered.rs (347 líneas)
¿Por qué existe?
Implementa el sistema de compilación en niveles (tiered JIT) de Forja: en lugar de compilar todo siempre, las funciones comienzan interpretadas y solo se compilan a código nativo cuando demuestran ser calientes (hot). Esto amortiza el costo de compilación concentrándolo en las funciones y loops que más se ejecutan.
Tier 0: ForjaFast VM (interpreter)
↓ hotness counter
Tier 1: JIT template-based (compilación rápida, ~1ms)
↓ hotness counter
Tier 2: JIT optimizado (SSA, register alloc, SIMD) [futuro]
Tiers
| Tier | Descripción |
Tier::Interpret | Intérprete de la ForjaFast VM — tier inicial de toda función |
Tier::JitSimple | JIT template-based: compilación rápida (~1 ms) sin optimizaciones pesadas |
Tier::JitOptimized | JIT optimizado (SSA, register allocation, SIMD) — planeado como futuro |
Tier::next() modela la cadena: Interpret → JitSimple → JitOptimized → None.
Contadores de hotness
| Componente | Descripción |
HotnessThresholds | Umbrales configurables: interpret_to_jit_simple (default 1 000 ejecuciones), jit_simple_to_optimized (default 100 000), osr_loop_iterations (default 10 000 iteraciones) |
FunctionState | Estado por función: tier actual, call_count, compiled_code (Option<CompiledCode>) y deopt_frame (Option<DeoptFrame>) |
FunctionState::tick() | Incrementa el contador y retorna ShouldCompile: No, o Yes(Tier) cuando supera el umbral del tier actual |
ShouldCompile | Resultado del tick: No | Yes(Tier) |
CompiledCode | Código compilado: code_ptr a memoria ejecutable, code_size y tier. unsafe impl Send/Sync (inmutable una vez creado) |
OSR y Deoptimización
| Mecanismo | Descripción |
| OSR (On-Stack Replacement) | on_loop_iteration(func_id, loop_pc) cuenta iteraciones por loop (clave "func_id:loop_pc") y, al superar osr_loop_iterations, permite compilar el loop a Tier 1 mientras está ejecutándose |
DeoptFrame | Snapshot del estado del intérprete para hacer bailout: pc, base_ptr, locals, stack y from_tier |
deoptimize(name, frame) | Cuando una asunción de tipo falla, devuelve la función a Tier::Interpret, elimina el código compilado y guarda el frame |
TieredJit — Orquestador
| Método | Descripción |
new() / with_thresholds(t) | Constructores (default y con umbrales personalizados) |
register_function(name) | Registra una función con estado inicial Interpret |
on_function_call(name) -> ShouldCompile | Notifica una ejecución y consulta si debe compilarse |
on_loop_iteration(func_id, loop_pc) -> bool | Contador OSR por loop; true si supera el umbral |
mark_compiled(name, tier, code) | Marca la función como compilada y actualiza las estadísticas |
deoptimize(name, frame) | Bailout al intérprete |
tier_of(name) -> Tier | Tier actual de la función (default Interpret) |
stats() -> &JitStats | Estadísticas: compilaciones por tier, OSR completados, deopts y tiempo de compilación (µs) |
Conexión con jit.rs y jit_engine.rs
| Punto de integración | Descripción |
NativeJIT (jit.rs:439) | Contiene el campo tiered: Option<TieredJit> y expone wrappers: on_function_call(), register_function(), mark_compiled(), deoptimize() (que además limpia el cache compiled), on_loop_iteration(), tier_of(), set_tiered(enabled) y tier_stats() |
JitOrchestrator (jit_engine.rs:742) | Mantiene un TieredJit propio. En ejecutar(), si es_jiteable(bytecode) aplica la cadena de optimización (optimizar_indices → loop_unrolling → fusionar_opcodes → especializar_bytecode) y luego, para cada FunctionDef, registra la función (register_function) y hace tick por cada Call (on_function_call) para decidir compilación; si el JIT nativo falla, ejecuta con ForjaFast como fallback |
| PGO | JitOrchestrator integra crate::pgo: los candidatos tier-2 del perfil fuerzan un tick, y loops calientes ajustan el factor de unrolling (8x vs 4x) |
Estado actual: el motor de hotness, OSR, deoptimización y estadísticas está implementado y testeado (umbrales, cadena de tiers, custom thresholds). El código real de JitOptimized (SSA/SIMD) está marcado como trabajo futuro.