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