diff --git a/Cargo.lock b/Cargo.lock index 55f9c7d9e79a..a8f65ce1a088 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -723,6 +723,7 @@ version = "0.117.0" dependencies = [ "arbitrary", "arbtest", + "capstone", "cranelift-assembler-x64-meta", ] @@ -730,8 +731,6 @@ dependencies = [ name = "cranelift-assembler-x64-fuzz" version = "0.0.0" dependencies = [ - "arbitrary", - "capstone", "cranelift-assembler-x64", "libfuzzer-sys", ] diff --git a/cranelift/assembler-x64/Cargo.toml b/cranelift/assembler-x64/Cargo.toml index e123c29915a5..996625260a40 100644 --- a/cranelift/assembler-x64/Cargo.toml +++ b/cranelift/assembler-x64/Cargo.toml @@ -8,9 +8,12 @@ rust-version.workspace = true [dependencies] arbitrary = { workspace = true, features = ["derive"], optional = true } +capstone = { workspace = true, optional = true } [dev-dependencies] +arbitrary = { workspace = true, features = ["derive"] } arbtest = "0.3.1" +capstone = { workspace = true } [build-dependencies] cranelift-assembler-x64-meta = { path = "meta", version = "0.117.0" } @@ -23,4 +26,4 @@ similar_names = { level = "allow", priority = 1 } wildcard_imports = { level = "allow", priority = 1 } [features] -arbitrary = ['dep:arbitrary'] +fuzz = ['dep:arbitrary', 'dep:capstone'] diff --git a/cranelift/assembler-x64/fuzz/Cargo.lock b/cranelift/assembler-x64/fuzz/Cargo.lock deleted file mode 100644 index 5d504548a3ec..000000000000 --- a/cranelift/assembler-x64/fuzz/Cargo.lock +++ /dev/null @@ -1,148 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "capstone" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b08ca438d9585a2b216b0c2e88ea51e096286c5f197f7be2526bb515ef775b6c" -dependencies = [ - "capstone-sys", - "libc", -] - -[[package]] -name = "capstone-sys" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7183271711ffb7c63a6480e4baf480e0140da59eeba9b18fcc8bf3478950e3" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "cc" -version = "1.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baee610e9452a8f6f0a1b6194ec09ff9e2d85dea54432acdae41aa0761c95d70" -dependencies = [ - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cranelift-assembler-x64" -version = "0.1.0" -dependencies = [ - "arbitrary", - "capstone", - "cranelift-assembler-x64-meta", -] - -[[package]] -name = "cranelift-assembler-x64-fuzz" -version = "0.0.0" -dependencies = [ - "cranelift-assembler-x64", - "libfuzzer-sys", -] - -[[package]] -name = "cranelift-assembler-x64-meta" -version = "0.1.0" - -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "jobserver" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.161" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" - -[[package]] -name = "libfuzzer-sys" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" -dependencies = [ - "arbitrary", - "cc", - "once_cell", -] - -[[package]] -name = "once_cell" -version = "1.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" - -[[package]] -name = "proc-macro2" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "syn" -version = "2.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/cranelift/assembler-x64/fuzz/Cargo.toml b/cranelift/assembler-x64/fuzz/Cargo.toml index 49bf0b3c47e3..384a47e3852a 100644 --- a/cranelift/assembler-x64/fuzz/Cargo.toml +++ b/cranelift/assembler-x64/fuzz/Cargo.toml @@ -10,9 +10,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = { workspace = true } -cranelift-assembler-x64 = { path = "..", features = ['arbitrary'] } -capstone = { workspace = true } -arbitrary = { workspace = true, features = ['derive'] } +cranelift-assembler-x64 = { path = "..", features = ['fuzz'] } [[bin]] name = "roundtrip" diff --git a/cranelift/assembler-x64/fuzz/fuzz_targets/roundtrip.rs b/cranelift/assembler-x64/fuzz/fuzz_targets/roundtrip.rs index c8dc4e24fdb3..7d1c236dfc63 100644 --- a/cranelift/assembler-x64/fuzz/fuzz_targets/roundtrip.rs +++ b/cranelift/assembler-x64/fuzz/fuzz_targets/roundtrip.rs @@ -1,120 +1,8 @@ #![no_main] -use arbitrary::Arbitrary; -use capstone::arch::{BuildsCapstone, BuildsCapstoneSyntax}; -use cranelift_assembler_x64::{AsReg, Inst, Registers}; +use cranelift_assembler_x64::{fuzz, Inst}; use libfuzzer_sys::fuzz_target; -// Generate a random assembly instruction and check its encoding and -// pretty-printing against a known-good disassembler. -// -// # Panics -// -// This function panics to express failure as expected by the `arbitrary` -// fuzzer infrastructure. It may fail during assembly, disassembly, or when -// comparing the disassembled strings. -fuzz_target!(|inst: Inst| { - // Check that we can actually assemble this instruction. - let assembled = assemble(&inst); - let expected = disassemble(&assembled); - - // Check that our pretty-printed output matches the known-good output. - let expected = expected.split_once(' ').unwrap().1; - let actual = inst.to_string(); - if expected != actual { - println!("> {inst}"); - println!(" debug: {inst:x?}"); - println!(" assembled: {}", pretty_print_hexadecimal(&assembled)); - assert_eq!(expected, &actual); - } +fuzz_target!(|inst: Inst| { + fuzz::roundtrip(&inst); }); - -/// Use this assembler to emit machine code into a byte buffer. -/// -/// This will skip any traps or label registrations, but this is fine for the -/// single-instruction disassembly we're doing here. -fn assemble(insn: &Inst) -> Vec { - let mut buffer = Vec::new(); - let offsets: Vec = Vec::new(); - insn.encode(&mut buffer, &offsets); - buffer -} - -/// Building a new `Capstone` each time is suboptimal (TODO). -fn disassemble(assembled: &[u8]) -> String { - let cs = capstone::Capstone::new() - .x86() - .mode(capstone::arch::x86::ArchMode::Mode64) - .syntax(capstone::arch::x86::ArchSyntax::Att) - .detail(true) - .build() - .expect("failed to create Capstone object"); - let insns = cs - .disasm_all(assembled, 0x0) - .expect("failed to disassemble"); - assert_eq!(insns.len(), 1, "not a single instruction: {assembled:x?}"); - let insn = insns.first().expect("at least one instruction"); - assert_eq!(assembled.len(), insn.len()); - insn.to_string() -} - -fn pretty_print_hexadecimal(hex: &[u8]) -> String { - use std::fmt::Write; - let mut s = String::with_capacity(hex.len() * 2); - for b in hex { - write!(&mut s, "{b:02X}").unwrap(); - } - s -} - -/// Fuzz-specific registers. -/// -/// For the fuzzer, we do not need any fancy register types; see [`FuzzReg`]. -#[derive(Arbitrary, Debug)] -pub struct FuzzRegs; - -impl Registers for FuzzRegs { - type ReadGpr = FuzzReg; - type ReadWriteGpr = FuzzReg; -} - -/// A simple `u8` register type for fuzzing only -#[derive(Clone, Copy, Debug)] -pub struct FuzzReg(u8); - -impl<'a> Arbitrary<'a> for FuzzReg { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - Ok(Self::new(u.int_in_range(0..=15)?)) - } -} - -impl AsReg for FuzzReg { - fn new(enc: u8) -> Self { - Self(enc) - } - fn enc(&self) -> u8 { - self.0 - } -} - -#[cfg(test)] -mod test { - use super::*; - use arbtest::arbtest; - use std::sync::atomic::{AtomicUsize, Ordering}; - - #[test] - fn smoke() { - let count = AtomicUsize::new(0); - arbtest(|u| { - let inst: Inst = u.arbitrary()?; - roundtrip(&inst); - println!("#{}: {inst}", count.fetch_add(1, Ordering::SeqCst)); - Ok(()) - }) - .budget_ms(1_000); - - // This will run the `roundtrip` fuzzer for one second. To repeatably - // test a single input, append `.seed(0x)`. - } -} diff --git a/cranelift/assembler-x64/meta/src/generate.rs b/cranelift/assembler-x64/meta/src/generate.rs index f15b4ebc8da0..a4a17230ae15 100644 --- a/cranelift/assembler-x64/meta/src/generate.rs +++ b/cranelift/assembler-x64/meta/src/generate.rs @@ -85,13 +85,16 @@ fn generate_inst_enum(f: &mut Formatter, insts: &[dsl::Inst]) { /// `#[derive(...)]` fn generate_derive(f: &mut Formatter) { f.line("#[derive(Clone, Debug)]", None); - f.line("#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]", None); + f.line("#[cfg_attr(any(test, feature = \"fuzz\"), derive(arbitrary::Arbitrary))]", None); } /// Adds a custom bound to the `Arbitrary` implementation which ensures that /// the associated registers are all `Arbitrary` as well. fn generate_derive_arbitrary_bounds(f: &mut Formatter) { - f.line("#[cfg_attr(feature = \"arbitrary\", arbitrary(bound = \"R: crate::arbitrary_impls::RegistersArbitrary\"))]", None); + f.line( + "#[cfg_attr(any(test, feature = \"fuzz\"), arbitrary(bound = \"R: crate::fuzz::RegistersArbitrary\"))]", + None, + ); } /// `impl std::fmt::Display for Inst { ... }` diff --git a/cranelift/assembler-x64/src/api.rs b/cranelift/assembler-x64/src/api.rs index 9e5bd4f29472..6b0d9e2da93c 100644 --- a/cranelift/assembler-x64/src/api.rs +++ b/cranelift/assembler-x64/src/api.rs @@ -67,17 +67,17 @@ impl CodeSink for Vec { /// Wrap [`CodeSink`]-specific labels. #[derive(Debug, Clone)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Label(pub u32); /// Wrap [`CodeSink`]-specific constant keys. #[derive(Debug, Clone)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Constant(pub u32); /// Wrap [`CodeSink`]-specific trap codes. #[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct TrapCode(pub NonZeroU8); /// A table mapping `KnownOffset` identifiers to their `i32` offset values. diff --git a/cranelift/assembler-x64/src/arbitrary_impls.rs b/cranelift/assembler-x64/src/arbitrary_impls.rs deleted file mode 100644 index 23000a651fca..000000000000 --- a/cranelift/assembler-x64/src/arbitrary_impls.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Manual impls of the `Arbitrary` trait for types throughout this crate. - -use crate::{AsReg, Gpr, NonRspGpr, Registers, Simm32, Simm32PlusKnownOffset}; -use arbitrary::{Arbitrary, Result, Unstructured}; - -impl Arbitrary<'_> for Simm32PlusKnownOffset { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - // For now, we don't generate offsets (TODO). - Ok(Self { - simm32: Simm32::arbitrary(u)?, - offset: None, - }) - } -} -impl Arbitrary<'_> for NonRspGpr { - fn arbitrary(u: &mut Unstructured<'_>) -> Result { - use crate::reg::enc::*; - let gpr = u.choose(&[ - RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15, - ])?; - Ok(Self::new(R::new(*gpr))) - } -} -impl<'a, R: AsReg> Arbitrary<'a> for Gpr { - fn arbitrary(u: &mut Unstructured<'a>) -> Result { - Ok(Self(R::new(u.int_in_range(0..=15)?))) - } -} - -/// Helper trait that's used to be the same as `Registers` except with an extra -/// `for<'a> Arbitrary<'a>` bound on all of the associated types. -pub trait RegistersArbitrary: - Registers Arbitrary<'a>, ReadWriteGpr: for<'a> Arbitrary<'a>> -{ -} - -impl RegistersArbitrary for R -where - R: Registers, - R::ReadGpr: for<'a> Arbitrary<'a>, - R::ReadWriteGpr: for<'a> Arbitrary<'a>, -{ -} diff --git a/cranelift/assembler-x64/src/fuzz.rs b/cranelift/assembler-x64/src/fuzz.rs new file mode 100644 index 000000000000..a2ce2bb42442 --- /dev/null +++ b/cranelift/assembler-x64/src/fuzz.rs @@ -0,0 +1,162 @@ +//! A fuzz testing oracle for roundtrip assembly-disassembly. +//! +//! This contains manual implementations of the `Arbitrary` trait for types +//! throughout this crate to avoid depending on the `arbitrary` crate +//! unconditionally (use the `fuzz` feature instead). + +use crate::{AsReg, Gpr, Inst, NonRspGpr, Registers, Simm32, Simm32PlusKnownOffset}; +use arbitrary::{Arbitrary, Result, Unstructured}; +use capstone::{arch::x86, arch::BuildsCapstone, arch::BuildsCapstoneSyntax, Capstone}; + +/// Take a random assembly instruction and check its encoding and +/// pretty-printing against a known-good disassembler. +/// +/// # Panics +/// +/// This function panics to express failure as expected by the `arbitrary` +/// fuzzer infrastructure. It may fail during assembly, disassembly, or when +/// comparing the disassembled strings. +pub fn roundtrip(inst: &Inst) { + // Check that we can actually assemble this instruction. + let assembled = assemble(inst); + let expected = disassemble(&assembled); + + // Check that our pretty-printed output matches the known-good output. + let expected = expected.split_once(' ').unwrap().1; + let actual = inst.to_string(); + if expected != actual { + println!("> {inst}"); + println!(" debug: {inst:x?}"); + println!(" assembled: {}", pretty_print_hexadecimal(&assembled)); + assert_eq!(expected, &actual); + } +} + +/// Use this assembler to emit machine code into a byte buffer. +/// +/// This will skip any traps or label registrations, but this is fine for the +/// single-instruction disassembly we're doing here. +fn assemble(insn: &Inst) -> Vec { + let mut buffer = Vec::new(); + let offsets: Vec = Vec::new(); + insn.encode(&mut buffer, &offsets); + buffer +} + +/// Building a new `Capstone` each time is suboptimal (TODO). +fn disassemble(assembled: &[u8]) -> String { + let cs = Capstone::new() + .x86() + .mode(x86::ArchMode::Mode64) + .syntax(x86::ArchSyntax::Att) + .detail(true) + .build() + .expect("failed to create Capstone object"); + let insns = cs + .disasm_all(assembled, 0x0) + .expect("failed to disassemble"); + assert_eq!(insns.len(), 1, "not a single instruction: {assembled:x?}"); + let insn = insns.first().expect("at least one instruction"); + assert_eq!(assembled.len(), insn.len()); + insn.to_string() +} + +fn pretty_print_hexadecimal(hex: &[u8]) -> String { + use std::fmt::Write; + let mut s = String::with_capacity(hex.len() * 2); + for b in hex { + write!(&mut s, "{b:02X}").unwrap(); + } + s +} + +/// Fuzz-specific registers. +/// +/// For the fuzzer, we do not need any fancy register types; see [`FuzzReg`]. +#[derive(Arbitrary, Debug)] +pub struct FuzzRegs; + +impl Registers for FuzzRegs { + type ReadGpr = FuzzReg; + type ReadWriteGpr = FuzzReg; +} + +/// A simple `u8` register type for fuzzing only. +#[derive(Clone, Copy, Debug)] +pub struct FuzzReg(u8); + +impl<'a> Arbitrary<'a> for FuzzReg { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Ok(Self::new(u.int_in_range(0..=15)?)) + } +} + +impl AsReg for FuzzReg { + fn new(enc: u8) -> Self { + Self(enc) + } + fn enc(&self) -> u8 { + self.0 + } +} + +impl Arbitrary<'_> for Simm32PlusKnownOffset { + fn arbitrary(u: &mut Unstructured<'_>) -> Result { + // For now, we don't generate offsets (TODO). + Ok(Self { + simm32: Simm32::arbitrary(u)?, + offset: None, + }) + } +} +impl Arbitrary<'_> for NonRspGpr { + fn arbitrary(u: &mut Unstructured<'_>) -> Result { + use crate::reg::enc::*; + let gpr = u.choose(&[ + RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15, + ])?; + Ok(Self::new(R::new(*gpr))) + } +} +impl<'a, R: AsReg> Arbitrary<'a> for Gpr { + fn arbitrary(u: &mut Unstructured<'a>) -> Result { + Ok(Self(R::new(u.int_in_range(0..=15)?))) + } +} + +/// Helper trait that's used to be the same as `Registers` except with an extra +/// `for<'a> Arbitrary<'a>` bound on all of the associated types. +pub trait RegistersArbitrary: + Registers Arbitrary<'a>, ReadWriteGpr: for<'a> Arbitrary<'a>> +{ +} + +impl RegistersArbitrary for R +where + R: Registers, + R::ReadGpr: for<'a> Arbitrary<'a>, + R::ReadWriteGpr: for<'a> Arbitrary<'a>, +{ +} + +#[cfg(test)] +mod test { + use super::*; + use arbtest::arbtest; + use std::sync::atomic::{AtomicUsize, Ordering}; + + #[test] + fn smoke() { + let count = AtomicUsize::new(0); + arbtest(|u| { + let inst: Inst = u.arbitrary()?; + roundtrip(&inst); + println!("#{}: {inst}", count.fetch_add(1, Ordering::SeqCst)); + Ok(()) + }) + .budget_ms(1_000); + + // This will run the `roundtrip` fuzzer for one second. To repeatably + // test a single input, append `.seed(0x)`. + } +} diff --git a/cranelift/assembler-x64/src/imm.rs b/cranelift/assembler-x64/src/imm.rs index 7ed465c5cbe3..e885308cf869 100644 --- a/cranelift/assembler-x64/src/imm.rs +++ b/cranelift/assembler-x64/src/imm.rs @@ -21,7 +21,7 @@ macro_rules! maybe_print_hex { /// An 8-bit immediate operand. #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Imm8(u8); impl Imm8 { @@ -50,7 +50,7 @@ impl Imm8 { /// A 16-bit immediate operand. #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Imm16(u16); impl Imm16 { @@ -83,7 +83,7 @@ impl Imm16 { /// 32 bits. When the operand size is 64 bits, the processor sign-extends all /// immediates to 64 bits prior to their use" (Intel SDM Vol. 2, 2.2.1.5). #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Imm32(u32); impl Imm32 { @@ -113,7 +113,7 @@ impl Imm32 { /// A 32-bit immediate like [`Imm32`], but with slightly different /// pretty-printing. #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub struct Simm32(i32); impl Simm32 { diff --git a/cranelift/assembler-x64/src/lib.rs b/cranelift/assembler-x64/src/lib.rs index 2cd34ecebce2..be7061c50cc1 100644 --- a/cranelift/assembler-x64/src/lib.rs +++ b/cranelift/assembler-x64/src/lib.rs @@ -11,8 +11,8 @@ mod mem; mod reg; mod rex; -#[cfg(feature = "arbitrary")] -mod arbitrary_impls; +#[cfg(any(test, feature = "fuzz"))] +pub mod fuzz; pub use api::{ AsReg, CodeSink, Constant, KnownOffsetTable, Label, RegisterVisitor, Registers, TrapCode, diff --git a/cranelift/assembler-x64/src/mem.rs b/cranelift/assembler-x64/src/mem.rs index 8d2d89df997d..1535d37f6581 100644 --- a/cranelift/assembler-x64/src/mem.rs +++ b/cranelift/assembler-x64/src/mem.rs @@ -7,7 +7,7 @@ use crate::rex::{encode_modrm, encode_sib, Imm, RexFlags}; /// x64 memory addressing modes. #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub enum Amode { ImmReg { base: R, @@ -75,7 +75,7 @@ impl Amode { /// For RIP-relative addressing, keep track of the [`CodeSink`]-specific target. #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub enum DeferredTarget { Label(Label), Constant(Constant), @@ -113,7 +113,7 @@ impl std::fmt::Display for Amode { /// The scaling factor for the index register in certain [`Amode`]s. #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] pub enum Scale { One, Two, @@ -160,7 +160,7 @@ impl Scale { /// A general-purpose register or memory operand. #[derive(Clone, Debug)] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] #[allow(clippy::module_name_repetitions)] pub enum GprMem { Gpr(R),