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)
468    BuiltinFunction::MakeContentText,
469    BuiltinFunction::MakeContentFragment,
470    BuiltinFunction::ApplyContentStyle,
471    // DateTime (4)
472    BuiltinFunction::DateTimeNow,
473    BuiltinFunction::DateTimeUtc,
474    BuiltinFunction::DateTimeParse,
475    BuiltinFunction::DateTimeFromEpoch,
476    // Concurrency (4)
477    BuiltinFunction::MutexCtor,
478    BuiltinFunction::AtomicCtor,
479    BuiltinFunction::LazyCtor,
480    BuiltinFunction::ChannelCtor,
481    // Math extras (7)
482    BuiltinFunction::Sign,
483    BuiltinFunction::Gcd,
484    BuiltinFunction::Lcm,
485    BuiltinFunction::Hypot,
486    BuiltinFunction::Clamp,
487    BuiltinFunction::IsNaN,
488    BuiltinFunction::IsFinite,
489];
490
491fn vm_only_opcode_reason(_opcode: OpCode) -> Option<&'static str> {
492    // All opcodes are now compiled by the JIT translator — either natively
493    // or via FFI trampoline calls to the VM runtime.  No interpreter
494    // fallback is required.
495    None
496}
497
498fn is_supported_builtin(_builtin: BuiltinFunction) -> bool {
499    // All builtins are now supported — either via dedicated JIT lowering
500    // or via the generic builtin FFI trampoline.
501    true
502}
503
504/// Run JIT compatibility preflight on a raw instruction slice.
505///
506/// This is the shared core used by both `preflight_blob_jit_compatibility`
507/// and `preflight_jit_compatibility`. It enables per-function JIT/interpreter
508/// decisions in the mixed function table.
509pub fn preflight_instructions(instructions: &[Instruction]) -> JitPreflightReport {
510    let mut report = JitPreflightReport::default();
511
512    for instr in instructions {
513        if vm_only_opcode_reason(instr.opcode).is_some() {
514            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
515        }
516
517        if instr.opcode == OpCode::BuiltinCall {
518            if let Some(Operand::Builtin(builtin)) = instr.operand {
519                if !is_supported_builtin(builtin) {
520                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
521                }
522            }
523        }
524    }
525
526    sort_opcodes(&mut report.vm_only_opcodes);
527    sort_builtins(&mut report.unsupported_builtins);
528    report
529}
530
531/// Run JIT compatibility preflight on a single function blob.
532///
533/// Same logic as the whole-program preflight but operating on a single
534/// `FunctionBlob`'s instruction stream. This enables per-function
535/// JIT/interpreter decisions in the mixed function table.
536pub fn preflight_blob_jit_compatibility(
537    blob: &shape_vm::bytecode::FunctionBlob,
538) -> JitPreflightReport {
539    preflight_instructions(&blob.instructions)
540}
541
542/// Run JIT preflight and collect all constructs that require VM fallback.
543pub fn preflight_jit_compatibility(program: &BytecodeProgram) -> JitPreflightReport {
544    let mut report = JitPreflightReport::default();
545
546    for instr in &program.instructions {
547        if vm_only_opcode_reason(instr.opcode).is_some() {
548            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
549        }
550
551        if instr.opcode == OpCode::BuiltinCall {
552            if let Some(Operand::Builtin(builtin)) = instr.operand {
553                if !is_supported_builtin(builtin) {
554                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
555                }
556            }
557        }
558    }
559
560    sort_opcodes(&mut report.vm_only_opcodes);
561    sort_builtins(&mut report.unsupported_builtins);
562    report
563}
564
565/// Build a program-specific JIT parity matrix.
566///
567/// This is intended for diagnostics/CI tooling that wants an automatic
568/// per-program support report.
569pub fn build_program_parity_matrix(program: &BytecodeProgram) -> Vec<JitParityEntry> {
570    let mut opcodes = Vec::new();
571    let mut builtins = Vec::new();
572
573    for instr in &program.instructions {
574        push_unique_opcode(&mut opcodes, instr.opcode);
575        if instr.opcode == OpCode::BuiltinCall {
576            if let Some(Operand::Builtin(builtin)) = instr.operand {
577                push_unique_builtin(&mut builtins, builtin);
578            }
579        }
580    }
581
582    sort_opcodes(&mut opcodes);
583    sort_builtins(&mut builtins);
584
585    let mut matrix = Vec::with_capacity(opcodes.len() + builtins.len());
586
587    for opcode in opcodes {
588        if let Some(reason) = vm_only_opcode_reason(opcode) {
589            matrix.push(JitParityEntry {
590                target: JitParityTarget::Opcode(opcode),
591                jit_supported: false,
592                reason,
593            });
594        } else {
595            matrix.push(JitParityEntry {
596                target: JitParityTarget::Opcode(opcode),
597                jit_supported: true,
598                reason: "Opcode is lowered by the JIT translator.",
599            });
600        }
601    }
602
603    for builtin in builtins {
604        if is_supported_builtin(builtin) {
605            matrix.push(JitParityEntry {
606                target: JitParityTarget::Builtin(builtin),
607                jit_supported: true,
608                reason: "Builtin is lowered by JIT builtin handlers.",
609            });
610        } else {
611            matrix.push(JitParityEntry {
612                target: JitParityTarget::Builtin(builtin),
613                jit_supported: false,
614                reason: "Builtin is not lowered by JIT and must run on VM.",
615            });
616        }
617    }
618
619    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
620    matrix
621}
622
623/// Build a full opcode parity matrix across the entire VM opcode surface.
624pub fn build_full_opcode_parity_matrix() -> Vec<JitParityEntry> {
625    let mut matrix = Vec::with_capacity(ALL_OPCODES.len());
626    for &opcode in ALL_OPCODES {
627        if let Some(reason) = vm_only_opcode_reason(opcode) {
628            matrix.push(JitParityEntry {
629                target: JitParityTarget::Opcode(opcode),
630                jit_supported: false,
631                reason,
632            });
633        } else {
634            matrix.push(JitParityEntry {
635                target: JitParityTarget::Opcode(opcode),
636                jit_supported: true,
637                reason: "Opcode is lowered by the JIT translator.",
638            });
639        }
640    }
641    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
642    matrix
643}
644
645/// Build a full builtin parity matrix across the entire BuiltinFunction surface.
646pub fn build_full_builtin_parity_matrix() -> Vec<JitParityEntry> {
647    let mut matrix = Vec::with_capacity(ALL_BUILTINS.len());
648    for &builtin in ALL_BUILTINS {
649        if is_supported_builtin(builtin) {
650            matrix.push(JitParityEntry {
651                target: JitParityTarget::Builtin(builtin),
652                jit_supported: true,
653                reason: "Builtin is lowered by JIT builtin handlers.",
654            });
655        } else {
656            matrix.push(JitParityEntry {
657                target: JitParityTarget::Builtin(builtin),
658                jit_supported: false,
659                reason: "Builtin is not lowered by JIT and must run on VM.",
660            });
661        }
662    }
663    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
664    matrix
665}
666
667/// Check if a bytecode program can be fully JIT-compiled
668#[inline(always)]
669pub fn can_jit_compile(program: &BytecodeProgram) -> bool {
670    preflight_jit_compatibility(program).can_jit()
671}
672
673/// Get a list of unsupported opcodes in a program (for debugging)
674#[inline(always)]
675pub fn get_unsupported_opcodes(program: &BytecodeProgram) -> Vec<OpCode> {
676    let report = preflight_jit_compatibility(program);
677    let mut unsupported = report.vm_only_opcodes;
678
679    if !report.unsupported_builtins.is_empty() && !unsupported.contains(&OpCode::BuiltinCall) {
680        unsupported.push(OpCode::BuiltinCall);
681    }
682
683    sort_opcodes(&mut unsupported);
684    unsupported
685}
686
687/// Get a list of opcodes that have placeholder (incomplete) implementations
688pub fn get_incomplete_opcodes(_program: &BytecodeProgram) -> Vec<OpCode> {
689    // All opcodes now have full implementations (native or FFI).
690    Vec::new()
691}
692
693#[cfg(test)]
694mod tests {
695    use super::*;
696    use shape_vm::bytecode::{Instruction, Operand};
697
698    #[test]
699    fn preflight_accepts_all_opcodes() {
700        // All opcodes are now supported — no VM-only gates remain.
701        let program = BytecodeProgram {
702            instructions: vec![Instruction::simple(OpCode::Await)],
703            ..Default::default()
704        };
705        let report = preflight_jit_compatibility(&program);
706        assert!(report.can_jit());
707    }
708
709    #[test]
710    fn preflight_accepts_all_builtins() {
711        // All builtins are now supported — dedicated or generic trampoline.
712        let program = BytecodeProgram {
713            instructions: vec![Instruction::new(
714                OpCode::BuiltinCall,
715                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
716            )],
717            ..Default::default()
718        };
719        let report = preflight_jit_compatibility(&program);
720        assert!(report.can_jit());
721    }
722
723    #[test]
724    fn parity_matrix_marks_all_builtins_supported() {
725        let program = BytecodeProgram {
726            instructions: vec![Instruction::new(
727                OpCode::BuiltinCall,
728                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
729            )],
730            ..Default::default()
731        };
732        let matrix = build_program_parity_matrix(&program);
733        assert!(matrix.iter().all(|row| row.jit_supported));
734    }
735
736    #[test]
737    fn preflight_instructions_compatible_slice() {
738        let instructions = vec![
739            Instruction::simple(OpCode::PushConst),
740            Instruction::simple(OpCode::Add),
741            Instruction::simple(OpCode::ReturnValue),
742        ];
743        let report = preflight_instructions(&instructions);
744        assert!(report.can_jit());
745    }
746
747    #[test]
748    fn preflight_instructions_all_opcodes_pass() {
749        // Even async opcodes now pass preflight.
750        let instructions = vec![
751            Instruction::simple(OpCode::PushConst),
752            Instruction::simple(OpCode::Await),
753            Instruction::simple(OpCode::ReturnValue),
754        ];
755        let report = preflight_instructions(&instructions);
756        assert!(report.can_jit());
757    }
758
759    #[test]
760    fn preflight_blob_passes_with_spawn_task() {
761        use shape_vm::bytecode::FunctionBlob;
762
763        let blob = FunctionBlob {
764            content_hash: shape_vm::bytecode::FunctionHash::ZERO,
765            name: "test_fn".to_string(),
766            arity: 0,
767            param_names: vec![],
768            locals_count: 0,
769            is_closure: false,
770            captures_count: 0,
771            is_async: false,
772            ref_params: vec![],
773            ref_mutates: vec![],
774            mutable_captures: vec![],
775            instructions: vec![
776                Instruction::simple(OpCode::PushConst),
777                Instruction::simple(OpCode::SpawnTask),
778                Instruction::simple(OpCode::ReturnValue),
779            ],
780            constants: vec![],
781            strings: vec![],
782            required_permissions: Default::default(),
783            dependencies: vec![],
784            callee_names: vec![],
785            type_schemas: vec![],
786            source_map: vec![],
787            foreign_dependencies: vec![],
788        };
789
790        let report = preflight_blob_jit_compatibility(&blob);
791        assert!(report.can_jit());
792    }
793
794    #[test]
795    fn all_opcodes_pass_preflight() {
796        // Exhaustive check: every opcode in ALL_OPCODES must pass preflight.
797        for &opcode in ALL_OPCODES {
798            assert!(
799                vm_only_opcode_reason(opcode).is_none(),
800                "Opcode {:?} should pass preflight",
801                opcode
802            );
803        }
804    }
805
806    #[test]
807    fn all_builtins_pass_preflight() {
808        // Exhaustive check: every builtin in ALL_BUILTINS must be supported.
809        for &builtin in ALL_BUILTINS {
810            assert!(
811                is_supported_builtin(builtin),
812                "Builtin {:?} should be supported",
813                builtin
814            );
815        }
816    }
817}