Skip to main content

shape_jit/compiler/
accessors.rs

1//! Accessor methods and utility functions
2//!
3//! This module contains simple accessor methods and helper functions
4//! for querying JIT compiler state.
5
6use super::setup::JITCompiler;
7use crate::context::JittedStrategyFn;
8use shape_vm::bytecode::{BuiltinFunction, BytecodeProgram, Instruction, OpCode, Operand};
9
10impl JITCompiler {
11    /// Get the function table for setting up JITContext
12    #[inline(always)]
13    pub fn get_function_table(&self) -> &[*const u8] {
14        &self.function_table
15    }
16
17    /// Get a compiled function pointer by function index
18    #[inline(always)]
19    pub fn get_function_by_index(&self, idx: usize) -> Option<JittedStrategyFn> {
20        self.function_table.get(idx).and_then(|&ptr| {
21            if ptr.is_null() {
22                None
23            } else {
24                Some(unsafe { std::mem::transmute(ptr) })
25            }
26        })
27    }
28}
29
30/// Program-level preflight report for JIT capability checks.
31#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub struct JitPreflightReport {
33    /// Opcodes that require VM execution for correct behavior.
34    pub vm_only_opcodes: Vec<OpCode>,
35    /// Builtins that are not lowered by the JIT translator.
36    pub unsupported_builtins: Vec<BuiltinFunction>,
37}
38
39impl JitPreflightReport {
40    /// True when the program can run safely in JIT without semantic downgrades.
41    pub fn can_jit(&self) -> bool {
42        self.vm_only_opcodes.is_empty() && self.unsupported_builtins.is_empty()
43    }
44
45    /// Human-readable blocker summary for diagnostics/logging.
46    pub fn blockers_summary(&self) -> String {
47        let mut parts = Vec::new();
48
49        if !self.vm_only_opcodes.is_empty() {
50            let opcodes = self
51                .vm_only_opcodes
52                .iter()
53                .map(|op| format!("{op:?}"))
54                .collect::<Vec<_>>()
55                .join(", ");
56            parts.push(format!("opcodes=[{opcodes}]"));
57        }
58
59        if !self.unsupported_builtins.is_empty() {
60            let builtins = self
61                .unsupported_builtins
62                .iter()
63                .map(|builtin| format!("{builtin:?}"))
64                .collect::<Vec<_>>()
65                .join(", ");
66            parts.push(format!("builtins=[{builtins}]"));
67        }
68
69        if parts.is_empty() {
70            "none".to_string()
71        } else {
72            parts.join("; ")
73        }
74    }
75}
76
77/// Program-level JIT parity entry (opcode/builtin support row).
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct JitParityEntry {
80    pub target: JitParityTarget,
81    pub jit_supported: bool,
82    pub reason: &'static str,
83}
84
85/// What the parity row describes.
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum JitParityTarget {
88    Opcode(OpCode),
89    Builtin(BuiltinFunction),
90}
91
92fn push_unique_opcode(out: &mut Vec<OpCode>, opcode: OpCode) {
93    if !out.contains(&opcode) {
94        out.push(opcode);
95    }
96}
97
98fn push_unique_builtin(out: &mut Vec<BuiltinFunction>, builtin: BuiltinFunction) {
99    if !out.contains(&builtin) {
100        out.push(builtin);
101    }
102}
103
104fn sort_opcodes(opcodes: &mut [OpCode]) {
105    opcodes.sort_by_key(|op| format!("{op:?}"));
106}
107
108fn sort_builtins(builtins: &mut [BuiltinFunction]) {
109    builtins.sort_by_key(|builtin| format!("{builtin:?}"));
110}
111
112const ALL_OPCODES: &[OpCode] = &[
113    OpCode::PushConst,
114    OpCode::PushNull,
115    OpCode::Pop,
116    OpCode::Dup,
117    OpCode::Swap,
118    OpCode::Add,
119    OpCode::Sub,
120    OpCode::Mul,
121    OpCode::Div,
122    OpCode::Mod,
123    OpCode::Neg,
124    OpCode::Pow,
125    OpCode::BitAnd,
126    OpCode::BitOr,
127    OpCode::BitShl,
128    OpCode::BitShr,
129    OpCode::BitNot,
130    OpCode::BitXor,
131    OpCode::Gt,
132    OpCode::Lt,
133    OpCode::Gte,
134    OpCode::Lte,
135    OpCode::Eq,
136    OpCode::Neq,
137    OpCode::GtInt,
138    OpCode::GtNumber,
139    OpCode::GtDecimal,
140    OpCode::LtInt,
141    OpCode::LtNumber,
142    OpCode::LtDecimal,
143    OpCode::GteInt,
144    OpCode::GteNumber,
145    OpCode::GteDecimal,
146    OpCode::LteInt,
147    OpCode::EqInt,
148    OpCode::EqNumber,
149    OpCode::NeqInt,
150    OpCode::NeqNumber,
151    OpCode::And,
152    OpCode::Or,
153    OpCode::Not,
154    OpCode::AddInt,
155    OpCode::AddNumber,
156    OpCode::AddDecimal,
157    OpCode::SubInt,
158    OpCode::SubNumber,
159    OpCode::SubDecimal,
160    OpCode::MulInt,
161    OpCode::MulNumber,
162    OpCode::MulDecimal,
163    OpCode::DivInt,
164    OpCode::DivNumber,
165    OpCode::DivDecimal,
166    OpCode::ModInt,
167    OpCode::Jump,
168    OpCode::JumpIfFalse,
169    OpCode::JumpIfFalseTrusted,
170    OpCode::JumpIfTrue,
171    OpCode::Call,
172    OpCode::Return,
173    OpCode::ReturnValue,
174    OpCode::CallValue,
175    OpCode::LoadLocal,
176    OpCode::LoadLocalTrusted,
177    OpCode::StoreLocal,
178    OpCode::LoadModuleBinding,
179    OpCode::StoreModuleBinding,
180    OpCode::LoadClosure,
181    OpCode::StoreClosure,
182    OpCode::MakeClosure,
183    OpCode::CloseUpvalue,
184    OpCode::MakeRef,
185    OpCode::DerefLoad,
186    OpCode::DerefStore,
187    OpCode::SetIndexRef,
188    OpCode::NewArray,
189    OpCode::NewTypedArray,
190    OpCode::NewObject,
191    OpCode::GetProp,
192    OpCode::SetProp,
193    OpCode::Length,
194    OpCode::ArrayPush,
195    OpCode::ArrayPop,
196    OpCode::MergeObject,
197    OpCode::SetLocalIndex,
198    OpCode::SetModuleBindingIndex,
199    OpCode::ArrayPushLocal,
200    OpCode::LoopStart,
201    OpCode::LoopEnd,
202    OpCode::Break,
203    OpCode::Continue,
204    OpCode::IterNext,
205    OpCode::IterDone,
206    OpCode::Pattern,
207    OpCode::CallMethod,
208    OpCode::PushTimeframe,
209    OpCode::PopTimeframe,
210    OpCode::RunSimulation,
211    OpCode::BuiltinCall,
212    OpCode::TypeCheck,
213    OpCode::Convert,
214    OpCode::ModNumber,
215    OpCode::ModDecimal,
216    OpCode::PowInt,
217    OpCode::PowNumber,
218    OpCode::PowDecimal,
219    OpCode::LteNumber,
220    OpCode::LteDecimal,
221    OpCode::SetupTry,
222    OpCode::PopHandler,
223    OpCode::Throw,
224    OpCode::TryUnwrap,
225    OpCode::UnwrapOption,
226    OpCode::ErrorContext,
227    OpCode::IsOk,
228    OpCode::IsErr,
229    OpCode::UnwrapOk,
230    OpCode::UnwrapErr,
231    OpCode::SliceAccess,
232    OpCode::NullCoalesce,
233    OpCode::MakeRange,
234    OpCode::GetDataField,
235    OpCode::GetDataRow,
236    OpCode::GetFieldTyped,
237    OpCode::SetFieldTyped,
238    OpCode::NewTypedObject,
239    OpCode::TypedMergeObject,
240    OpCode::WrapTypeAnnotation,
241    OpCode::Yield,
242    OpCode::Suspend,
243    OpCode::Resume,
244    OpCode::Poll,
245    OpCode::AwaitBar,
246    OpCode::AwaitTick,
247    OpCode::Await,
248    OpCode::SpawnTask,
249    OpCode::EmitAlert,
250    OpCode::EmitEvent,
251    OpCode::JoinInit,
252    OpCode::JoinAwait,
253    OpCode::CancelTask,
254    OpCode::AsyncScopeEnter,
255    OpCode::AsyncScopeExit,
256    OpCode::LoadColF64,
257    OpCode::LoadColI64,
258    OpCode::LoadColBool,
259    OpCode::LoadColStr,
260    OpCode::BindSchema,
261    OpCode::BoxTraitObject,
262    OpCode::DynMethodCall,
263    OpCode::Nop,
264    OpCode::Halt,
265    OpCode::Debug,
266    OpCode::IntToNumber,
267    OpCode::NumberToInt,
268    OpCode::CallForeign,
269    OpCode::AddTyped,
270    OpCode::SubTyped,
271    OpCode::MulTyped,
272    OpCode::DivTyped,
273    OpCode::ModTyped,
274    OpCode::CmpTyped,
275    OpCode::StoreLocalTyped,
276    OpCode::CastWidth,
277];
278
279const ALL_BUILTINS: &[BuiltinFunction] = &[
280    // Math (18)
281    BuiltinFunction::Abs,
282    BuiltinFunction::Sqrt,
283    BuiltinFunction::Ln,
284    BuiltinFunction::Pow,
285    BuiltinFunction::Exp,
286    BuiltinFunction::Log,
287    BuiltinFunction::Min,
288    BuiltinFunction::Max,
289    BuiltinFunction::Floor,
290    BuiltinFunction::Ceil,
291    BuiltinFunction::Round,
292    BuiltinFunction::Sin,
293    BuiltinFunction::Cos,
294    BuiltinFunction::Tan,
295    BuiltinFunction::Asin,
296    BuiltinFunction::Acos,
297    BuiltinFunction::Atan,
298    BuiltinFunction::StdDev,
299    // Array (8)
300    BuiltinFunction::Range,
301    BuiltinFunction::Slice,
302    BuiltinFunction::Push,
303    BuiltinFunction::Pop,
304    BuiltinFunction::First,
305    BuiltinFunction::Last,
306    BuiltinFunction::Zip,
307    BuiltinFunction::Filled,
308    // HOF (8)
309    BuiltinFunction::Map,
310    BuiltinFunction::Filter,
311    BuiltinFunction::Reduce,
312    BuiltinFunction::ForEach,
313    BuiltinFunction::Find,
314    BuiltinFunction::FindIndex,
315    BuiltinFunction::Some,
316    BuiltinFunction::Every,
317    // Utility (5)
318    BuiltinFunction::Print,
319    BuiltinFunction::Format,
320    BuiltinFunction::Len,
321    BuiltinFunction::Snapshot,
322    BuiltinFunction::Exit,
323    // Object (1)
324    BuiltinFunction::ObjectRest,
325    // Control (1)
326    BuiltinFunction::ControlFold,
327    // Type (7)
328    BuiltinFunction::TypeOf,
329    BuiltinFunction::IsNumber,
330    BuiltinFunction::IsString,
331    BuiltinFunction::IsBool,
332    BuiltinFunction::IsArray,
333    BuiltinFunction::IsObject,
334    BuiltinFunction::IsDataRow,
335    // Conversion (13)
336    BuiltinFunction::ToString,
337    BuiltinFunction::ToNumber,
338    BuiltinFunction::ToBool,
339    BuiltinFunction::IntoInt,
340    BuiltinFunction::IntoNumber,
341    BuiltinFunction::IntoDecimal,
342    BuiltinFunction::IntoBool,
343    BuiltinFunction::IntoString,
344    BuiltinFunction::TryIntoInt,
345    BuiltinFunction::TryIntoNumber,
346    BuiltinFunction::TryIntoDecimal,
347    BuiltinFunction::TryIntoBool,
348    BuiltinFunction::TryIntoString,
349    // Native ptr (8)
350    BuiltinFunction::NativePtrSize,
351    BuiltinFunction::NativePtrNewCell,
352    BuiltinFunction::NativePtrFreeCell,
353    BuiltinFunction::NativePtrReadPtr,
354    BuiltinFunction::NativePtrWritePtr,
355    BuiltinFunction::NativeTableFromArrowC,
356    BuiltinFunction::NativeTableFromArrowCTyped,
357    BuiltinFunction::NativeTableBindType,
358    // Format (2)
359    BuiltinFunction::FormatValueWithMeta,
360    BuiltinFunction::FormatValueWithSpec,
361    // Math intrinsics (6)
362    BuiltinFunction::IntrinsicSum,
363    BuiltinFunction::IntrinsicMean,
364    BuiltinFunction::IntrinsicMin,
365    BuiltinFunction::IntrinsicMax,
366    BuiltinFunction::IntrinsicStd,
367    BuiltinFunction::IntrinsicVariance,
368    // Random (5)
369    BuiltinFunction::IntrinsicRandom,
370    BuiltinFunction::IntrinsicRandomInt,
371    BuiltinFunction::IntrinsicRandomSeed,
372    BuiltinFunction::IntrinsicRandomNormal,
373    BuiltinFunction::IntrinsicRandomArray,
374    // Distribution (5)
375    BuiltinFunction::IntrinsicDistUniform,
376    BuiltinFunction::IntrinsicDistLognormal,
377    BuiltinFunction::IntrinsicDistExponential,
378    BuiltinFunction::IntrinsicDistPoisson,
379    BuiltinFunction::IntrinsicDistSampleN,
380    // Stochastic (4)
381    BuiltinFunction::IntrinsicBrownianMotion,
382    BuiltinFunction::IntrinsicGbm,
383    BuiltinFunction::IntrinsicOuProcess,
384    BuiltinFunction::IntrinsicRandomWalk,
385    // Rolling window (7)
386    BuiltinFunction::IntrinsicRollingSum,
387    BuiltinFunction::IntrinsicRollingMean,
388    BuiltinFunction::IntrinsicRollingStd,
389    BuiltinFunction::IntrinsicRollingMin,
390    BuiltinFunction::IntrinsicRollingMax,
391    BuiltinFunction::IntrinsicEma,
392    BuiltinFunction::IntrinsicLinearRecurrence,
393    // Series transform (7)
394    BuiltinFunction::IntrinsicShift,
395    BuiltinFunction::IntrinsicDiff,
396    BuiltinFunction::IntrinsicPctChange,
397    BuiltinFunction::IntrinsicFillna,
398    BuiltinFunction::IntrinsicCumsum,
399    BuiltinFunction::IntrinsicCumprod,
400    BuiltinFunction::IntrinsicClip,
401    // Statistics (4)
402    BuiltinFunction::IntrinsicCorrelation,
403    BuiltinFunction::IntrinsicCovariance,
404    BuiltinFunction::IntrinsicPercentile,
405    BuiltinFunction::IntrinsicMedian,
406    // Char codes (2)
407    BuiltinFunction::IntrinsicCharCode,
408    BuiltinFunction::IntrinsicFromCharCode,
409    // Series (1)
410    BuiltinFunction::IntrinsicSeries,
411    // Vector intrinsics (11)
412    BuiltinFunction::IntrinsicVecAbs,
413    BuiltinFunction::IntrinsicVecSqrt,
414    BuiltinFunction::IntrinsicVecLn,
415    BuiltinFunction::IntrinsicVecExp,
416    BuiltinFunction::IntrinsicVecAdd,
417    BuiltinFunction::IntrinsicVecSub,
418    BuiltinFunction::IntrinsicVecMul,
419    BuiltinFunction::IntrinsicVecDiv,
420    BuiltinFunction::IntrinsicVecMax,
421    BuiltinFunction::IntrinsicVecMin,
422    BuiltinFunction::IntrinsicVecSelect,
423    // Matrix (2)
424    BuiltinFunction::IntrinsicMatMulVec,
425    BuiltinFunction::IntrinsicMatMulMat,
426    // Eval helpers (6)
427    BuiltinFunction::EvalTimeRef,
428    BuiltinFunction::EvalDateTimeExpr,
429    BuiltinFunction::EvalDataDateTimeRef,
430    BuiltinFunction::EvalDataSet,
431    BuiltinFunction::EvalDataRelative,
432    BuiltinFunction::EvalDataRelativeRange,
433    // Option/Result ctors (3)
434    BuiltinFunction::SomeCtor,
435    BuiltinFunction::OkCtor,
436    BuiltinFunction::ErrCtor,
437    // Collection ctors (4)
438    BuiltinFunction::HashMapCtor,
439    BuiltinFunction::SetCtor,
440    BuiltinFunction::DequeCtor,
441    BuiltinFunction::PriorityQueueCtor,
442    // JSON (5)
443    BuiltinFunction::JsonObjectGet,
444    BuiltinFunction::JsonArrayAt,
445    BuiltinFunction::JsonObjectKeys,
446    BuiltinFunction::JsonArrayLen,
447    BuiltinFunction::JsonObjectLen,
448    // Window functions (14)
449    BuiltinFunction::WindowRowNumber,
450    BuiltinFunction::WindowRank,
451    BuiltinFunction::WindowDenseRank,
452    BuiltinFunction::WindowNtile,
453    BuiltinFunction::WindowLag,
454    BuiltinFunction::WindowLead,
455    BuiltinFunction::WindowFirstValue,
456    BuiltinFunction::WindowLastValue,
457    BuiltinFunction::WindowNthValue,
458    BuiltinFunction::WindowSum,
459    BuiltinFunction::WindowAvg,
460    BuiltinFunction::WindowMin,
461    BuiltinFunction::WindowMax,
462    BuiltinFunction::WindowCount,
463    // Join (1)
464    BuiltinFunction::JoinExecute,
465    // Reflection (1)
466    BuiltinFunction::Reflect,
467    // Content (3 + 6 constructors)
468    BuiltinFunction::MakeContentText,
469    BuiltinFunction::MakeContentFragment,
470    BuiltinFunction::ApplyContentStyle,
471    BuiltinFunction::MakeContentChartFromValue,
472    BuiltinFunction::ContentChart,
473    BuiltinFunction::ContentTextCtor,
474    BuiltinFunction::ContentTableCtor,
475    BuiltinFunction::ContentCodeCtor,
476    BuiltinFunction::ContentKvCtor,
477    BuiltinFunction::ContentFragmentCtor,
478    // DateTime (4)
479    BuiltinFunction::DateTimeNow,
480    BuiltinFunction::DateTimeUtc,
481    BuiltinFunction::DateTimeParse,
482    BuiltinFunction::DateTimeFromEpoch,
483    // Concurrency (4)
484    BuiltinFunction::MutexCtor,
485    BuiltinFunction::AtomicCtor,
486    BuiltinFunction::LazyCtor,
487    BuiltinFunction::ChannelCtor,
488    // Math extras (7)
489    BuiltinFunction::Sign,
490    BuiltinFunction::Gcd,
491    BuiltinFunction::Lcm,
492    BuiltinFunction::Hypot,
493    BuiltinFunction::Clamp,
494    BuiltinFunction::IsNaN,
495    BuiltinFunction::IsFinite,
496    // Table construction
497    BuiltinFunction::MakeTableFromRows,
498];
499
500fn vm_only_opcode_reason(_opcode: OpCode) -> Option<&'static str> {
501    // All opcodes are now compiled by the JIT translator — either natively
502    // or via FFI trampoline calls to the VM runtime.  No interpreter
503    // fallback is required.
504    None
505}
506
507fn is_supported_builtin(_builtin: BuiltinFunction) -> bool {
508    // All builtins are now supported — either via dedicated JIT lowering
509    // or via the generic builtin FFI trampoline.
510    true
511}
512
513/// Run JIT compatibility preflight on a raw instruction slice.
514///
515/// This is the shared core used by both `preflight_blob_jit_compatibility`
516/// and `preflight_jit_compatibility`. It enables per-function JIT/interpreter
517/// decisions in the mixed function table.
518pub fn preflight_instructions(instructions: &[Instruction]) -> JitPreflightReport {
519    let mut report = JitPreflightReport::default();
520
521    for instr in instructions {
522        if vm_only_opcode_reason(instr.opcode).is_some() {
523            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
524        }
525
526        if instr.opcode == OpCode::BuiltinCall {
527            if let Some(Operand::Builtin(builtin)) = instr.operand {
528                if !is_supported_builtin(builtin) {
529                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
530                }
531            }
532        }
533    }
534
535    sort_opcodes(&mut report.vm_only_opcodes);
536    sort_builtins(&mut report.unsupported_builtins);
537    report
538}
539
540/// Run JIT compatibility preflight on a single function blob.
541///
542/// Same logic as the whole-program preflight but operating on a single
543/// `FunctionBlob`'s instruction stream. This enables per-function
544/// JIT/interpreter decisions in the mixed function table.
545pub fn preflight_blob_jit_compatibility(
546    blob: &shape_vm::bytecode::FunctionBlob,
547) -> JitPreflightReport {
548    preflight_instructions(&blob.instructions)
549}
550
551/// Run JIT preflight and collect all constructs that require VM fallback.
552pub fn preflight_jit_compatibility(program: &BytecodeProgram) -> JitPreflightReport {
553    let mut report = JitPreflightReport::default();
554
555    for instr in &program.instructions {
556        if vm_only_opcode_reason(instr.opcode).is_some() {
557            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
558        }
559
560        if instr.opcode == OpCode::BuiltinCall {
561            if let Some(Operand::Builtin(builtin)) = instr.operand {
562                if !is_supported_builtin(builtin) {
563                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
564                }
565            }
566        }
567    }
568
569    sort_opcodes(&mut report.vm_only_opcodes);
570    sort_builtins(&mut report.unsupported_builtins);
571    report
572}
573
574/// Build a program-specific JIT parity matrix.
575///
576/// This is intended for diagnostics/CI tooling that wants an automatic
577/// per-program support report.
578pub fn build_program_parity_matrix(program: &BytecodeProgram) -> Vec<JitParityEntry> {
579    let mut opcodes = Vec::new();
580    let mut builtins = Vec::new();
581
582    for instr in &program.instructions {
583        push_unique_opcode(&mut opcodes, instr.opcode);
584        if instr.opcode == OpCode::BuiltinCall {
585            if let Some(Operand::Builtin(builtin)) = instr.operand {
586                push_unique_builtin(&mut builtins, builtin);
587            }
588        }
589    }
590
591    sort_opcodes(&mut opcodes);
592    sort_builtins(&mut builtins);
593
594    let mut matrix = Vec::with_capacity(opcodes.len() + builtins.len());
595
596    for opcode in opcodes {
597        if let Some(reason) = vm_only_opcode_reason(opcode) {
598            matrix.push(JitParityEntry {
599                target: JitParityTarget::Opcode(opcode),
600                jit_supported: false,
601                reason,
602            });
603        } else {
604            matrix.push(JitParityEntry {
605                target: JitParityTarget::Opcode(opcode),
606                jit_supported: true,
607                reason: "Opcode is lowered by the JIT translator.",
608            });
609        }
610    }
611
612    for builtin in builtins {
613        if is_supported_builtin(builtin) {
614            matrix.push(JitParityEntry {
615                target: JitParityTarget::Builtin(builtin),
616                jit_supported: true,
617                reason: "Builtin is lowered by JIT builtin handlers.",
618            });
619        } else {
620            matrix.push(JitParityEntry {
621                target: JitParityTarget::Builtin(builtin),
622                jit_supported: false,
623                reason: "Builtin is not lowered by JIT and must run on VM.",
624            });
625        }
626    }
627
628    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
629    matrix
630}
631
632/// Build a full opcode parity matrix across the entire VM opcode surface.
633pub fn build_full_opcode_parity_matrix() -> Vec<JitParityEntry> {
634    let mut matrix = Vec::with_capacity(ALL_OPCODES.len());
635    for &opcode in ALL_OPCODES {
636        if let Some(reason) = vm_only_opcode_reason(opcode) {
637            matrix.push(JitParityEntry {
638                target: JitParityTarget::Opcode(opcode),
639                jit_supported: false,
640                reason,
641            });
642        } else {
643            matrix.push(JitParityEntry {
644                target: JitParityTarget::Opcode(opcode),
645                jit_supported: true,
646                reason: "Opcode is lowered by the JIT translator.",
647            });
648        }
649    }
650    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
651    matrix
652}
653
654/// Build a full builtin parity matrix across the entire BuiltinFunction surface.
655pub fn build_full_builtin_parity_matrix() -> Vec<JitParityEntry> {
656    let mut matrix = Vec::with_capacity(ALL_BUILTINS.len());
657    for &builtin in ALL_BUILTINS {
658        if is_supported_builtin(builtin) {
659            matrix.push(JitParityEntry {
660                target: JitParityTarget::Builtin(builtin),
661                jit_supported: true,
662                reason: "Builtin is lowered by JIT builtin handlers.",
663            });
664        } else {
665            matrix.push(JitParityEntry {
666                target: JitParityTarget::Builtin(builtin),
667                jit_supported: false,
668                reason: "Builtin is not lowered by JIT and must run on VM.",
669            });
670        }
671    }
672    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
673    matrix
674}
675
676/// Check if a bytecode program can be fully JIT-compiled
677#[inline(always)]
678pub fn can_jit_compile(program: &BytecodeProgram) -> bool {
679    preflight_jit_compatibility(program).can_jit()
680}
681
682/// Get a list of unsupported opcodes in a program (for debugging)
683#[inline(always)]
684pub fn get_unsupported_opcodes(program: &BytecodeProgram) -> Vec<OpCode> {
685    let report = preflight_jit_compatibility(program);
686    let mut unsupported = report.vm_only_opcodes;
687
688    if !report.unsupported_builtins.is_empty() && !unsupported.contains(&OpCode::BuiltinCall) {
689        unsupported.push(OpCode::BuiltinCall);
690    }
691
692    sort_opcodes(&mut unsupported);
693    unsupported
694}
695
696/// Get a list of opcodes that have placeholder (incomplete) implementations
697pub fn get_incomplete_opcodes(_program: &BytecodeProgram) -> Vec<OpCode> {
698    // All opcodes now have full implementations (native or FFI).
699    Vec::new()
700}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705    use shape_vm::bytecode::{Instruction, Operand};
706
707    #[test]
708    fn preflight_accepts_all_opcodes() {
709        // All opcodes are now supported — no VM-only gates remain.
710        let program = BytecodeProgram {
711            instructions: vec![Instruction::simple(OpCode::Await)],
712            ..Default::default()
713        };
714        let report = preflight_jit_compatibility(&program);
715        assert!(report.can_jit());
716    }
717
718    #[test]
719    fn preflight_accepts_all_builtins() {
720        // All builtins are now supported — dedicated or generic trampoline.
721        let program = BytecodeProgram {
722            instructions: vec![Instruction::new(
723                OpCode::BuiltinCall,
724                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
725            )],
726            ..Default::default()
727        };
728        let report = preflight_jit_compatibility(&program);
729        assert!(report.can_jit());
730    }
731
732    #[test]
733    fn parity_matrix_marks_all_builtins_supported() {
734        let program = BytecodeProgram {
735            instructions: vec![Instruction::new(
736                OpCode::BuiltinCall,
737                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
738            )],
739            ..Default::default()
740        };
741        let matrix = build_program_parity_matrix(&program);
742        assert!(matrix.iter().all(|row| row.jit_supported));
743    }
744
745    #[test]
746    fn preflight_instructions_compatible_slice() {
747        let instructions = vec![
748            Instruction::simple(OpCode::PushConst),
749            Instruction::simple(OpCode::Add),
750            Instruction::simple(OpCode::ReturnValue),
751        ];
752        let report = preflight_instructions(&instructions);
753        assert!(report.can_jit());
754    }
755
756    #[test]
757    fn preflight_instructions_all_opcodes_pass() {
758        // Even async opcodes now pass preflight.
759        let instructions = vec![
760            Instruction::simple(OpCode::PushConst),
761            Instruction::simple(OpCode::Await),
762            Instruction::simple(OpCode::ReturnValue),
763        ];
764        let report = preflight_instructions(&instructions);
765        assert!(report.can_jit());
766    }
767
768    #[test]
769    fn preflight_blob_passes_with_spawn_task() {
770        use shape_vm::bytecode::FunctionBlob;
771
772        let blob = FunctionBlob {
773            content_hash: shape_vm::bytecode::FunctionHash::ZERO,
774            name: "test_fn".to_string(),
775            arity: 0,
776            param_names: vec![],
777            locals_count: 0,
778            is_closure: false,
779            captures_count: 0,
780            is_async: false,
781            ref_params: vec![],
782            ref_mutates: vec![],
783            mutable_captures: vec![],
784            instructions: vec![
785                Instruction::simple(OpCode::PushConst),
786                Instruction::simple(OpCode::SpawnTask),
787                Instruction::simple(OpCode::ReturnValue),
788            ],
789            constants: vec![],
790            strings: vec![],
791            required_permissions: Default::default(),
792            dependencies: vec![],
793            callee_names: vec![],
794            type_schemas: vec![],
795            source_map: vec![],
796            foreign_dependencies: vec![],
797        };
798
799        let report = preflight_blob_jit_compatibility(&blob);
800        assert!(report.can_jit());
801    }
802
803    #[test]
804    fn all_opcodes_pass_preflight() {
805        // Exhaustive check: every opcode in ALL_OPCODES must pass preflight.
806        for &opcode in ALL_OPCODES {
807            assert!(
808                vm_only_opcode_reason(opcode).is_none(),
809                "Opcode {:?} should pass preflight",
810                opcode
811            );
812        }
813    }
814
815    #[test]
816    fn all_builtins_pass_preflight() {
817        // Exhaustive check: every builtin in ALL_BUILTINS must be supported.
818        for &builtin in ALL_BUILTINS {
819            assert!(
820                is_supported_builtin(builtin),
821                "Builtin {:?} should be supported",
822                builtin
823            );
824        }
825    }
826}