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