/home/runner/actions-runner/_work/fuel-vm/fuel-vm/fuel-asm/src/macros.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! # The `impl_instructions!` macro |
2 | | //! |
3 | | //! The heart of this crate's implementation is the private `impl_instructions!` macro. |
4 | | //! This macro is used to generate the `Instruction` and `Opcode` types along with their |
5 | | //! implementations. |
6 | | //! |
7 | | //! The intention is to allow for having a single source of truth from which each of the |
8 | | //! instruction-related types and implementations are derived. |
9 | | //! |
10 | | //! Its usage looks like this: |
11 | | //! |
12 | | //! ```rust,ignore |
13 | | //! impl_instructions! { |
14 | | //! "Adds two registers." |
15 | | //! 0x10 ADD add [RegId RegId RegId] |
16 | | //! "Bitwise ANDs two registers." |
17 | | //! 0x11 AND and [RegId RegId RegId] |
18 | | //! // ... |
19 | | //! } |
20 | | //! ``` |
21 | | //! |
22 | | //! Each instruction's row includes: |
23 | | //! |
24 | | //! - A short docstring. |
25 | | //! - The Opcode byte value. |
26 | | //! - An uppercase identifier (for generating variants and types). |
27 | | //! - A lowercase identifier (for generating the shorthand instruction constructor). |
28 | | //! - The instruction layout (for the `new` and `unpack` functions). |
29 | | //! |
30 | | //! The following sections describe each of the items that are derived from the |
31 | | //! `impl_instructions!` table in more detail. |
32 | | //! |
33 | | //! ## The `Opcode` enum |
34 | | //! |
35 | | //! Represents the bytecode portion of an instruction. |
36 | | //! |
37 | | //! ```rust,ignore |
38 | | //! /// Solely the opcode portion of an instruction represented as a single byte. |
39 | | //! #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
40 | | //! #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
41 | | //! #[repr(u8)] |
42 | | //! pub enum Opcode { |
43 | | //! /// Adds two registers. |
44 | | //! ADD = 0x10, |
45 | | //! /// Bitwise ANDs two registers. |
46 | | //! AND = 0x11, |
47 | | //! // ... |
48 | | //! } |
49 | | //! ``` |
50 | | //! |
51 | | //! A `TryFrom<u8>` implementation is also provided, producing an `Err(InvalidOpcode)` in |
52 | | //! the case that the byte represents a reserved or undefined value. |
53 | | //! |
54 | | //! ```rust |
55 | | //! # use fuel_asm::{InvalidOpcode, Opcode}; |
56 | | //! assert_eq!(Opcode::try_from(0x10), Ok(Opcode::ADD)); |
57 | | //! assert_eq!(Opcode::try_from(0x11), Ok(Opcode::AND)); |
58 | | //! assert_eq!(Opcode::try_from(0), Err(InvalidOpcode)); |
59 | | //! ``` |
60 | | //! |
61 | | //! ## The `Instruction` enum |
62 | | //! |
63 | | //! Represents a single, full instruction, discriminated by its `Opcode`. |
64 | | //! |
65 | | //! ```rust,ignore |
66 | | //! /// Representation of a single instruction for the interpreter. |
67 | | //! /// |
68 | | //! /// The opcode is represented in the tag (variant), or may be retrieved in the form of an |
69 | | //! /// `Opcode` byte using the `opcode` method. |
70 | | //! /// |
71 | | //! /// The register and immediate data associated with the instruction is represented within |
72 | | //! /// an inner unit type wrapper around the 3 remaining bytes. |
73 | | //! #[derive(Clone, Copy, Eq, Hash, PartialEq)] |
74 | | //! #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
75 | | //! pub enum Instruction { |
76 | | //! /// Adds two registers. |
77 | | //! ADD(op::ADD), |
78 | | //! /// Bitwise ANDs two registers. |
79 | | //! AND(op::AND), |
80 | | //! // ... |
81 | | //! } |
82 | | //! ``` |
83 | | //! |
84 | | //! The `From<Instruction> for u32` (aka `RawInstruction`) and `TryFrom<u32> for |
85 | | //! Instruction` implementations can be found in the crate root. |
86 | | //! |
87 | | //! ## A unique unit type per operation |
88 | | //! |
89 | | //! In order to reduce the likelihood of misusing unrelated register IDs or immediate |
90 | | //! values, we generate a unique unit type for each type of operation (i.e instruction |
91 | | //! variant) and guard access to the relevant register IDs and immediate values behind |
92 | | //! each type's unique methods. |
93 | | //! |
94 | | //! These unique operation types are generated as follows within a dedicated `op` module: |
95 | | //! |
96 | | //! ```rust,ignore |
97 | | //! pub mod op { |
98 | | //! //! Definitions and implementations for each unique instruction type, one for each |
99 | | //! //! unique `Opcode` variant. |
100 | | //! |
101 | | //! // A unique type for each operation. |
102 | | //! |
103 | | //! /// Adds two registers. |
104 | | //! pub struct ADD([u8; 3]); |
105 | | //! |
106 | | //! /// Bitwise ANDs two registers. |
107 | | //! pub struct AND([u8; 3]); |
108 | | //! |
109 | | //! // ... |
110 | | //! |
111 | | //! // An implementation for each unique type. |
112 | | //! |
113 | | //! impl ADD { |
114 | | //! pub const OPCODE: Opcode = Opcode::ADD; |
115 | | //! |
116 | | //! /// Construct the instruction from its parts. |
117 | | //! pub fn new(ra: RegId, rb: RegId, rc: RegId) -> Self { |
118 | | //! Self(pack::bytes_from_ra_rb_rc(ra, rb, rc)) |
119 | | //! } |
120 | | //! |
121 | | //! /// Convert the instruction into its parts. |
122 | | //! pub fn unpack(self) -> (RegId, RegId, RegId) { |
123 | | //! unpack::ra_rb_rc_from_bytes(self.0) |
124 | | //! } |
125 | | //! } |
126 | | //! |
127 | | //! impl AND { |
128 | | //! // ... |
129 | | //! } |
130 | | //! |
131 | | //! // ... |
132 | | //! |
133 | | //! // A short-hand `Instruction` constructor for each operation to make it easier to |
134 | | //! // hand-write assembly for tests and benchmarking. As these constructors are public and |
135 | | //! // accept literal values, we check that the values are within range. |
136 | | //! |
137 | | //! /// Adds two registers. |
138 | | //! pub fn add(ra: u8, rb: u8, rc: u8) -> Instruction { |
139 | | //! ADD::new(check_reg_id(ra), check_reg_id(rb), check_reg_id(rc)).into() |
140 | | //! } |
141 | | //! |
142 | | //! /// Bitwise ANDs two registers. |
143 | | //! pub fn and(ra: u8, rb: u8, rc: u8) -> Instruction { |
144 | | //! AND::new(check_reg_id(ra), check_reg_id(rb), check_reg_id(rc)).into() |
145 | | //! } |
146 | | //! |
147 | | //! // ... |
148 | | //! }; |
149 | | //! ``` |
150 | | //! |
151 | | //! ### Instruction Layout |
152 | | //! |
153 | | //! The function signatures of the `new` and `unpack` functions are derived from the |
154 | | //! instruction's data layout described in the `impl_instructions!` table. |
155 | | //! |
156 | | //! For example, the `unpack` method for `ADD` looks like this: |
157 | | //! |
158 | | //! ```rust,ignore |
159 | | //! // 0x10 ADD add [RegId RegId RegId] |
160 | | //! pub fn unpack(self) -> (RegId, RegId, RegId) |
161 | | //! ``` |
162 | | //! |
163 | | //! While the `unpack` method for `ADDI` looks like this: |
164 | | //! |
165 | | //! ```rust,ignore |
166 | | //! // 0x50 ADDI addi [RegId RegId Imm12] |
167 | | //! pub fn unpack(self) -> (RegId, RegId, Imm12) |
168 | | //! ``` |
169 | | //! |
170 | | //! ### Shorthand Constructors |
171 | | //! |
172 | | //! The shorthand instruction constructors (e.g. `add`, `and`, etc) are specifically |
173 | | //! designed to make it easier to handwrite assembly for tests or benchmarking. Unlike the |
174 | | //! `$OP::new` constructors which require typed register ID or immediate inputs, the |
175 | | //! instruction constructors allow for constructing `Instruction`s from convenient literal |
176 | | //! value inputs. E.g. |
177 | | //! |
178 | | //! ```rust |
179 | | //! use fuel_asm::{op, Instruction}; |
180 | | //! |
181 | | //! // A sample program to perform ecrecover |
182 | | //! let program: Vec<Instruction> = vec![ |
183 | | //! op::move_(0x10, 0x01), // set r[0x10] := $one |
184 | | //! op::slli(0x20, 0x10, 5), // set r[0x20] := `r[0x10] << 5 == 32` |
185 | | //! op::slli(0x21, 0x10, 6), // set r[0x21] := `r[0x10] << 6 == 64` |
186 | | //! op::aloc(0x21), // alloc `r[0x21] == 64` to the heap |
187 | | //! op::addi(0x10, 0x07, 1), // set r[0x10] := `$hp + 1` (allocated heap) |
188 | | //! op::move_(0x11, 0x04), // set r[0x11] := $ssp |
189 | | //! op::add(0x12, 0x04, 0x20), // set r[0x12] := `$ssp + r[0x20]` |
190 | | //! op::eck1(0x10, 0x11, 0x12),// recover public key in memory[r[0x10], 64] |
191 | | //! op::ret(0x01), // return `1` |
192 | | //! ]; |
193 | | //! ``` |
194 | | |
195 | | // Generate a shorthand free function named after the $op for constructing an |
196 | | // `Instruction`. |
197 | | macro_rules! op_constructor { |
198 | | ($doc:literal $Op:ident $op:ident[$ra:ident : RegId]) => { |
199 | | #[doc = $doc] |
200 | 12.3k | pub fn $op<A: CheckRegId>($ra: A) -> Instruction { |
201 | 12.3k | $Op::new($ra.check()).into() |
202 | 12.3k | } |
203 | | |
204 | | #[cfg(feature = "typescript")] |
205 | | const _: () = { |
206 | | use super::*; |
207 | | |
208 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
209 | | #[doc = $doc] |
210 | 0 | pub fn $op($ra: u8) -> typescript::Instruction { |
211 | 0 | crate::op::$op($ra).into() |
212 | 0 | } |
213 | | }; |
214 | | }; |
215 | | ($doc:literal $Op:ident $op:ident[$ra:ident : RegId $rb:ident : RegId]) => { |
216 | | #[doc = $doc] |
217 | 2.27k | pub fn $op<A: CheckRegId, B: CheckRegId>($ra: A, $rb: B) -> Instruction { |
218 | 2.27k | $Op::new($ra.check(), $rb.check()).into() |
219 | 2.27k | } |
220 | | |
221 | | #[cfg(feature = "typescript")] |
222 | | const _: () = { |
223 | | use super::*; |
224 | | |
225 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
226 | | #[doc = $doc] |
227 | 0 | pub fn $op($ra: u8, $rb: u8) -> typescript::Instruction { |
228 | 0 | crate::op::$op($ra, $rb).into() |
229 | 0 | } |
230 | | }; |
231 | | }; |
232 | | ( |
233 | | $doc:literal |
234 | | $Op:ident |
235 | | $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId] |
236 | | ) => { |
237 | | #[doc = $doc] |
238 | 1.04k | pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>( |
239 | 1.04k | $ra: A, |
240 | 1.04k | $rb: B, |
241 | 1.04k | $rc: C, |
242 | 1.04k | ) -> Instruction { |
243 | 1.04k | $Op::new($ra.check(), $rb.check(), $rc.check()).into() |
244 | 1.04k | } |
245 | | |
246 | | #[cfg(feature = "typescript")] |
247 | | const _: () = { |
248 | | use super::*; |
249 | | |
250 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
251 | | #[doc = $doc] |
252 | 0 | pub fn $op($ra: u8, $rb: u8, $rc: u8) -> typescript::Instruction { |
253 | 0 | crate::op::$op($ra, $rb, $rc).into() |
254 | 0 | } |
255 | | }; |
256 | | }; |
257 | | ( |
258 | | $doc:literal |
259 | | $Op:ident |
260 | | $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId] |
261 | | ) => { |
262 | | #[doc = $doc] |
263 | 4.71k | pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId, D: CheckRegId>( |
264 | 4.71k | $ra: A, |
265 | 4.71k | $rb: B, |
266 | 4.71k | $rc: C, |
267 | 4.71k | $rd: D, |
268 | 4.71k | ) -> Instruction { |
269 | 4.71k | $Op::new($ra.check(), $rb.check(), $rc.check(), $rd.check()).into() |
270 | 4.71k | } |
271 | | |
272 | | #[cfg(feature = "typescript")] |
273 | | const _: () = { |
274 | | use super::*; |
275 | | |
276 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
277 | | #[doc = $doc] |
278 | 0 | pub fn $op($ra: u8, $rb: u8, $rc: u8, $rd: u8) -> typescript::Instruction { |
279 | 0 | crate::op::$op($ra, $rb, $rc, $rd).into() |
280 | 0 | } |
281 | | }; |
282 | | }; |
283 | | ( |
284 | | $doc:literal |
285 | | $Op:ident |
286 | | $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06] |
287 | | ) => { |
288 | | #[doc = $doc] |
289 | 82 | pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>( |
290 | 82 | $ra: A, |
291 | 82 | $rb: B, |
292 | 82 | $rc: C, |
293 | 82 | $imm: u8, |
294 | 82 | ) -> Instruction { |
295 | 82 | $Op::new($ra.check(), $rb.check(), $rc.check(), check_imm06($imm)).into() |
296 | 82 | } |
297 | | |
298 | | #[cfg(feature = "typescript")] |
299 | | const _: () = { |
300 | | use super::*; |
301 | | |
302 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
303 | | #[doc = $doc] |
304 | 0 | pub fn $op($ra: u8, $rb: u8, $rc: u8, $imm: u8) -> typescript::Instruction { |
305 | 0 | crate::op::$op($ra, $rb, $rc, $imm).into() |
306 | 0 | } |
307 | | }; |
308 | | }; |
309 | | ( |
310 | | $doc:literal |
311 | | $Op:ident |
312 | | $op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12] |
313 | | ) => { |
314 | | #[doc = $doc] |
315 | 44.2k | pub fn $op<A: CheckRegId, B: CheckRegId>( |
316 | 44.2k | $ra: A, |
317 | 44.2k | $rb: B, |
318 | 44.2k | $imm: u16, |
319 | 44.2k | ) -> Instruction { |
320 | 44.2k | $Op::new($ra.check(), $rb.check(), check_imm12($imm)).into() |
321 | 44.2k | } |
322 | | |
323 | | #[cfg(feature = "typescript")] |
324 | | const _: () = { |
325 | | use super::*; |
326 | | |
327 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
328 | | #[doc = $doc] |
329 | 0 | pub fn $op($ra: u8, $rb: u8, $imm: u16) -> typescript::Instruction { |
330 | 0 | crate::op::$op($ra, $rb, $imm).into() |
331 | 0 | } |
332 | | }; |
333 | | }; |
334 | | ($doc:literal $Op:ident $op:ident[$ra:ident : RegId $imm:ident : Imm18]) => { |
335 | | #[doc = $doc] |
336 | 14.2k | pub fn $op<A: CheckRegId>($ra: A, $imm: u32) -> Instruction { |
337 | 14.2k | $Op::new($ra.check(), check_imm18($imm)).into() |
338 | 14.2k | } |
339 | | |
340 | | #[cfg(feature = "typescript")] |
341 | | const _: () = { |
342 | | use super::*; |
343 | | |
344 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
345 | | #[doc = $doc] |
346 | 0 | pub fn $op($ra: u8, $imm: u32) -> typescript::Instruction { |
347 | 0 | crate::op::$op($ra, $imm).into() |
348 | 0 | } |
349 | | }; |
350 | | }; |
351 | | ($doc:literal $Op:ident $op:ident[$imm:ident : Imm24]) => { |
352 | | #[doc = $doc] |
353 | 2.12k | pub fn $op($imm: u32) -> Instruction { |
354 | 2.12k | $Op::new(check_imm24($imm)).into() |
355 | 2.12k | } |
356 | | |
357 | | #[cfg(feature = "typescript")] |
358 | | const _: () = { |
359 | | use super::*; |
360 | | |
361 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
362 | | #[doc = $doc] |
363 | 0 | pub fn $op($imm: u32) -> typescript::Instruction { |
364 | 0 | crate::op::$op($imm).into() |
365 | 0 | } |
366 | | }; |
367 | | }; |
368 | | ($doc:literal $Op:ident $op:ident[]) => { |
369 | | #[doc = $doc] |
370 | 17.0k | pub fn $op() -> Instruction { |
371 | 17.0k | $Op::new().into() |
372 | 17.0k | } |
373 | | |
374 | | #[cfg(feature = "typescript")] |
375 | | const _: () = { |
376 | | use super::*; |
377 | | |
378 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
379 | | #[doc = $doc] |
380 | 0 | pub fn $op() -> typescript::Instruction { |
381 | 0 | crate::op::$op().into() |
382 | 0 | } |
383 | | }; |
384 | | }; |
385 | | } |
386 | | |
387 | | // Generate approriate `new` constructor for the instruction |
388 | | macro_rules! op_new { |
389 | | // Generate a constructor based on the field layout. |
390 | | ($Op:ident $ra:ident : RegId) => { |
391 | | impl $Op { |
392 | | /// Construct the instruction from its parts. |
393 | 12.3k | pub fn new($ra: RegId) -> Self { |
394 | 12.3k | Self(pack::bytes_from_ra($ra)) |
395 | 12.3k | } |
396 | | } |
397 | | |
398 | | #[cfg(feature = "typescript")] |
399 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
400 | | impl $Op { |
401 | | #[wasm_bindgen(constructor)] |
402 | | /// Construct the instruction from its parts. |
403 | 0 | pub fn new_typescript($ra: RegId) -> Self { |
404 | 0 | Self::new($ra) |
405 | 0 | } |
406 | | } |
407 | | }; |
408 | | ($Op:ident $ra:ident : RegId $rb:ident : RegId) => { |
409 | | impl $Op { |
410 | | /// Construct the instruction from its parts. |
411 | 2.27k | pub fn new($ra: RegId, $rb: RegId) -> Self { |
412 | 2.27k | Self(pack::bytes_from_ra_rb($ra, $rb)) |
413 | 2.27k | } |
414 | | } |
415 | | |
416 | | #[cfg(feature = "typescript")] |
417 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
418 | | impl $Op { |
419 | | #[wasm_bindgen(constructor)] |
420 | | /// Construct the instruction from its parts. |
421 | 0 | pub fn new_typescript($ra: RegId, $rb: RegId) -> Self { |
422 | 0 | Self::new($ra, $rb) |
423 | 0 | } |
424 | | } |
425 | | }; |
426 | | ($Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId) => { |
427 | | impl $Op { |
428 | | /// Construct the instruction from its parts. |
429 | 1.04k | pub fn new($ra: RegId, $rb: RegId, $rc: RegId) -> Self { |
430 | 1.04k | Self(pack::bytes_from_ra_rb_rc($ra, $rb, $rc)) |
431 | 1.04k | } |
432 | | } |
433 | | |
434 | | #[cfg(feature = "typescript")] |
435 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
436 | | impl $Op { |
437 | | #[wasm_bindgen(constructor)] |
438 | | /// Construct the instruction from its parts. |
439 | 0 | pub fn new_typescript($ra: RegId, $rb: RegId, $rc: RegId) -> Self { |
440 | 0 | Self::new($ra, $rb, $rc) |
441 | 0 | } |
442 | | } |
443 | | }; |
444 | | ( |
445 | | $Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId |
446 | | ) => { |
447 | | impl $Op { |
448 | | /// Construct the instruction from its parts. |
449 | 4.71k | pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $rd: RegId) -> Self { |
450 | 4.71k | Self(pack::bytes_from_ra_rb_rc_rd($ra, $rb, $rc, $rd)) |
451 | 4.71k | } |
452 | | } |
453 | | |
454 | | #[cfg(feature = "typescript")] |
455 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
456 | | impl $Op { |
457 | | #[wasm_bindgen(constructor)] |
458 | | /// Construct the instruction from its parts. |
459 | 0 | pub fn new_typescript( |
460 | 0 | $ra: RegId, |
461 | 0 | $rb: RegId, |
462 | 0 | $rc: RegId, |
463 | 0 | $rd: RegId, |
464 | 0 | ) -> Self { |
465 | 0 | Self::new($ra, $rb, $rc, $rd) |
466 | 0 | } |
467 | | } |
468 | | }; |
469 | | ( |
470 | | $Op:ident |
471 | | $ra:ident : RegId |
472 | | $rb:ident : RegId |
473 | | $rc:ident : RegId |
474 | | $imm:ident : Imm06 |
475 | | ) => { |
476 | | impl $Op { |
477 | | /// Construct the instruction from its parts. |
478 | 806 | pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $imm: Imm06) -> Self { |
479 | 806 | Self(pack::bytes_from_ra_rb_rc_imm06($ra, $rb, $rc, $imm)) |
480 | 806 | } |
481 | | } |
482 | | |
483 | | #[cfg(feature = "typescript")] |
484 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
485 | | impl $Op { |
486 | | #[wasm_bindgen(constructor)] |
487 | | /// Construct the instruction from its parts. |
488 | 0 | pub fn new_typescript( |
489 | 0 | $ra: RegId, |
490 | 0 | $rb: RegId, |
491 | 0 | $rc: RegId, |
492 | 0 | $imm: Imm06, |
493 | 0 | ) -> Self { |
494 | 0 | Self::new($ra, $rb, $rc, $imm) |
495 | 0 | } |
496 | | } |
497 | | }; |
498 | | ($Op:ident $ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12) => { |
499 | | impl $Op { |
500 | | /// Construct the instruction from its parts. |
501 | 44.6k | pub fn new($ra: RegId, $rb: RegId, $imm: Imm12) -> Self { |
502 | 44.6k | Self(pack::bytes_from_ra_rb_imm12($ra, $rb, $imm)) |
503 | 44.6k | } |
504 | | } |
505 | | |
506 | | #[cfg(feature = "typescript")] |
507 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
508 | | impl $Op { |
509 | | #[wasm_bindgen(constructor)] |
510 | | /// Construct the instruction from its parts. |
511 | 0 | pub fn new_typescript($ra: RegId, $rb: RegId, $imm: Imm12) -> Self { |
512 | 0 | Self::new($ra, $rb, $imm) |
513 | 0 | } |
514 | | } |
515 | | }; |
516 | | ($Op:ident $ra:ident : RegId $imm:ident : Imm18) => { |
517 | | impl $Op { |
518 | | /// Construct the instruction from its parts. |
519 | 14.2k | pub fn new($ra: RegId, $imm: Imm18) -> Self { |
520 | 14.2k | Self(pack::bytes_from_ra_imm18($ra, $imm)) |
521 | 14.2k | } |
522 | | } |
523 | | |
524 | | #[cfg(feature = "typescript")] |
525 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
526 | | impl $Op { |
527 | | #[wasm_bindgen(constructor)] |
528 | | /// Construct the instruction from its parts. |
529 | 0 | pub fn new_typescript($ra: RegId, $imm: Imm18) -> Self { |
530 | 0 | Self::new($ra, $imm) |
531 | 0 | } |
532 | | } |
533 | | }; |
534 | | ($Op:ident $imm:ident : Imm24) => { |
535 | | impl $Op { |
536 | | /// Construct the instruction from its parts. |
537 | 2.12k | pub fn new($imm: Imm24) -> Self { |
538 | 2.12k | Self(pack::bytes_from_imm24($imm)) |
539 | 2.12k | } |
540 | | } |
541 | | |
542 | | #[cfg(feature = "typescript")] |
543 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
544 | | impl $Op { |
545 | | #[wasm_bindgen(constructor)] |
546 | | /// Construct the instruction from its parts. |
547 | 0 | pub fn new_typescript($imm: Imm24) -> Self { |
548 | 0 | Self::new($imm) |
549 | 0 | } |
550 | | } |
551 | | }; |
552 | | ($Op:ident) => { |
553 | | impl $Op { |
554 | | /// Construct the instruction. |
555 | | #[allow(clippy::new_without_default)] |
556 | 17.0k | pub fn new() -> Self { |
557 | 17.0k | Self([0; 3]) |
558 | 17.0k | } |
559 | | } |
560 | | |
561 | | #[cfg(feature = "typescript")] |
562 | 0 | #[wasm_bindgen::prelude::wasm_bindgen] |
563 | | impl $Op { |
564 | | #[wasm_bindgen(constructor)] |
565 | | /// Construct the instruction. |
566 | | #[allow(clippy::new_without_default)] |
567 | 0 | pub fn new_typescript() -> Self { |
568 | 0 | Self::new() |
569 | 0 | } |
570 | | } |
571 | | }; |
572 | | } |
573 | | |
574 | | // Generate an accessor method for each field. Recurse based on layout. |
575 | | macro_rules! op_accessors { |
576 | | ($Op:ident $ra:ident: RegId) => { |
577 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
578 | | impl $Op { |
579 | | /// Access the ID for register A. |
580 | 0 | pub fn ra(&self) -> RegId { |
581 | 0 | unpack::ra_from_bytes(self.0) |
582 | 0 | } |
583 | | } |
584 | | }; |
585 | | ($Op:ident $ra:ident: RegId $rb:ident: RegId) => { |
586 | | op_accessors!($Op ra: RegId); |
587 | | |
588 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
589 | | impl $Op { |
590 | | /// Access the ID for register B. |
591 | 0 | pub fn rb(&self) -> RegId { |
592 | 0 | unpack::rb_from_bytes(self.0) |
593 | 0 | } |
594 | | } |
595 | | }; |
596 | | ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId) => { |
597 | | op_accessors!($Op $ra: RegId $rb: RegId); |
598 | | |
599 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
600 | | impl $Op { |
601 | | /// Access the ID for register C. |
602 | 0 | pub fn rc(&self) -> RegId { |
603 | 0 | unpack::rc_from_bytes(self.0) |
604 | 0 | } |
605 | | } |
606 | | }; |
607 | | ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $rd:ident: RegId) => { |
608 | | op_accessors!($Op $ra: RegId $rb: RegId $rc: RegId); |
609 | | |
610 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
611 | | impl $Op { |
612 | | /// Access the ID for register D. |
613 | 0 | pub fn rd(&self) -> RegId { |
614 | 0 | unpack::rd_from_bytes(self.0) |
615 | 0 | } |
616 | | } |
617 | | }; |
618 | | ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $imm:ident: Imm06) => { |
619 | | op_accessors!($Op $ra: RegId rb: RegId $rc: RegId); |
620 | | |
621 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
622 | | impl $Op { |
623 | | /// Access the 6-bit immediate value. |
624 | 0 | pub fn imm06(&self) -> Imm06 { |
625 | 0 | unpack::imm06_from_bytes(self.0) |
626 | 0 | } |
627 | | } |
628 | | }; |
629 | | ($Op:ident $ra:ident: RegId $rb:ident: RegId $imm:ident: Imm12) => { |
630 | | op_accessors!($Op $ra: RegId $rb: RegId); |
631 | | |
632 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
633 | | impl $Op { |
634 | | /// Access the 12-bit immediate value. |
635 | 0 | pub fn imm12(&self) -> Imm12 { |
636 | 0 | unpack::imm12_from_bytes(self.0) |
637 | 0 | } |
638 | | } |
639 | | }; |
640 | | ($Op:ident $ra:ident: RegId $imm:ident: Imm18) => { |
641 | | op_accessors!($Op $ra: RegId); |
642 | | |
643 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
644 | | impl $Op { |
645 | | /// Access the 18-bit immediate value. |
646 | 0 | pub fn imm18(&self) -> Imm18 { |
647 | 0 | unpack::imm18_from_bytes(self.0) |
648 | 0 | } |
649 | | } |
650 | | }; |
651 | | ($Op:ident $ra:ident: Imm24) => { |
652 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
653 | | impl $Op { |
654 | | /// Access the 24-bit immediate value. |
655 | 0 | pub fn imm24(&self) -> Imm24 { |
656 | 0 | unpack::imm24_from_bytes(self.0) |
657 | 0 | } |
658 | | } |
659 | | }; |
660 | | ($Op:ident) => {}; |
661 | | } |
662 | | |
663 | | // Generate a method for converting the instruction into its parts. |
664 | | macro_rules! op_unpack { |
665 | | (RegId) => { |
666 | | /// Convert the instruction into its parts. |
667 | 6.12k | pub fn unpack(self) -> RegId { |
668 | 6.12k | unpack::ra_from_bytes(self.0) |
669 | 6.12k | } |
670 | | }; |
671 | | (RegId RegId) => { |
672 | | /// Convert the instruction into its parts. |
673 | 2.28k | pub fn unpack(self) -> (RegId, RegId) { |
674 | 2.28k | unpack::ra_rb_from_bytes(self.0) |
675 | 2.28k | } |
676 | | }; |
677 | | (RegId RegId RegId) => { |
678 | | /// Convert the instruction into its parts. |
679 | 7.67k | pub fn unpack(self) -> (RegId, RegId, RegId) { |
680 | 7.67k | unpack::ra_rb_rc_from_bytes(self.0) |
681 | 7.67k | } |
682 | | }; |
683 | | (RegId RegId RegId RegId) => { |
684 | | /// Convert the instruction into its parts. |
685 | 208k | pub fn unpack(self) -> (RegId, RegId, RegId, RegId) { |
686 | 208k | unpack::ra_rb_rc_rd_from_bytes(self.0) |
687 | 208k | } |
688 | | }; |
689 | | (RegId RegId RegId Imm06) => { |
690 | | /// Convert the instruction into its parts. |
691 | 824 | pub fn unpack(self) -> (RegId, RegId, RegId, Imm06) { |
692 | 824 | unpack::ra_rb_rc_imm06_from_bytes(self.0) |
693 | 824 | } |
694 | | }; |
695 | | (RegId RegId Imm12) => { |
696 | | /// Convert the instruction into its parts. |
697 | 306k | pub fn unpack(self) -> (RegId, RegId, Imm12) { |
698 | 306k | unpack::ra_rb_imm12_from_bytes(self.0) |
699 | 306k | } |
700 | | }; |
701 | | (RegId Imm18) => { |
702 | | /// Convert the instruction into its parts. |
703 | 489k | pub fn unpack(self) -> (RegId, Imm18) { |
704 | 489k | unpack::ra_imm18_from_bytes(self.0) |
705 | 489k | } |
706 | | }; |
707 | | (Imm24) => { |
708 | | /// Convert the instruction into its parts. |
709 | 202k | pub fn unpack(self) -> Imm24 { |
710 | 202k | unpack::imm24_from_bytes(self.0) |
711 | 202k | } |
712 | | }; |
713 | | () => {}; |
714 | | } |
715 | | |
716 | | // Generate a method for checking that the reserved part of the |
717 | | // instruction is zero. This is private, as invalid instructions |
718 | | // cannot be constructed outside this crate. |
719 | | macro_rules! op_reserved_part { |
720 | | (RegId) => { |
721 | 6.23k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
722 | 6.23k | let (_, imm) = unpack::ra_imm18_from_bytes(self.0); |
723 | 6.23k | imm.0 == 0 |
724 | 6.23k | } |
725 | | }; |
726 | | (RegId RegId) => { |
727 | 2.42k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
728 | 2.42k | let (_, _, imm) = unpack::ra_rb_imm12_from_bytes(self.0); |
729 | 2.42k | imm.0 == 0 |
730 | 2.42k | } |
731 | | }; |
732 | | (RegId RegId RegId) => { |
733 | 8.01k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
734 | 8.01k | let (_, _, _, imm) = unpack::ra_rb_rc_imm06_from_bytes(self.0); |
735 | 8.01k | imm.0 == 0 |
736 | 8.01k | } |
737 | | }; |
738 | | (RegId RegId RegId RegId) => { |
739 | 208k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
740 | 208k | true |
741 | 208k | } |
742 | | }; |
743 | | (RegId RegId RegId Imm06) => { |
744 | 940 | pub(crate) fn reserved_part_is_zero(self) -> bool { |
745 | 940 | true |
746 | 940 | } |
747 | | }; |
748 | | (RegId RegId Imm12) => { |
749 | 306k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
750 | 306k | true |
751 | 306k | } |
752 | | }; |
753 | | (RegId Imm18) => { |
754 | 489k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
755 | 489k | true |
756 | 489k | } |
757 | | }; |
758 | | (Imm24) => { |
759 | 202k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
760 | 202k | true |
761 | 202k | } |
762 | | }; |
763 | | () => { |
764 | 413k | pub(crate) fn reserved_part_is_zero(self) -> bool { |
765 | 413k | self.0 == [0; 3] |
766 | 413k | } |
767 | | }; |
768 | | } |
769 | | |
770 | | // Generate a private fn for use within the `Instruction::reg_ids` implementation. |
771 | | macro_rules! op_reg_ids { |
772 | | (RegId) => { |
773 | 0 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
774 | 0 | let ra = self.unpack(); |
775 | 0 | [Some(ra), None, None, None] |
776 | 0 | } |
777 | | }; |
778 | | (RegId RegId) => { |
779 | 0 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
780 | 0 | let (ra, rb) = self.unpack(); |
781 | 0 | [Some(ra), Some(rb), None, None] |
782 | 0 | } |
783 | | }; |
784 | | (RegId RegId RegId) => { |
785 | 0 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
786 | 0 | let (ra, rb, rc) = self.unpack(); |
787 | 0 | [Some(ra), Some(rb), Some(rc), None] |
788 | 0 | } |
789 | | }; |
790 | | (RegId RegId RegId RegId) => { |
791 | 48 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
792 | 48 | let (ra, rb, rc, rd) = self.unpack(); |
793 | 48 | [Some(ra), Some(rb), Some(rc), Some(rd)] |
794 | 48 | } |
795 | | }; |
796 | | (RegId RegId RegId Imm06) => { |
797 | 26 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
798 | 26 | let (ra, rb, rc, _) = self.unpack(); |
799 | 26 | [Some(ra), Some(rb), Some(rc), None] |
800 | 26 | } |
801 | | }; |
802 | | (RegId RegId Imm12) => { |
803 | 49 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
804 | 49 | let (ra, rb, _) = self.unpack(); |
805 | 49 | [Some(ra), Some(rb), None, None] |
806 | 49 | } |
807 | | }; |
808 | | (RegId Imm18) => { |
809 | 6 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
810 | 6 | let (ra, _) = self.unpack(); |
811 | 6 | [Some(ra), None, None, None] |
812 | 6 | } |
813 | | }; |
814 | | ($($rest:tt)*) => { |
815 | 19 | pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] { |
816 | 19 | [None; 4] |
817 | 19 | } |
818 | | }; |
819 | | } |
820 | | |
821 | | // Generate test constructors that can be used to generate instructions from non-matching |
822 | | // input. |
823 | | #[cfg(test)] |
824 | | macro_rules! op_test_construct_fn { |
825 | | (RegId) => { |
826 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
827 | | /// ones. |
828 | 15 | pub fn test_construct( |
829 | 15 | ra: RegId, |
830 | 15 | _rb: RegId, |
831 | 15 | _rc: RegId, |
832 | 15 | _rd: RegId, |
833 | 15 | _imm: u32, |
834 | 15 | ) -> Self { |
835 | 15 | Self(pack::bytes_from_ra(ra)) |
836 | 15 | } |
837 | | }; |
838 | | (RegId RegId) => { |
839 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
840 | | /// ones. |
841 | 27 | pub fn test_construct( |
842 | 27 | ra: RegId, |
843 | 27 | rb: RegId, |
844 | 27 | _rc: RegId, |
845 | 27 | _rd: RegId, |
846 | 27 | _imm: u32, |
847 | 27 | ) -> Self { |
848 | 27 | Self(pack::bytes_from_ra_rb(ra, rb)) |
849 | 27 | } |
850 | | }; |
851 | | (RegId RegId RegId) => { |
852 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
853 | | /// ones. |
854 | 69 | pub fn test_construct( |
855 | 69 | ra: RegId, |
856 | 69 | rb: RegId, |
857 | 69 | rc: RegId, |
858 | 69 | _rd: RegId, |
859 | 69 | _imm: u32, |
860 | 69 | ) -> Self { |
861 | 69 | Self(pack::bytes_from_ra_rb_rc(ra, rb, rc)) |
862 | 69 | } |
863 | | }; |
864 | | (RegId RegId RegId RegId) => { |
865 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
866 | | /// ones. |
867 | 27 | pub fn test_construct( |
868 | 27 | ra: RegId, |
869 | 27 | rb: RegId, |
870 | 27 | rc: RegId, |
871 | 27 | rd: RegId, |
872 | 27 | _imm: u32, |
873 | 27 | ) -> Self { |
874 | 27 | Self(pack::bytes_from_ra_rb_rc_rd(ra, rb, rc, rd)) |
875 | 27 | } |
876 | | }; |
877 | | (RegId RegId RegId Imm06) => { |
878 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
879 | | /// ones. |
880 | 3 | pub fn test_construct( |
881 | 3 | ra: RegId, |
882 | 3 | rb: RegId, |
883 | 3 | rc: RegId, |
884 | 3 | _rd: RegId, |
885 | 3 | imm: u32, |
886 | 3 | ) -> Self { |
887 | 3 | Self(pack::bytes_from_ra_rb_rc_imm06( |
888 | 3 | ra, |
889 | 3 | rb, |
890 | 3 | rc, |
891 | 3 | Imm06::from(imm as u8), |
892 | 3 | )) |
893 | 3 | } |
894 | | }; |
895 | | (RegId RegId Imm12) => { |
896 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
897 | | /// ones. |
898 | 0 | pub fn test_construct( |
899 | 0 | ra: RegId, |
900 | 0 | rb: RegId, |
901 | 0 | _rc: RegId, |
902 | 0 | _rd: RegId, |
903 | 0 | imm: u32, |
904 | 0 | ) -> Self { |
905 | 0 | Self(pack::bytes_from_ra_rb_imm12( |
906 | 0 | ra, |
907 | 0 | rb, |
908 | 0 | Imm12::from(imm as u16), |
909 | 0 | )) |
910 | 0 | } |
911 | | }; |
912 | | (RegId Imm18) => { |
913 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
914 | | /// ones. |
915 | 0 | pub fn test_construct( |
916 | 0 | ra: RegId, |
917 | 0 | _rb: RegId, |
918 | 0 | _rc: RegId, |
919 | 0 | _rd: RegId, |
920 | 0 | imm: u32, |
921 | 0 | ) -> Self { |
922 | 0 | Self(pack::bytes_from_ra_imm18(ra, Imm18::from(imm))) |
923 | 0 | } |
924 | | }; |
925 | | (Imm24) => { |
926 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
927 | | /// ones. |
928 | 0 | pub fn test_construct( |
929 | 0 | _ra: RegId, |
930 | 0 | _rb: RegId, |
931 | 0 | _rc: RegId, |
932 | 0 | _rd: RegId, |
933 | 0 | imm: u32, |
934 | 0 | ) -> Self { |
935 | 0 | Self(pack::bytes_from_imm24(Imm24::from(imm))) |
936 | 0 | } |
937 | | }; |
938 | | () => { |
939 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable |
940 | | /// ones. |
941 | | #[allow(clippy::new_without_default)] |
942 | 0 | pub fn test_construct( |
943 | 0 | _ra: RegId, |
944 | 0 | _rb: RegId, |
945 | 0 | _rc: RegId, |
946 | 0 | _rd: RegId, |
947 | 0 | _imm: u32, |
948 | 0 | ) -> Self { |
949 | 0 | Self([0; 3]) |
950 | 0 | } |
951 | | }; |
952 | | } |
953 | | |
954 | | // Debug implementations for each instruction. |
955 | | macro_rules! op_debug_fmt { |
956 | | ($Op:ident[$ra:ident : RegId]) => { |
957 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
958 | 0 | let ra = self.unpack(); |
959 | 0 | f.debug_struct(stringify!($Op)) |
960 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
961 | 0 | .finish() |
962 | 0 | } |
963 | | }; |
964 | | ($Op:ident[$ra:ident : RegId $rb:ident : RegId]) => { |
965 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
966 | 0 | let (ra, rb) = self.unpack(); |
967 | 0 | f.debug_struct(stringify!($Op)) |
968 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
969 | 0 | .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb))) |
970 | 0 | .finish() |
971 | 0 | } |
972 | | }; |
973 | | ($Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId]) => { |
974 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
975 | 0 | let (ra, rb, rc) = self.unpack(); |
976 | 0 | f.debug_struct(stringify!($Op)) |
977 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
978 | 0 | .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb))) |
979 | 0 | .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc))) |
980 | 0 | .finish() |
981 | 0 | } |
982 | | }; |
983 | | ( |
984 | | $Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId] |
985 | | ) => { |
986 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
987 | 0 | let (ra, rb, rc, rd) = self.unpack(); |
988 | 0 | f.debug_struct(stringify!($Op)) |
989 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
990 | 0 | .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb))) |
991 | 0 | .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc))) |
992 | 0 | .field(stringify!($rd), &format_args!("{:#02x}", u8::from(rd))) |
993 | 0 | .finish() |
994 | 0 | } |
995 | | }; |
996 | | ( |
997 | | $Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06] |
998 | | ) => { |
999 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1000 | 0 | let (ra, rb, rc, imm) = self.unpack(); |
1001 | 0 | f.debug_struct(stringify!($Op)) |
1002 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
1003 | 0 | .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb))) |
1004 | 0 | .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc))) |
1005 | 0 | .field(stringify!($imm), &u8::from(imm)) |
1006 | 0 | .finish() |
1007 | 0 | } |
1008 | | }; |
1009 | | ($Op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12]) => { |
1010 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1011 | 0 | let (ra, rb, imm) = self.unpack(); |
1012 | 0 | f.debug_struct(stringify!($Op)) |
1013 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
1014 | 0 | .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb))) |
1015 | 0 | .field(stringify!($imm), &u16::from(imm)) |
1016 | 0 | .finish() |
1017 | 0 | } |
1018 | | }; |
1019 | | ($Op:ident[$ra:ident : RegId $imm:ident : Imm18]) => { |
1020 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1021 | 0 | let (ra, imm) = self.unpack(); |
1022 | 0 | f.debug_struct(stringify!($Op)) |
1023 | 0 | .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra))) |
1024 | 0 | .field(stringify!($imm), &u32::from(imm)) |
1025 | 0 | .finish() |
1026 | 0 | } |
1027 | | }; |
1028 | | ($Op:ident[$imm:ident : Imm24]) => { |
1029 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1030 | 0 | let imm = self.unpack(); |
1031 | 0 | f.debug_struct(stringify!($Op)) |
1032 | 0 | .field(stringify!($imm), &u32::from(imm)) |
1033 | 0 | .finish() |
1034 | 0 | } |
1035 | | }; |
1036 | | ($Op:ident[]) => { |
1037 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1038 | 0 | f.debug_struct(stringify!($Op)).finish() |
1039 | 0 | } |
1040 | | }; |
1041 | | } |
1042 | | |
1043 | | // Recursively declares a unique struct for each opcode. |
1044 | | macro_rules! decl_op_struct { |
1045 | | ($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => { |
1046 | | #[doc = $doc] |
1047 | | #[derive(Clone, Copy, Eq, Hash, PartialEq)] |
1048 | 212 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
1049 | 0 | #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] |
1050 | | pub struct $Op(pub (super) [u8; 3]); |
1051 | | decl_op_struct!($($rest)*); |
1052 | | }; |
1053 | | () => {}; |
1054 | | } |
1055 | | |
1056 | | /// This macro is intentionaly private. See the module-level documentation for a thorough |
1057 | | /// explanation of how this macro works. |
1058 | | macro_rules! impl_instructions { |
1059 | | // Define the `Opcode` enum. |
1060 | | (decl_opcode_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => { |
1061 | | /// Solely the opcode portion of an instruction represented as a single byte. |
1062 | | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
1063 | 0 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
1064 | | #[repr(u8)] |
1065 | | pub enum Opcode { |
1066 | | $( |
1067 | | #[doc = $doc] |
1068 | | $Op = $ix, |
1069 | | )* |
1070 | | } |
1071 | | }; |
1072 | | |
1073 | | // Define the `Instruction` enum. |
1074 | | (decl_instruction_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => { |
1075 | | /// Representation of a single instruction for the interpreter. |
1076 | | /// |
1077 | | /// The opcode is represented in the tag (variant), or may be retrieved in the form of an |
1078 | | /// `Opcode` byte using the `opcode` method. |
1079 | | /// |
1080 | | /// The register and immediate data associated with the instruction is represented within |
1081 | | /// an inner unit type wrapper around the 3 remaining bytes. |
1082 | | #[derive(Clone, Copy, Eq, Hash, PartialEq)] |
1083 | 424 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] |
1084 | | pub enum Instruction { |
1085 | | $( |
1086 | | #[doc = $doc] |
1087 | | $Op(op::$Op), |
1088 | | )* |
1089 | | } |
1090 | | }; |
1091 | | |
1092 | | // Recursively generate a test constructor for each opcode |
1093 | | (impl_opcode_test_construct $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => { |
1094 | | #[cfg(test)] |
1095 | | #[allow(clippy::cast_possible_truncation)] |
1096 | | impl crate::_op::$Op { |
1097 | | op_test_construct_fn!($($field)*); |
1098 | | } |
1099 | | impl_instructions!(impl_opcode_test_construct $($rest)*); |
1100 | | }; |
1101 | | (impl_opcode_test_construct) => {}; |
1102 | | |
1103 | | // Recursively generate a test constructor for each opcode |
1104 | | (tests $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => { |
1105 | | op_test!($Op $op [$($field)*]); |
1106 | | impl_instructions!(tests $($rest)*); |
1107 | | }; |
1108 | | (tests) => {}; |
1109 | | |
1110 | | // Implement constructors and accessors for register and immediate values. |
1111 | | (impl_op $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => { |
1112 | | impl $Op { |
1113 | | /// The associated 8-bit Opcode value. |
1114 | | pub const OPCODE: Opcode = Opcode::$Op; |
1115 | | } |
1116 | | |
1117 | | op_new!($Op $($fname: $field)*); |
1118 | | op_accessors!($Op $($fname: $field)*); |
1119 | | |
1120 | | impl $Op { |
1121 | | op_unpack!($($field)*); |
1122 | | op_reserved_part!($($field)*); |
1123 | | op_reg_ids!($($field)*); |
1124 | | } |
1125 | | |
1126 | | op_constructor!($doc $Op $op [$($fname: $field)*]); |
1127 | | |
1128 | | impl From<$Op> for [u8; 3] { |
1129 | 0 | fn from($Op(arr): $Op) -> Self { |
1130 | 0 | arr |
1131 | 0 | } |
1132 | | } |
1133 | | |
1134 | | impl From<$Op> for [u8; 4] { |
1135 | 272k | fn from($Op([a, b, c]): $Op) -> Self { |
1136 | 272k | [$Op::OPCODE as u8, a, b, c] |
1137 | 272k | } |
1138 | | } |
1139 | | |
1140 | | impl From<$Op> for u32 { |
1141 | 0 | fn from(op: $Op) -> Self { |
1142 | 0 | u32::from_be_bytes(op.into()) |
1143 | 0 | } |
1144 | | } |
1145 | | |
1146 | | impl From<$Op> for Instruction { |
1147 | 98.1k | fn from(op: $Op) -> Self { |
1148 | 98.1k | Instruction::$Op(op) |
1149 | 98.1k | } |
1150 | | } |
1151 | | |
1152 | | #[cfg(feature = "typescript")] |
1153 | | impl From<$Op> for typescript::Instruction { |
1154 | 0 | fn from(opcode: $Op) -> Self { |
1155 | 0 | typescript::Instruction::new(opcode.into()) |
1156 | 0 | } |
1157 | | } |
1158 | | |
1159 | | impl core::fmt::Debug for $Op { |
1160 | | op_debug_fmt!($Op [$($fname: $field)*]); |
1161 | | } |
1162 | | |
1163 | | impl_instructions!(impl_op $($rest)*); |
1164 | | }; |
1165 | | (impl_op) => {}; |
1166 | | |
1167 | | // Implement functions for all opcode variants |
1168 | | (impl_opcode $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => { |
1169 | | impl core::convert::TryFrom<u8> for Opcode { |
1170 | | type Error = InvalidOpcode; |
1171 | 1.63M | fn try_from(u: u8) -> Result<Self, Self::Error> { |
1172 | 1.63M | match u { |
1173 | | $( |
1174 | 6.79k | $ix => Ok(Opcode::$Op), |
1175 | | )* |
1176 | 685 | _ => Err(InvalidOpcode), |
1177 | | } |
1178 | 1.63M | } |
1179 | | } |
1180 | | |
1181 | | impl Opcode { |
1182 | | /// Construct the instruction from all possible raw fields, ignoring inapplicable ones. |
1183 | | #[cfg(test)] |
1184 | 141 | pub fn test_construct(self, ra: RegId, rb: RegId, rc: RegId, rd: RegId, imm: u32) -> Instruction { |
1185 | 141 | match self { |
1186 | | $( |
1187 | 3 | Self::$Op => Instruction::$Op(crate::_op::$Op::test_construct(ra, rb, rc, rd, imm)), |
1188 | | )* |
1189 | | } |
1190 | 141 | } |
1191 | | } |
1192 | | }; |
1193 | | |
1194 | | // Implement accessors for register and immediate values. |
1195 | | (impl_instruction $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => { |
1196 | | impl Instruction { |
1197 | | /// This instruction's opcode. |
1198 | 1.02M | pub fn opcode(&self) -> Opcode { |
1199 | 1.02M | match self { |
1200 | | $( |
1201 | 0 | Self::$Op(_) => Opcode::$Op, |
1202 | | )* |
1203 | | } |
1204 | 1.02M | } |
1205 | | |
1206 | | /// Unpacks all register IDs into a slice of options. |
1207 | 148 | pub fn reg_ids(&self) -> [Option<RegId>; 4] { |
1208 | 148 | match self { |
1209 | | $( |
1210 | 0 | Self::$Op(op) => op.reg_ids(), |
1211 | | )* |
1212 | | } |
1213 | 148 | } |
1214 | | } |
1215 | | |
1216 | | impl From<Instruction> for [u8; 4] { |
1217 | 272k | fn from(inst: Instruction) -> Self { |
1218 | 272k | match inst { |
1219 | | $( |
1220 | 6.80k | Instruction::$Op(op) => op.into(), |
1221 | | )* |
1222 | | } |
1223 | 272k | } |
1224 | | } |
1225 | | |
1226 | | #[cfg(feature = "typescript")] |
1227 | | impl From<Instruction> for typescript::Instruction { |
1228 | 0 | fn from(inst: Instruction) -> Self { |
1229 | 0 | typescript::Instruction::new(inst) |
1230 | 0 | } |
1231 | | } |
1232 | | |
1233 | | impl core::convert::TryFrom<[u8; 4]> for Instruction { |
1234 | | type Error = InvalidOpcode; |
1235 | 1.63M | fn try_from([op, a, b, c]: [u8; 4]) -> Result<Self, Self::Error> { |
1236 | 1.63M | match Opcode::try_from(op)?379 { |
1237 | | $( |
1238 | | Opcode::$Op => Ok(Self::$Op({ |
1239 | 6.79k | let op = op::$Op([a, b, c]); |
1240 | 6.79k | if !op.reserved_part_is_zero() { |
1241 | 4 | return Err(InvalidOpcode); |
1242 | 6.79k | } |
1243 | 6.79k | op |
1244 | | })), |
1245 | | )* |
1246 | | } |
1247 | 1.63M | } |
1248 | | } |
1249 | | |
1250 | | impl core::fmt::Debug for Instruction { |
1251 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
1252 | 0 | match self { |
1253 | | $( |
1254 | 0 | Self::$Op(op) => op.fmt(f), |
1255 | | )* |
1256 | | } |
1257 | 0 | } |
1258 | | } |
1259 | | }; |
1260 | | |
1261 | | // Entrypoint to the macro, generates structs, methods, opcode enum and instruction enum |
1262 | | // separately. |
1263 | | ($($tts:tt)*) => { |
1264 | | mod _op { |
1265 | | use super::*; |
1266 | | decl_op_struct!($($tts)*); |
1267 | | impl_instructions!(impl_op $($tts)*); |
1268 | | } |
1269 | | impl_instructions!(decl_opcode_enum $($tts)*); |
1270 | | impl_instructions!(decl_instruction_enum $($tts)*); |
1271 | | impl_instructions!(impl_opcode $($tts)*); |
1272 | | impl_instructions!(impl_instruction $($tts)*); |
1273 | | impl_instructions!(impl_opcode_test_construct $($tts)*); |
1274 | | |
1275 | | |
1276 | | #[cfg(test)] |
1277 | | mod opcode_tests { |
1278 | | use super::*; |
1279 | | impl_instructions!(tests $($tts)*); |
1280 | | } |
1281 | | }; |
1282 | | } |
1283 | | |
1284 | | /// Defines the enum with `TryFrom` trait implementation. |
1285 | | #[macro_export] |
1286 | | macro_rules! enum_try_from { |
1287 | | ( |
1288 | | $(#[$meta:meta])* $vis:vis enum $name:ident { |
1289 | | $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* |
1290 | | }, |
1291 | | $from:ident |
1292 | | ) => { |
1293 | | $(#[$meta])* |
1294 | | $vis enum $name { |
1295 | | $($(#[$vmeta])* $vname $(= $val)?,)* |
1296 | | } |
1297 | | |
1298 | | impl core::convert::TryFrom<$from> for $name { |
1299 | | type Error = $crate::PanicReason; |
1300 | | |
1301 | 472 | fn try_from(v: $from) -> Result<Self, Self::Error> { |
1302 | 472 | match v { |
1303 | 4 | $(x if x == $name::$vname as $from => Ok($name::$vname),)* |
1304 | 0 | _ => Err($crate::PanicReason::InvalidMetadataIdentifier), |
1305 | | } |
1306 | 472 | } |
1307 | | } |
1308 | | } |
1309 | | } |
1310 | | |
1311 | | #[cfg(test)] |
1312 | | // Generate a test for the instruction. |
1313 | | macro_rules! op_test { |
1314 | | ($Op:ident $op:ident[RegId]) => { |
1315 | | #[test] |
1316 | 9 | fn $op() { |
1317 | 9 | crate::macros::test_reserved_part(Opcode::$Op, true, false, false, false); |
1318 | 9 | } |
1319 | | }; |
1320 | | ($Op:ident $op:ident[RegId RegId]) => { |
1321 | | #[test] |
1322 | 11 | fn $op() { |
1323 | 11 | crate::macros::test_reserved_part(Opcode::$Op, true, true, false, false); |
1324 | 11 | } |
1325 | | }; |
1326 | | ($Op:ident $op:ident[RegId RegId RegId]) => { |
1327 | | #[test] |
1328 | 27 | fn $op() { |
1329 | 27 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, false); |
1330 | 27 | } |
1331 | | }; |
1332 | | ($Op:ident $op:ident[RegId RegId RegId RegId]) => { |
1333 | | #[test] |
1334 | 19 | fn $op() { |
1335 | 19 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true); |
1336 | 19 | } |
1337 | | }; |
1338 | | ($Op:ident $op:ident[RegId RegId RegId Imm06]) => { |
1339 | | #[test] |
1340 | 11 | fn $op() { |
1341 | 11 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true); |
1342 | 11 | } |
1343 | | }; |
1344 | | ($Op:ident $op:ident[RegId RegId Imm12]) => { |
1345 | | #[test] |
1346 | 20 | fn $op() { |
1347 | 20 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true); |
1348 | 20 | } |
1349 | | }; |
1350 | | ($Op:ident $op:ident[RegId Imm18]) => { |
1351 | | #[test] |
1352 | 6 | fn $op() { |
1353 | 6 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true); |
1354 | 6 | } |
1355 | | }; |
1356 | | ($Op:ident $op:ident[Imm24]) => { |
1357 | | #[test] |
1358 | 7 | fn $op() { |
1359 | 7 | crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true); |
1360 | 7 | } |
1361 | | }; |
1362 | | ($Op:ident $op:ident[]) => { |
1363 | | #[test] |
1364 | 1 | fn $op() { |
1365 | 1 | crate::macros::test_reserved_part(Opcode::$Op, false, false, false, false); |
1366 | 1 | } |
1367 | | }; |
1368 | | } |
1369 | | |
1370 | | #[cfg(test)] |
1371 | 888 | fn bytes(a: u8, b: u8, c: u8, d: u8) -> [u8; 3] { |
1372 | 888 | use crate::RegId; |
1373 | 888 | crate::pack::bytes_from_ra_rb_rc_rd( |
1374 | 888 | RegId::new(a), |
1375 | 888 | RegId::new(b), |
1376 | 888 | RegId::new(c), |
1377 | 888 | RegId::new(d), |
1378 | 888 | ) |
1379 | 888 | } |
1380 | | |
1381 | | #[cfg(test)] |
1382 | 111 | pub(crate) fn test_reserved_part( |
1383 | 111 | opcode: crate::Opcode, |
1384 | 111 | zero_should_pass: bool, |
1385 | 111 | first_should_pass: bool, |
1386 | 111 | second_should_pass: bool, |
1387 | 111 | third_should_pass: bool, |
1388 | 111 | ) { |
1389 | 111 | use crate::Instruction; |
1390 | 111 | |
1391 | 111 | // Args: 0 |
1392 | 111 | let [a, b, c] = bytes(0, 0, 0, 0); |
1393 | 111 | Instruction::try_from([opcode as u8, a, b, c]).unwrap(); |
1394 | 111 | let [a, b, c] = bytes(1, 0, 0, 0); |
1395 | 111 | let zero_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok(); |
1396 | 111 | assert_eq!( |
1397 | | zero_should_pass, zero_is_error, |
1398 | 0 | "Opcode: {opcode:?} failed zero" |
1399 | | ); |
1400 | | |
1401 | | // Args: 1 |
1402 | 111 | let [a, b, c] = bytes(0, 0, 0, 0); |
1403 | 111 | Instruction::try_from([opcode as u8, a, b, c]).unwrap(); |
1404 | 111 | let [a, b, c] = bytes(0, 1, 0, 0); |
1405 | 111 | let first_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok(); |
1406 | 111 | assert_eq!( |
1407 | | first_should_pass, first_is_error, |
1408 | 0 | "Opcode: {opcode:?} failed first" |
1409 | | ); |
1410 | | |
1411 | | // Args: 2 |
1412 | 111 | let [a, b, c] = bytes(0, 0, 0, 0); |
1413 | 111 | Instruction::try_from([opcode as u8, a, b, c]).unwrap(); |
1414 | 111 | let [a, b, c] = bytes(0, 0, 1, 0); |
1415 | 111 | let second_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok(); |
1416 | 111 | assert_eq!( |
1417 | | second_should_pass, second_is_error, |
1418 | 0 | "Opcode: {opcode:?} failed second" |
1419 | | ); |
1420 | | |
1421 | | // Args: 3 |
1422 | 111 | let [a, b, c] = bytes(0, 0, 0, 0); |
1423 | 111 | Instruction::try_from([opcode as u8, a, b, c]).unwrap(); |
1424 | 111 | let [a, b, c] = bytes(0, 0, 0, 1); |
1425 | 111 | let third_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok(); |
1426 | 111 | assert_eq!( |
1427 | | third_should_pass, third_is_error, |
1428 | 0 | "Opcode: {opcode:?} failed third" |
1429 | | ); |
1430 | 111 | } |