1use super::setup::JITCompiler;
7use crate::context::JittedStrategyFn;
8use shape_vm::bytecode::{BuiltinFunction, BytecodeProgram, Instruction, OpCode, Operand};
9
10impl JITCompiler {
11 #[inline(always)]
13 pub fn get_function_table(&self) -> &[*const u8] {
14 &self.function_table
15 }
16
17 #[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#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub struct JitPreflightReport {
33 pub vm_only_opcodes: Vec<OpCode>,
35 pub unsupported_builtins: Vec<BuiltinFunction>,
37}
38
39impl JitPreflightReport {
40 pub fn can_jit(&self) -> bool {
42 self.vm_only_opcodes.is_empty() && self.unsupported_builtins.is_empty()
43 }
44
45 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#[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#[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::BitAnd,
119 OpCode::BitOr,
120 OpCode::BitShl,
121 OpCode::BitShr,
122 OpCode::BitNot,
123 OpCode::BitXor,
124 OpCode::GtInt,
125 OpCode::GtNumber,
126 OpCode::GtDecimal,
127 OpCode::LtInt,
128 OpCode::LtNumber,
129 OpCode::LtDecimal,
130 OpCode::GteInt,
131 OpCode::GteNumber,
132 OpCode::GteDecimal,
133 OpCode::LteInt,
134 OpCode::EqInt,
135 OpCode::EqNumber,
136 OpCode::NeqInt,
137 OpCode::NeqNumber,
138 OpCode::EqString,
139 OpCode::GtString,
140 OpCode::LtString,
141 OpCode::GteString,
142 OpCode::LteString,
143 OpCode::EqDecimal,
144 OpCode::IsNull,
145 OpCode::And,
146 OpCode::Or,
147 OpCode::Not,
148 OpCode::AddInt,
149 OpCode::AddNumber,
150 OpCode::AddDecimal,
151 OpCode::SubInt,
152 OpCode::SubNumber,
153 OpCode::SubDecimal,
154 OpCode::MulInt,
155 OpCode::MulNumber,
156 OpCode::MulDecimal,
157 OpCode::DivInt,
158 OpCode::DivNumber,
159 OpCode::DivDecimal,
160 OpCode::ModInt,
161 OpCode::NegInt,
162 OpCode::NegNumber,
163 OpCode::Jump,
164 OpCode::JumpIfFalse,
165 OpCode::JumpIfFalseTrusted,
166 OpCode::JumpIfTrue,
167 OpCode::Call,
168 OpCode::Return,
169 OpCode::ReturnValue,
170 OpCode::CallValue,
171 OpCode::CallClosure,
172 OpCode::CallFunctionIndirect,
173 OpCode::LoadLocal,
174 OpCode::LoadLocalTrusted,
175 OpCode::StoreLocal,
176 OpCode::LoadModuleBinding,
177 OpCode::StoreModuleBinding,
178 OpCode::LoadClosure,
179 OpCode::StoreClosure,
180 OpCode::MakeClosure,
181 OpCode::CloseUpvalue,
182 OpCode::MakeRef,
183 OpCode::MakeFieldRef,
189 OpCode::MakeIndexRef,
190 OpCode::DerefLoad,
191 OpCode::DerefStore,
192 OpCode::SetIndexRef,
193 OpCode::NewArray,
194 OpCode::NewTypedArray,
195 OpCode::NewObject,
196 OpCode::GetProp,
197 OpCode::SetProp,
198 OpCode::Length,
199 OpCode::ArrayPush,
200 OpCode::ArrayPop,
201 OpCode::MergeObject,
202 OpCode::SetLocalIndex,
203 OpCode::SetModuleBindingIndex,
204 OpCode::ArrayPushLocal,
205 OpCode::LoopStart,
206 OpCode::LoopEnd,
207 OpCode::Break,
208 OpCode::Continue,
209 OpCode::IterNext,
210 OpCode::IterDone,
211 OpCode::CallMethod,
212 OpCode::PushTimeframe,
213 OpCode::PopTimeframe,
214 OpCode::BuiltinCall,
215 OpCode::TypeCheck,
216 OpCode::Convert,
217 OpCode::ModNumber,
218 OpCode::ModDecimal,
219 OpCode::PowInt,
220 OpCode::PowNumber,
221 OpCode::PowDecimal,
222 OpCode::LteNumber,
223 OpCode::LteDecimal,
224 OpCode::SetupTry,
225 OpCode::PopHandler,
226 OpCode::Throw,
227 OpCode::TryUnwrap,
228 OpCode::UnwrapOption,
229 OpCode::ErrorContext,
230 OpCode::IsOk,
231 OpCode::IsErr,
232 OpCode::UnwrapOk,
233 OpCode::UnwrapErr,
234 OpCode::SliceAccess,
235 OpCode::NullCoalesce,
236 OpCode::MakeRange,
237 OpCode::GetDataField,
238 OpCode::GetDataRow,
239 OpCode::GetFieldTyped,
240 OpCode::SetFieldTyped,
241 OpCode::NewTypedObject,
242 OpCode::TypedMergeObject,
243 OpCode::WrapTypeAnnotation,
244 OpCode::Yield,
245 OpCode::Suspend,
246 OpCode::Resume,
247 OpCode::Poll,
248 OpCode::AwaitBar,
249 OpCode::AwaitTick,
250 OpCode::Await,
251 OpCode::SpawnTask,
252 OpCode::EmitAlert,
253 OpCode::EmitEvent,
254 OpCode::JoinInit,
255 OpCode::JoinAwait,
256 OpCode::CancelTask,
257 OpCode::AsyncScopeEnter,
258 OpCode::AsyncScopeExit,
259 OpCode::LoadColF64,
260 OpCode::LoadColI64,
261 OpCode::LoadColBool,
262 OpCode::LoadColStr,
263 OpCode::BindSchema,
264 OpCode::BoxTraitObject,
265 OpCode::DynMethodCall,
266 OpCode::Nop,
267 OpCode::Halt,
268 OpCode::IntToNumber,
269 OpCode::NumberToInt,
270 OpCode::CallForeign,
271 OpCode::AddTyped,
272 OpCode::SubTyped,
273 OpCode::MulTyped,
274 OpCode::DivTyped,
275 OpCode::ModTyped,
276 OpCode::CmpTyped,
277 OpCode::StoreLocalTyped,
278 OpCode::StoreModuleBindingTyped,
279 OpCode::CastWidth,
280 OpCode::NewTypedArrayF64,
282 OpCode::NewTypedArrayI64,
283 OpCode::NewTypedArrayI32,
284 OpCode::NewTypedArrayBool,
285 OpCode::TypedArrayGetF64,
286 OpCode::TypedArrayGetI64,
287 OpCode::TypedArrayGetI32,
288 OpCode::TypedArrayGetBool,
289 OpCode::TypedArraySetF64,
290 OpCode::TypedArraySetI64,
291 OpCode::TypedArraySetI32,
292 OpCode::TypedArraySetBool,
293 OpCode::TypedArrayPushF64,
294 OpCode::TypedArrayPushI64,
295 OpCode::TypedArrayPushI32,
296 OpCode::TypedArrayPushBool,
297 OpCode::TypedArrayLen,
298 OpCode::NewTypedArrayI8,
303 OpCode::TypedArrayGetI8,
304 OpCode::TypedArrayPushI8,
305 OpCode::TypedArraySetI8,
306 OpCode::NewTypedArrayU8,
307 OpCode::TypedArrayGetU8,
308 OpCode::TypedArrayPushU8,
309 OpCode::TypedArraySetU8,
310 OpCode::NewTypedArrayI16,
311 OpCode::TypedArrayGetI16,
312 OpCode::TypedArrayPushI16,
313 OpCode::TypedArraySetI16,
314 OpCode::NewTypedArrayU16,
315 OpCode::TypedArrayGetU16,
316 OpCode::TypedArrayPushU16,
317 OpCode::TypedArraySetU16,
318 OpCode::NewTypedArrayU32,
319 OpCode::TypedArrayGetU32,
320 OpCode::TypedArrayPushU32,
321 OpCode::TypedArraySetU32,
322 OpCode::NewTypedArrayF32,
332 OpCode::TypedArrayGetF32,
333 OpCode::TypedArrayPushF32,
334 OpCode::TypedArraySetF32,
335 OpCode::NewTypedArrayChar,
336 OpCode::TypedArrayGetChar,
337 OpCode::TypedArrayPushChar,
338 OpCode::TypedArraySetChar,
339];
340
341const ALL_BUILTINS: &[BuiltinFunction] = &[
342 BuiltinFunction::Abs,
344 BuiltinFunction::Sqrt,
345 BuiltinFunction::Ln,
346 BuiltinFunction::Pow,
347 BuiltinFunction::Exp,
348 BuiltinFunction::Log,
349 BuiltinFunction::Min,
350 BuiltinFunction::Max,
351 BuiltinFunction::Floor,
352 BuiltinFunction::Ceil,
353 BuiltinFunction::Round,
354 BuiltinFunction::Sin,
355 BuiltinFunction::Cos,
356 BuiltinFunction::Tan,
357 BuiltinFunction::Asin,
358 BuiltinFunction::Acos,
359 BuiltinFunction::Atan,
360 BuiltinFunction::StdDev,
361 BuiltinFunction::Range,
363 BuiltinFunction::Slice,
364 BuiltinFunction::Push,
365 BuiltinFunction::Pop,
366 BuiltinFunction::First,
367 BuiltinFunction::Last,
368 BuiltinFunction::Zip,
369 BuiltinFunction::Filled,
370 BuiltinFunction::Map,
372 BuiltinFunction::Filter,
373 BuiltinFunction::Reduce,
374 BuiltinFunction::ForEach,
375 BuiltinFunction::Find,
376 BuiltinFunction::FindIndex,
377 BuiltinFunction::Some,
378 BuiltinFunction::Every,
379 BuiltinFunction::Print,
381 BuiltinFunction::Format,
382 BuiltinFunction::Snapshot,
383 BuiltinFunction::Exit,
384 BuiltinFunction::ObjectRest,
386 BuiltinFunction::ControlFold,
388 BuiltinFunction::TypeOf,
390 BuiltinFunction::IsNumber,
391 BuiltinFunction::IsString,
392 BuiltinFunction::IsBool,
393 BuiltinFunction::IsArray,
394 BuiltinFunction::IsObject,
395 BuiltinFunction::IsDataRow,
396 BuiltinFunction::ToString,
398 BuiltinFunction::ToNumber,
399 BuiltinFunction::ToBool,
400 BuiltinFunction::NativePtrSize,
402 BuiltinFunction::NativePtrNewCell,
403 BuiltinFunction::NativePtrFreeCell,
404 BuiltinFunction::NativePtrReadPtr,
405 BuiltinFunction::NativePtrWritePtr,
406 BuiltinFunction::NativeTableFromArrowC,
407 BuiltinFunction::NativeTableFromArrowCTyped,
408 BuiltinFunction::NativeTableBindType,
409 BuiltinFunction::FormatValueWithMeta,
411 BuiltinFunction::FormatValueWithSpec,
412 BuiltinFunction::FStringContentText,
417 BuiltinFunction::FStringContentStyledText,
418 BuiltinFunction::FStringContentFragment,
419 BuiltinFunction::IntrinsicMean,
423 BuiltinFunction::IntrinsicMin,
424 BuiltinFunction::IntrinsicMax,
425 BuiltinFunction::IntrinsicStd,
426 BuiltinFunction::IntrinsicVariance,
427 BuiltinFunction::IntrinsicRandom,
429 BuiltinFunction::IntrinsicRandomInt,
430 BuiltinFunction::IntrinsicRandomSeed,
431 BuiltinFunction::IntrinsicRandomNormal,
432 BuiltinFunction::IntrinsicRandomArray,
433 BuiltinFunction::IntrinsicDistUniform,
435 BuiltinFunction::IntrinsicDistLognormal,
436 BuiltinFunction::IntrinsicDistExponential,
437 BuiltinFunction::IntrinsicDistPoisson,
438 BuiltinFunction::IntrinsicDistSampleN,
439 BuiltinFunction::IntrinsicBrownianMotion,
441 BuiltinFunction::IntrinsicGbm,
442 BuiltinFunction::IntrinsicOuProcess,
443 BuiltinFunction::IntrinsicRandomWalk,
444 BuiltinFunction::IntrinsicRollingSum,
446 BuiltinFunction::IntrinsicRollingMean,
447 BuiltinFunction::IntrinsicRollingStd,
448 BuiltinFunction::IntrinsicRollingMin,
449 BuiltinFunction::IntrinsicRollingMax,
450 BuiltinFunction::IntrinsicEma,
451 BuiltinFunction::IntrinsicLinearRecurrence,
452 BuiltinFunction::IntrinsicShift,
454 BuiltinFunction::IntrinsicDiff,
455 BuiltinFunction::IntrinsicPctChange,
456 BuiltinFunction::IntrinsicFillna,
457 BuiltinFunction::IntrinsicCumsum,
458 BuiltinFunction::IntrinsicCumprod,
459 BuiltinFunction::IntrinsicClip,
460 BuiltinFunction::IntrinsicCorrelation,
462 BuiltinFunction::IntrinsicCovariance,
463 BuiltinFunction::IntrinsicPercentile,
464 BuiltinFunction::IntrinsicMedian,
465 BuiltinFunction::IntrinsicAtan2,
467 BuiltinFunction::IntrinsicSinh,
468 BuiltinFunction::IntrinsicCosh,
469 BuiltinFunction::IntrinsicTanh,
470 BuiltinFunction::IntrinsicCharCode,
472 BuiltinFunction::IntrinsicFromCharCode,
473 BuiltinFunction::IntrinsicSeries,
475 BuiltinFunction::IntrinsicVecAbs,
477 BuiltinFunction::IntrinsicVecSqrt,
478 BuiltinFunction::IntrinsicVecLn,
479 BuiltinFunction::IntrinsicVecExp,
480 BuiltinFunction::IntrinsicVecAdd,
481 BuiltinFunction::IntrinsicVecSub,
482 BuiltinFunction::IntrinsicVecMul,
483 BuiltinFunction::IntrinsicVecDiv,
484 BuiltinFunction::IntrinsicVecMax,
485 BuiltinFunction::IntrinsicVecMin,
486 BuiltinFunction::IntrinsicVecSelect,
487 BuiltinFunction::IntrinsicVecAddI64,
488 BuiltinFunction::IntrinsicMatMulVec,
490 BuiltinFunction::IntrinsicMatMulMat,
491 BuiltinFunction::IntrinsicMatAdd,
492 BuiltinFunction::IntrinsicMatSub,
493 BuiltinFunction::EvalTimeRef,
495 BuiltinFunction::EvalDateTimeExpr,
496 BuiltinFunction::EvalDataDateTimeRef,
497 BuiltinFunction::EvalDataSet,
498 BuiltinFunction::EvalDataRelative,
499 BuiltinFunction::EvalDataRelativeRange,
500 BuiltinFunction::SomeCtor,
502 BuiltinFunction::OkCtor,
503 BuiltinFunction::ErrCtor,
504 BuiltinFunction::HashMapCtor,
506 BuiltinFunction::SetCtor,
507 BuiltinFunction::DequeCtor,
508 BuiltinFunction::PriorityQueueCtor,
509 BuiltinFunction::JsonObjectGet,
511 BuiltinFunction::JsonArrayAt,
512 BuiltinFunction::JsonObjectKeys,
513 BuiltinFunction::JsonArrayLen,
514 BuiltinFunction::JsonObjectLen,
515 BuiltinFunction::WindowRowNumber,
517 BuiltinFunction::WindowRank,
518 BuiltinFunction::WindowDenseRank,
519 BuiltinFunction::WindowNtile,
520 BuiltinFunction::WindowLag,
521 BuiltinFunction::WindowLead,
522 BuiltinFunction::WindowFirstValue,
523 BuiltinFunction::WindowLastValue,
524 BuiltinFunction::WindowNthValue,
525 BuiltinFunction::WindowSum,
526 BuiltinFunction::WindowAvg,
527 BuiltinFunction::WindowMin,
528 BuiltinFunction::WindowMax,
529 BuiltinFunction::WindowCount,
530 BuiltinFunction::JoinExecute,
532 BuiltinFunction::Reflect,
534 BuiltinFunction::ContentChart,
536 BuiltinFunction::ContentTextCtor,
537 BuiltinFunction::ContentTableCtor,
538 BuiltinFunction::ContentCodeCtor,
539 BuiltinFunction::ContentKvCtor,
540 BuiltinFunction::ContentFragmentCtor,
541 BuiltinFunction::DateTimeNow,
543 BuiltinFunction::DateTimeUtc,
544 BuiltinFunction::DateTimeParse,
545 BuiltinFunction::DateTimeFromEpoch,
546 BuiltinFunction::DateTimeFromParts,
547 BuiltinFunction::DateTimeFromUnixSecs,
548 BuiltinFunction::MutexCtor,
550 BuiltinFunction::AtomicCtor,
551 BuiltinFunction::LazyCtor,
552 BuiltinFunction::ChannelCtor,
553 BuiltinFunction::Sign,
555 BuiltinFunction::Gcd,
556 BuiltinFunction::Lcm,
557 BuiltinFunction::Hypot,
558 BuiltinFunction::Clamp,
559 BuiltinFunction::IsNaN,
560 BuiltinFunction::IsFinite,
561 BuiltinFunction::MakeTableFromRows,
563 BuiltinFunction::TableBuilderNew,
569 BuiltinFunction::CodeBuilderNew,
570 BuiltinFunction::KeyValueBuilderNew,
571];
572
573fn vm_only_opcode_reason(opcode: OpCode) -> Option<&'static str> {
574 match opcode {
639 OpCode::AllocSharedModuleBinding
640 | OpCode::LoadSharedModuleBinding
641 | OpCode::StoreSharedModuleBinding => Some(
642 "A.1C.3 outer-scope Shared module-binding opcode; Cranelift lowering pending",
643 ),
644 OpCode::ConvertToInt
645 | OpCode::ConvertToNumber
646 | OpCode::ConvertToString
647 | OpCode::ConvertToBool
648 | OpCode::ConvertToDecimal
649 | OpCode::ConvertToChar
650 | OpCode::TryConvertToInt
651 | OpCode::TryConvertToNumber
652 | OpCode::TryConvertToString
653 | OpCode::TryConvertToBool
654 | OpCode::TryConvertToDecimal
655 | OpCode::TryConvertToChar => Some(
656 "WS-12: ConvertTo*/TryConvertTo* (`as` cast) not lowered by the \
657 JIT translator; VM-only until per-kind typed convert bodies land",
658 ),
659 _ => None,
660 }
661}
662
663fn is_supported_builtin(_builtin: BuiltinFunction) -> bool {
664 true
667}
668
669pub fn preflight_instructions(instructions: &[Instruction]) -> JitPreflightReport {
675 let mut report = JitPreflightReport::default();
676
677 for instr in instructions {
678 if vm_only_opcode_reason(instr.opcode).is_some() {
679 push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
680 }
681
682 if instr.opcode == OpCode::BuiltinCall {
683 if let Some(Operand::Builtin(builtin)) = instr.operand {
684 if !is_supported_builtin(builtin) {
685 push_unique_builtin(&mut report.unsupported_builtins, builtin);
686 }
687 }
688 }
689 }
690
691 sort_opcodes(&mut report.vm_only_opcodes);
692 sort_builtins(&mut report.unsupported_builtins);
693 report
694}
695
696pub fn preflight_blob_jit_compatibility(
702 blob: &shape_vm::bytecode::FunctionBlob,
703) -> JitPreflightReport {
704 preflight_instructions(&blob.instructions)
705}
706
707pub fn preflight_jit_compatibility(program: &BytecodeProgram) -> JitPreflightReport {
709 let mut report = JitPreflightReport::default();
710
711 for instr in &program.instructions {
712 if vm_only_opcode_reason(instr.opcode).is_some() {
713 push_unique_opcode(&mut report.vm_only_opcodes, instr.opcode);
714 }
715
716 if instr.opcode == OpCode::BuiltinCall {
717 if let Some(Operand::Builtin(builtin)) = instr.operand {
718 if !is_supported_builtin(builtin) {
719 push_unique_builtin(&mut report.unsupported_builtins, builtin);
720 }
721 }
722 }
723 }
724
725 sort_opcodes(&mut report.vm_only_opcodes);
726 sort_builtins(&mut report.unsupported_builtins);
727 report
728}
729
730pub fn build_program_parity_matrix(program: &BytecodeProgram) -> Vec<JitParityEntry> {
735 let mut opcodes = Vec::new();
736 let mut builtins = Vec::new();
737
738 for instr in &program.instructions {
739 push_unique_opcode(&mut opcodes, instr.opcode);
740 if instr.opcode == OpCode::BuiltinCall {
741 if let Some(Operand::Builtin(builtin)) = instr.operand {
742 push_unique_builtin(&mut builtins, builtin);
743 }
744 }
745 }
746
747 sort_opcodes(&mut opcodes);
748 sort_builtins(&mut builtins);
749
750 let mut matrix = Vec::with_capacity(opcodes.len() + builtins.len());
751
752 for opcode in opcodes {
753 if let Some(reason) = vm_only_opcode_reason(opcode) {
754 matrix.push(JitParityEntry {
755 target: JitParityTarget::Opcode(opcode),
756 jit_supported: false,
757 reason,
758 });
759 } else {
760 matrix.push(JitParityEntry {
761 target: JitParityTarget::Opcode(opcode),
762 jit_supported: true,
763 reason: "Opcode is lowered by the JIT translator.",
764 });
765 }
766 }
767
768 for builtin in builtins {
769 if is_supported_builtin(builtin) {
770 matrix.push(JitParityEntry {
771 target: JitParityTarget::Builtin(builtin),
772 jit_supported: true,
773 reason: "Builtin is lowered by JIT builtin handlers.",
774 });
775 } else {
776 matrix.push(JitParityEntry {
777 target: JitParityTarget::Builtin(builtin),
778 jit_supported: false,
779 reason: "Builtin is not lowered by JIT and must run on VM.",
780 });
781 }
782 }
783
784 matrix.sort_by_key(|entry| format!("{:?}", entry.target));
785 matrix
786}
787
788pub fn build_full_opcode_parity_matrix() -> Vec<JitParityEntry> {
790 let mut matrix = Vec::with_capacity(ALL_OPCODES.len());
791 for &opcode in ALL_OPCODES {
792 if let Some(reason) = vm_only_opcode_reason(opcode) {
793 matrix.push(JitParityEntry {
794 target: JitParityTarget::Opcode(opcode),
795 jit_supported: false,
796 reason,
797 });
798 } else {
799 matrix.push(JitParityEntry {
800 target: JitParityTarget::Opcode(opcode),
801 jit_supported: true,
802 reason: "Opcode is lowered by the JIT translator.",
803 });
804 }
805 }
806 matrix.sort_by_key(|entry| format!("{:?}", entry.target));
807 matrix
808}
809
810pub fn build_full_builtin_parity_matrix() -> Vec<JitParityEntry> {
812 let mut matrix = Vec::with_capacity(ALL_BUILTINS.len());
813 for &builtin in ALL_BUILTINS {
814 if is_supported_builtin(builtin) {
815 matrix.push(JitParityEntry {
816 target: JitParityTarget::Builtin(builtin),
817 jit_supported: true,
818 reason: "Builtin is lowered by JIT builtin handlers.",
819 });
820 } else {
821 matrix.push(JitParityEntry {
822 target: JitParityTarget::Builtin(builtin),
823 jit_supported: false,
824 reason: "Builtin is not lowered by JIT and must run on VM.",
825 });
826 }
827 }
828 matrix.sort_by_key(|entry| format!("{:?}", entry.target));
829 matrix
830}
831
832#[inline(always)]
834pub fn can_jit_compile(program: &BytecodeProgram) -> bool {
835 preflight_jit_compatibility(program).can_jit()
836}
837
838#[inline(always)]
840pub fn get_unsupported_opcodes(program: &BytecodeProgram) -> Vec<OpCode> {
841 let report = preflight_jit_compatibility(program);
842 let mut unsupported = report.vm_only_opcodes;
843
844 if !report.unsupported_builtins.is_empty() && !unsupported.contains(&OpCode::BuiltinCall) {
845 unsupported.push(OpCode::BuiltinCall);
846 }
847
848 sort_opcodes(&mut unsupported);
849 unsupported
850}
851
852pub fn get_incomplete_opcodes(_program: &BytecodeProgram) -> Vec<OpCode> {
854 Vec::new()
856}
857
858#[cfg(test)]
859mod tests {
860 use super::*;
861 use shape_vm::bytecode::{Instruction, Operand};
862
863 #[test]
864 fn preflight_accepts_all_opcodes() {
865 let program = BytecodeProgram {
867 instructions: vec![Instruction::simple(OpCode::Await)],
868 ..Default::default()
869 };
870 let report = preflight_jit_compatibility(&program);
871 assert!(report.can_jit());
872 }
873
874 #[test]
875 fn preflight_accepts_all_builtins() {
876 let program = BytecodeProgram {
878 instructions: vec![Instruction::new(
879 OpCode::BuiltinCall,
880 Some(Operand::Builtin(BuiltinFunction::Snapshot)),
881 )],
882 ..Default::default()
883 };
884 let report = preflight_jit_compatibility(&program);
885 assert!(report.can_jit());
886 }
887
888 #[test]
889 fn parity_matrix_marks_all_builtins_supported() {
890 let program = BytecodeProgram {
891 instructions: vec![Instruction::new(
892 OpCode::BuiltinCall,
893 Some(Operand::Builtin(BuiltinFunction::Snapshot)),
894 )],
895 ..Default::default()
896 };
897 let matrix = build_program_parity_matrix(&program);
898 assert!(matrix.iter().all(|row| row.jit_supported));
899 }
900
901 #[test]
902 fn preflight_instructions_compatible_slice() {
903 let instructions = vec![
904 Instruction::simple(OpCode::PushConst),
905 Instruction::simple(OpCode::AddInt),
906 Instruction::simple(OpCode::ReturnValue),
907 ];
908 let report = preflight_instructions(&instructions);
909 assert!(report.can_jit());
910 }
911
912 #[test]
913 fn preflight_instructions_all_opcodes_pass() {
914 let instructions = vec![
916 Instruction::simple(OpCode::PushConst),
917 Instruction::simple(OpCode::Await),
918 Instruction::simple(OpCode::ReturnValue),
919 ];
920 let report = preflight_instructions(&instructions);
921 assert!(report.can_jit());
922 }
923
924 #[test]
925 fn preflight_blob_passes_with_spawn_task() {
926 use shape_vm::bytecode::FunctionBlob;
927
928 let blob = FunctionBlob {
929 content_hash: shape_vm::bytecode::FunctionHash::ZERO,
930 name: "test_fn".to_string(),
931 arity: 0,
932 param_names: vec![],
933 locals_count: 0,
934 is_closure: false,
935 captures_count: 0,
936 is_async: false,
937 ref_params: vec![],
938 ref_mutates: vec![],
939 mutable_captures: vec![],
940 instructions: vec![
941 Instruction::simple(OpCode::PushConst),
942 Instruction::simple(OpCode::SpawnTask),
943 Instruction::simple(OpCode::ReturnValue),
944 ],
945 constants: vec![],
946 strings: vec![],
947 required_permissions: Default::default(),
948 dependencies: vec![],
949 callee_names: vec![],
950 type_schemas: vec![],
951 source_map: vec![],
952 foreign_dependencies: vec![],
953 frame_descriptor: None,
954 };
955
956 let report = preflight_blob_jit_compatibility(&blob);
957 assert!(report.can_jit());
958 }
959
960 #[test]
961 fn all_opcodes_pass_preflight() {
962 for &opcode in ALL_OPCODES {
964 assert!(
965 vm_only_opcode_reason(opcode).is_none(),
966 "Opcode {:?} should pass preflight",
967 opcode
968 );
969 }
970 }
971
972 #[test]
973 fn all_builtins_pass_preflight() {
974 for &builtin in ALL_BUILTINS {
976 assert!(
977 is_supported_builtin(builtin),
978 "Builtin {:?} should be supported",
979 builtin
980 );
981 }
982 }
983
984 #[test]
997 fn a1d2_preflight_accepts_load_owned_mutable_capture() {
998 let program = BytecodeProgram {
999 instructions: vec![
1000 Instruction::new(OpCode::LoadOwnedMutableCapture, Some(Operand::Local(0))),
1001 Instruction::simple(OpCode::ReturnValue),
1002 ],
1003 ..Default::default()
1004 };
1005 let report = preflight_jit_compatibility(&program);
1006 assert!(
1007 report.can_jit(),
1008 "A.1D.2: LoadOwnedMutableCapture is now lowered by MirToIR; \
1009 preflight must accept it"
1010 );
1011 assert!(
1012 !report
1013 .vm_only_opcodes
1014 .contains(&OpCode::LoadOwnedMutableCapture),
1015 "A.1D.2 removes LoadOwnedMutableCapture from vm_only_opcode_reason"
1016 );
1017 }
1018
1019 #[test]
1020 fn a1d2_preflight_accepts_store_owned_mutable_capture() {
1021 let program = BytecodeProgram {
1022 instructions: vec![
1023 Instruction::new(OpCode::StoreOwnedMutableCapture, Some(Operand::Local(0))),
1024 Instruction::simple(OpCode::ReturnValue),
1025 ],
1026 ..Default::default()
1027 };
1028 let report = preflight_jit_compatibility(&program);
1029 assert!(
1030 report.can_jit(),
1031 "A.1D.2: StoreOwnedMutableCapture is now lowered by MirToIR; \
1032 preflight must accept it"
1033 );
1034 assert!(
1035 !report
1036 .vm_only_opcodes
1037 .contains(&OpCode::StoreOwnedMutableCapture),
1038 "A.1D.2 removes StoreOwnedMutableCapture from vm_only_opcode_reason"
1039 );
1040 }
1041
1042 #[test]
1049 fn a1e_preflight_accepts_load_shared_capture() {
1050 let program = BytecodeProgram {
1051 instructions: vec![
1052 Instruction::new(OpCode::LoadSharedCapture, Some(Operand::Local(0))),
1053 Instruction::simple(OpCode::ReturnValue),
1054 ],
1055 ..Default::default()
1056 };
1057 let report = preflight_jit_compatibility(&program);
1058 assert!(
1059 report.can_jit(),
1060 "A.1E: LoadSharedCapture is now lowered by MirToIR; \
1061 preflight must accept it"
1062 );
1063 assert!(
1064 !report.vm_only_opcodes.contains(&OpCode::LoadSharedCapture),
1065 "A.1E removes LoadSharedCapture from vm_only_opcode_reason"
1066 );
1067 }
1068
1069 #[test]
1070 fn a1e_preflight_accepts_store_shared_capture() {
1071 let program = BytecodeProgram {
1072 instructions: vec![
1073 Instruction::new(OpCode::StoreSharedCapture, Some(Operand::Local(0))),
1074 Instruction::simple(OpCode::ReturnValue),
1075 ],
1076 ..Default::default()
1077 };
1078 let report = preflight_jit_compatibility(&program);
1079 assert!(
1080 report.can_jit(),
1081 "A.1E: StoreSharedCapture is now lowered by MirToIR; \
1082 preflight must accept it"
1083 );
1084 assert!(
1085 !report.vm_only_opcodes.contains(&OpCode::StoreSharedCapture),
1086 "A.1E removes StoreSharedCapture from vm_only_opcode_reason"
1087 );
1088 }
1089
1090 #[test]
1091 fn a1c_preflight_immutable_closure_body_still_jits() {
1092 let program = BytecodeProgram {
1095 instructions: vec![
1096 Instruction::simple(OpCode::PushConst),
1097 Instruction::simple(OpCode::AddInt),
1098 Instruction::simple(OpCode::ReturnValue),
1099 ],
1100 ..Default::default()
1101 };
1102 let report = preflight_jit_compatibility(&program);
1103 assert!(report.can_jit());
1104 }
1105
1106 #[test]
1112 fn cell_identity_preflight_accepts_outer_shared_local_opcodes() {
1113 for op in [
1124 OpCode::AllocSharedLocal,
1125 OpCode::LoadSharedLocal,
1126 OpCode::StoreSharedLocal,
1127 OpCode::DropSharedLocal,
1128 ] {
1129 let program = BytecodeProgram {
1130 instructions: vec![
1131 Instruction::new(op, Some(Operand::Local(0))),
1132 Instruction::simple(OpCode::ReturnValue),
1133 ],
1134 ..Default::default()
1135 };
1136 let report = preflight_jit_compatibility(&program);
1137 assert!(
1138 report.can_jit(),
1139 "cell-identity: outer-scope Shared local opcode {:?} \
1140 must pass preflight after the handshake fix",
1141 op
1142 );
1143 assert!(
1144 !report.vm_only_opcodes.contains(&op),
1145 "cell-identity: {:?} must be removed from vm_only_opcode_reason",
1146 op
1147 );
1148 }
1149 }
1150
1151 #[test]
1152 fn session1_preflight_still_rejects_outer_shared_module_binding_opcodes() {
1153 for op in [
1158 OpCode::AllocSharedModuleBinding,
1159 OpCode::LoadSharedModuleBinding,
1160 OpCode::StoreSharedModuleBinding,
1161 ] {
1162 let program = BytecodeProgram {
1163 instructions: vec![
1164 Instruction::new(op, Some(Operand::Local(0))),
1165 Instruction::simple(OpCode::ReturnValue),
1166 ],
1167 ..Default::default()
1168 };
1169 let report = preflight_jit_compatibility(&program);
1170 assert!(
1171 !report.can_jit(),
1172 "Outer-scope Shared module-binding opcode {:?} must \
1173 remain preflight-rejected pending A.1C.3 JIT lowering",
1174 op
1175 );
1176 }
1177 }
1178
1179 #[test]
1180 fn a1e_preflight_accepts_capture_side_shared_opcodes() {
1181 for op in [OpCode::LoadSharedCapture, OpCode::StoreSharedCapture] {
1185 let program = BytecodeProgram {
1186 instructions: vec![
1187 Instruction::new(op, Some(Operand::Local(0))),
1188 Instruction::simple(OpCode::ReturnValue),
1189 ],
1190 ..Default::default()
1191 };
1192 let report = preflight_jit_compatibility(&program);
1193 assert!(
1194 report.can_jit(),
1195 "Capture-side Shared opcode {:?} must pass preflight after A.1E",
1196 op
1197 );
1198 }
1199 }
1200
1201 #[test]
1202 fn ws12_preflight_rejects_convert_cast_opcodes() {
1203 for op in [
1213 OpCode::ConvertToInt,
1214 OpCode::ConvertToNumber,
1215 OpCode::ConvertToString,
1216 OpCode::ConvertToBool,
1217 OpCode::ConvertToDecimal,
1218 OpCode::ConvertToChar,
1219 OpCode::TryConvertToInt,
1220 OpCode::TryConvertToNumber,
1221 OpCode::TryConvertToString,
1222 OpCode::TryConvertToBool,
1223 OpCode::TryConvertToDecimal,
1224 OpCode::TryConvertToChar,
1225 ] {
1226 let program = BytecodeProgram {
1227 instructions: vec![
1228 Instruction::simple(op),
1229 Instruction::simple(OpCode::ReturnValue),
1230 ],
1231 ..Default::default()
1232 };
1233 let report = preflight_jit_compatibility(&program);
1234 assert!(
1235 !report.can_jit(),
1236 "WS-12: cast opcode {:?} must be preflight-rejected \
1237 (JIT translator does not lower it)",
1238 op
1239 );
1240 assert!(
1241 report.vm_only_opcodes.contains(&op),
1242 "WS-12: {:?} must appear in vm_only_opcodes",
1243 op
1244 );
1245 }
1246 }
1247}