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