macro_rules! op_constructor {
($doc:literal $Op:ident $op:ident[$ra:ident : RegId]) => {
#[doc = $doc]
pub fn $op<A: CheckRegId>($ra: A) -> Instruction {
$Op::new($ra.check()).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8) -> typescript::Instruction {
crate::op::$op($ra).into()
}
};
};
($doc:literal $Op:ident $op:ident[$ra:ident : RegId $rb:ident : RegId]) => {
#[doc = $doc]
pub fn $op<A: CheckRegId, B: CheckRegId>($ra: A, $rb: B) -> Instruction {
$Op::new($ra.check(), $rb.check()).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $rb: u8) -> typescript::Instruction {
crate::op::$op($ra, $rb).into()
}
};
};
(
$doc:literal
$Op:ident
$op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId]
) => {
#[doc = $doc]
pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>(
$ra: A,
$rb: B,
$rc: C,
) -> Instruction {
$Op::new($ra.check(), $rb.check(), $rc.check()).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $rb: u8, $rc: u8) -> typescript::Instruction {
crate::op::$op($ra, $rb, $rc).into()
}
};
};
(
$doc:literal
$Op:ident
$op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId]
) => {
#[doc = $doc]
pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId, D: CheckRegId>(
$ra: A,
$rb: B,
$rc: C,
$rd: D,
) -> Instruction {
$Op::new($ra.check(), $rb.check(), $rc.check(), $rd.check()).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $rb: u8, $rc: u8, $rd: u8) -> typescript::Instruction {
crate::op::$op($ra, $rb, $rc, $rd).into()
}
};
};
(
$doc:literal
$Op:ident
$op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06]
) => {
#[doc = $doc]
pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>(
$ra: A,
$rb: B,
$rc: C,
$imm: u8,
) -> Instruction {
$Op::new($ra.check(), $rb.check(), $rc.check(), check_imm06($imm)).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $rb: u8, $rc: u8, $imm: u8) -> typescript::Instruction {
crate::op::$op($ra, $rb, $rc, $imm).into()
}
};
};
(
$doc:literal
$Op:ident
$op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12]
) => {
#[doc = $doc]
pub fn $op<A: CheckRegId, B: CheckRegId>(
$ra: A,
$rb: B,
$imm: u16,
) -> Instruction {
$Op::new($ra.check(), $rb.check(), check_imm12($imm)).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $rb: u8, $imm: u16) -> typescript::Instruction {
crate::op::$op($ra, $rb, $imm).into()
}
};
};
($doc:literal $Op:ident $op:ident[$ra:ident : RegId $imm:ident : Imm18]) => {
#[doc = $doc]
pub fn $op<A: CheckRegId>($ra: A, $imm: u32) -> Instruction {
$Op::new($ra.check(), check_imm18($imm)).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($ra: u8, $imm: u32) -> typescript::Instruction {
crate::op::$op($ra, $imm).into()
}
};
};
($doc:literal $Op:ident $op:ident[$imm:ident : Imm24]) => {
#[doc = $doc]
pub fn $op($imm: u32) -> Instruction {
$Op::new(check_imm24($imm)).into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op($imm: u32) -> typescript::Instruction {
crate::op::$op($imm).into()
}
};
};
($doc:literal $Op:ident $op:ident[]) => {
#[doc = $doc]
pub fn $op() -> Instruction {
$Op::new().into()
}
#[cfg(feature = "typescript")]
const _: () = {
use super::*;
#[wasm_bindgen::prelude::wasm_bindgen]
#[doc = $doc]
pub fn $op() -> typescript::Instruction {
crate::op::$op().into()
}
};
};
}
macro_rules! op_new {
($Op:ident $ra:ident : RegId) => {
impl $Op {
pub fn new($ra: RegId) -> Self {
Self(pack::bytes_from_ra($ra))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($ra: RegId) -> Self {
Self::new($ra)
}
}
};
($Op:ident $ra:ident : RegId $rb:ident : RegId) => {
impl $Op {
pub fn new($ra: RegId, $rb: RegId) -> Self {
Self(pack::bytes_from_ra_rb($ra, $rb))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($ra: RegId, $rb: RegId) -> Self {
Self::new($ra, $rb)
}
}
};
($Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId) => {
impl $Op {
pub fn new($ra: RegId, $rb: RegId, $rc: RegId) -> Self {
Self(pack::bytes_from_ra_rb_rc($ra, $rb, $rc))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($ra: RegId, $rb: RegId, $rc: RegId) -> Self {
Self::new($ra, $rb, $rc)
}
}
};
(
$Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId
) => {
impl $Op {
pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $rd: RegId) -> Self {
Self(pack::bytes_from_ra_rb_rc_rd($ra, $rb, $rc, $rd))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript(
$ra: RegId,
$rb: RegId,
$rc: RegId,
$rd: RegId,
) -> Self {
Self::new($ra, $rb, $rc, $rd)
}
}
};
(
$Op:ident
$ra:ident : RegId
$rb:ident : RegId
$rc:ident : RegId
$imm:ident : Imm06
) => {
impl $Op {
pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $imm: Imm06) -> Self {
Self(pack::bytes_from_ra_rb_rc_imm06($ra, $rb, $rc, $imm))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript(
$ra: RegId,
$rb: RegId,
$rc: RegId,
$imm: Imm06,
) -> Self {
Self::new($ra, $rb, $rc, $imm)
}
}
};
($Op:ident $ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12) => {
impl $Op {
pub fn new($ra: RegId, $rb: RegId, $imm: Imm12) -> Self {
Self(pack::bytes_from_ra_rb_imm12($ra, $rb, $imm))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($ra: RegId, $rb: RegId, $imm: Imm12) -> Self {
Self::new($ra, $rb, $imm)
}
}
};
($Op:ident $ra:ident : RegId $imm:ident : Imm18) => {
impl $Op {
pub fn new($ra: RegId, $imm: Imm18) -> Self {
Self(pack::bytes_from_ra_imm18($ra, $imm))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($ra: RegId, $imm: Imm18) -> Self {
Self::new($ra, $imm)
}
}
};
($Op:ident $imm:ident : Imm24) => {
impl $Op {
pub fn new($imm: Imm24) -> Self {
Self(pack::bytes_from_imm24($imm))
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
pub fn new_typescript($imm: Imm24) -> Self {
Self::new($imm)
}
}
};
($Op:ident) => {
impl $Op {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self([0; 3])
}
}
#[cfg(feature = "typescript")]
#[wasm_bindgen::prelude::wasm_bindgen]
impl $Op {
#[wasm_bindgen(constructor)]
#[allow(clippy::new_without_default)]
pub fn new_typescript() -> Self {
Self::new()
}
}
};
}
macro_rules! op_accessors {
($Op:ident $ra:ident: RegId) => {
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn ra(&self) -> RegId {
unpack::ra_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $rb:ident: RegId) => {
op_accessors!($Op ra: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn rb(&self) -> RegId {
unpack::rb_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId) => {
op_accessors!($Op $ra: RegId $rb: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn rc(&self) -> RegId {
unpack::rc_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $rd:ident: RegId) => {
op_accessors!($Op $ra: RegId $rb: RegId $rc: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn rd(&self) -> RegId {
unpack::rd_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $imm:ident: Imm06) => {
op_accessors!($Op $ra: RegId rb: RegId $rc: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn imm06(&self) -> Imm06 {
unpack::imm06_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $rb:ident: RegId $imm:ident: Imm12) => {
op_accessors!($Op $ra: RegId $rb: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn imm12(&self) -> Imm12 {
unpack::imm12_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: RegId $imm:ident: Imm18) => {
op_accessors!($Op $ra: RegId);
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn imm18(&self) -> Imm18 {
unpack::imm18_from_bytes(self.0)
}
}
};
($Op:ident $ra:ident: Imm24) => {
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
impl $Op {
pub fn imm24(&self) -> Imm24 {
unpack::imm24_from_bytes(self.0)
}
}
};
($Op:ident) => {};
}
macro_rules! op_unpack {
(RegId) => {
pub fn unpack(self) -> RegId {
unpack::ra_from_bytes(self.0)
}
};
(RegId RegId) => {
pub fn unpack(self) -> (RegId, RegId) {
unpack::ra_rb_from_bytes(self.0)
}
};
(RegId RegId RegId) => {
pub fn unpack(self) -> (RegId, RegId, RegId) {
unpack::ra_rb_rc_from_bytes(self.0)
}
};
(RegId RegId RegId RegId) => {
pub fn unpack(self) -> (RegId, RegId, RegId, RegId) {
unpack::ra_rb_rc_rd_from_bytes(self.0)
}
};
(RegId RegId RegId Imm06) => {
pub fn unpack(self) -> (RegId, RegId, RegId, Imm06) {
unpack::ra_rb_rc_imm06_from_bytes(self.0)
}
};
(RegId RegId Imm12) => {
pub fn unpack(self) -> (RegId, RegId, Imm12) {
unpack::ra_rb_imm12_from_bytes(self.0)
}
};
(RegId Imm18) => {
pub fn unpack(self) -> (RegId, Imm18) {
unpack::ra_imm18_from_bytes(self.0)
}
};
(Imm24) => {
pub fn unpack(self) -> Imm24 {
unpack::imm24_from_bytes(self.0)
}
};
() => {};
}
macro_rules! op_reserved_part {
(RegId) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
let (_, imm) = unpack::ra_imm18_from_bytes(self.0);
imm.0 == 0
}
};
(RegId RegId) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
let (_, _, imm) = unpack::ra_rb_imm12_from_bytes(self.0);
imm.0 == 0
}
};
(RegId RegId RegId) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
let (_, _, _, imm) = unpack::ra_rb_rc_imm06_from_bytes(self.0);
imm.0 == 0
}
};
(RegId RegId RegId RegId) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
true
}
};
(RegId RegId RegId Imm06) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
true
}
};
(RegId RegId Imm12) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
true
}
};
(RegId Imm18) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
true
}
};
(Imm24) => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
true
}
};
() => {
pub(crate) fn reserved_part_is_zero(self) -> bool {
self.0 == [0; 3]
}
};
}
macro_rules! op_reg_ids {
(RegId) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let ra = self.unpack();
[Some(ra), None, None, None]
}
};
(RegId RegId) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, rb) = self.unpack();
[Some(ra), Some(rb), None, None]
}
};
(RegId RegId RegId) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, rb, rc) = self.unpack();
[Some(ra), Some(rb), Some(rc), None]
}
};
(RegId RegId RegId RegId) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, rb, rc, rd) = self.unpack();
[Some(ra), Some(rb), Some(rc), Some(rd)]
}
};
(RegId RegId RegId Imm06) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, rb, rc, _) = self.unpack();
[Some(ra), Some(rb), Some(rc), None]
}
};
(RegId RegId Imm12) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, rb, _) = self.unpack();
[Some(ra), Some(rb), None, None]
}
};
(RegId Imm18) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
let (ra, _) = self.unpack();
[Some(ra), None, None, None]
}
};
($($rest:tt)*) => {
pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
[None; 4]
}
};
}
#[cfg(test)]
macro_rules! op_test_construct_fn {
(RegId) => {
pub fn test_construct(
ra: RegId,
_rb: RegId,
_rc: RegId,
_rd: RegId,
_imm: u32,
) -> Self {
Self(pack::bytes_from_ra(ra))
}
};
(RegId RegId) => {
pub fn test_construct(
ra: RegId,
rb: RegId,
_rc: RegId,
_rd: RegId,
_imm: u32,
) -> Self {
Self(pack::bytes_from_ra_rb(ra, rb))
}
};
(RegId RegId RegId) => {
pub fn test_construct(
ra: RegId,
rb: RegId,
rc: RegId,
_rd: RegId,
_imm: u32,
) -> Self {
Self(pack::bytes_from_ra_rb_rc(ra, rb, rc))
}
};
(RegId RegId RegId RegId) => {
pub fn test_construct(
ra: RegId,
rb: RegId,
rc: RegId,
rd: RegId,
_imm: u32,
) -> Self {
Self(pack::bytes_from_ra_rb_rc_rd(ra, rb, rc, rd))
}
};
(RegId RegId RegId Imm06) => {
pub fn test_construct(
ra: RegId,
rb: RegId,
rc: RegId,
_rd: RegId,
imm: u32,
) -> Self {
Self(pack::bytes_from_ra_rb_rc_imm06(
ra,
rb,
rc,
Imm06::from(imm as u8),
))
}
};
(RegId RegId Imm12) => {
pub fn test_construct(
ra: RegId,
rb: RegId,
_rc: RegId,
_rd: RegId,
imm: u32,
) -> Self {
Self(pack::bytes_from_ra_rb_imm12(
ra,
rb,
Imm12::from(imm as u16),
))
}
};
(RegId Imm18) => {
pub fn test_construct(
ra: RegId,
_rb: RegId,
_rc: RegId,
_rd: RegId,
imm: u32,
) -> Self {
Self(pack::bytes_from_ra_imm18(ra, Imm18::from(imm)))
}
};
(Imm24) => {
pub fn test_construct(
_ra: RegId,
_rb: RegId,
_rc: RegId,
_rd: RegId,
imm: u32,
) -> Self {
Self(pack::bytes_from_imm24(Imm24::from(imm)))
}
};
() => {
#[allow(clippy::new_without_default)]
pub fn test_construct(
_ra: RegId,
_rb: RegId,
_rc: RegId,
_rd: RegId,
_imm: u32,
) -> Self {
Self([0; 3])
}
};
}
macro_rules! op_debug_fmt {
($Op:ident[$ra:ident : RegId]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let ra = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.finish()
}
};
($Op:ident[$ra:ident : RegId $rb:ident : RegId]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, rb) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
.finish()
}
};
($Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, rb, rc) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
.field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
.finish()
}
};
(
$Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId]
) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, rb, rc, rd) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
.field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
.field(stringify!($rd), &format_args!("{:#02x}", u8::from(rd)))
.finish()
}
};
(
$Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06]
) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, rb, rc, imm) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
.field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
.field(stringify!($imm), &u8::from(imm))
.finish()
}
};
($Op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, rb, imm) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
.field(stringify!($imm), &u16::from(imm))
.finish()
}
};
($Op:ident[$ra:ident : RegId $imm:ident : Imm18]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let (ra, imm) = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
.field(stringify!($imm), &u32::from(imm))
.finish()
}
};
($Op:ident[$imm:ident : Imm24]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let imm = self.unpack();
f.debug_struct(stringify!($Op))
.field(stringify!($imm), &u32::from(imm))
.finish()
}
};
($Op:ident[]) => {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(stringify!($Op)).finish()
}
};
}
macro_rules! decl_op_struct {
($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
#[doc = $doc]
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
pub struct $Op(pub (super) [u8; 3]);
decl_op_struct!($($rest)*);
};
() => {};
}
macro_rules! impl_instructions {
(decl_opcode_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum Opcode {
$(
#[doc = $doc]
$Op = $ix,
)*
}
};
(decl_instruction_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Instruction {
$(
#[doc = $doc]
$Op(op::$Op),
)*
}
};
(impl_opcode_test_construct $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
#[cfg(test)]
#[allow(clippy::cast_possible_truncation)]
impl crate::_op::$Op {
op_test_construct_fn!($($field)*);
}
impl_instructions!(impl_opcode_test_construct $($rest)*);
};
(impl_opcode_test_construct) => {};
(tests $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
op_test!($Op $op [$($field)*]);
impl_instructions!(tests $($rest)*);
};
(tests) => {};
(impl_op $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
impl $Op {
pub const OPCODE: Opcode = Opcode::$Op;
pub fn from_raw_args(args: [u8; 3]) -> Result<Self, InvalidOpcode> {
let op = Self(args);
if !op.reserved_part_is_zero() {
return Err(InvalidOpcode);
}
Ok(op)
}
}
op_new!($Op $($fname: $field)*);
op_accessors!($Op $($fname: $field)*);
impl $Op {
op_unpack!($($field)*);
op_reserved_part!($($field)*);
op_reg_ids!($($field)*);
}
op_constructor!($doc $Op $op [$($fname: $field)*]);
impl From<$Op> for [u8; 3] {
fn from($Op(arr): $Op) -> Self {
arr
}
}
impl From<$Op> for [u8; 4] {
fn from($Op([a, b, c]): $Op) -> Self {
[$Op::OPCODE as u8, a, b, c]
}
}
impl From<$Op> for u32 {
fn from(op: $Op) -> Self {
u32::from_be_bytes(op.into())
}
}
impl From<$Op> for Instruction {
fn from(op: $Op) -> Self {
Instruction::$Op(op)
}
}
#[cfg(feature = "typescript")]
impl From<$Op> for typescript::Instruction {
fn from(opcode: $Op) -> Self {
typescript::Instruction::new(opcode.into())
}
}
impl core::fmt::Debug for $Op {
op_debug_fmt!($Op [$($fname: $field)*]);
}
impl_instructions!(impl_op $($rest)*);
};
(impl_op) => {};
(impl_opcode $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
impl core::convert::TryFrom<u8> for Opcode {
type Error = InvalidOpcode;
fn try_from(u: u8) -> Result<Self, Self::Error> {
match u {
$(
$ix => Ok(Opcode::$Op),
)*
_ => Err(InvalidOpcode),
}
}
}
impl Opcode {
#[cfg(test)]
pub fn test_construct(self, ra: RegId, rb: RegId, rc: RegId, rd: RegId, imm: u32) -> Instruction {
match self {
$(
Self::$Op => Instruction::$Op(crate::_op::$Op::test_construct(ra, rb, rc, rd, imm)),
)*
}
}
}
};
(impl_instruction $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
impl Instruction {
pub fn opcode(&self) -> Opcode {
match self {
$(
Self::$Op(_) => Opcode::$Op,
)*
}
}
pub fn reg_ids(&self) -> [Option<RegId>; 4] {
match self {
$(
Self::$Op(op) => op.reg_ids(),
)*
}
}
}
impl From<Instruction> for [u8; 4] {
fn from(inst: Instruction) -> Self {
match inst {
$(
Instruction::$Op(op) => op.into(),
)*
}
}
}
#[cfg(feature = "typescript")]
impl From<Instruction> for typescript::Instruction {
fn from(inst: Instruction) -> Self {
typescript::Instruction::new(inst)
}
}
impl core::convert::TryFrom<[u8; 4]> for Instruction {
type Error = InvalidOpcode;
fn try_from([op, a, b, c]: [u8; 4]) -> Result<Self, Self::Error> {
let op = match op {
$(
$ix => {
let op = op::$Op([a, b, c]);
if !op.reserved_part_is_zero() {
return Err(InvalidOpcode);
}
Self::$Op(op)
},
)*
_ => return Err(InvalidOpcode),
};
Ok(op)
}
}
impl core::fmt::Debug for Instruction {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
$(
Self::$Op(op) => op.fmt(f),
)*
}
}
}
};
($($tts:tt)*) => {
mod _op {
use super::*;
decl_op_struct!($($tts)*);
impl_instructions!(impl_op $($tts)*);
}
impl_instructions!(decl_opcode_enum $($tts)*);
impl_instructions!(decl_instruction_enum $($tts)*);
impl_instructions!(impl_opcode $($tts)*);
impl_instructions!(impl_instruction $($tts)*);
impl_instructions!(impl_opcode_test_construct $($tts)*);
#[cfg(test)]
mod opcode_tests {
use super::*;
impl_instructions!(tests $($tts)*);
}
};
}
#[macro_export]
macro_rules! enum_try_from {
(
$(#[$meta:meta])* $vis:vis enum $name:ident {
$($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
},
$from:ident
) => {
$(#[$meta])*
$vis enum $name {
$($(#[$vmeta])* $vname $(= $val)?,)*
}
impl core::convert::TryFrom<$from> for $name {
type Error = $crate::PanicReason;
fn try_from(v: $from) -> Result<Self, Self::Error> {
match v {
$(x if x == $name::$vname as $from => Ok($name::$vname),)*
_ => Err($crate::PanicReason::InvalidMetadataIdentifier),
}
}
}
}
}
#[cfg(test)]
macro_rules! op_test {
($Op:ident $op:ident[RegId]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, false, false, false);
}
};
($Op:ident $op:ident[RegId RegId]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, false, false);
}
};
($Op:ident $op:ident[RegId RegId RegId]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, false);
}
};
($Op:ident $op:ident[RegId RegId RegId RegId]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
}
};
($Op:ident $op:ident[RegId RegId RegId Imm06]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
}
};
($Op:ident $op:ident[RegId RegId Imm12]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
}
};
($Op:ident $op:ident[RegId Imm18]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
}
};
($Op:ident $op:ident[Imm24]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
}
};
($Op:ident $op:ident[]) => {
#[test]
fn $op() {
crate::macros::test_reserved_part(Opcode::$Op, false, false, false, false);
}
};
}
#[cfg(test)]
fn bytes(a: u8, b: u8, c: u8, d: u8) -> [u8; 3] {
use crate::RegId;
crate::pack::bytes_from_ra_rb_rc_rd(
RegId::new(a),
RegId::new(b),
RegId::new(c),
RegId::new(d),
)
}
#[cfg(test)]
pub(crate) fn test_reserved_part(
opcode: crate::Opcode,
zero_should_pass: bool,
first_should_pass: bool,
second_should_pass: bool,
third_should_pass: bool,
) {
use crate::Instruction;
let [a, b, c] = bytes(0, 0, 0, 0);
Instruction::try_from([opcode as u8, a, b, c]).unwrap();
let [a, b, c] = bytes(1, 0, 0, 0);
let zero_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
assert_eq!(
zero_should_pass, zero_is_error,
"Opcode: {opcode:?} failed zero"
);
let [a, b, c] = bytes(0, 0, 0, 0);
Instruction::try_from([opcode as u8, a, b, c]).unwrap();
let [a, b, c] = bytes(0, 1, 0, 0);
let first_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
assert_eq!(
first_should_pass, first_is_error,
"Opcode: {opcode:?} failed first"
);
let [a, b, c] = bytes(0, 0, 0, 0);
Instruction::try_from([opcode as u8, a, b, c]).unwrap();
let [a, b, c] = bytes(0, 0, 1, 0);
let second_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
assert_eq!(
second_should_pass, second_is_error,
"Opcode: {opcode:?} failed second"
);
let [a, b, c] = bytes(0, 0, 0, 0);
Instruction::try_from([opcode as u8, a, b, c]).unwrap();
let [a, b, c] = bytes(0, 0, 0, 1);
let third_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
assert_eq!(
third_should_pass, third_is_error,
"Opcode: {opcode:?} failed third"
);
}