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}