pulley_interpreter/
op.rs

1//! Pulley bytecode operations with their operands.
2
3#[cfg(feature = "encode")]
4use crate::encode::Encode;
5use crate::imms::*;
6use crate::regs::*;
7
8macro_rules! define_op {
9    (
10        $(
11            $( #[$attr:meta] )*
12            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
13        )*
14    ) => {
15        /// A complete, materialized operation/instruction.
16        ///
17        /// This type is useful for debugging, writing tests, etc... but is not
18        /// actually ever used by the interpreter, encoder, or decoder, all of
19        /// which avoid materializing ops.
20        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
21        #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
22        pub enum Op {
23            $(
24                $( #[$attr] )*
25                $name($name),
26            )*
27            /// An extended operation/instruction.
28            ExtendedOp(ExtendedOp),
29        }
30
31        $(
32            $( #[$attr] )*
33            #[derive(Clone, Copy, Debug, PartialEq, Eq)]
34            #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
35            pub struct $name { $(
36                $(
37                    // TODO: add doc comments to all fields and update all
38                    // the macros to match them.
39                    #[expect(missing_docs, reason = "macro-generated code")]
40                    pub $field : $field_ty,
41                )*
42            )? }
43
44            impl From<$name> for Op {
45                #[inline]
46                fn from(op: $name) -> Self {
47                    Self::$name(op)
48                }
49            }
50        )*
51    };
52}
53for_each_op!(define_op);
54
55impl From<ExtendedOp> for Op {
56    #[inline]
57    fn from(op: ExtendedOp) -> Self {
58        Op::ExtendedOp(op)
59    }
60}
61
62macro_rules! define_extended_op {
63    (
64        $(
65            $( #[$attr:meta] )*
66            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
67        )*
68    ) => {
69        /// An extended operation/instruction.
70        ///
71        /// These tend to be colder than `Op`s.
72        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
73        #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
74        pub enum ExtendedOp {
75            $(
76                $( #[$attr] )*
77                $name($name),
78            )*
79        }
80
81        $(
82            $( #[$attr] )*
83            #[derive(Clone, Copy, Debug, PartialEq, Eq)]
84            #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
85            pub struct $name { $(
86                $(
87                    // TODO: add doc comments to all fields and update all
88                    // the macros to match them.
89                    #[expect(missing_docs, reason = "macro-generated code")]
90                    pub $field : $field_ty,
91                )*
92            )? }
93
94            #[doc(hidden)]
95            #[expect(non_camel_case_types, reason = "used in macros as an alternative to camel case")]
96            pub type $snake_name = $name;
97
98            impl From<$name> for Op {
99                #[inline]
100                fn from(op: $name) -> Self {
101                    Self::ExtendedOp(ExtendedOp::$name(op))
102                }
103            }
104
105            impl From<$name> for ExtendedOp {
106                #[inline]
107                fn from(op: $name) -> Self {
108                    Self::$name(op)
109                }
110            }
111        )*
112    };
113}
114for_each_extended_op!(define_extended_op);
115
116macro_rules! define_op_encode {
117    (
118        $(
119            $( #[$attr:meta] )*
120            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
121        )*
122    ) => {
123        impl Op {
124            /// Encode this op into the given sink.
125            #[cfg(feature = "encode")]
126            pub fn encode<E>(&self, into: &mut E)
127            where
128                E: Extend<u8>,
129            {
130                match self {
131                    $(
132                        Self::$name(op) => op.encode(into),
133                    )*
134                    Self::ExtendedOp(op) => op.encode(into),
135                }
136            }
137
138            /// Returns the encoded size of this op.
139            #[cfg(feature = "encode")]
140            pub fn width(&self) -> u8 {
141                match self {
142                    $(
143                        Self::$name(_) => <$name as Encode>::WIDTH,
144                    )*
145                    Self::ExtendedOp(op) => op.width(),
146                }
147            }
148        }
149
150        $(
151            impl $name {
152                /// Encode this
153                #[doc = concat!("`", stringify!($name), "`")]
154                /// op into the given sink.
155                #[cfg(feature = "encode")]
156                pub fn encode<E>(&self, into: &mut E)
157                where
158                    E: Extend<u8>,
159                {
160                    crate::encode::$snake_name(into $( $( , self.$field )* )?);
161                }
162            }
163        )*
164    };
165}
166for_each_op!(define_op_encode);
167
168macro_rules! define_extended_op_encode {
169    (
170        $(
171            $( #[$attr:meta] )*
172            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
173        )*
174    ) => {
175        impl ExtendedOp {
176            /// Encode this extended op into the given sink.
177            #[cfg(feature = "encode")]
178            pub fn encode<E>(&self, into: &mut E)
179            where
180                E: Extend<u8>,
181            {
182                match self {
183                    $(
184                        Self::$name(op) => op.encode(into),
185                    )*
186                }
187            }
188
189            /// Returns the encoded size of this op.
190            #[cfg(feature = "encode")]
191            pub fn width(&self) -> u8 {
192                match self {
193                    $(
194                        Self::$name(_) => <$name as Encode>::WIDTH,
195                    )*
196                }
197            }
198        }
199
200        $(
201            impl $name {
202                /// Encode this
203                #[doc = concat!("`", stringify!($name), "`")]
204                /// op into the given sink.
205                #[cfg(feature = "encode")]
206                pub fn encode<E>(&self, into: &mut E)
207                where
208                    E: Extend<u8>,
209                {
210                    crate::encode::$snake_name(into $( $( , self.$field )* )?);
211                }
212            }
213        )*
214    };
215}
216for_each_extended_op!(define_extended_op_encode);
217
218/// A visitor that materializes whole `Op`s as it decodes the bytecode stream.
219#[cfg(feature = "decode")]
220#[derive(Default)]
221pub struct MaterializeOpsVisitor<B> {
222    bytecode: B,
223}
224
225#[cfg(feature = "decode")]
226impl<B> MaterializeOpsVisitor<B> {
227    /// Create a new op-materializing visitor for the given bytecode.
228    pub fn new(bytecode: B) -> Self {
229        Self { bytecode }
230    }
231}
232
233macro_rules! define_materialize_op_visitor {
234    (
235        $(
236            $( #[$attr:meta] )*
237            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
238        )*
239    ) => {
240        #[cfg(feature = "decode")]
241        impl<B: crate::decode::BytecodeStream> crate::decode::OpVisitor for MaterializeOpsVisitor<B> {
242            type BytecodeStream = B;
243
244            fn bytecode(&mut self) -> &mut Self::BytecodeStream {
245                &mut self.bytecode
246            }
247
248            type Return = crate::op::Op;
249
250            $(
251                $( #[$attr] )*
252                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return {
253                    crate::op::Op::$name(crate::op::$name { $( $(
254                        $field,
255                    )* )? })
256                }
257            )*
258        }
259    };
260}
261for_each_op!(define_materialize_op_visitor);
262
263macro_rules! define_materialize_extended_op_visitor {
264    (
265        $(
266            $( #[$attr:meta] )*
267            $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;
268        )*
269    ) => {
270        #[cfg(feature = "decode")]
271        impl<B: crate::decode::BytecodeStream> crate::decode::ExtendedOpVisitor for MaterializeOpsVisitor<B> {
272            $(
273                $( #[$attr] )*
274                fn $snake_name(&mut self $( $( , $field : $field_ty )* )? ) -> Self::Return {
275                    crate::op::ExtendedOp::$name(crate::op::$name { $( $(
276                        $field,
277                    )* )? }).into()
278                }
279            )*
280        }
281    };
282}
283for_each_extended_op!(define_materialize_extended_op_visitor);