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::CallMethod,
207    OpCode::PushTimeframe,
208    OpCode::PopTimeframe,
209    OpCode::BuiltinCall,
210    OpCode::TypeCheck,
211    OpCode::Convert,
212    OpCode::ModNumber,
213    OpCode::ModDecimal,
214    OpCode::PowInt,
215    OpCode::PowNumber,
216    OpCode::PowDecimal,
217    OpCode::LteNumber,
218    OpCode::LteDecimal,
219    OpCode::SetupTry,
220    OpCode::PopHandler,
221    OpCode::Throw,
222    OpCode::TryUnwrap,
223    OpCode::UnwrapOption,
224    OpCode::ErrorContext,
225    OpCode::IsOk,
226    OpCode::IsErr,
227    OpCode::UnwrapOk,
228    OpCode::UnwrapErr,
229    OpCode::SliceAccess,
230    OpCode::NullCoalesce,
231    OpCode::MakeRange,
232    OpCode::GetDataField,
233    OpCode::GetDataRow,
234    OpCode::GetFieldTyped,
235    OpCode::SetFieldTyped,
236    OpCode::NewTypedObject,
237    OpCode::TypedMergeObject,
238    OpCode::WrapTypeAnnotation,
239    OpCode::Yield,
240    OpCode::Suspend,
241    OpCode::Resume,
242    OpCode::Poll,
243    OpCode::AwaitBar,
244    OpCode::AwaitTick,
245    OpCode::Await,
246    OpCode::SpawnTask,
247    OpCode::EmitAlert,
248    OpCode::EmitEvent,
249    OpCode::JoinInit,
250    OpCode::JoinAwait,
251    OpCode::CancelTask,
252    OpCode::AsyncScopeEnter,
253    OpCode::AsyncScopeExit,
254    OpCode::LoadColF64,
255    OpCode::LoadColI64,
256    OpCode::LoadColBool,
257    OpCode::LoadColStr,
258    OpCode::BindSchema,
259    OpCode::BoxTraitObject,
260    OpCode::DynMethodCall,
261    OpCode::Nop,
262    OpCode::Halt,
263    OpCode::Debug,
264    OpCode::IntToNumber,
265    OpCode::NumberToInt,
266    OpCode::CallForeign,
267    OpCode::AddTyped,
268    OpCode::SubTyped,
269    OpCode::MulTyped,
270    OpCode::DivTyped,
271    OpCode::ModTyped,
272    OpCode::CmpTyped,
273    OpCode::StoreLocalTyped,
274    OpCode::StoreModuleBindingTyped,
275    OpCode::CastWidth,
276];
277
278const ALL_BUILTINS: &[BuiltinFunction] = &[
279    // Math (18)
280    BuiltinFunction::Abs,
281    BuiltinFunction::Sqrt,
282    BuiltinFunction::Ln,
283    BuiltinFunction::Pow,
284    BuiltinFunction::Exp,
285    BuiltinFunction::Log,
286    BuiltinFunction::Min,
287    BuiltinFunction::Max,
288    BuiltinFunction::Floor,
289    BuiltinFunction::Ceil,
290    BuiltinFunction::Round,
291    BuiltinFunction::Sin,
292    BuiltinFunction::Cos,
293    BuiltinFunction::Tan,
294    BuiltinFunction::Asin,
295    BuiltinFunction::Acos,
296    BuiltinFunction::Atan,
297    BuiltinFunction::StdDev,
298    // Array (8)
299    BuiltinFunction::Range,
300    BuiltinFunction::Slice,
301    BuiltinFunction::Push,
302    BuiltinFunction::Pop,
303    BuiltinFunction::First,
304    BuiltinFunction::Last,
305    BuiltinFunction::Zip,
306    BuiltinFunction::Filled,
307    // HOF (8)
308    BuiltinFunction::Map,
309    BuiltinFunction::Filter,
310    BuiltinFunction::Reduce,
311    BuiltinFunction::ForEach,
312    BuiltinFunction::Find,
313    BuiltinFunction::FindIndex,
314    BuiltinFunction::Some,
315    BuiltinFunction::Every,
316    // Utility (5)
317    BuiltinFunction::Print,
318    BuiltinFunction::Format,
319    BuiltinFunction::Len,
320    BuiltinFunction::Snapshot,
321    BuiltinFunction::Exit,
322    // Object (1)
323    BuiltinFunction::ObjectRest,
324    // Control (1)
325    BuiltinFunction::ControlFold,
326    // Type (7)
327    BuiltinFunction::TypeOf,
328    BuiltinFunction::IsNumber,
329    BuiltinFunction::IsString,
330    BuiltinFunction::IsBool,
331    BuiltinFunction::IsArray,
332    BuiltinFunction::IsObject,
333    BuiltinFunction::IsDataRow,
334    // Conversion (3)
335    BuiltinFunction::ToString,
336    BuiltinFunction::ToNumber,
337    BuiltinFunction::ToBool,
338    // Native ptr (8)
339    BuiltinFunction::NativePtrSize,
340    BuiltinFunction::NativePtrNewCell,
341    BuiltinFunction::NativePtrFreeCell,
342    BuiltinFunction::NativePtrReadPtr,
343    BuiltinFunction::NativePtrWritePtr,
344    BuiltinFunction::NativeTableFromArrowC,
345    BuiltinFunction::NativeTableFromArrowCTyped,
346    BuiltinFunction::NativeTableBindType,
347    // Format (2)
348    BuiltinFunction::FormatValueWithMeta,
349    BuiltinFunction::FormatValueWithSpec,
350    // Math intrinsics (6)
351    BuiltinFunction::IntrinsicSum,
352    BuiltinFunction::IntrinsicMean,
353    BuiltinFunction::IntrinsicMin,
354    BuiltinFunction::IntrinsicMax,
355    BuiltinFunction::IntrinsicStd,
356    BuiltinFunction::IntrinsicVariance,
357    // Random (5)
358    BuiltinFunction::IntrinsicRandom,
359    BuiltinFunction::IntrinsicRandomInt,
360    BuiltinFunction::IntrinsicRandomSeed,
361    BuiltinFunction::IntrinsicRandomNormal,
362    BuiltinFunction::IntrinsicRandomArray,
363    // Distribution (5)
364    BuiltinFunction::IntrinsicDistUniform,
365    BuiltinFunction::IntrinsicDistLognormal,
366    BuiltinFunction::IntrinsicDistExponential,
367    BuiltinFunction::IntrinsicDistPoisson,
368    BuiltinFunction::IntrinsicDistSampleN,
369    // Stochastic (4)
370    BuiltinFunction::IntrinsicBrownianMotion,
371    BuiltinFunction::IntrinsicGbm,
372    BuiltinFunction::IntrinsicOuProcess,
373    BuiltinFunction::IntrinsicRandomWalk,
374    // Rolling window (7)
375    BuiltinFunction::IntrinsicRollingSum,
376    BuiltinFunction::IntrinsicRollingMean,
377    BuiltinFunction::IntrinsicRollingStd,
378    BuiltinFunction::IntrinsicRollingMin,
379    BuiltinFunction::IntrinsicRollingMax,
380    BuiltinFunction::IntrinsicEma,
381    BuiltinFunction::IntrinsicLinearRecurrence,
382    // Series transform (7)
383    BuiltinFunction::IntrinsicShift,
384    BuiltinFunction::IntrinsicDiff,
385    BuiltinFunction::IntrinsicPctChange,
386    BuiltinFunction::IntrinsicFillna,
387    BuiltinFunction::IntrinsicCumsum,
388    BuiltinFunction::IntrinsicCumprod,
389    BuiltinFunction::IntrinsicClip,
390    // Statistics (4)
391    BuiltinFunction::IntrinsicCorrelation,
392    BuiltinFunction::IntrinsicCovariance,
393    BuiltinFunction::IntrinsicPercentile,
394    BuiltinFunction::IntrinsicMedian,
395    // Trigonometric (4)
396    BuiltinFunction::IntrinsicAtan2,
397    BuiltinFunction::IntrinsicSinh,
398    BuiltinFunction::IntrinsicCosh,
399    BuiltinFunction::IntrinsicTanh,
400    // Char codes (2)
401    BuiltinFunction::IntrinsicCharCode,
402    BuiltinFunction::IntrinsicFromCharCode,
403    // Series (1)
404    BuiltinFunction::IntrinsicSeries,
405    // Vector intrinsics (11)
406    BuiltinFunction::IntrinsicVecAbs,
407    BuiltinFunction::IntrinsicVecSqrt,
408    BuiltinFunction::IntrinsicVecLn,
409    BuiltinFunction::IntrinsicVecExp,
410    BuiltinFunction::IntrinsicVecAdd,
411    BuiltinFunction::IntrinsicVecSub,
412    BuiltinFunction::IntrinsicVecMul,
413    BuiltinFunction::IntrinsicVecDiv,
414    BuiltinFunction::IntrinsicVecMax,
415    BuiltinFunction::IntrinsicVecMin,
416    BuiltinFunction::IntrinsicVecSelect,
417    // Matrix (2)
418    BuiltinFunction::IntrinsicMatMulVec,
419    BuiltinFunction::IntrinsicMatMulMat,
420    // Eval helpers (6)
421    BuiltinFunction::EvalTimeRef,
422    BuiltinFunction::EvalDateTimeExpr,
423    BuiltinFunction::EvalDataDateTimeRef,
424    BuiltinFunction::EvalDataSet,
425    BuiltinFunction::EvalDataRelative,
426    BuiltinFunction::EvalDataRelativeRange,
427    // Option/Result ctors (3)
428    BuiltinFunction::SomeCtor,
429    BuiltinFunction::OkCtor,
430    BuiltinFunction::ErrCtor,
431    // Collection ctors (4)
432    BuiltinFunction::HashMapCtor,
433    BuiltinFunction::SetCtor,
434    BuiltinFunction::DequeCtor,
435    BuiltinFunction::PriorityQueueCtor,
436    // JSON (5)
437    BuiltinFunction::JsonObjectGet,
438    BuiltinFunction::JsonArrayAt,
439    BuiltinFunction::JsonObjectKeys,
440    BuiltinFunction::JsonArrayLen,
441    BuiltinFunction::JsonObjectLen,
442    // Window functions (14)
443    BuiltinFunction::WindowRowNumber,
444    BuiltinFunction::WindowRank,
445    BuiltinFunction::WindowDenseRank,
446    BuiltinFunction::WindowNtile,
447    BuiltinFunction::WindowLag,
448    BuiltinFunction::WindowLead,
449    BuiltinFunction::WindowFirstValue,
450    BuiltinFunction::WindowLastValue,
451    BuiltinFunction::WindowNthValue,
452    BuiltinFunction::WindowSum,
453    BuiltinFunction::WindowAvg,
454    BuiltinFunction::WindowMin,
455    BuiltinFunction::WindowMax,
456    BuiltinFunction::WindowCount,
457    // Join (1)
458    BuiltinFunction::JoinExecute,
459    // Reflection (1)
460    BuiltinFunction::Reflect,
461    // Content (3 + 6 constructors)
462    BuiltinFunction::MakeContentText,
463    BuiltinFunction::MakeContentFragment,
464    BuiltinFunction::ApplyContentStyle,
465    BuiltinFunction::MakeContentChartFromValue,
466    BuiltinFunction::ContentChart,
467    BuiltinFunction::ContentTextCtor,
468    BuiltinFunction::ContentTableCtor,
469    BuiltinFunction::ContentCodeCtor,
470    BuiltinFunction::ContentKvCtor,
471    BuiltinFunction::ContentFragmentCtor,
472    // DateTime (6)
473    BuiltinFunction::DateTimeNow,
474    BuiltinFunction::DateTimeUtc,
475    BuiltinFunction::DateTimeParse,
476    BuiltinFunction::DateTimeFromEpoch,
477    BuiltinFunction::DateTimeFromParts,
478    BuiltinFunction::DateTimeFromUnixSecs,
479    // Concurrency (4)
480    BuiltinFunction::MutexCtor,
481    BuiltinFunction::AtomicCtor,
482    BuiltinFunction::LazyCtor,
483    BuiltinFunction::ChannelCtor,
484    // Math extras (7)
485    BuiltinFunction::Sign,
486    BuiltinFunction::Gcd,
487    BuiltinFunction::Lcm,
488    BuiltinFunction::Hypot,
489    BuiltinFunction::Clamp,
490    BuiltinFunction::IsNaN,
491    BuiltinFunction::IsFinite,
492    // Table construction
493    BuiltinFunction::MakeTableFromRows,
494];
495
496fn vm_only_opcode_reason(_opcode: OpCode) -> Option<&'static str> {
497    // All opcodes are now compiled by the JIT translator — either natively
498    // or via FFI trampoline calls to the VM runtime.  No interpreter
499    // fallback is required.
500    None
501}
502
503fn is_supported_builtin(_builtin: BuiltinFunction) -> bool {
504    // All builtins are now supported — either via dedicated JIT lowering
505    // or via the generic builtin FFI trampoline.
506    true
507}
508
509/// Run JIT compatibility preflight on a raw instruction slice.
510///
511/// This is the shared core used by both `preflight_blob_jit_compatibility`
512/// and `preflight_jit_compatibility`. It enables per-function JIT/interpreter
513/// decisions in the mixed function table.
514pub fn preflight_instructions(instructions: &[Instruction]) -> JitPreflightReport {
515    let mut report = JitPreflightReport::default();
516
517    for instr in instructions {
518        if vm_only_opcode_reason(instr.opcode).is_some() {
519            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
520        }
521
522        if instr.opcode == OpCode::BuiltinCall {
523            if let Some(Operand::Builtin(builtin)) = instr.operand {
524                if !is_supported_builtin(builtin) {
525                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
526                }
527            }
528        }
529    }
530
531    sort_opcodes(&mut report.vm_only_opcodes);
532    sort_builtins(&mut report.unsupported_builtins);
533    report
534}
535
536/// Run JIT compatibility preflight on a single function blob.
537///
538/// Same logic as the whole-program preflight but operating on a single
539/// `FunctionBlob`'s instruction stream. This enables per-function
540/// JIT/interpreter decisions in the mixed function table.
541pub fn preflight_blob_jit_compatibility(
542    blob: &shape_vm::bytecode::FunctionBlob,
543) -> JitPreflightReport {
544    preflight_instructions(&blob.instructions)
545}
546
547/// Run JIT preflight and collect all constructs that require VM fallback.
548pub fn preflight_jit_compatibility(program: &BytecodeProgram) -> JitPreflightReport {
549    let mut report = JitPreflightReport::default();
550
551    for instr in &program.instructions {
552        if vm_only_opcode_reason(instr.opcode).is_some() {
553            push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
554        }
555
556        if instr.opcode == OpCode::BuiltinCall {
557            if let Some(Operand::Builtin(builtin)) = instr.operand {
558                if !is_supported_builtin(builtin) {
559                    push_unique_builtin(&mut report.unsupported_builtins, builtin);
560                }
561            }
562        }
563    }
564
565    sort_opcodes(&mut report.vm_only_opcodes);
566    sort_builtins(&mut report.unsupported_builtins);
567    report
568}
569
570/// Build a program-specific JIT parity matrix.
571///
572/// This is intended for diagnostics/CI tooling that wants an automatic
573/// per-program support report.
574pub fn build_program_parity_matrix(program: &BytecodeProgram) -> Vec<JitParityEntry> {
575    let mut opcodes = Vec::new();
576    let mut builtins = Vec::new();
577
578    for instr in &program.instructions {
579        push_unique_opcode(&mut opcodes, instr.opcode);
580        if instr.opcode == OpCode::BuiltinCall {
581            if let Some(Operand::Builtin(builtin)) = instr.operand {
582                push_unique_builtin(&mut builtins, builtin);
583            }
584        }
585    }
586
587    sort_opcodes(&mut opcodes);
588    sort_builtins(&mut builtins);
589
590    let mut matrix = Vec::with_capacity(opcodes.len() + builtins.len());
591
592    for opcode in opcodes {
593        if let Some(reason) = vm_only_opcode_reason(opcode) {
594            matrix.push(JitParityEntry {
595                target: JitParityTarget::Opcode(opcode),
596                jit_supported: false,
597                reason,
598            });
599        } else {
600            matrix.push(JitParityEntry {
601                target: JitParityTarget::Opcode(opcode),
602                jit_supported: true,
603                reason: "Opcode is lowered by the JIT translator.",
604            });
605        }
606    }
607
608    for builtin in builtins {
609        if is_supported_builtin(builtin) {
610            matrix.push(JitParityEntry {
611                target: JitParityTarget::Builtin(builtin),
612                jit_supported: true,
613                reason: "Builtin is lowered by JIT builtin handlers.",
614            });
615        } else {
616            matrix.push(JitParityEntry {
617                target: JitParityTarget::Builtin(builtin),
618                jit_supported: false,
619                reason: "Builtin is not lowered by JIT and must run on VM.",
620            });
621        }
622    }
623
624    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
625    matrix
626}
627
628/// Build a full opcode parity matrix across the entire VM opcode surface.
629pub fn build_full_opcode_parity_matrix() -> Vec<JitParityEntry> {
630    let mut matrix = Vec::with_capacity(ALL_OPCODES.len());
631    for &opcode in ALL_OPCODES {
632        if let Some(reason) = vm_only_opcode_reason(opcode) {
633            matrix.push(JitParityEntry {
634                target: JitParityTarget::Opcode(opcode),
635                jit_supported: false,
636                reason,
637            });
638        } else {
639            matrix.push(JitParityEntry {
640                target: JitParityTarget::Opcode(opcode),
641                jit_supported: true,
642                reason: "Opcode is lowered by the JIT translator.",
643            });
644        }
645    }
646    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
647    matrix
648}
649
650/// Build a full builtin parity matrix across the entire BuiltinFunction surface.
651pub fn build_full_builtin_parity_matrix() -> Vec<JitParityEntry> {
652    let mut matrix = Vec::with_capacity(ALL_BUILTINS.len());
653    for &builtin in ALL_BUILTINS {
654        if is_supported_builtin(builtin) {
655            matrix.push(JitParityEntry {
656                target: JitParityTarget::Builtin(builtin),
657                jit_supported: true,
658                reason: "Builtin is lowered by JIT builtin handlers.",
659            });
660        } else {
661            matrix.push(JitParityEntry {
662                target: JitParityTarget::Builtin(builtin),
663                jit_supported: false,
664                reason: "Builtin is not lowered by JIT and must run on VM.",
665            });
666        }
667    }
668    matrix.sort_by_key(|entry| format!("{:?}", entry.target));
669    matrix
670}
671
672/// Check if a bytecode program can be fully JIT-compiled
673#[inline(always)]
674pub fn can_jit_compile(program: &BytecodeProgram) -> bool {
675    preflight_jit_compatibility(program).can_jit()
676}
677
678/// Get a list of unsupported opcodes in a program (for debugging)
679#[inline(always)]
680pub fn get_unsupported_opcodes(program: &BytecodeProgram) -> Vec<OpCode> {
681    let report = preflight_jit_compatibility(program);
682    let mut unsupported = report.vm_only_opcodes;
683
684    if !report.unsupported_builtins.is_empty() && !unsupported.contains(&OpCode::BuiltinCall) {
685        unsupported.push(OpCode::BuiltinCall);
686    }
687
688    sort_opcodes(&mut unsupported);
689    unsupported
690}
691
692/// Get a list of opcodes that have placeholder (incomplete) implementations
693pub fn get_incomplete_opcodes(_program: &BytecodeProgram) -> Vec<OpCode> {
694    // All opcodes now have full implementations (native or FFI).
695    Vec::new()
696}
697
698#[cfg(test)]
699mod tests {
700    use super::*;
701    use shape_vm::bytecode::{Instruction, Operand};
702
703    #[test]
704    fn preflight_accepts_all_opcodes() {
705        // All opcodes are now supported — no VM-only gates remain.
706        let program = BytecodeProgram {
707            instructions: vec![Instruction::simple(OpCode::Await)],
708            ..Default::default()
709        };
710        let report = preflight_jit_compatibility(&program);
711        assert!(report.can_jit());
712    }
713
714    #[test]
715    fn preflight_accepts_all_builtins() {
716        // All builtins are now supported — dedicated or generic trampoline.
717        let program = BytecodeProgram {
718            instructions: vec![Instruction::new(
719                OpCode::BuiltinCall,
720                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
721            )],
722            ..Default::default()
723        };
724        let report = preflight_jit_compatibility(&program);
725        assert!(report.can_jit());
726    }
727
728    #[test]
729    fn parity_matrix_marks_all_builtins_supported() {
730        let program = BytecodeProgram {
731            instructions: vec![Instruction::new(
732                OpCode::BuiltinCall,
733                Some(Operand::Builtin(BuiltinFunction::Snapshot)),
734            )],
735            ..Default::default()
736        };
737        let matrix = build_program_parity_matrix(&program);
738        assert!(matrix.iter().all(|row| row.jit_supported));
739    }
740
741    #[test]
742    fn preflight_instructions_compatible_slice() {
743        let instructions = vec![
744            Instruction::simple(OpCode::PushConst),
745            Instruction::simple(OpCode::Add),
746            Instruction::simple(OpCode::ReturnValue),
747        ];
748        let report = preflight_instructions(&instructions);
749        assert!(report.can_jit());
750    }
751
752    #[test]
753    fn preflight_instructions_all_opcodes_pass() {
754        // Even async opcodes now pass preflight.
755        let instructions = vec![
756            Instruction::simple(OpCode::PushConst),
757            Instruction::simple(OpCode::Await),
758            Instruction::simple(OpCode::ReturnValue),
759        ];
760        let report = preflight_instructions(&instructions);
761        assert!(report.can_jit());
762    }
763
764    #[test]
765    fn preflight_blob_passes_with_spawn_task() {
766        use shape_vm::bytecode::FunctionBlob;
767
768        let blob = FunctionBlob {
769            content_hash: shape_vm::bytecode::FunctionHash::ZERO,
770            name: "test_fn".to_string(),
771            arity: 0,
772            param_names: vec![],
773            locals_count: 0,
774            is_closure: false,
775            captures_count: 0,
776            is_async: false,
777            ref_params: vec![],
778            ref_mutates: vec![],
779            mutable_captures: vec![],
780            instructions: vec![
781                Instruction::simple(OpCode::PushConst),
782                Instruction::simple(OpCode::SpawnTask),
783                Instruction::simple(OpCode::ReturnValue),
784            ],
785            constants: vec![],
786            strings: vec![],
787            required_permissions: Default::default(),
788            dependencies: vec![],
789            callee_names: vec![],
790            type_schemas: vec![],
791            source_map: vec![],
792            foreign_dependencies: vec![],
793            frame_descriptor: None,
794        };
795
796        let report = preflight_blob_jit_compatibility(&blob);
797        assert!(report.can_jit());
798    }
799
800    #[test]
801    fn all_opcodes_pass_preflight() {
802        // Exhaustive check: every opcode in ALL_OPCODES must pass preflight.
803        for &opcode in ALL_OPCODES {
804            assert!(
805                vm_only_opcode_reason(opcode).is_none(),
806                "Opcode {:?} should pass preflight",
807                opcode
808            );
809        }
810    }
811
812    #[test]
813    fn all_builtins_pass_preflight() {
814        // Exhaustive check: every builtin in ALL_BUILTINS must be supported.
815        for &builtin in ALL_BUILTINS {
816            assert!(
817                is_supported_builtin(builtin),
818                "Builtin {:?} should be supported",
819                builtin
820            );
821        }
822    }
823}