xref: /wasmtime-44.0.1/pulley/src/opcode.rs (revision 0fbbb754)
1 //! Pulley opcodes (without operands).
2 
3 macro_rules! define_opcode {
4     (
5         $(
6             $( #[$attr:meta] )*
7             $snake_name:ident = $name:ident $( {
8                 $(
9                     $( #[$field_attr:meta] )*
10                     $field:ident : $field_ty:ty
11                 ),*
12             } )? ;
13         )*
14     ) => {
15         /// An opcode without its immediates and operands.
16         #[repr(u8)]
17         #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
18         pub enum Opcode {
19             $(
20                 $( #[$attr] )*
21                 $name,
22             )*
23             /// The extended-op opcode. An `ExtendedOpcode` follows this opcode.
24             ExtendedOp,
25         }
26 
27         impl Opcode {
28             /// The value of the maximum defined opcode.
29             pub const MAX: u8 = Opcode::ExtendedOp as u8;
30         }
31     }
32 }
33 for_each_op!(define_opcode);
34 
35 impl Opcode {
36     /// Create a new `Opcode` from the given byte.
37     ///
38     /// Returns `None` if `byte` is not a valid opcode.
39     pub fn new(byte: u8) -> Option<Self> {
40         if byte <= Self::MAX {
41             Some(unsafe { Self::unchecked_new(byte) })
42         } else {
43             None
44         }
45     }
46 
47     /// Like `new` but does not check whether `byte` is a valid opcode.
48     ///
49     /// # Safety
50     ///
51     /// It is unsafe to pass a `byte` that is not a valid opcode.
52     pub unsafe fn unchecked_new(byte: u8) -> Self {
53         debug_assert!(byte <= Self::MAX);
54         unsafe { core::mem::transmute(byte) }
55     }
56 }
57 
58 macro_rules! define_extended_opcode {
59     (
60         $(
61             $( #[$attr:meta] )*
62             $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
63         )*
64     ) => {
65         /// An extended opcode.
66         #[repr(u16)]
67         #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
68         pub enum ExtendedOpcode {
69             $(
70                 $( #[$attr] )*
71                     $name,
72             )*
73         }
74 
75         impl ExtendedOpcode {
76             /// The value of the maximum defined extended opcode.
77             pub const MAX: u16 = $(
78                 if true { 1 } else { ExtendedOpcode::$name as u16 } +
79             )* 0 - 1;
80         }
81     };
82 }
83 for_each_extended_op!(define_extended_opcode);
84 
85 impl ExtendedOpcode {
86     /// Create a new `ExtendedOpcode` from the given bytes.
87     ///
88     /// Returns `None` if `bytes` is not a valid extended opcode.
89     pub fn new(bytes: u16) -> Option<Self> {
90         if bytes <= Self::MAX {
91             Some(unsafe { Self::unchecked_new(bytes) })
92         } else {
93             None
94         }
95     }
96 
97     /// Like `new` but does not check whether `bytes` is a valid opcode.
98     ///
99     /// # Safety
100     ///
101     /// It is unsafe to pass `bytes` that is not a valid opcode.
102     pub unsafe fn unchecked_new(byte: u16) -> Self {
103         debug_assert!(byte <= Self::MAX);
104         unsafe { core::mem::transmute(byte) }
105     }
106 }
107 
108 #[cfg(test)]
109 mod tests {
110     use super::*;
111 
112     #[test]
113     fn max_values() {
114         assert!(Opcode::new(Opcode::MAX).is_some());
115         assert!(Opcode::new(Opcode::MAX + 1).is_none());
116         assert!(ExtendedOpcode::new(ExtendedOpcode::MAX).is_some());
117         assert!(ExtendedOpcode::new(ExtendedOpcode::MAX + 1).is_none());
118     }
119 }
120