Skip to main content

shape_jit/translator/
types.rs

1//! Type definitions for the bytecode-to-IR translator
2
3use cranelift::codegen;
4use cranelift::codegen::ir::FuncRef;
5use cranelift::prelude::*;
6use std::collections::HashMap;
7
8use super::storage::TypedStack;
9use crate::context::SimulationKernelConfig;
10use crate::optimizer::FunctionOptimizationPlan;
11use shape_vm::bytecode::{BytecodeProgram, DeoptInfo};
12use shape_vm::feedback::FeedbackVector;
13use shape_vm::type_tracking::{SlotKind, StorageHint};
14
15/// Compilation mode for BytecodeToIR
16#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
17pub enum CompilationMode {
18    /// Standard mode: uses ctx_ptr for all access (JITContext-based)
19    #[default]
20    Standard,
21    /// Kernel mode: uses (cursor_index, series_ptrs, state_ptr) directly
22    /// Bypasses JITContext for maximum simulation throughput
23    Kernel,
24}
25
26/// FFI function references for heap operations
27#[allow(dead_code)]
28pub struct FFIFuncRefs {
29    pub(crate) new_array: FuncRef,
30    pub(crate) new_object: FuncRef,
31    pub(crate) get_prop: FuncRef,
32    pub(crate) set_prop: FuncRef,
33    pub(crate) length: FuncRef,
34    pub(crate) array_get: FuncRef,
35    pub(crate) call_function: FuncRef,
36    pub(crate) call_value: FuncRef,
37    pub(crate) call_foreign: FuncRef,
38    pub(crate) call_foreign_native: FuncRef,
39    pub(crate) call_foreign_dynamic: FuncRef,
40    pub(crate) call_foreign_native_0: FuncRef,
41    pub(crate) call_foreign_native_1: FuncRef,
42    pub(crate) call_foreign_native_2: FuncRef,
43    pub(crate) call_foreign_native_3: FuncRef,
44    pub(crate) call_foreign_native_4: FuncRef,
45    pub(crate) call_foreign_native_5: FuncRef,
46    pub(crate) call_foreign_native_6: FuncRef,
47    pub(crate) call_foreign_native_7: FuncRef,
48    pub(crate) call_foreign_native_8: FuncRef,
49    pub(crate) iter_next: FuncRef,
50    pub(crate) iter_done: FuncRef,
51    pub(crate) call_method: FuncRef,
52    pub(crate) type_of: FuncRef,
53    pub(crate) type_check: FuncRef,
54    // Result type operations (Ok/Err)
55    pub(crate) make_ok: FuncRef,
56    pub(crate) make_err: FuncRef,
57    pub(crate) is_ok: FuncRef,
58    pub(crate) is_err: FuncRef,
59    pub(crate) is_result: FuncRef,
60    pub(crate) unwrap_ok: FuncRef,
61    pub(crate) unwrap_err: FuncRef,
62    pub(crate) unwrap_or: FuncRef,
63    pub(crate) result_inner: FuncRef,
64    // Option type operations (Some/None)
65    pub(crate) make_some: FuncRef,
66    pub(crate) is_some: FuncRef,
67    pub(crate) is_none: FuncRef,
68    pub(crate) unwrap_some: FuncRef,
69    pub(crate) array_first: FuncRef,
70    pub(crate) array_last: FuncRef,
71    pub(crate) array_min: FuncRef,
72    pub(crate) array_max: FuncRef,
73    pub(crate) slice: FuncRef,
74    pub(crate) range: FuncRef,
75    pub(crate) make_range: FuncRef,
76    pub(crate) to_string: FuncRef,
77    pub(crate) to_number: FuncRef,
78    pub(crate) print: FuncRef,
79    pub(crate) sin: FuncRef,
80    pub(crate) cos: FuncRef,
81    pub(crate) tan: FuncRef,
82    pub(crate) asin: FuncRef,
83    pub(crate) acos: FuncRef,
84    pub(crate) atan: FuncRef,
85    pub(crate) exp: FuncRef,
86    pub(crate) ln: FuncRef,
87    pub(crate) log: FuncRef,
88    pub(crate) pow: FuncRef,
89    pub(crate) control_fold: FuncRef,
90    pub(crate) control_reduce: FuncRef,
91    pub(crate) control_map: FuncRef,
92    pub(crate) control_filter: FuncRef,
93    pub(crate) control_foreach: FuncRef,
94    pub(crate) control_find: FuncRef,
95    pub(crate) control_find_index: FuncRef,
96    pub(crate) control_some: FuncRef,
97    pub(crate) control_every: FuncRef,
98    pub(crate) array_push: FuncRef,
99    pub(crate) array_pop: FuncRef,
100    pub(crate) array_push_elem: FuncRef,
101    pub(crate) array_push_local: FuncRef,
102    pub(crate) array_reserve_local: FuncRef,
103    pub(crate) array_zip: FuncRef,
104    pub(crate) array_filled: FuncRef,
105    pub(crate) array_reverse: FuncRef,
106    pub(crate) array_push_element: FuncRef,
107    pub(crate) make_closure: FuncRef,
108    pub(crate) eval_datetime_expr: FuncRef,
109    pub(crate) eval_time_reference: FuncRef,
110    pub(crate) eval_data_datetime_ref: FuncRef,
111    pub(crate) eval_data_relative: FuncRef,
112    pub(crate) intrinsic_series: FuncRef,
113    pub(crate) series_method: FuncRef,
114    pub(crate) format_error: FuncRef,
115    pub(crate) object_rest: FuncRef,
116    pub(crate) format: FuncRef,
117    pub(crate) generic_add: FuncRef,
118    pub(crate) generic_sub: FuncRef,
119    pub(crate) generic_mul: FuncRef,
120    pub(crate) generic_div: FuncRef,
121    pub(crate) series_shift: FuncRef,
122    pub(crate) series_fillna: FuncRef,
123    pub(crate) series_rolling_mean: FuncRef,
124    pub(crate) series_rolling_sum: FuncRef,
125    pub(crate) series_rolling_std: FuncRef,
126    pub(crate) intrinsic_rolling_std: FuncRef,
127    pub(crate) series_cumsum: FuncRef,
128    pub(crate) series_gt: FuncRef,
129    pub(crate) series_lt: FuncRef,
130    pub(crate) series_gte: FuncRef,
131    pub(crate) series_lte: FuncRef,
132    pub(crate) intrinsic_sum: FuncRef,
133    pub(crate) intrinsic_mean: FuncRef,
134    pub(crate) intrinsic_min: FuncRef,
135    pub(crate) intrinsic_max: FuncRef,
136    pub(crate) intrinsic_std: FuncRef,
137    pub(crate) intrinsic_variance: FuncRef,
138    pub(crate) intrinsic_median: FuncRef,
139    pub(crate) intrinsic_percentile: FuncRef,
140    pub(crate) intrinsic_correlation: FuncRef,
141    pub(crate) intrinsic_covariance: FuncRef,
142    // Vector intrinsics (boxed interface - legacy)
143    pub(crate) intrinsic_vec_abs: FuncRef,
144    pub(crate) intrinsic_vec_sqrt: FuncRef,
145    pub(crate) intrinsic_vec_ln: FuncRef,
146    pub(crate) intrinsic_vec_exp: FuncRef,
147    pub(crate) intrinsic_vec_add: FuncRef,
148    pub(crate) intrinsic_vec_sub: FuncRef,
149    pub(crate) intrinsic_vec_mul: FuncRef,
150    pub(crate) intrinsic_vec_div: FuncRef,
151    pub(crate) intrinsic_vec_max: FuncRef,
152    pub(crate) intrinsic_vec_min: FuncRef,
153    pub(crate) intrinsic_matmul_vec: FuncRef,
154    pub(crate) intrinsic_matmul_mat: FuncRef,
155
156    // Raw pointer SIMD operations (zero-copy, high performance)
157    // Binary: simd_op(ptr_a, ptr_b, len) -> result_ptr
158    pub(crate) simd_add: FuncRef,
159    pub(crate) simd_sub: FuncRef,
160    pub(crate) simd_mul: FuncRef,
161    pub(crate) simd_div: FuncRef,
162    pub(crate) simd_max: FuncRef,
163    pub(crate) simd_min: FuncRef,
164    // Scalar broadcast: simd_op_scalar(ptr, scalar, len) -> result_ptr
165    pub(crate) simd_add_scalar: FuncRef,
166    pub(crate) simd_sub_scalar: FuncRef,
167    pub(crate) simd_mul_scalar: FuncRef,
168    pub(crate) simd_div_scalar: FuncRef,
169    // Comparison: simd_cmp(ptr_a, ptr_b, len) -> mask_ptr (1.0/0.0)
170    pub(crate) simd_gt: FuncRef,
171    pub(crate) simd_lt: FuncRef,
172    pub(crate) simd_gte: FuncRef,
173    pub(crate) simd_lte: FuncRef,
174    pub(crate) simd_eq: FuncRef,
175    pub(crate) simd_neq: FuncRef,
176    // Memory management
177    pub(crate) simd_free: FuncRef,
178
179    pub(crate) series_rolling_min: FuncRef,
180    pub(crate) series_rolling_max: FuncRef,
181    pub(crate) series_ema: FuncRef,
182    pub(crate) series_diff: FuncRef,
183    pub(crate) series_pct_change: FuncRef,
184    pub(crate) series_cumprod: FuncRef,
185    pub(crate) series_clip: FuncRef,
186    pub(crate) series_broadcast: FuncRef,
187    pub(crate) series_highest_index: FuncRef,
188    pub(crate) series_lowest_index: FuncRef,
189    pub(crate) time_current_time: FuncRef,
190    pub(crate) time_symbol: FuncRef,
191    pub(crate) time_last_row: FuncRef,
192    pub(crate) time_range: FuncRef,
193    pub(crate) get_all_rows: FuncRef,
194    pub(crate) align_series: FuncRef,
195    pub(crate) run_simulation: FuncRef,
196    // Generic DataFrame access (industry-agnostic)
197    pub(crate) get_field: FuncRef,
198    pub(crate) get_row_ref: FuncRef,
199    pub(crate) row_get_field: FuncRef,
200    pub(crate) get_row_timestamp: FuncRef,
201    // Type-specialized field access (JIT optimization)
202    pub(crate) get_field_typed: FuncRef,
203    pub(crate) set_field_typed: FuncRef,
204    // TypedObject allocation
205    pub(crate) typed_object_alloc: FuncRef,
206    // TypedObject merge (O(1) memcpy-based)
207    pub(crate) typed_merge_object: FuncRef,
208    // Typed column access (Arrow-backed LoadCol* opcodes)
209    pub(crate) load_col_f64: FuncRef,
210    pub(crate) load_col_i64: FuncRef,
211    pub(crate) load_col_bool: FuncRef,
212    pub(crate) load_col_str: FuncRef,
213    // GC safepoint poll (called at loop headers)
214    pub(crate) gc_safepoint: FuncRef,
215    // Reference operations
216    pub(crate) set_index_ref: FuncRef,
217    // Array info (data_ptr, length) extraction via stable Rust API
218    pub(crate) array_info: FuncRef,
219    // HOF inlining array operations
220    pub(crate) hof_array_alloc: FuncRef,
221    pub(crate) hof_array_push: FuncRef,
222    // Async task operations
223    pub(crate) spawn_task: FuncRef,
224    pub(crate) join_init: FuncRef,
225    pub(crate) join_await: FuncRef,
226    pub(crate) cancel_task: FuncRef,
227    pub(crate) async_scope_enter: FuncRef,
228    pub(crate) async_scope_exit: FuncRef,
229    // Shape guard operations (HashMap hidden class)
230    pub(crate) hashmap_shape_id: FuncRef,
231    pub(crate) hashmap_value_at: FuncRef,
232    // Generic builtin trampoline (handles any builtin not lowered by dedicated JIT paths)
233    pub(crate) generic_builtin: FuncRef,
234}
235
236/// Information about a function eligible for inlining at call sites.
237///
238/// Straight-line functions (no branches, no loops) with < 80 instructions
239/// are considered inline candidates. Non-leaf functions (with calls) are
240/// allowed — nested calls are handled recursively up to depth 4.
241#[derive(Debug, Clone)]
242pub(crate) struct InlineCandidate {
243    /// Entry point in program.instructions
244    pub entry_point: usize,
245    /// Number of instructions in the function body
246    pub instruction_count: usize,
247    /// Number of parameters
248    pub arity: u16,
249    /// Number of local variables
250    pub locals_count: u16,
251}
252
253/// Describes a per-guard spill block to be emitted at function epilogue.
254///
255/// When a speculative guard fails, the JIT needs to store all live locals
256/// and operand stack values to ctx_buf so the VM can reconstruct the
257/// interpreter frame at the exact guard failure point. Each `DeferredSpill`
258/// describes one such spill block.
259#[derive(Clone)]
260pub(crate) struct DeferredSpill {
261    /// The Cranelift block for this spill point.
262    pub block: Block,
263    /// Deopt ID to pass to the shared deopt block.
264    pub deopt_id: u32,
265    /// Live locals at the guard point: (bytecode_idx, Cranelift Variable).
266    pub live_locals: Vec<(u16, Variable)>,
267    /// SlotKind for each live local (parallel to live_locals).
268    pub local_kinds: Vec<SlotKind>,
269    /// Number of operand stack entries already on the JIT stack (via stack_vars).
270    pub on_stack_count: usize,
271    /// Number of extra values passed as block params (pre-popped operands).
272    pub extra_param_count: usize,
273    /// Locals that hold raw f64 values (from float-unboxed loops).
274    /// These need `bitcast(I64, f64_val)` before storing to ctx_buf.
275    pub f64_locals: std::collections::HashSet<u16>,
276    /// Locals that hold raw i64 values (from integer-unboxed loops).
277    /// These are stored directly to ctx_buf (raw i64 fits in u64).
278    pub int_locals: std::collections::HashSet<u16>,
279    /// Inline frames for multi-frame deopt (innermost-first).
280    /// Each entry contains the caller frame's live locals to spill.
281    pub inline_frames: Vec<DeferredInlineFrame>,
282}
283
284/// A caller frame's spill data for multi-frame inline deopt.
285#[derive(Clone)]
286pub(crate) struct DeferredInlineFrame {
287    /// Live locals for this caller frame: (ctx_buf_position, Cranelift Variable).
288    pub live_locals: Vec<(u16, Variable)>,
289    /// SlotKind for each live local (parallel to live_locals).
290    pub local_kinds: Vec<SlotKind>,
291    /// Locals that hold raw f64 values.
292    pub f64_locals: std::collections::HashSet<u16>,
293    /// Locals that hold raw i64 values.
294    pub int_locals: std::collections::HashSet<u16>,
295}
296
297/// Context for an inline frame, pushed onto a stack during inlining.
298///
299/// Captures the caller's state at the point of the inline call so that
300/// multi-frame deopt can reconstruct the full call stack.
301#[derive(Clone)]
302pub(crate) struct InlineFrameContext {
303    /// Function ID of the caller (from bytecode).
304    pub function_id: u16,
305    /// Function ID of the callee being inlined.
306    /// Used by deeper inline levels to identify their caller.
307    pub callee_fn_id: u16,
308    /// Bytecode IP of the CallValue/Call in the caller.
309    pub call_site_ip: usize,
310    /// Caller's live locals at call site: (bytecode_idx, Cranelift Variable).
311    pub locals_snapshot: Vec<(u16, Variable)>,
312    /// SlotKind for each local in the snapshot.
313    pub local_kinds: Vec<SlotKind>,
314    /// Caller's operand stack depth at call site.
315    pub stack_depth: usize,
316    /// Caller's f64-unboxed locals at call site.
317    pub f64_locals: std::collections::HashSet<u16>,
318    /// Caller's int-unboxed locals at call site.
319    pub int_locals: std::collections::HashSet<u16>,
320}
321
322/// Loop context for Break/Continue handling
323pub(super) struct LoopContext {
324    pub(super) start_block: Block,
325    pub(super) end_block: Block,
326}
327
328/// Tracks which locals were newly unboxed at a specific loop nesting level.
329/// Used for scope-stacked unboxing: each nested loop pushes a scope with its
330/// delta (newly unboxed locals), and pops it on exit to rebox only those locals.
331pub(crate) struct UnboxedScope {
332    /// Locals newly unboxed as raw i64 at this loop level (delta from outer scope).
333    pub int_locals: std::collections::HashSet<u16>,
334    /// Locals newly unboxed as raw f64 at this loop level (delta from outer scope).
335    pub f64_locals: std::collections::HashSet<u16>,
336    /// Module bindings newly unboxed as raw i64 at this loop level.
337    pub int_module_bindings: std::collections::HashSet<u16>,
338    /// The loop_stack.len() when this scope was opened.
339    pub depth: usize,
340}
341
342/// Information for 4x loop unrolling at the back-edge.
343///
344/// Set at LoopStart for eligible loops, consumed at the back-edge Jump.
345/// The back-edge emits 3 additional body copies with intermediate bounds
346/// checks, turning each header iteration into 4 body executions.
347pub(crate) struct UnrollInfo {
348    /// First body instruction to re-compile (after JumpIfFalse)
349    pub body_start: usize,
350    /// Last body instruction (exclusive, the Jump back-edge itself)
351    pub body_end: usize,
352    /// Induction variable local slot
353    pub iv_slot: u16,
354    /// Loop bound local slot
355    pub bound_slot: u16,
356    /// Comparison condition (e.g., SignedLessThan for `i < n`)
357    pub bound_cmp: IntCC,
358    /// Unroll factor (2, 4, ...). A value of 1 means no unrolling.
359    pub factor: u8,
360}
361
362/// Bytecode to IR compiler helper
363pub struct BytecodeToIR<'a, 'b> {
364    pub(crate) builder: &'a mut FunctionBuilder<'b>,
365    pub(crate) program: &'a BytecodeProgram,
366    pub(crate) ctx_ptr: Value,
367    pub(crate) stack_depth: usize,
368    pub(crate) stack_vars: HashMap<usize, Variable>,
369    pub(crate) locals: HashMap<u16, Variable>,
370    pub(crate) next_var: usize,
371    pub(crate) blocks: HashMap<usize, Block>,
372    pub(crate) current_block_idx: usize,
373    pub(crate) ffi: FFIFuncRefs,
374    pub(super) loop_stack: Vec<LoopContext>,
375    pub(crate) loop_ends: HashMap<usize, usize>,
376    pub(crate) exit_block: Option<Block>,
377    pub(crate) compile_time_sp: usize,
378    pub(crate) merge_blocks: std::collections::HashSet<usize>,
379    pub(crate) block_stack_depth: HashMap<usize, usize>,
380    pub(crate) pending_data_offset: Option<Value>,
381    pub(crate) exception_handlers: Vec<usize>,
382    pub(crate) current_instr_idx: usize,
383    /// User-defined function references for direct JIT calls (bypasses FFI overhead)
384    pub(crate) user_funcs: HashMap<u16, FuncRef>,
385    /// User-defined function arity by function id.
386    /// Used by direct-call lowering to validate call-site argc against the
387    /// callee's fixed signature.
388    pub(crate) user_func_arities: HashMap<u16, u16>,
389    /// Stack type tracking for typed compilation
390    /// Maps stack position to known StorageHint (if type is known at compile time)
391    /// Enables optimizations like NaN-sentinel operations for Option<f64>
392    pub(crate) stack_types: HashMap<usize, StorageHint>,
393    /// Local variable type tracking
394    /// Maps local variable index to StorageHint (tracked when storing)
395    pub(crate) local_types: HashMap<u16, StorageHint>,
396    /// Module-binding storage hints (index -> StorageHint).
397    pub(crate) module_binding_types: HashMap<u16, StorageHint>,
398    /// Typed stack for StorageType-aware compilation
399    /// Tracks both values and their CraneliftRepr for unboxed operations
400    pub(crate) typed_stack: TypedStack,
401
402    // ========================================================================
403    // Kernel Mode Fields (only used when mode == Kernel)
404    // ========================================================================
405    /// Compilation mode: Standard (JITContext) or Kernel (direct pointers)
406    pub(crate) mode: CompilationMode,
407    /// Kernel mode: cursor index (current row in simulation)
408    pub(crate) kernel_cursor_index: Option<Value>,
409    /// Kernel mode: pointer to series data pointers (Vec<*const f64>)
410    pub(crate) kernel_series_ptrs: Option<Value>,
411    /// Kernel mode: pointer to state buffer (TypedObject)
412    pub(crate) kernel_state_ptr: Option<Value>,
413    /// Kernel mode: configuration for column/field mappings
414    #[allow(dead_code)]
415    pub(crate) kernel_config: Option<SimulationKernelConfig>,
416    /// Loop analysis results for optimization (LICM, bounds hoisting, etc.)
417    pub(crate) loop_info: HashMap<usize, super::loop_analysis::LoopInfo>,
418    /// Phase-based optimization plan built from strict static analysis.
419    pub(crate) optimization_plan: FunctionOptimizationPlan,
420    /// Hoisted loop-invariant locals: maps local index to pre-loaded Cranelift Value.
421    /// Populated at LoopStart, consulted by LoadLocal to avoid redundant memory loads.
422    pub(crate) hoisted_locals: HashMap<u16, Value>,
423    /// Cache of f64 Cranelift Values for local variables.
424    /// Populated by StoreLocal when the value has repr F64, used by LoadLocal
425    /// to skip redundant i64->f64 bitcasts. Cleared at block boundaries.
426    pub(crate) local_f64_cache: HashMap<u16, Value>,
427
428    // ========================================================================
429    // Function Inlining Fields
430    // ========================================================================
431    /// Inline candidate functions (fn_id → candidate info)
432    pub(crate) inline_candidates: HashMap<u16, InlineCandidate>,
433    /// Base offset for local variable remapping during inlining.
434    /// When > 0, all local accesses are offset to avoid caller/callee collisions.
435    pub(crate) inline_local_base: u16,
436    /// Current inlining depth (to prevent deep nesting)
437    pub(crate) inline_depth: u8,
438
439    // ========================================================================
440    // Reference Tracking
441    // ========================================================================
442    /// Maps local variable index to the stack slot allocated by MakeRef.
443    /// After any function call, referenced locals must be reloaded from their
444    /// stack slots because the callee may have modified the value through the reference.
445    pub(crate) ref_stack_slots: HashMap<u16, codegen::ir::StackSlot>,
446
447    // ========================================================================
448    // Integer Unboxing (Sprint 5.1 — Native Integer Arithmetic)
449    // ========================================================================
450    /// Locals currently holding raw i64 values (within an integer-unboxed loop).
451    /// NOT cleared at block boundaries — persists for the entire loop scope.
452    /// Empty outside of unboxed loops.
453    pub(crate) unboxed_int_locals: std::collections::HashSet<u16>,
454
455    /// Module bindings currently promoted to Cranelift Variables holding raw i64.
456    /// Maps module binding index → Cranelift Variable for register-promoted access.
457    /// Empty outside of unboxed loops.
458    pub(crate) unboxed_int_module_bindings: std::collections::HashSet<u16>,
459
460    /// Cranelift Variables created for promoted module bindings.
461    /// During an unboxed loop, Load/StoreModuleBinding uses these Variables
462    /// instead of memory loads/stores.
463    pub(crate) promoted_module_bindings: HashMap<u16, Variable>,
464
465    /// Non-unboxed module bindings promoted to loop-carried registers.
466    /// These stay boxed but avoid repeated ctx.locals[] loads/stores in hot loops.
467    pub(crate) register_carried_module_bindings: std::collections::HashSet<u16>,
468
469    /// The loop_stack depth at which integer unboxing was activated.
470    /// Only the loop at this depth should rebox locals on exit.
471    /// Prevents nested inner loops from prematurely clearing the outer loop's unboxed state.
472    pub(crate) unboxed_loop_depth: usize,
473
474    /// Scope stack for nested loop unboxing. Each entry records the delta
475    /// (newly unboxed locals) at a specific loop nesting level. On loop exit,
476    /// only the delta locals are reboxed, preserving outer loop's unboxed state.
477    pub(crate) unboxed_scope_stack: Vec<UnboxedScope>,
478
479    /// The loop_stack depth at which loop-carried module-binding promotion was activated.
480    pub(crate) register_carried_loop_depth: usize,
481
482    /// Pending rebox at loop exit: set of locals that must be converted from
483    /// raw i64 back to NaN-boxed at the start of the loop's end_block.
484    /// Set by compile_loop_end, consumed by the main compile loop at the next block switch.
485    pub(crate) pending_rebox: Option<std::collections::HashSet<u16>>,
486
487    /// Pending rebox for module bindings at loop exit.
488    pub(crate) pending_rebox_module_bindings: Option<std::collections::HashSet<u16>>,
489
490    /// Pending flush of boxed, register-carried module bindings at loop exit.
491    pub(crate) pending_flush_module_bindings: Option<std::collections::HashSet<u16>>,
492
493    // ========================================================================
494    // Float Unboxing (Sprint 5.2 — Float Loop Variables)
495    // ========================================================================
496    /// Locals currently holding raw f64 values (within a float-unboxed loop).
497    /// NOT cleared at block boundaries — persists for the entire loop scope.
498    /// Empty outside of unboxed loops.
499    pub(crate) unboxed_f64_locals: std::collections::HashSet<u16>,
500
501    /// Cranelift Variables (typed as F64) created for float-unboxed locals.
502    /// Maps local index → f64-typed Cranelift Variable.
503    pub(crate) f64_local_vars: HashMap<u16, Variable>,
504
505    /// Pending rebox of f64 locals at loop exit: convert raw f64 → NaN-boxed i64.
506    pub(crate) pending_rebox_f64: Option<std::collections::HashSet<u16>>,
507
508    // ========================================================================
509    // Invariant IntToNumber Hoisting (LICM for int→f64 conversions)
510    // ========================================================================
511    /// Pre-converted f64 values for loop-invariant int locals that feed IntToNumber.
512    /// Maps local index → f64-typed Cranelift Variable holding the precomputed f64.
513    /// When IntToNumber encounters one of these locals, it uses the precomputed
514    /// value instead of re-emitting fcvt_from_sint every iteration.
515    pub(crate) precomputed_f64_for_invariant_int: HashMap<u16, Variable>,
516
517    /// Scope stack tracking which locals were precomputed at each loop nesting level.
518    /// Popped at compile_loop_end to remove entries from precomputed_f64_for_invariant_int.
519    pub(crate) precomputed_f64_scope_stack: Vec<Vec<u16>>,
520
521    // ========================================================================
522    // Skip Ranges (for main function compilation — skip function bodies)
523    // ========================================================================
524    /// Instruction index ranges to skip during compilation.
525    /// Used when compiling the main strategy to exclude function body instructions
526    /// that are compiled separately via `compile_function_with_user_funcs`.
527    pub(crate) skip_ranges: Vec<(usize, usize)>,
528
529    // ========================================================================
530    // Array LICM (Loop-Invariant Code Motion for Array Access)
531    // ========================================================================
532    /// Hoisted array data pointers for invariant locals used as arrays.
533    /// Maps local index → (data_ptr, length) extracted once at loop entry.
534    /// Eliminates redundant tag checks + pointer extraction per iteration.
535    pub(crate) hoisted_array_info: HashMap<u16, (Value, Value)>,
536
537    /// Hoisted reference-array data pointers for invariant ref locals used in SetIndexRef.
538    /// Maps ref_slot local index → (data_ptr, length) from deref + extraction at loop entry.
539    /// Eliminates redundant deref + tag check + pointer extraction per iteration.
540    pub(crate) hoisted_ref_array_info: HashMap<u16, (Value, Value)>,
541
542    /// Function parameters inferred as numeric by local bytecode analysis.
543    /// These are used as compile-time hints for LoadLocal typed-stack tracking.
544    pub(crate) numeric_param_hints: std::collections::HashSet<u16>,
545
546    /// Shared deopt block (when emitted by guarded helper paths).
547    /// Branching here marks the function result signal as negative.
548    pub(crate) deopt_block: Option<Block>,
549    /// Signal variable returned by the function:
550    /// 0 = success, negative = early-exit/deopt requested.
551    pub(crate) deopt_signal_var: Option<Variable>,
552
553    /// Collected deopt points emitted during compilation.
554    /// Each entry describes how to reconstruct interpreter state when a
555    /// speculative guard fails at a specific program point.
556    pub(crate) deopt_points: Vec<DeoptInfo>,
557
558    /// Function locals count (from bytecode function metadata).
559    /// Used by emit_deopt_point_with_spill to compute stack-value bc_idx offsets.
560    pub(crate) func_locals_count: u16,
561
562    /// Deferred spill blocks to be emitted at function epilogue.
563    /// Each entry describes a per-guard spill block that stores live
564    /// locals + operand stack values to ctx_buf before jumping to the
565    /// shared deopt block.
566    pub(crate) deferred_spills: Vec<DeferredSpill>,
567
568    /// Pending loop unroll info: set at LoopStart, consumed at the back-edge Jump.
569    /// When present, the back-edge emits 3 extra body copies for 4x unrolling.
570    pub(crate) pending_unroll: Option<UnrollInfo>,
571
572    /// ArrayPushLocal instruction sites proven safe for unchecked inline push.
573    pub(crate) trusted_array_push_local_sites: std::collections::HashSet<usize>,
574
575    /// Trusted ArrayPushLocal sites that can index directly with a loop IV.
576    /// Maps instruction index -> IV local slot.
577    pub(crate) trusted_array_push_local_iv_by_site: HashMap<usize, u16>,
578
579    // ========================================================================
580    // Shape Guard Tracking (HashMap hidden classes)
581    // ========================================================================
582    /// Shape IDs that were guarded during compilation.
583    /// Used to register shape dependencies with the DeoptTracker so that
584    /// shape transitions can invalidate stale JIT code.
585    pub(crate) shape_guards_emitted: Vec<shape_value::shape_graph::ShapeId>,
586
587    // ========================================================================
588    // Feedback-Guided Speculation (Tier 2 JIT)
589    // ========================================================================
590    /// Feedback vector snapshot from the interpreter's type profiling.
591    /// When present (Tier 2 compilation), enables speculative IR emission:
592    /// - Monomorphic call sites → direct call + callee guard
593    /// - Monomorphic property access → guarded field load
594    /// - Stable arithmetic → typed fast path with type guard
595    pub(crate) feedback: Option<FeedbackVector>,
596
597    // ========================================================================
598    // Multi-Frame Inline Deopt
599    // ========================================================================
600    /// The bytecode function ID of the function currently being compiled.
601    /// Set by `compile_optimizing_function` and used to tag inline frame
602    /// contexts with the correct caller function_id.
603    pub(crate) compiling_function_id: u16,
604
605    /// Stack of inline frame contexts for multi-frame deopt.
606    /// Pushed when entering an inline call, popped when exiting.
607    /// Used to reconstruct the full call stack on guard failure inside inlined code.
608    pub(crate) inline_frame_stack: Vec<InlineFrameContext>,
609}