pulley_interpreter/
decode.rs

1//! Decoding support for pulley bytecode.
2
3use crate::imms::*;
4use crate::opcode::*;
5use crate::regs::*;
6use alloc::vec::Vec;
7use core::convert::Infallible;
8use core::ptr::NonNull;
9use cranelift_bitset::ScalarBitSet;
10use cranelift_bitset::scalar::ScalarBitSetStorage;
11
12/// Either an `Ok(T)` or an `Err(DecodingError)`.
13pub type Result<T, E = DecodingError> = core::result::Result<T, E>;
14
15/// An error when decoding Pulley bytecode.
16pub enum DecodingError {
17    /// Reached the end of the bytecode stream before we finished decoding a
18    /// single bytecode.
19    UnexpectedEof {
20        /// The position in the bytecode stream where this error occurred.
21        position: usize,
22    },
23
24    /// Found an invalid opcode.
25    InvalidOpcode {
26        /// The position in the bytecode stream where this error occurred.
27        position: usize,
28        /// The invalid opcode that was found.
29        code: u8,
30    },
31
32    /// Found an invalid extended opcode.
33    InvalidExtendedOpcode {
34        /// The position in the bytecode stream where this error occurred.
35        position: usize,
36        /// The invalid extended opcode that was found.
37        code: u16,
38    },
39
40    /// Found an invalid register.
41    InvalidReg {
42        /// The position in the bytecode stream where this error occurred.
43        position: usize,
44        /// The invalid register that was found.
45        reg: u8,
46    },
47}
48
49impl core::fmt::Debug for DecodingError {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        core::fmt::Display::fmt(self, f)
52    }
53}
54
55impl core::fmt::Display for DecodingError {
56    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57        match self {
58            Self::UnexpectedEof { position } => {
59                write!(f, "unexpected end-of-file at bytecode offset {position:#x}")
60            }
61            Self::InvalidOpcode { position, code } => {
62                write!(
63                    f,
64                    "found invalid opcode {code:#x} at bytecode offset {position:#x}"
65                )
66            }
67            Self::InvalidExtendedOpcode { position, code } => {
68                write!(
69                    f,
70                    "found invalid opcode {code:#x} at bytecode offset {position:#x}"
71                )
72            }
73            Self::InvalidReg { position, reg } => {
74                write!(
75                    f,
76                    "found invalid register {reg:#x} at bytecode offset {position:#x}"
77                )
78            }
79        }
80    }
81}
82
83#[cfg(feature = "std")]
84impl std::error::Error for DecodingError {}
85
86/// An abstraction over any kind of bytecode stream.
87///
88/// There are two primary implementations:
89///
90/// 1. `SafeBytecodeStream`: A thin wrapper around an index into a `&[u8]`. This
91///    implementation is 100% safe code.
92///
93/// 2. `UnsafeBytecodeStream`: A thin wrapper over a raw pointer. This
94///    implementation is wildly unsafe and will result in memory unsafety and
95///    other terrors when given invalid bytecode, or even valid bytecode
96///    encoding a program that itself does not preserve memory safety.
97pub trait BytecodeStream: Copy {
98    /// The type of error that this bytecode stream produces on invalid
99    /// operations.
100    type Error;
101
102    /// Create an "unexpected end-of-stream" error at the current position.
103    fn unexpected_eof(&self) -> Self::Error;
104
105    /// Create an "invalid opcode" error at the current position.
106    fn invalid_opcode(&self, code: u8) -> Self::Error;
107
108    /// Create an "invalid extended opcode" error at the current position.
109    fn invalid_extended_opcode(&self, code: u16) -> Self::Error;
110
111    /// Create an "invalid register" error at the current position.
112    fn invalid_reg(&self, reg: u8) -> Self::Error;
113
114    /// Read `N` bytes from this bytecode stream, advancing the stream's
115    /// position at the same time.
116    fn read<const N: usize>(&mut self) -> Result<[u8; N], Self::Error>;
117}
118
119/// A 100% safe implementation of a bytecode stream.
120///
121/// This is a thin wrapper around an index into a `&[u8]`.
122#[derive(Clone, Copy, Debug)]
123pub struct SafeBytecodeStream<'a> {
124    bytecode: &'a [u8],
125    position: usize,
126}
127
128impl<'a> SafeBytecodeStream<'a> {
129    /// Create a new `SafeBytecodeStream` from the given slice and with an
130    /// initial position pointing at the start of the slice.
131    pub fn new(bytecode: &'a [u8]) -> Self {
132        Self {
133            bytecode,
134            position: 0,
135        }
136    }
137
138    /// Get this stream's current position within its underlying slice.
139    pub fn position(&self) -> usize {
140        self.position
141    }
142
143    /// Get this stream's underlying bytecode slice.
144    pub fn as_slice(&self) -> &[u8] {
145        &self.bytecode
146    }
147}
148
149impl BytecodeStream for SafeBytecodeStream<'_> {
150    fn read<const N: usize>(&mut self) -> Result<[u8; N], Self::Error> {
151        let (bytes, rest) = self
152            .bytecode
153            .split_first_chunk()
154            .ok_or_else(|| self.unexpected_eof())?;
155        self.bytecode = rest;
156        self.position += N;
157        Ok(*bytes)
158    }
159
160    type Error = DecodingError;
161
162    fn unexpected_eof(&self) -> Self::Error {
163        DecodingError::UnexpectedEof {
164            position: self.position,
165        }
166    }
167
168    fn invalid_opcode(&self, code: u8) -> Self::Error {
169        DecodingError::InvalidOpcode {
170            position: self.position - 1,
171            code,
172        }
173    }
174
175    fn invalid_extended_opcode(&self, code: u16) -> Self::Error {
176        DecodingError::InvalidExtendedOpcode {
177            position: self.position,
178            code,
179        }
180    }
181
182    fn invalid_reg(&self, reg: u8) -> Self::Error {
183        DecodingError::InvalidReg {
184            position: self.position,
185            reg,
186        }
187    }
188}
189
190/// An unsafe bytecode stream.
191///
192/// This is a wrapper over a raw pointer to bytecode somewhere in memory.
193#[derive(Clone, Copy, Debug)]
194pub struct UnsafeBytecodeStream(NonNull<u8>);
195
196impl UnsafeBytecodeStream {
197    /// Construct a new `UnsafeBytecodeStream` pointing at the given PC.
198    ///
199    /// # Safety
200    ///
201    /// The given `pc` must point to valid Pulley bytecode, and it is the
202    /// caller's responsibility to ensure that the resulting
203    /// `UnsafeBytecodeStream` is only used to access the valid bytecode. For
204    /// example, if the current bytecode instruction unconditionally jumps to a
205    /// new PC, this stream must not be used to read just after the
206    /// unconditional jump instruction because there is no guarantee that that
207    /// memory is part of the bytecode stream or not.
208    pub unsafe fn new(pc: NonNull<u8>) -> Self {
209        UnsafeBytecodeStream(pc)
210    }
211
212    /// Get a new `UnsafeBytecodeStream` pointing at the bytecode that is at the
213    /// given relative offset from this stream's current position.
214    ///
215    /// # Safety
216    ///
217    /// Same as the `new` constructor. May only be used when it is guaranteed
218    /// that the address at `self._as_ptr() + offset` contains valid Pulley
219    /// bytecode.
220    pub unsafe fn offset(&self, offset: isize) -> Self {
221        UnsafeBytecodeStream(unsafe { NonNull::new_unchecked(self.0.as_ptr().offset(offset)) })
222    }
223
224    /// Get this stream's underlying raw pointer.
225    pub fn as_ptr(&self) -> NonNull<u8> {
226        self.0
227    }
228}
229
230impl BytecodeStream for UnsafeBytecodeStream {
231    fn read<const N: usize>(&mut self) -> Result<[u8; N], Self::Error> {
232        let bytes = unsafe { self.0.cast::<[u8; N]>().as_ptr().read() };
233        self.0 = unsafe { NonNull::new_unchecked(self.0.as_ptr().add(N)) };
234        Ok(bytes)
235    }
236
237    type Error = Infallible;
238
239    fn unexpected_eof(&self) -> Self::Error {
240        unsafe { crate::unreachable_unchecked() }
241    }
242
243    fn invalid_opcode(&self, _code: u8) -> Self::Error {
244        unsafe { crate::unreachable_unchecked() }
245    }
246
247    fn invalid_extended_opcode(&self, _code: u16) -> Self::Error {
248        unsafe { crate::unreachable_unchecked() }
249    }
250
251    fn invalid_reg(&self, _reg: u8) -> Self::Error {
252        unsafe { crate::unreachable_unchecked() }
253    }
254}
255
256/// Anything that can be decoded from a bytecode stream, e.g. opcodes,
257/// immediates, registers, etc...
258pub trait Decode: Sized {
259    /// Decode this type from the given bytecode stream.
260    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
261    where
262        T: BytecodeStream;
263}
264
265impl Decode for u8 {
266    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
267    where
268        T: BytecodeStream,
269    {
270        bytecode.read::<1>().map(|a| a[0])
271    }
272}
273
274impl Decode for u16 {
275    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
276    where
277        T: BytecodeStream,
278    {
279        Ok(u16::from_le_bytes(bytecode.read()?))
280    }
281}
282
283impl Decode for u32 {
284    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
285    where
286        T: BytecodeStream,
287    {
288        Ok(u32::from_le_bytes(bytecode.read()?))
289    }
290}
291
292impl Decode for u64 {
293    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
294    where
295        T: BytecodeStream,
296    {
297        Ok(u64::from_le_bytes(bytecode.read()?))
298    }
299}
300
301impl Decode for u128 {
302    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
303    where
304        T: BytecodeStream,
305    {
306        Ok(u128::from_le_bytes(bytecode.read()?))
307    }
308}
309
310impl Decode for i8 {
311    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
312    where
313        T: BytecodeStream,
314    {
315        bytecode.read::<1>().map(|a| a[0] as i8)
316    }
317}
318
319impl Decode for i16 {
320    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
321    where
322        T: BytecodeStream,
323    {
324        Ok(i16::from_le_bytes(bytecode.read()?))
325    }
326}
327
328impl Decode for i32 {
329    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
330    where
331        T: BytecodeStream,
332    {
333        Ok(i32::from_le_bytes(bytecode.read()?))
334    }
335}
336
337impl Decode for i64 {
338    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
339    where
340        T: BytecodeStream,
341    {
342        Ok(i64::from_le_bytes(bytecode.read()?))
343    }
344}
345
346impl Decode for i128 {
347    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
348    where
349        T: BytecodeStream,
350    {
351        Ok(i128::from_le_bytes(bytecode.read()?))
352    }
353}
354
355impl Decode for XReg {
356    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
357    where
358        T: BytecodeStream,
359    {
360        let byte = u8::decode(bytecode)?;
361        XReg::new(byte).ok_or_else(|| bytecode.invalid_reg(byte))
362    }
363}
364
365impl Decode for FReg {
366    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
367    where
368        T: BytecodeStream,
369    {
370        let byte = u8::decode(bytecode)?;
371        FReg::new(byte).ok_or_else(|| bytecode.invalid_reg(byte))
372    }
373}
374
375impl Decode for VReg {
376    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
377    where
378        T: BytecodeStream,
379    {
380        let byte = u8::decode(bytecode)?;
381        VReg::new(byte).ok_or_else(|| bytecode.invalid_reg(byte))
382    }
383}
384
385impl Decode for PcRelOffset {
386    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
387    where
388        T: BytecodeStream,
389    {
390        i32::decode(bytecode).map(|x| Self::from(x))
391    }
392}
393
394impl Decode for Opcode {
395    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
396    where
397        T: BytecodeStream,
398    {
399        let byte = u8::decode(bytecode)?;
400        match Opcode::new(byte) {
401            Some(v) => Ok(v),
402            None => Err(bytecode.invalid_opcode(byte)),
403        }
404    }
405}
406
407impl Decode for ExtendedOpcode {
408    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
409    where
410        T: BytecodeStream,
411    {
412        let word = u16::decode(bytecode)?;
413        match ExtendedOpcode::new(word) {
414            Some(v) => Ok(v),
415            None => Err(bytecode.invalid_extended_opcode(word)),
416        }
417    }
418}
419
420impl<D: Reg, S1: Reg, S2: Reg> Decode for BinaryOperands<D, S1, S2> {
421    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
422    where
423        T: BytecodeStream,
424    {
425        u16::decode(bytecode).map(|bits| Self::from_bits(bits))
426    }
427}
428
429impl<D: Reg, S1: Reg> Decode for BinaryOperands<D, S1, U6> {
430    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
431    where
432        T: BytecodeStream,
433    {
434        u16::decode(bytecode).map(|bits| Self::from_bits(bits))
435    }
436}
437
438impl<S: Decode + ScalarBitSetStorage> Decode for ScalarBitSet<S> {
439    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
440    where
441        T: BytecodeStream,
442    {
443        S::decode(bytecode).map(ScalarBitSet::from)
444    }
445}
446
447impl<R: Reg + Decode> Decode for UpperRegSet<R> {
448    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
449    where
450        T: BytecodeStream,
451    {
452        ScalarBitSet::decode(bytecode).map(Self::from)
453    }
454}
455
456impl Decode for AddrO32 {
457    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
458    where
459        T: BytecodeStream,
460    {
461        Ok(AddrO32 {
462            addr: XReg::decode(bytecode)?,
463            offset: i32::decode(bytecode)?,
464        })
465    }
466}
467
468impl Decode for AddrZ {
469    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
470    where
471        T: BytecodeStream,
472    {
473        Ok(AddrZ {
474            addr: XReg::decode(bytecode)?,
475            offset: i32::decode(bytecode)?,
476        })
477    }
478}
479
480impl Decode for AddrG32 {
481    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
482    where
483        T: BytecodeStream,
484    {
485        Ok(AddrG32::from_bits(u32::decode(bytecode)?))
486    }
487}
488
489impl Decode for AddrG32Bne {
490    fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
491    where
492        T: BytecodeStream,
493    {
494        Ok(AddrG32Bne::from_bits(u32::decode(bytecode)?))
495    }
496}
497
498/// A Pulley bytecode decoder.
499///
500/// Does not materialize bytecode instructions, instead all decoding methods are
501/// given an `OpVisitor` implementation and the appropriate visitor methods are
502/// called upon decoding an instruction. This minimizes the amount of times we
503/// branch on the opcode, avoids constructing temporary storage, and plays well
504/// with our variable-length instruction encoding.
505#[derive(Default)]
506pub struct Decoder {
507    _private: (),
508}
509
510impl Decoder {
511    /// Create a new decoder.
512    pub fn new() -> Self {
513        Self::default()
514    }
515
516    /// Decode all instructions in the visitor's bytecode stream.
517    ///
518    /// The associated visitor method is invoked after each instruction is
519    /// decoded.
520    pub fn decode_all<'a, V>(visitor: &mut V) -> Result<Vec<V::Return>>
521    where
522        V: OpVisitor<BytecodeStream = SafeBytecodeStream<'a>> + ExtendedOpVisitor,
523    {
524        let mut decoder = Decoder::new();
525        let mut results = Vec::new();
526
527        while !visitor.bytecode().as_slice().is_empty() {
528            results.push(decoder.decode_one(visitor)?);
529        }
530
531        Ok(results)
532    }
533}
534
535/// An `OpVisitor` combinator to sequence one visitor and then another.
536pub struct SequencedVisitor<'a, F, V1, V2> {
537    join: F,
538    v1: &'a mut V1,
539    v2: &'a mut V2,
540}
541
542impl<'a, F, V1, V2> SequencedVisitor<'a, F, V1, V2> {
543    /// Create a new sequenced visitor.
544    ///
545    /// The given `join` function is used to combine the results of each
546    /// sub-visitor so that it can be returned from this combined visitor.
547    pub fn new(join: F, v1: &'a mut V1, v2: &'a mut V2) -> Self {
548        SequencedVisitor { join, v1, v2 }
549    }
550}
551
552macro_rules! define_decoder {
553    (
554        $(
555            $( #[$attr:meta] )*
556                $snake_name:ident = $name:ident $( {
557                $(
558                    $( #[$field_attr:meta] )*
559                    $field:ident : $field_ty:ty
560                ),*
561            } )? ;
562        )*
563    ) => {
564        impl Decoder {
565            /// Decode one instruction from the visitor's bytestream.
566            ///
567            /// Upon decoding, the visitor's associated callback is invoked and
568            /// the results returned.
569            #[inline(always)]
570            pub fn decode_one<V>(
571                &mut self,
572                visitor: &mut V,
573            ) -> Result<V::Return, <V::BytecodeStream as BytecodeStream>::Error>
574            where
575                V: OpVisitor + ExtendedOpVisitor,
576            {
577                visitor.before_visit();
578
579                let byte = u8::decode(visitor.bytecode())?;
580                let opcode = Opcode::new(byte).ok_or_else(|| {
581                    visitor.bytecode().invalid_opcode(byte)
582                })?;
583
584                match opcode {
585                    $(
586                        Opcode::$name => {
587                            $(
588                                $(
589                                    let $field = <$field_ty>::decode(
590                                        visitor.bytecode(),
591                                    )?;
592                                )*
593                            )?
594
595                            let ret = visitor.$snake_name($( $( $field ),* )?);
596                            visitor.after_visit();
597                            Ok(ret)
598                        },
599                    )*
600                    Opcode::ExtendedOp => {
601                        decode_one_extended(visitor)
602                    }
603                }
604            }
605        }
606
607        /// Callbacks upon decoding instructions from bytecode.
608        ///
609        /// Implement this trait for your type, give an instance of your type to
610        /// a `Decoder` method, and the `Decoder` will invoke the associated
611        /// method for each instruction that it decodes. For example, if the
612        /// `Decoder` decodes an `xadd32` instruction, then it will invoke the
613        /// `xadd32` visitor method, passing along any decoded immediates,
614        /// operands, etc... as arguments.
615        pub trait OpVisitor {
616            /// The type of this visitor's bytecode stream.
617            type BytecodeStream: BytecodeStream;
618
619            /// Get this visitor's underlying bytecode stream.
620            fn bytecode(&mut self) -> &mut Self::BytecodeStream;
621
622            /// The type of values returned by each visitor method.
623            type Return;
624
625            /// A callback invoked before starting to decode an instruction.
626            ///
627            /// Does nothing by default.
628            fn before_visit(&mut self) {}
629
630            /// A callback invoked after an instruction has been completely
631            /// decoded.
632            ///
633            /// Does nothing by default.
634            fn after_visit(&mut self) {}
635
636            $(
637                $( #[$attr] )*
638                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return;
639            )*
640        }
641
642        impl<F, T, V1, V2> OpVisitor for SequencedVisitor<'_, F, V1, V2>
643        where
644            F: FnMut(V1::Return, V2::Return) -> T,
645            V1: OpVisitor,
646            V2: OpVisitor<BytecodeStream = V1::BytecodeStream>,
647        {
648            type BytecodeStream = V1::BytecodeStream;
649
650            fn bytecode(&mut self) -> &mut Self::BytecodeStream {
651                self.v1.bytecode()
652            }
653
654            type Return = T;
655
656            fn before_visit(&mut self) {
657                self.v1.before_visit();
658                self.v2.before_visit();
659            }
660
661            fn after_visit(&mut self) {
662                *self.v2.bytecode() = *self.v1.bytecode();
663                self.v1.after_visit();
664                self.v2.after_visit();
665            }
666
667            $(
668                $( #[$attr] )*
669                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return {
670                    let a = self.v1.$snake_name( $( $( $field , )* )? );
671                    let b = self.v2.$snake_name( $( $( $field , )* )? );
672                    (self.join)(a, b)
673                }
674            )*
675        }
676    };
677}
678for_each_op!(define_decoder);
679
680macro_rules! define_extended_decoder {
681    (
682        $(
683            $( #[$attr:meta] )*
684                $snake_name:ident = $name:ident $( {
685                $(
686                    $( #[$field_attr:meta] )*
687                    $field:ident : $field_ty:ty
688                ),*
689            } )? ;
690        )*
691    ) => {
692        /// Like `OpVisitor` but for extended operations.
693        pub trait ExtendedOpVisitor: OpVisitor {
694            $(
695                $( #[$attr] )*
696                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return;
697            )*
698        }
699
700        fn decode_one_extended<V>(
701            visitor: &mut V,
702        ) -> Result<V::Return, <V::BytecodeStream as BytecodeStream>::Error>
703        where
704            V: ExtendedOpVisitor,
705        {
706            let code = u16::decode(visitor.bytecode())?;
707            let opcode = ExtendedOpcode::new(code).ok_or_else(|| {
708                visitor.bytecode().invalid_extended_opcode(code)
709            })?;
710
711            match opcode {
712                $(
713                    ExtendedOpcode::$name => {
714                        $(
715                            $(
716                                let $field = <$field_ty>::decode(
717                                    visitor.bytecode(),
718                                )?;
719                            )*
720                        )?
721
722                        let ret = visitor.$snake_name($( $( $field ),* )?);
723                        visitor.after_visit();
724                        Ok(ret)
725                    }
726                )*
727            }
728        }
729
730
731        impl<F, T, V1, V2> ExtendedOpVisitor for SequencedVisitor<'_, F, V1, V2>
732        where
733            F: FnMut(V1::Return, V2::Return) -> T,
734            V1: ExtendedOpVisitor,
735            V2: ExtendedOpVisitor<BytecodeStream = V1::BytecodeStream>,
736        {
737            $(
738                $( #[$attr] )*
739                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return {
740                    let a = self.v1.$snake_name( $( $( $field , )* )? );
741                    let b = self.v2.$snake_name( $( $( $field , )* )? );
742                    (self.join)(a, b)
743                }
744            )*
745        }
746    };
747}
748for_each_extended_op!(define_extended_decoder);
749
750/// Functions for decoding the operands of an instruction, assuming the opcode
751/// has already been decoded.
752pub mod operands {
753    use super::*;
754
755    macro_rules! define_operands_decoder {
756        (
757            $(
758                $( #[$attr:meta] )*
759                    $snake_name:ident = $name:ident $( {
760                    $(
761                        $( #[$field_attr:meta] )*
762                        $field:ident : $field_ty:ty
763                    ),*
764                } )? ;
765            )*
766        ) => {
767            $(
768                #[allow(unused_variables, reason = "macro-generated")]
769                #[expect(missing_docs, reason = "macro-generated")]
770                pub fn $snake_name<T: BytecodeStream>(pc: &mut T) -> Result<($($($field_ty,)*)?), T::Error> {
771                    Ok((($($((<$field_ty>::decode(pc))?,)*)?)))
772                }
773            )*
774        };
775    }
776
777    for_each_op!(define_operands_decoder);
778
779    /// Decode an extended opcode from `pc` to match the payload of the
780    /// "extended" opcode.
781    pub fn extended<T: BytecodeStream>(pc: &mut T) -> Result<(ExtendedOpcode,), T::Error> {
782        Ok((ExtendedOpcode::decode(pc)?,))
783    }
784
785    for_each_extended_op!(define_operands_decoder);
786}