1use core::{fmt, marker::PhantomData};
2
3use crate::marshal::MarshalError;
4
5use super::{OpArg, OpArgByte, OpArgType, oparg};
6
7macro_rules! define_opcodes {
8 (
9 #[repr($typ:ident)]
10 $opcode_vis:vis enum $opcode_name:ident;
11
12 $(#[$instr_meta:meta])*
13 $instr_vis:vis enum $instr_name:ident {
14 $(
15 $(#[$op_meta:meta])*
16 $op_name:ident $({ $arg_name:ident: Arg<$arg_type:ty> $(,)? })? = $op_id:expr
17 ),* $(,)?
18 }
19 ) => {
20 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
21 #[repr($typ)]
22 $opcode_vis enum $opcode_name {
23 $($op_name = $op_id),*
24 }
25
26 impl $opcode_name {
27 #[doc = concat!("Converts this opcode to [`", stringify!($instr_name), "`].")]
28 #[must_use]
29 #[inline]
30 $opcode_vis const fn as_instruction(&self) -> $instr_name {
31 unsafe { core::mem::transmute(*self) }
39 }
40
41 #[must_use]
43 #[inline]
44 $opcode_vis const fn deoptimize(self) -> Self {
45 match self.deopt() {
46 Some(v) => v,
47 None => {
48 match self.to_base() {
50 Some(v) => v,
51 None => self,
52 }
53 }
54 }
55 }
56
57 pub(super) const fn try_from_numeric(value: $typ) -> Result<Self, $crate::marshal::MarshalError> {
62 match value {
63 $($op_id => Ok(Self::$op_name),)*
64 _ => Err($crate::marshal::MarshalError::InvalidBytecode),
65 }
66 }
67
68 #[must_use]
70 #[inline]
71 pub(super) const fn as_numeric(self) -> $typ {
72 self as $typ
75 }
76 }
77
78 impl From<$opcode_name> for $instr_name {
79 fn from(opcode: $opcode_name) -> Self {
80 opcode.as_instruction()
81 }
82 }
83
84
85 impl TryFrom<$typ> for $opcode_name {
86 type Error = $crate::marshal::MarshalError;
87
88 fn try_from(value: $typ) -> Result<Self, Self::Error> {
89 Self::try_from_numeric(value)
90 }
91 }
92
93 impl From<$opcode_name> for $typ {
94 fn from(opcode: $opcode_name) -> Self {
95 opcode.as_numeric()
96 }
97 }
98
99 #[derive(Clone, Copy, Debug)]
100 #[repr($typ)] $instr_vis enum $instr_name {
102 $(
103 $(#[$op_meta])*
104 $op_name $({ $arg_name: Arg<$arg_type> })? = $op_id ),*
106 }
107
108 const _: () = assert!(core::mem::size_of::<$instr_name>() == core::mem::size_of::<$typ>());
114
115 impl $instr_name {
116 #[doc = concat!("Get the corresponding [`", stringify!($opcode_name), "`].")]
117 #[must_use]
118 #[inline]
119 $instr_vis const fn as_opcode(&self) -> $opcode_name {
120 unsafe { core::mem::transmute(*self) }
126 }
127
128 #[must_use]
129 $instr_vis const fn label_arg(&self) -> Option<Arg<oparg::Label>> {
130 define_opcodes!(@match self, Self, [$($op_name $({ $arg_name : $arg_type })?),*])
132 }
133
134 #[must_use]
135 pub const fn to_base(self) -> Option<Self> {
136 if let Some(op) = self.as_opcode().to_base() {
137 Some(op.as_instruction())
138 } else {
139 None
140 }
141 }
142
143 #[must_use]
144 pub const fn to_instrumented(self) -> Option<Self> {
145 if let Some(op) = self.as_opcode().to_instrumented() {
146 Some(op.as_instruction())
147 } else {
148 None
149 }
150 }
151
152 #[must_use]
154 $instr_vis const fn is_instrumented(&self) -> bool {
155 self.as_opcode().is_instrumented()
156 }
157
158 #[must_use]
159 $instr_vis const fn is_unconditional_jump(&self) -> bool {
160 self.as_opcode().is_unconditional_jump()
161 }
162
163 #[must_use]
164 $instr_vis const fn is_block_push(&self) -> bool {
165 self.as_opcode().is_block_push()
166 }
167
168 #[must_use]
169 $instr_vis const fn is_scope_exit(&self) -> bool {
170 self.as_opcode().is_scope_exit()
171 }
172
173 #[must_use]
174 $instr_vis const fn is_terminator(&self) -> bool {
175 self.as_opcode().is_terminator()
176 }
177
178 #[must_use]
179 $instr_vis const fn is_no_fallthrough(&self) -> bool {
180 self.as_opcode().is_no_fallthrough()
181 }
182
183 #[must_use]
184 $instr_vis const fn has_target(&self) -> bool {
185 self.as_opcode().has_target()
186 }
187
188 #[must_use]
189 $instr_vis const fn has_jump(&self) -> bool {
190 self.as_opcode().has_jump()
191 }
192
193 #[must_use]
194 $instr_vis const fn has_arg(&self) -> bool {
195 self.as_opcode().has_arg()
196 }
197
198 #[must_use]
199 $instr_vis const fn has_const(&self) -> bool {
200 self.as_opcode().has_const()
201 }
202
203 #[must_use]
204 $instr_vis const fn has_eval_break(&self) -> bool {
205 self.as_opcode().has_eval_break()
206 }
207
208 #[must_use]
209 $instr_vis const fn is_assembler(&self) -> bool {
210 self.as_opcode().is_assembler()
211 }
212
213 #[must_use]
214 $instr_vis const fn cache_entries(&self) -> usize{
215 self.as_opcode().cache_entries()
216 }
217
218 #[must_use]
220 $instr_vis const fn deoptimize(&self) -> Self {
221 self.as_opcode().deoptimize().as_instruction()
222 }
223
224 #[must_use]
225 $instr_vis fn stack_effect_jump(&self, oparg: u32) -> i32 {
226 self.as_opcode().stack_effect_jump(oparg)
227 }
228
229 #[must_use]
230 $instr_vis fn stack_effect_info(&self, oparg: u32) -> StackEffect {
231 self.as_opcode().stack_effect_info(oparg)
232 }
233
234 #[must_use]
235 $instr_vis fn stack_effect(&self, oparg: u32) -> i32 {
236 self.as_opcode().stack_effect(oparg)
237 }
238 }
239
240 impl From<$instr_name> for $opcode_name {
241 fn from(instr: $instr_name) -> Self {
242 instr.as_opcode()
243 }
244 }
245
246 impl TryFrom<$typ> for $instr_name {
247 type Error = $crate::marshal::MarshalError;
248
249 fn try_from(value: $typ) -> Result<Self, Self::Error> {
250 $opcode_name::try_from_numeric(value).map(Into::into)
251 }
252 }
253
254 impl From<$instr_name> for $typ {
255 fn from(instr: $instr_name) -> Self {
256 instr.as_opcode().into()
257 }
258 }
259 };
260
261 (@match $self:expr, $name:ident, []) => {
263 None
264 };
265
266 (@match $self:expr, $name:ident, [$variant:ident { $field:ident : Label } , $($rest:tt)*]) => {
268 match $self {
269 $name::$variant { $field } => Some(*$field),
270 other => define_opcodes!(@match other, $name, [$($rest)*]),
271 }
272 };
273
274 (@match $self:expr, $name:ident, [$variant:ident { $field:ident : Label }]) => {
276 match $self {
277 $name::$variant { $field } => Some(*$field),
278 other => define_opcodes!(@match other, $name, []),
279 }
280 };
281
282 (@match $self:expr, $name:ident, [$variant:ident { $field:ident : $type:ty } , $($rest:tt)*]) => {
284 match $self {
285 $name::$variant { .. } => None,
286 other => define_opcodes!(@match other, $name, [$($rest)*]),
287 }
288 };
289
290 (@match $self:expr, $name:ident, [$variant:ident { $field:ident : $type:ty }]) => {
292 match $self {
293 $name::$variant { .. } => None,
294 _ => define_opcodes!(@match _, $name, []),
295 }
296 };
297
298 (@match $self:expr, $name:ident, [$variant:ident , $($rest:tt)*]) => {
300 match $self {
301 $name::$variant => None,
302 other => define_opcodes!(@match other, $name, [$($rest)*]),
303 }
304 };
305
306 (@match $self:expr, $name:ident, [$variant:ident]) => {
308 match $self {
309 $name::$variant => None,
310 _ => define_opcodes!(@match _, $name, []),
311 }
312 };
313}
314
315define_opcodes!(
316 #[repr(u8)]
317 pub enum Opcode;
318
319 pub enum Instruction {
320 Cache = 0,
321 BinarySlice = 1,
322 BuildTemplate = 2,
323 BinaryOpInplaceAddUnicode = 3,
324 CallFunctionEx = 4,
325 CheckEgMatch = 5,
326 CheckExcMatch = 6,
327 CleanupThrow = 7,
328 DeleteSubscr = 8,
329 EndFor = 9,
330 EndSend = 10,
331 ExitInitCheck = 11,
332 FormatSimple = 12,
333 FormatWithSpec = 13,
334 GetAiter = 14,
335 GetAnext = 15,
336 GetIter = 16,
337 Reserved = 17,
338 GetLen = 18,
339 GetYieldFromIter = 19,
340 InterpreterExit = 20,
341 LoadBuildClass = 21,
342 LoadLocals = 22,
343 MakeFunction = 23,
344 MatchKeys = 24,
345 MatchMapping = 25,
346 MatchSequence = 26,
347 Nop = 27,
348 NotTaken = 28,
349 PopExcept = 29,
350 PopIter = 30,
351 PopTop = 31,
352 PushExcInfo = 32,
353 PushNull = 33,
354 ReturnGenerator = 34,
355 ReturnValue = 35,
356 SetupAnnotations = 36,
357 StoreSlice = 37,
358 StoreSubscr = 38,
359 ToBool = 39,
360 UnaryInvert = 40,
361 UnaryNegative = 41,
362 UnaryNot = 42,
363 WithExceptStart = 43,
364 BinaryOp {
365 op: Arg<oparg::BinaryOperator>,
366 } = 44,
367 BuildInterpolation {
368 format: Arg<u32>,
369 } = 45,
370 BuildList {
371 count: Arg<u32>,
372 } = 46,
373 BuildMap {
374 count: Arg<u32>,
375 } = 47,
376 BuildSet {
377 count: Arg<u32>,
378 } = 48,
379 BuildSlice {
380 argc: Arg<oparg::BuildSliceArgCount>,
381 } = 49,
382 BuildString {
383 count: Arg<u32>,
384 } = 50,
385 BuildTuple {
386 count: Arg<u32>,
387 } = 51,
388 Call {
389 argc: Arg<u32>,
390 } = 52,
391 CallIntrinsic1 {
392 func: Arg<oparg::IntrinsicFunction1>,
393 } = 53,
394 CallIntrinsic2 {
395 func: Arg<oparg::IntrinsicFunction2>,
396 } = 54,
397 CallKw {
398 argc: Arg<u32>,
399 } = 55,
400 CompareOp {
401 opname: Arg<oparg::ComparisonOperator>,
402 } = 56,
403 ContainsOp {
404 invert: Arg<oparg::Invert>,
405 } = 57,
406 ConvertValue {
407 oparg: Arg<oparg::ConvertValueOparg>,
408 } = 58,
409 Copy {
410 i: Arg<u32>,
411 } = 59,
412 CopyFreeVars {
413 n: Arg<u32>,
414 } = 60,
415 DeleteAttr {
416 namei: Arg<oparg::NameIdx>,
417 } = 61,
418 DeleteDeref {
419 i: Arg<oparg::VarNum>,
420 } = 62,
421 DeleteFast {
422 var_num: Arg<oparg::VarNum>,
423 } = 63,
424 DeleteGlobal {
425 namei: Arg<oparg::NameIdx>,
426 } = 64,
427 DeleteName {
428 namei: Arg<oparg::NameIdx>,
429 } = 65,
430 DictMerge {
431 i: Arg<u32>,
432 } = 66,
433 DictUpdate {
434 i: Arg<u32>,
435 } = 67,
436 EndAsyncFor = 68,
437 ExtendedArg = 69,
438 ForIter {
439 delta: Arg<oparg::Label>,
440 } = 70,
441 GetAwaitable {
442 r#where: Arg<u32>,
443 } = 71,
444 ImportFrom {
445 namei: Arg<oparg::NameIdx>,
446 } = 72,
447 ImportName {
448 namei: Arg<oparg::NameIdx>,
449 } = 73,
450 IsOp {
451 invert: Arg<oparg::Invert>,
452 } = 74,
453 JumpBackward {
454 delta: Arg<oparg::Label>,
455 } = 75,
456 JumpBackwardNoInterrupt {
457 delta: Arg<oparg::Label>,
458 } = 76,
459 JumpForward {
460 delta: Arg<oparg::Label>,
461 } = 77,
462 ListAppend {
463 i: Arg<u32>,
464 } = 78,
465 ListExtend {
466 i: Arg<u32>,
467 } = 79,
468 LoadAttr {
469 namei: Arg<oparg::LoadAttr>,
470 } = 80,
471 LoadCommonConstant {
472 idx: Arg<oparg::CommonConstant>,
473 } = 81,
474 LoadConst {
475 consti: Arg<oparg::ConstIdx>,
476 } = 82,
477 LoadDeref {
478 i: Arg<oparg::VarNum>,
479 } = 83,
480 LoadFast {
481 var_num: Arg<oparg::VarNum>,
482 } = 84,
483 LoadFastAndClear {
484 var_num: Arg<oparg::VarNum>,
485 } = 85,
486 LoadFastBorrow {
487 var_num: Arg<oparg::VarNum>,
488 } = 86,
489 LoadFastBorrowLoadFastBorrow {
490 var_nums: Arg<oparg::VarNums>,
491 } = 87,
492 LoadFastCheck {
493 var_num: Arg<oparg::VarNum>,
494 } = 88,
495 LoadFastLoadFast {
496 var_nums: Arg<oparg::VarNums>,
497 } = 89,
498 LoadFromDictOrDeref {
499 i: Arg<oparg::VarNum>,
500 } = 90,
501 LoadFromDictOrGlobals {
502 i: Arg<oparg::NameIdx>,
503 } = 91,
504 LoadGlobal {
505 namei: Arg<oparg::NameIdx>,
506 } = 92,
507 LoadName {
508 namei: Arg<oparg::NameIdx>,
509 } = 93,
510 LoadSmallInt {
511 i: Arg<u32>,
512 } = 94,
513 LoadSpecial {
514 method: Arg<oparg::SpecialMethod>,
515 } = 95,
516 LoadSuperAttr {
517 namei: Arg<oparg::LoadSuperAttr>,
518 } = 96,
519 MakeCell {
520 i: Arg<oparg::VarNum>,
521 } = 97,
522 MapAdd {
523 i: Arg<u32>,
524 } = 98,
525 MatchClass {
526 count: Arg<u32>,
527 } = 99,
528 PopJumpIfFalse {
529 delta: Arg<oparg::Label>,
530 } = 100,
531 PopJumpIfNone {
532 delta: Arg<oparg::Label>,
533 } = 101,
534 PopJumpIfNotNone {
535 delta: Arg<oparg::Label>,
536 } = 102,
537 PopJumpIfTrue {
538 delta: Arg<oparg::Label>,
539 } = 103,
540 RaiseVarargs {
541 argc: Arg<oparg::RaiseKind>,
542 } = 104,
543 Reraise {
544 depth: Arg<u32>,
545 } = 105,
546 Send {
547 delta: Arg<oparg::Label>,
548 } = 106,
549 SetAdd {
550 i: Arg<u32>,
551 } = 107,
552 SetFunctionAttribute {
553 flag: Arg<oparg::MakeFunctionFlag>,
554 } = 108,
555 SetUpdate {
556 i: Arg<u32>,
557 } = 109,
558 StoreAttr {
559 namei: Arg<oparg::NameIdx>,
560 } = 110,
561 StoreDeref {
562 i: Arg<oparg::VarNum>,
563 } = 111,
564 StoreFast {
565 var_num: Arg<oparg::VarNum>,
566 } = 112,
567 StoreFastLoadFast {
568 var_nums: Arg<oparg::VarNums>,
569 } = 113,
570 StoreFastStoreFast {
571 var_nums: Arg<oparg::VarNums>,
572 } = 114,
573 StoreGlobal {
574 namei: Arg<oparg::NameIdx>,
575 } = 115,
576 StoreName {
577 namei: Arg<oparg::NameIdx>,
578 } = 116,
579 Swap {
580 i: Arg<u32>,
581 } = 117,
582 UnpackEx {
583 counts: Arg<oparg::UnpackExArgs>,
584 } = 118,
585 UnpackSequence {
586 count: Arg<u32>,
587 } = 119,
588 YieldValue {
589 arg: Arg<u32>,
590 } = 120,
591 Resume {
592 context: Arg<oparg::ResumeContext>,
593 } = 128,
594 BinaryOpAddFloat = 129,
595 BinaryOpAddInt = 130,
596 BinaryOpAddUnicode = 131,
597 BinaryOpExtend = 132,
598 BinaryOpMultiplyFloat = 133,
599 BinaryOpMultiplyInt = 134,
600 BinaryOpSubscrDict = 135,
601 BinaryOpSubscrGetitem = 136,
602 BinaryOpSubscrListInt = 137,
603 BinaryOpSubscrListSlice = 138,
604 BinaryOpSubscrStrInt = 139,
605 BinaryOpSubscrTupleInt = 140,
606 BinaryOpSubtractFloat = 141,
607 BinaryOpSubtractInt = 142,
608 CallAllocAndEnterInit = 143,
609 CallBoundMethodExactArgs = 144,
610 CallBoundMethodGeneral = 145,
611 CallBuiltinClass = 146,
612 CallBuiltinFast = 147,
613 CallBuiltinFastWithKeywords = 148,
614 CallBuiltinO = 149,
615 CallIsinstance = 150,
616 CallKwBoundMethod = 151,
617 CallKwNonPy = 152,
618 CallKwPy = 153,
619 CallLen = 154,
620 CallListAppend = 155,
621 CallMethodDescriptorFast = 156,
622 CallMethodDescriptorFastWithKeywords = 157,
623 CallMethodDescriptorNoargs = 158,
624 CallMethodDescriptorO = 159,
625 CallNonPyGeneral = 160,
626 CallPyExactArgs = 161,
627 CallPyGeneral = 162,
628 CallStr1 = 163,
629 CallTuple1 = 164,
630 CallType1 = 165,
631 CompareOpFloat = 166,
632 CompareOpInt = 167,
633 CompareOpStr = 168,
634 ContainsOpDict = 169,
635 ContainsOpSet = 170,
636 ForIterGen = 171,
637 ForIterList = 172,
638 ForIterRange = 173,
639 ForIterTuple = 174,
640 JumpBackwardJit = 175,
641 JumpBackwardNoJit = 176,
642 LoadAttrClass = 177,
643 LoadAttrClassWithMetaclassCheck = 178,
644 LoadAttrGetattributeOverridden = 179,
645 LoadAttrInstanceValue = 180,
646 LoadAttrMethodLazyDict = 181,
647 LoadAttrMethodNoDict = 182,
648 LoadAttrMethodWithValues = 183,
649 LoadAttrModule = 184,
650 LoadAttrNondescriptorNoDict = 185,
651 LoadAttrNondescriptorWithValues = 186,
652 LoadAttrProperty = 187,
653 LoadAttrSlot = 188,
654 LoadAttrWithHint = 189,
655 LoadConstImmortal = 190,
656 LoadConstMortal = 191,
657 LoadGlobalBuiltin = 192,
658 LoadGlobalModule = 193,
659 LoadSuperAttrAttr = 194,
660 LoadSuperAttrMethod = 195,
661 ResumeCheck = 196,
662 SendGen = 197,
663 StoreAttrInstanceValue = 198,
664 StoreAttrSlot = 199,
665 StoreAttrWithHint = 200,
666 StoreSubscrDict = 201,
667 StoreSubscrListInt = 202,
668 ToBoolAlwaysTrue = 203,
669 ToBoolBool = 204,
670 ToBoolInt = 205,
671 ToBoolList = 206,
672 ToBoolNone = 207,
673 ToBoolStr = 208,
674 UnpackSequenceList = 209,
675 UnpackSequenceTuple = 210,
676 UnpackSequenceTwoTuple = 211,
677 InstrumentedEndFor = 234,
678 InstrumentedPopIter = 235,
679 InstrumentedEndSend = 236,
680 InstrumentedForIter = 237,
681 InstrumentedInstruction = 238,
682 InstrumentedJumpForward = 239,
683 InstrumentedNotTaken = 240,
684 InstrumentedPopJumpIfTrue = 241,
685 InstrumentedPopJumpIfFalse = 242,
686 InstrumentedPopJumpIfNone = 243,
687 InstrumentedPopJumpIfNotNone = 244,
688 InstrumentedResume = 245,
689 InstrumentedReturnValue = 246,
690 InstrumentedYieldValue = 247,
691 InstrumentedEndAsyncFor = 248,
692 InstrumentedLoadSuperAttr = 249,
693 InstrumentedCall = 250,
694 InstrumentedCallKw = 251,
695 InstrumentedCallFunctionEx = 252,
696 InstrumentedJumpBackward = 253,
697 InstrumentedLine = 254,
698 EnterExecutor = 255,
699 }
700);
701
702define_opcodes!(
703 #[repr(u16)]
704 pub enum PseudoOpcode;
705
706 pub enum PseudoInstruction {
707 AnnotationsPlaceholder = 256,
708 Jump { delta: Arg<oparg::Label> } = 257,
709 JumpIfFalse { delta: Arg<oparg::Label> } = 258,
710 JumpIfTrue { delta: Arg<oparg::Label> } = 259,
711 JumpNoInterrupt { delta: Arg<oparg::Label> } = 260,
712 LoadClosure { i: Arg<oparg::NameIdx> } = 261,
713 PopBlock = 262,
714 SetupCleanup { delta: Arg<oparg::Label> } = 263,
715 SetupFinally { delta: Arg<oparg::Label> } = 264,
716 SetupWith { delta: Arg<oparg::Label> } = 265,
717 StoreFastMaybeNull { var_num: Arg<oparg::NameIdx> } = 266,
718 }
719);
720
721impl Opcode {
722 #[must_use]
723 pub const fn is_unconditional_jump(&self) -> bool {
724 matches!(
725 self,
726 Self::JumpForward | Self::JumpBackward | Self::JumpBackwardNoInterrupt
727 )
728 }
729
730 #[must_use]
732 pub const fn is_assembler(&self) -> bool {
733 matches!(
734 self,
735 Self::JumpForward | Self::JumpBackward | Self::JumpBackwardNoInterrupt
736 )
737 }
738
739 #[must_use]
740 pub const fn is_scope_exit(&self) -> bool {
741 matches!(self, Self::ReturnValue | Self::RaiseVarargs | Self::Reraise)
742 }
743
744 #[must_use]
746 pub const fn is_terminator(&self) -> bool {
747 self.has_jump() || self.is_scope_exit()
748 }
749
750 #[must_use]
752 pub const fn is_no_fallthrough(&self) -> bool {
753 self.is_scope_exit() || self.is_unconditional_jump()
754 }
755
756 #[must_use]
758 pub const fn has_target(&self) -> bool {
759 self.has_jump() || self.is_block_push()
760 }
761
762 #[must_use]
763 pub const fn is_block_push(&self) -> bool {
764 false
765 }
766
767 #[must_use]
769 pub fn stack_effect(&self, oparg: u32) -> i32 {
770 self.stack_effect_info(oparg).effect()
771 }
772
773 #[must_use]
779 pub fn stack_effect_jump(&self, oparg: u32) -> i32 {
780 self.stack_effect(oparg)
781 }
782}
783
784impl PseudoOpcode {
785 #[must_use]
786 pub const fn is_block_push(&self) -> bool {
787 matches!(
788 self,
789 Self::SetupCleanup | Self::SetupFinally | Self::SetupWith
790 )
791 }
792
793 #[must_use]
794 pub const fn is_scope_exit(&self) -> bool {
795 false
796 }
797
798 #[must_use]
799 pub const fn is_unconditional_jump(&self) -> bool {
800 matches!(self, Self::Jump | Self::JumpNoInterrupt)
801 }
802
803 #[must_use]
804 pub const fn is_assembler(&self) -> bool {
805 false
806 }
807
808 #[must_use]
810 pub const fn is_terminator(&self) -> bool {
811 self.has_jump()
812 }
813
814 #[must_use]
816 pub const fn is_no_fallthrough(&self) -> bool {
817 self.is_unconditional_jump()
818 }
819
820 #[must_use]
822 pub const fn has_target(&self) -> bool {
823 self.has_jump() || self.is_block_push()
824 }
825
826 #[must_use]
828 pub fn stack_effect(&self, oparg: u32) -> i32 {
829 if self.is_block_push() {
830 0
831 } else {
832 self.stack_effect_info(oparg).effect()
833 }
834 }
835
836 #[must_use]
844 pub fn stack_effect_jump(&self, oparg: u32) -> i32 {
845 match self {
846 Self::SetupFinally | Self::SetupWith => 1,
847 Self::SetupCleanup => 2,
848 _ => self.stack_effect(oparg),
849 }
850 }
851}
852
853macro_rules! either_real_pseudo {
854 (
856 $(#[$meta:meta])*
857 $vis:vis const fn $name:ident(&self $(, $arg:ident : $arg_ty:ty)*) -> $ret:ty
858 ) => {
859 $(#[$meta])*
860 $vis const fn $name(&self $(, $arg: $arg_ty)*) -> $ret {
861 match self {
862 Self::Real(v) => v.$name($($arg),*),
863 Self::Pseudo(v) => v.$name($($arg),*),
864 }
865 }
866 };
867
868 (
870 $(#[$meta:meta])*
871 $vis:vis fn $name:ident(&self $(, $arg:ident : $arg_ty:ty)*) -> $ret:ty
872 ) => {
873 $(#[$meta])*
874 $vis fn $name(&self $(, $arg: $arg_ty)*) -> $ret {
875 match self {
876 Self::Real(v) => v.$name($($arg),*),
877 Self::Pseudo(v) => v.$name($($arg),*),
878 }
879 }
880 };
881}
882
883#[derive(Clone, Copy, Debug)]
884pub enum AnyInstruction {
885 Real(Instruction),
886 Pseudo(PseudoInstruction),
887}
888
889impl AnyInstruction {
890 either_real_pseudo!(
891 #[must_use]
892 pub const fn is_unconditional_jump(&self) -> bool
893 );
894
895 either_real_pseudo!(
896 #[must_use]
897 pub const fn is_scope_exit(&self) -> bool
898 );
899
900 either_real_pseudo!(
901 #[must_use]
902 pub const fn is_terminator(&self) -> bool
903 );
904
905 either_real_pseudo!(
906 #[must_use]
907 pub const fn is_no_fallthrough(&self) -> bool
908 );
909
910 either_real_pseudo!(
911 #[must_use]
912 pub const fn has_target(&self) -> bool
913 );
914
915 either_real_pseudo!(
916 #[must_use]
917 pub const fn has_jump(&self) -> bool
918 );
919
920 either_real_pseudo!(
921 #[must_use]
922 pub const fn has_arg(&self) -> bool
923 );
924
925 either_real_pseudo!(
926 #[must_use]
927 pub const fn has_const(&self) -> bool
928 );
929
930 either_real_pseudo!(
931 #[must_use]
932 pub const fn has_eval_break(&self) -> bool
933 );
934
935 either_real_pseudo!(
936 #[must_use]
937 pub const fn is_assembler(&self) -> bool
938 );
939
940 either_real_pseudo!(
941 #[must_use]
942 pub fn stack_effect(&self, oparg: u32) -> i32
943 );
944
945 either_real_pseudo!(
946 #[must_use]
947 pub fn stack_effect_jump(&self, oparg: u32) -> i32
948 );
949
950 either_real_pseudo!(
951 #[must_use]
952 pub fn stack_effect_info(&self, oparg: u32) -> StackEffect
953 );
954}
955
956impl From<Instruction> for AnyInstruction {
957 fn from(value: Instruction) -> Self {
958 Self::Real(value)
959 }
960}
961
962impl From<PseudoInstruction> for AnyInstruction {
963 fn from(value: PseudoInstruction) -> Self {
964 Self::Pseudo(value)
965 }
966}
967
968impl TryFrom<u8> for AnyInstruction {
969 type Error = MarshalError;
970
971 fn try_from(value: u8) -> Result<Self, Self::Error> {
972 Ok(Instruction::try_from(value)?.into())
973 }
974}
975
976impl TryFrom<u16> for AnyInstruction {
977 type Error = MarshalError;
978
979 fn try_from(value: u16) -> Result<Self, Self::Error> {
980 match u8::try_from(value) {
981 Ok(v) => v.try_into(),
982 Err(_) => Ok(PseudoInstruction::try_from(value)?.into()),
983 }
984 }
985}
986
987impl From<Opcode> for AnyInstruction {
988 fn from(value: Opcode) -> Self {
989 Self::Real(value.into())
990 }
991}
992
993impl From<PseudoOpcode> for AnyInstruction {
994 fn from(value: PseudoOpcode) -> Self {
995 Self::Pseudo(value.into())
996 }
997}
998
999impl From<AnyOpcode> for AnyInstruction {
1000 fn from(value: AnyOpcode) -> Self {
1001 match value {
1002 AnyOpcode::Real(op) => op.into(),
1003 AnyOpcode::Pseudo(op) => op.into(),
1004 }
1005 }
1006}
1007
1008impl AnyInstruction {
1009 #[must_use]
1011 pub const fn real(self) -> Option<Instruction> {
1012 match self {
1013 Self::Real(ins) => Some(ins),
1014 _ => None,
1015 }
1016 }
1017
1018 #[must_use]
1020 pub const fn pseudo(self) -> Option<PseudoInstruction> {
1021 match self {
1022 Self::Pseudo(ins) => Some(ins),
1023 _ => None,
1024 }
1025 }
1026
1027 #[must_use]
1029 pub const fn real_opcode(self) -> Option<Opcode> {
1030 match self.real() {
1031 Some(ins) => Some(ins.as_opcode()),
1032 _ => None,
1033 }
1034 }
1035
1036 #[must_use]
1038 pub const fn pseudo_opcode(self) -> Option<PseudoOpcode> {
1039 match self.pseudo() {
1040 Some(ins) => Some(ins.as_opcode()),
1041 _ => None,
1042 }
1043 }
1044
1045 #[must_use]
1051 pub const fn expect_real(self) -> Instruction {
1052 self.real()
1053 .expect("Expected AnyInstruction::Real, found AnyInstruction::Pseudo")
1054 }
1055
1056 #[must_use]
1062 pub const fn expect_pseudo(self) -> PseudoInstruction {
1063 self.pseudo()
1064 .expect("Expected AnyInstruction::Pseudo, found AnyInstruction::Real")
1065 }
1066
1067 #[must_use]
1069 pub const fn is_pop_block(self) -> bool {
1070 matches!(self, Self::Pseudo(PseudoInstruction::PopBlock))
1071 }
1072
1073 #[must_use]
1075 pub const fn is_block_push(self) -> bool {
1076 matches!(self, Self::Pseudo(p) if p.is_block_push())
1077 }
1078}
1079
1080#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1081pub enum AnyOpcode {
1082 Real(Opcode),
1083 Pseudo(PseudoOpcode),
1084}
1085
1086impl From<Opcode> for AnyOpcode {
1087 fn from(value: Opcode) -> Self {
1088 Self::Real(value)
1089 }
1090}
1091
1092impl From<PseudoOpcode> for AnyOpcode {
1093 fn from(value: PseudoOpcode) -> Self {
1094 Self::Pseudo(value)
1095 }
1096}
1097
1098impl TryFrom<u8> for AnyOpcode {
1099 type Error = MarshalError;
1100
1101 fn try_from(value: u8) -> Result<Self, Self::Error> {
1102 Ok(Opcode::try_from(value)?.into())
1103 }
1104}
1105
1106impl TryFrom<u16> for AnyOpcode {
1107 type Error = MarshalError;
1108
1109 fn try_from(value: u16) -> Result<Self, Self::Error> {
1110 match u8::try_from(value) {
1111 Ok(v) => v.try_into(),
1112 Err(_) => Ok(PseudoOpcode::try_from(value)?.into()),
1113 }
1114 }
1115}
1116
1117impl From<AnyInstruction> for AnyOpcode {
1118 fn from(value: AnyInstruction) -> Self {
1119 match value {
1120 AnyInstruction::Real(instr) => Self::Real(instr.into()),
1121 AnyInstruction::Pseudo(instr) => Self::Pseudo(instr.into()),
1122 }
1123 }
1124}
1125
1126impl AnyOpcode {
1127 #[must_use]
1129 pub const fn real(self) -> Option<Opcode> {
1130 match self {
1131 Self::Real(op) => Some(op),
1132 _ => None,
1133 }
1134 }
1135
1136 #[must_use]
1138 pub const fn pseudo(self) -> Option<PseudoOpcode> {
1139 match self {
1140 Self::Pseudo(op) => Some(op),
1141 _ => None,
1142 }
1143 }
1144
1145 #[must_use]
1151 pub const fn expect_real(self) -> Opcode {
1152 self.real()
1153 .expect("Expected AnyOpcode::Real, found AnyOpcode::Pseudo")
1154 }
1155
1156 #[must_use]
1162 pub const fn expect_pseudo(self) -> PseudoOpcode {
1163 self.pseudo()
1164 .expect("Expected AnyOpcode::Pseudo, found AnyOpcode::Real")
1165 }
1166
1167 either_real_pseudo!(
1168 #[must_use]
1169 pub const fn has_arg(&self) -> bool
1170 );
1171
1172 either_real_pseudo!(
1173 #[must_use]
1174 pub const fn has_jump(&self) -> bool
1175 );
1176
1177 either_real_pseudo!(
1178 #[must_use]
1179 pub const fn has_free(&self) -> bool
1180 );
1181
1182 either_real_pseudo!(
1183 #[must_use]
1184 pub const fn has_local(&self) -> bool
1185 );
1186
1187 either_real_pseudo!(
1188 #[must_use]
1189 pub const fn has_name(&self) -> bool
1190 );
1191
1192 either_real_pseudo!(
1193 #[must_use]
1194 pub const fn has_const(&self) -> bool
1195 );
1196
1197 either_real_pseudo!(
1198 #[must_use]
1199 pub const fn is_instrumented(&self) -> bool
1200 );
1201
1202 either_real_pseudo!(
1203 #[must_use]
1204 pub const fn is_block_push(&self) -> bool
1205 );
1206
1207 either_real_pseudo!(
1208 #[must_use]
1209 pub fn stack_effect_jump(&self, oparg: u32) -> i32
1210 );
1211
1212 either_real_pseudo!(
1213 #[must_use]
1214 pub fn stack_effect(&self, oparg: u32) -> i32
1215 );
1216
1217 #[must_use]
1218 pub const fn deopt(&self) -> Option<Self> {
1219 match self {
1220 Self::Real(opcode) => {
1221 if let Some(op) = opcode.deopt() {
1222 Some(Self::Real(op))
1223 } else {
1224 None
1225 }
1226 }
1227 Self::Pseudo(opcode) => {
1228 if let Some(op) = opcode.deopt() {
1229 Some(Self::Pseudo(op))
1230 } else {
1231 None
1232 }
1233 }
1234 }
1235 }
1236}
1237
1238#[derive(Clone, Copy)]
1240pub struct StackEffect {
1241 pushed: u32,
1243 popped: u32,
1245}
1246
1247impl StackEffect {
1248 #[must_use]
1250 pub const fn new(pushed: u32, popped: u32) -> Self {
1251 Self { pushed, popped }
1252 }
1253
1254 #[must_use]
1256 pub fn effect(self) -> i32 {
1257 self.into()
1258 }
1259
1260 #[must_use]
1262 pub const fn pushed(self) -> u32 {
1263 self.pushed
1264 }
1265
1266 #[must_use]
1268 pub const fn popped(self) -> u32 {
1269 self.popped
1270 }
1271}
1272
1273impl From<StackEffect> for i32 {
1274 fn from(effect: StackEffect) -> Self {
1275 (effect.pushed() as Self) - (effect.popped() as Self)
1276 }
1277}
1278
1279#[derive(Copy, Clone)]
1280pub struct Arg<T: OpArgType>(PhantomData<T>);
1281
1282impl<T: OpArgType> Arg<T> {
1283 #[inline]
1284 #[must_use]
1285 pub const fn marker() -> Self {
1286 Self(PhantomData)
1287 }
1288
1289 #[inline]
1290 pub fn new(arg: T) -> (Self, OpArg) {
1291 (Self(PhantomData), OpArg::new(arg.into()))
1292 }
1293
1294 #[inline]
1295 pub fn new_single(arg: T) -> (Self, OpArgByte)
1296 where
1297 T: Into<u8>,
1298 {
1299 (Self(PhantomData), OpArgByte::new(arg.into()))
1300 }
1301
1302 #[inline(always)]
1303 #[must_use]
1304 pub fn get(self, arg: OpArg) -> T {
1305 self.try_get(arg).unwrap()
1306 }
1307
1308 #[inline(always)]
1309 pub fn try_get(self, arg: OpArg) -> Result<T, MarshalError> {
1310 T::try_from(u32::from(arg)).map_err(|_| MarshalError::InvalidBytecode)
1311 }
1312
1313 #[inline(always)]
1316 #[must_use]
1317 pub unsafe fn get_unchecked(self, arg: OpArg) -> T {
1318 unsafe { T::try_from(u32::from(arg)).unwrap_unchecked() }
1320 }
1321}
1322
1323impl<T: OpArgType> PartialEq for Arg<T> {
1324 fn eq(&self, _: &Self) -> bool {
1325 true
1326 }
1327}
1328
1329impl<T: OpArgType> Eq for Arg<T> {}
1330
1331impl<T: OpArgType> fmt::Debug for Arg<T> {
1332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1333 write!(f, "Arg<{}>", core::any::type_name::<T>())
1334 }
1335}
1336
1337const _: () = assert!(core::mem::size_of::<Instruction>() == 1);
1340const _: () = assert!(core::mem::size_of::<PseudoInstruction>() == 2);
1341
1342#[cfg(test)]
1343mod tests {
1344 use super::*;
1345
1346 #[test]
1347 fn eval_break_flags_match_cpython_jump_metadata() {
1348 assert!(Opcode::JumpBackward.has_eval_break());
1349 assert!(!Opcode::JumpBackwardNoInterrupt.has_eval_break());
1350 assert!(!Opcode::JumpForward.has_eval_break());
1351
1352 assert!(PseudoOpcode::Jump.has_eval_break());
1353 assert!(!PseudoOpcode::JumpIfFalse.has_eval_break());
1354 assert!(!PseudoOpcode::JumpIfTrue.has_eval_break());
1355 assert!(!PseudoOpcode::JumpNoInterrupt.has_eval_break());
1356
1357 assert!(AnyInstruction::from(PseudoOpcode::Jump).has_eval_break());
1358 }
1359
1360 #[test]
1361 fn terminator_flags_match_cpython_opcode_utils() {
1362 assert!(Opcode::JumpForward.is_terminator());
1363 assert!(Opcode::PopJumpIfFalse.is_terminator());
1364 assert!(Opcode::ForIter.is_terminator());
1365 assert!(Opcode::ReturnValue.is_terminator());
1366 assert!(!Opcode::Nop.is_terminator());
1367
1368 assert!(PseudoOpcode::JumpIfTrue.is_terminator());
1369 assert!(PseudoOpcode::JumpNoInterrupt.is_terminator());
1370 assert!(!PseudoOpcode::SetupFinally.is_terminator());
1371 assert!(!PseudoOpcode::SetupWith.is_terminator());
1372 assert!(!PseudoOpcode::SetupCleanup.is_terminator());
1373 assert!(!PseudoOpcode::PopBlock.is_terminator());
1374
1375 assert!(AnyInstruction::from(PseudoOpcode::JumpIfFalse).is_terminator());
1376 }
1377
1378 #[test]
1379 fn assembler_flags_match_cpython_opcode_utils() {
1380 assert!(Opcode::JumpForward.is_assembler());
1381 assert!(Opcode::JumpBackward.is_assembler());
1382 assert!(Opcode::JumpBackwardNoInterrupt.is_assembler());
1383 assert!(!Opcode::PopJumpIfFalse.is_assembler());
1384 assert!(!Opcode::Nop.is_assembler());
1385
1386 assert!(!PseudoOpcode::Jump.is_assembler());
1387 assert!(!PseudoOpcode::JumpNoInterrupt.is_assembler());
1388 assert!(!AnyInstruction::from(PseudoOpcode::Jump).is_assembler());
1389 }
1390
1391 #[test]
1392 fn target_flags_match_cpython_opcode_utils() {
1393 assert!(Opcode::JumpForward.has_target());
1394 assert!(Opcode::ForIter.has_target());
1395 assert!(!Opcode::ReturnValue.has_target());
1396 assert!(!Opcode::Nop.has_target());
1397
1398 assert!(PseudoOpcode::Jump.has_target());
1399 assert!(PseudoOpcode::SetupFinally.has_target());
1400 assert!(PseudoOpcode::SetupWith.has_target());
1401 assert!(PseudoOpcode::SetupCleanup.has_target());
1402 assert!(!PseudoOpcode::PopBlock.has_target());
1403
1404 assert!(AnyInstruction::from(PseudoOpcode::SetupFinally).has_target());
1405 }
1406
1407 #[test]
1408 fn arg_flags_match_cpython_opcode_metadata() {
1409 assert!(Opcode::LoadConst.has_arg());
1410 assert!(Opcode::YieldValue.has_arg());
1411 assert!(!Opcode::Nop.has_arg());
1412 assert!(!Opcode::ReturnValue.has_arg());
1413
1414 assert!(PseudoOpcode::Jump.has_arg());
1415 assert!(PseudoOpcode::JumpIfFalse.has_arg());
1416 assert!(PseudoOpcode::JumpIfTrue.has_arg());
1417 assert!(PseudoOpcode::JumpNoInterrupt.has_arg());
1418 assert!(PseudoOpcode::LoadClosure.has_arg());
1419 assert!(PseudoOpcode::SetupCleanup.has_arg());
1420 assert!(PseudoOpcode::SetupFinally.has_arg());
1421 assert!(PseudoOpcode::SetupWith.has_arg());
1422 assert!(PseudoOpcode::StoreFastMaybeNull.has_arg());
1423 assert!(!PseudoOpcode::AnnotationsPlaceholder.has_arg());
1424 assert!(!PseudoOpcode::PopBlock.has_arg());
1425 }
1426
1427 #[test]
1428 fn const_flags_match_cpython_opcode_metadata() {
1429 assert!(Opcode::LoadConst.has_const());
1430 assert!(Opcode::LoadConstImmortal.has_const());
1431 assert!(Opcode::LoadConstMortal.has_const());
1432 assert!(!Opcode::LoadSmallInt.has_const());
1433 assert!(!Opcode::Nop.has_const());
1434
1435 assert!(!PseudoOpcode::LoadClosure.has_const());
1436 assert!(!AnyInstruction::from(PseudoOpcode::Jump).has_const());
1437 }
1438
1439 #[test]
1440 fn stack_effects_match_cpython_opcode_metadata() {
1441 assert_eq!(Opcode::ForIter.stack_effect_info(0).popped(), 1);
1442 assert_eq!(Opcode::ForIter.stack_effect_info(0).pushed(), 2);
1443 assert_eq!(Opcode::ForIter.stack_effect(0), 1);
1444 assert_eq!(Opcode::ForIter.stack_effect_jump(0), 1);
1445
1446 assert_eq!(Opcode::EndAsyncFor.stack_effect_info(0).popped(), 2);
1447 assert_eq!(Opcode::EndAsyncFor.stack_effect_info(0).pushed(), 0);
1448 assert_eq!(Opcode::PopJumpIfFalse.stack_effect(0), -1);
1449 assert_eq!(Opcode::PopJumpIfFalse.stack_effect_jump(0), -1);
1450
1451 assert_eq!(PseudoOpcode::SetupFinally.stack_effect_info(0).pushed(), 1);
1452 assert_eq!(PseudoOpcode::SetupFinally.stack_effect(0), 0);
1453 assert_eq!(PseudoOpcode::SetupFinally.stack_effect_jump(0), 1);
1454 assert_eq!(PseudoOpcode::SetupCleanup.stack_effect_info(0).pushed(), 2);
1455 assert_eq!(PseudoOpcode::SetupCleanup.stack_effect(0), 0);
1456 assert_eq!(PseudoOpcode::SetupCleanup.stack_effect_jump(0), 2);
1457 }
1458
1459 #[test]
1460 fn no_fallthrough_flags_match_cpython_basicblock_nofallthrough() {
1461 assert!(Opcode::JumpForward.is_no_fallthrough());
1462 assert!(Opcode::ReturnValue.is_no_fallthrough());
1463 assert!(!Opcode::PopJumpIfFalse.is_no_fallthrough());
1464 assert!(!Opcode::ForIter.is_no_fallthrough());
1465 assert!(!Opcode::Nop.is_no_fallthrough());
1466
1467 assert!(PseudoOpcode::Jump.is_no_fallthrough());
1468 assert!(PseudoOpcode::JumpNoInterrupt.is_no_fallthrough());
1469 assert!(!PseudoOpcode::JumpIfFalse.is_no_fallthrough());
1470 assert!(!PseudoOpcode::SetupFinally.is_no_fallthrough());
1471 assert!(!PseudoOpcode::SetupWith.is_no_fallthrough());
1472
1473 assert!(AnyInstruction::from(PseudoOpcode::Jump).is_no_fallthrough());
1474 }
1475
1476 mod reference {
1481 use super::Opcode;
1482
1483 pub(super) const fn deopt(op: Opcode) -> Option<Opcode> {
1484 Some(match op {
1485 Opcode::ResumeCheck => Opcode::Resume,
1486 Opcode::LoadConstMortal | Opcode::LoadConstImmortal => Opcode::LoadConst,
1487 Opcode::ToBoolAlwaysTrue
1488 | Opcode::ToBoolBool
1489 | Opcode::ToBoolInt
1490 | Opcode::ToBoolList
1491 | Opcode::ToBoolNone
1492 | Opcode::ToBoolStr => Opcode::ToBool,
1493 Opcode::BinaryOpMultiplyInt
1494 | Opcode::BinaryOpAddInt
1495 | Opcode::BinaryOpSubtractInt
1496 | Opcode::BinaryOpMultiplyFloat
1497 | Opcode::BinaryOpAddFloat
1498 | Opcode::BinaryOpSubtractFloat
1499 | Opcode::BinaryOpAddUnicode
1500 | Opcode::BinaryOpSubscrListInt
1501 | Opcode::BinaryOpSubscrListSlice
1502 | Opcode::BinaryOpSubscrTupleInt
1503 | Opcode::BinaryOpSubscrStrInt
1504 | Opcode::BinaryOpSubscrDict
1505 | Opcode::BinaryOpSubscrGetitem
1506 | Opcode::BinaryOpExtend
1507 | Opcode::BinaryOpInplaceAddUnicode => Opcode::BinaryOp,
1508 Opcode::StoreSubscrDict | Opcode::StoreSubscrListInt => Opcode::StoreSubscr,
1509 Opcode::SendGen => Opcode::Send,
1510 Opcode::UnpackSequenceTwoTuple
1511 | Opcode::UnpackSequenceTuple
1512 | Opcode::UnpackSequenceList => Opcode::UnpackSequence,
1513 Opcode::StoreAttrInstanceValue
1514 | Opcode::StoreAttrSlot
1515 | Opcode::StoreAttrWithHint => Opcode::StoreAttr,
1516 Opcode::LoadGlobalModule | Opcode::LoadGlobalBuiltin => Opcode::LoadGlobal,
1517 Opcode::LoadSuperAttrAttr | Opcode::LoadSuperAttrMethod => Opcode::LoadSuperAttr,
1518 Opcode::LoadAttrInstanceValue
1519 | Opcode::LoadAttrModule
1520 | Opcode::LoadAttrWithHint
1521 | Opcode::LoadAttrSlot
1522 | Opcode::LoadAttrClass
1523 | Opcode::LoadAttrClassWithMetaclassCheck
1524 | Opcode::LoadAttrProperty
1525 | Opcode::LoadAttrGetattributeOverridden
1526 | Opcode::LoadAttrMethodWithValues
1527 | Opcode::LoadAttrMethodNoDict
1528 | Opcode::LoadAttrMethodLazyDict
1529 | Opcode::LoadAttrNondescriptorWithValues
1530 | Opcode::LoadAttrNondescriptorNoDict => Opcode::LoadAttr,
1531 Opcode::CompareOpFloat | Opcode::CompareOpInt | Opcode::CompareOpStr => {
1532 Opcode::CompareOp
1533 }
1534 Opcode::ContainsOpSet | Opcode::ContainsOpDict => Opcode::ContainsOp,
1535 Opcode::JumpBackwardNoJit | Opcode::JumpBackwardJit => Opcode::JumpBackward,
1536 Opcode::ForIterList
1537 | Opcode::ForIterTuple
1538 | Opcode::ForIterRange
1539 | Opcode::ForIterGen => Opcode::ForIter,
1540 Opcode::CallBoundMethodExactArgs
1541 | Opcode::CallPyExactArgs
1542 | Opcode::CallType1
1543 | Opcode::CallStr1
1544 | Opcode::CallTuple1
1545 | Opcode::CallBuiltinClass
1546 | Opcode::CallBuiltinO
1547 | Opcode::CallBuiltinFast
1548 | Opcode::CallBuiltinFastWithKeywords
1549 | Opcode::CallLen
1550 | Opcode::CallIsinstance
1551 | Opcode::CallListAppend
1552 | Opcode::CallMethodDescriptorO
1553 | Opcode::CallMethodDescriptorFastWithKeywords
1554 | Opcode::CallMethodDescriptorNoargs
1555 | Opcode::CallMethodDescriptorFast
1556 | Opcode::CallAllocAndEnterInit
1557 | Opcode::CallPyGeneral
1558 | Opcode::CallBoundMethodGeneral
1559 | Opcode::CallNonPyGeneral => Opcode::Call,
1560 Opcode::CallKwBoundMethod | Opcode::CallKwPy | Opcode::CallKwNonPy => {
1561 Opcode::CallKw
1562 }
1563 _ => return None,
1564 })
1565 }
1566
1567 pub(super) const fn deoptimize(op: Opcode) -> Opcode {
1568 match deopt(op) {
1569 Some(v) => v,
1570 None => match op.to_base() {
1571 Some(v) => v,
1572 None => op,
1573 },
1574 }
1575 }
1576
1577 pub(super) const fn cache_entries(op: Opcode) -> usize {
1578 match deoptimize(op) {
1579 Opcode::StoreSubscr => 1,
1580 Opcode::ToBool => 3,
1581 Opcode::BinaryOp => 5,
1582 Opcode::Call => 3,
1583 Opcode::CallKw => 3,
1584 Opcode::CompareOp => 1,
1585 Opcode::ContainsOp => 1,
1586 Opcode::ForIter => 1,
1587 Opcode::JumpBackward => 1,
1588 Opcode::LoadAttr => 9,
1589 Opcode::LoadGlobal => 4,
1590 Opcode::LoadSuperAttr => 1,
1591 Opcode::PopJumpIfFalse => 1,
1592 Opcode::PopJumpIfNone => 1,
1593 Opcode::PopJumpIfNotNone => 1,
1594 Opcode::PopJumpIfTrue => 1,
1595 Opcode::Send => 1,
1596 Opcode::StoreAttr => 4,
1597 Opcode::UnpackSequence => 1,
1598 _ => 0,
1599 }
1600 }
1601 }
1602
1603 #[test]
1604 fn cache_entries_and_deopt_tables_match_reference_impl() {
1605 let mut checked = 0;
1606 for byte in 0u8..=255 {
1607 let Ok(op) = Opcode::try_from_u8(byte) else {
1608 continue;
1609 };
1610
1611 assert_eq!(
1612 op.deopt(),
1613 reference::deopt(op),
1614 "deopt() mismatch for {op:?}"
1615 );
1616 assert_eq!(
1617 op.cache_entries(),
1618 reference::cache_entries(op),
1619 "cache_entries() mismatch for {op:?}"
1620 );
1621 checked += 1;
1622 }
1623
1624 assert!(checked > 200);
1627 }
1628
1629 #[test]
1637 fn opcode_instruction_numeric_conversions_match_try_from_numeric() {
1638 let mut checked = 0;
1639 for byte in 0u8..=255 {
1640 let Ok(op) = Opcode::try_from_u8(byte) else {
1641 continue;
1642 };
1643
1644 assert_eq!(op.as_numeric(), byte, "as_numeric() mismatch for {op:?}");
1645
1646 let instr = op.as_instruction();
1647 assert_eq!(
1648 instr.as_opcode(),
1649 op,
1650 "as_instruction()/as_opcode() round trip mismatch for {op:?}"
1651 );
1652
1653 let instr_via_try_from = Instruction::try_from(byte).unwrap();
1654 assert_eq!(
1655 instr_via_try_from.as_opcode(),
1656 op,
1657 "Instruction::try_from({byte}) mismatch"
1658 );
1659
1660 checked += 1;
1661 }
1662
1663 assert!(checked > 200);
1664 }
1665
1666 #[test]
1670 fn pseudo_opcode_instruction_numeric_conversions_match_try_from_numeric() {
1671 let mut checked = 0;
1672 for value in 0u16..=u16::MAX {
1673 let Ok(op) = PseudoOpcode::try_from_u16(value) else {
1674 continue;
1675 };
1676
1677 assert_eq!(op.as_numeric(), value, "as_numeric() mismatch for {op:?}");
1678
1679 let instr = op.as_instruction();
1680 assert_eq!(
1681 instr.as_opcode(),
1682 op,
1683 "as_instruction()/as_opcode() round trip mismatch for {op:?}"
1684 );
1685
1686 let instr_via_try_from = PseudoInstruction::try_from(value).unwrap();
1687 assert_eq!(
1688 instr_via_try_from.as_opcode(),
1689 op,
1690 "PseudoInstruction::try_from({value}) mismatch"
1691 );
1692
1693 checked += 1;
1694 }
1695
1696 assert_eq!(checked, 11);
1698 }
1699}