Coverage Report

Created: 2024-09-26 18:15

/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
}