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}