diff --git a/src/csharp/Intel/Generator/InstructionInfo/Rust/RustInstrInfoGenerator.cs b/src/csharp/Intel/Generator/InstructionInfo/Rust/RustInstrInfoGenerator.cs index 9a82bf898..9970f3ee9 100644 --- a/src/csharp/Intel/Generator/InstructionInfo/Rust/RustInstrInfoGenerator.cs +++ b/src/csharp/Intel/Generator/InstructionInfo/Rust/RustInstrInfoGenerator.cs @@ -42,7 +42,7 @@ protected override void Generate((InstructionDef def, uint dword1, uint dword2)[ using (var writer = new FileWriter(TargetLanguage.Rust, FileUtils.OpenWrite(filename))) { writer.WriteFileHeader(); writer.WriteLine(RustConstants.AttributeNoRustFmt); - writer.WriteLine($"pub(crate) static TABLE: [(u32, u32); {infos.Length}] = ["); + writer.WriteLine($"pub(crate) const TABLE: [(u32, u32); {infos.Length}] = ["); using (writer.Indent()) { foreach (var info in infos) writer.WriteLine($"({NumberFormatter.FormatHexUInt32WithSep(info.dword1)}, {NumberFormatter.FormatHexUInt32WithSep(info.dword2)}),// {info.def.Code.Name(idConverter)}"); @@ -119,7 +119,7 @@ void GenerateOpAccesses(FileWriter writer) { var opInfo = opInfos[index]; writer.WriteLine(RustConstants.AttributeNoRustFmt); var name = idConverter.Constant($"OpAccess_{index}"); - writer.WriteLine($"pub(super) static {name}: [{opAccessTypeStr}; {opInfo.Values.Length}] = ["); + writer.WriteLine($"pub(super) const {name}: [{opAccessTypeStr}; {opInfo.Values.Length}] = ["); using (writer.Indent()) { foreach (var value in opInfo.Values) { var v = ToOpAccess(value); diff --git a/src/rust/iced-x86/src/code.rs b/src/rust/iced-x86/src/code.rs index 1f9c3b84a..659b9ca83 100644 --- a/src/rust/iced-x86/src/code.rs +++ b/src/rust/iced-x86/src/code.rs @@ -5,6 +5,8 @@ use crate::iced_constants::IcedConstants; use crate::iced_error::IcedError; #[cfg(feature = "instr_info")] use crate::info::enums::*; +#[cfg(feature = "instr_info")] +use crate::info::info_flags::{code_info_flags, InfoFlags1 as InfoFlags1Type, InfoFlags2 as InfoFlags2Type, OpAccessOptions}; use crate::mnemonics; use crate::*; use core::iter::{ExactSizeIterator, FusedIterator, Iterator}; @@ -45142,3 +45144,70 @@ impl Code { } } } + +#[cfg(feature = "instr_info")] +impl Code { + pub(crate) const fn info_flags(&self) -> &(InfoFlags1Type, InfoFlags2Type) { + code_info_flags(*self) + } + + pub(crate) const fn info_flags1(&self) -> &InfoFlags1Type { + &self.info_flags().0 + } + + pub(crate) const fn op0_info(&self) -> OpInfo0 { + self.info_flags1().op0_info() + } + + /// Gets operand #0's OpAccess + #[must_use] + #[inline] + pub const fn op0_access(&self, options: OpAccessOptions) -> OpAccess { + self.info_flags1().op0_access(options) + } + + /// Gets operand #1's OpAccess + #[must_use] + #[inline] + pub const fn op1_access(&self, _options: OpAccessOptions) -> OpAccess { + self.info_flags1().op1_access() + } + + /// Gets operand #2's OpAccess + #[must_use] + #[inline] + pub const fn op2_access(&self, _options: OpAccessOptions) -> OpAccess { + self.info_flags1().op2_access() + } + + /// Gets operand #3's OpAccess + #[must_use] + #[inline] + pub const fn op3_access(&self, _options: OpAccessOptions) -> OpAccess { + self.info_flags1().op3_access() + } + + /// Gets operand #4's OpAccess + #[must_use] + #[inline] + pub const fn op4_access(&self, _options: OpAccessOptions) -> OpAccess { + self.info_flags1().op4_access() + } + + /// Gets an operand's [`OpAccess`] + /// # Arguments + /// + /// * `operand`: Operand number. If the operand does not exist the function will return `OpAccess::None` + #[must_use] + #[inline] + pub const fn op_access(&self, operand: u32, options: OpAccessOptions) -> OpAccess { + match operand { + 0 => self.op0_access(options), + 1 => self.op1_access(options), + 2 => self.op2_access(options), + 3 => self.op3_access(options), + 4 => self.op4_access(options), + _ => OpAccess::None, + } + } +} diff --git a/src/rust/iced-x86/src/info/enums.rs b/src/rust/iced-x86/src/info/enums.rs index 4ea6d19bc..9263f49fa 100644 --- a/src/rust/iced-x86/src/info/enums.rs +++ b/src/rust/iced-x86/src/info/enums.rs @@ -7,7 +7,7 @@ use core::fmt; // GENERATOR-BEGIN: OpAccesses // ⚠️This was generated by GENERATOR!🦹‍♂️ #[rustfmt::skip] -pub(super) static OP_ACCESS_1: [OpAccess; 7] = [ +pub(super) const OP_ACCESS_1: [OpAccess; 7] = [ OpAccess::None, OpAccess::CondRead, OpAccess::NoMemAccess, @@ -17,7 +17,7 @@ pub(super) static OP_ACCESS_1: [OpAccess; 7] = [ OpAccess::Write, ]; #[rustfmt::skip] -pub(super) static OP_ACCESS_2: [OpAccess; 3] = [ +pub(super) const OP_ACCESS_2: [OpAccess; 3] = [ OpAccess::None, OpAccess::Read, OpAccess::ReadWrite, diff --git a/src/rust/iced-x86/src/info/factory.rs b/src/rust/iced-x86/src/info/factory.rs index 55785fb78..de177c5d7 100644 --- a/src/rust/iced-x86/src/info/factory.rs +++ b/src/rust/iced-x86/src/info/factory.rs @@ -132,6 +132,23 @@ impl InstructionInfoFactory { /// assert_eq!(regs[2].register(), Register::ESI); /// assert_eq!(regs[2].access(), OpAccess::Read); /// ``` + /// + /// Compared to [`Code::op_access`], the operand accesses are specified based on the operands. For example + /// `xor rax, rax` will have a single operand write and no operand reads, since it always sets + /// `rax` to zero. + /// ``` + /// use iced_x86::*; + /// // xor rax, rax + /// let bytes = b"\x48\x31\xc0"; + /// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE); + /// let mut info_factory = InstructionInfoFactory::new(); + /// + /// let instr = decoder.decode(); + /// let info = info_factory.info(&instr); + /// + /// assert_eq!(info.op0_access(), OpAccess::Write); + /// assert_eq!(info.op1_access(), OpAccess::None); + /// ``` #[must_use] #[inline] pub fn info(&mut self, instruction: &Instruction) -> &InstructionInfo { @@ -159,9 +176,7 @@ impl InstructionInfoFactory { info.used_registers.clear(); info.used_memory_locations.clear(); - let (flags1, flags2) = crate::info::info_table::TABLE[instruction.code() as usize]; - - // SAFETY: the transmutes on the generated data (flags1,flags2) are safe since we only generate valid enum variants + let (flags1, flags2) = *instruction.code().info_flags(); let code_size = instruction.code_size(); const _: () = assert!(InstructionInfoOptions::NO_MEMORY_USAGE == Flags::NO_MEMORY_USAGE); @@ -170,12 +185,11 @@ impl InstructionInfoFactory { if code_size == CodeSize::Code64 || code_size == CodeSize::Unknown { flags |= Flags::IS_64BIT; } - let encoding = (flags2 >> InfoFlags2::ENCODING_SHIFT) & InfoFlags2::ENCODING_MASK; - if encoding != EncodingKind::Legacy as u32 { + if flags2.encoding_kind() != EncodingKind::Legacy { flags |= Flags::ZERO_EXT_VEC_REGS; } - let op0_info = unsafe { mem::transmute(((flags1 >> InfoFlags1::OP_INFO0_SHIFT) & InfoFlags1::OP_INFO0_MASK) as u8) }; + let op0_info = instruction.code().op0_info(); let op0_access = match op0_info { OpInfo0::None => OpAccess::None, OpInfo0::Read => OpAccess::Read, @@ -255,23 +269,12 @@ impl InstructionInfoFactory { debug_assert!(instruction.op_count() as usize <= IcedConstants::MAX_OP_COUNT); info.op_accesses[0] = op0_access; - let op1_info: OpInfo1 = unsafe { mem::transmute(((flags1 >> InfoFlags1::OP_INFO1_SHIFT) & InfoFlags1::OP_INFO1_MASK) as u8) }; - info.op_accesses[1] = OP_ACCESS_1[op1_info as usize]; - let op2_info: OpInfo2 = unsafe { mem::transmute(((flags1 >> InfoFlags1::OP_INFO2_SHIFT) & InfoFlags1::OP_INFO2_MASK) as u8) }; - info.op_accesses[2] = OP_ACCESS_2[op2_info as usize]; - info.op_accesses[3] = if (flags1 & ((InfoFlags1::OP_INFO3_MASK) << InfoFlags1::OP_INFO3_SHIFT)) != 0 { - const _: () = assert!(InstrInfoConstants::OP_INFO3_COUNT == 2); - OpAccess::Read - } else { - OpAccess::None - }; - info.op_accesses[4] = if (flags1 & ((InfoFlags1::OP_INFO4_MASK) << InfoFlags1::OP_INFO4_SHIFT)) != 0 { - const _: () = assert!(InstrInfoConstants::OP_INFO4_COUNT == 2); - OpAccess::Read - } else { - OpAccess::None - }; + info.op_accesses[1] = flags1.op1_access(); + info.op_accesses[2] = flags1.op2_access(); + info.op_accesses[3] = flags1.op3_access(); + info.op_accesses[4] = flags1.op4_access(); const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5); + let op1_info = flags1.op1_info(); for i in 0..(instruction.op_count() as usize) { // SAFETY: valid index since i < instruction.op_count() (<= MAX_OP_COUNT) and op_accesses.len() (== MAX_OP_COUNT) @@ -321,7 +324,7 @@ impl InstructionInfoFactory { const _: () = assert!(InfoFlags1::IGNORES_SEGMENT == 1 << 31); const _: () = assert!(Register::None as u32 == 0); let segment_register = - unsafe { mem::transmute((instruction.memory_segment() as u32 & !((flags1 as i32 >> 31) as u32)) as RegisterUnderlyingType) }; + unsafe { mem::transmute((instruction.memory_segment() as u32 & !flags1.sign_mask()) as RegisterUnderlyingType) }; let base_register = instruction.memory_base(); if base_register == Register::RIP { if (flags & Flags::NO_MEMORY_USAGE) == 0 { @@ -360,7 +363,7 @@ impl InstructionInfoFactory { Self::add_memory_segment_register(flags, info, segment_register, OpAccess::Read); } } else { - let (index_register, scale) = if (flags1 & InfoFlags1::IGNORES_INDEX_VA) != 0 { + let (index_register, scale) = if flags1.ignores_index_va() { let index = instruction.memory_index(); if (flags & Flags::NO_REGISTER_USAGE) == 0 && index != Register::None { Self::add_register(flags, info, index, OpAccess::Read); @@ -427,18 +430,13 @@ impl InstructionInfoFactory { } } - let implied_access = unsafe { mem::transmute(((flags1 >> InfoFlags1::IMPLIED_ACCESS_SHIFT) & InfoFlags1::IMPLIED_ACCESS_MASK) as u8) }; + let implied_access = flags1.implied_access(); if implied_access != ImpliedAccess::None { Self::add_implied_accesses(implied_access, instruction, info, flags); } if instruction.has_op_mask() && (flags & Flags::NO_REGISTER_USAGE) == 0 { - Self::add_register( - flags, - info, - instruction.op_mask(), - if (flags1 & InfoFlags1::OP_MASK_READ_WRITE) != 0 { OpAccess::ReadWrite } else { OpAccess::Read }, - ); + Self::add_register(flags, info, instruction.op_mask(), if flags1.op_mask_read_write() { OpAccess::ReadWrite } else { OpAccess::Read }); } info } diff --git a/src/rust/iced-x86/src/info/info_flags.rs b/src/rust/iced-x86/src/info/info_flags.rs new file mode 100644 index 000000000..c45d0d67e --- /dev/null +++ b/src/rust/iced-x86/src/info/info_flags.rs @@ -0,0 +1,261 @@ +use super::enums::{ + ImpliedAccess, InfoFlags1 as InfoFlags1Consts, InfoFlags2 as InfoFlags2Consts, OpInfo0, OpInfo1, OpInfo2, OpInfo3, OpInfo4, OP_ACCESS_1, + OP_ACCESS_2, +}; +use super::{Code, EncodingKind, OpAccess}; +use crate::info_table::TABLE; +use core::{fmt, mem}; + +pub(crate) const fn code_info_flags(code: Code) -> &'static (InfoFlags1, InfoFlags2) { + // SAFETY: info_table::TABLE has a generated entry for each Code + let u32_tuple: &'static (u32, u32) = unsafe { &*TABLE.as_ptr().offset(code as isize) }; + // SAFETY: Creating InfoFlags1 and InfoFlags2 from the table elements is safe since the + // generator only generates valid flags + unsafe { mem::transmute(u32_tuple) } +} + +#[repr(transparent)] +#[derive(Clone, Copy)] +pub(crate) struct InfoFlags2(u32); +impl InfoFlags2 { + pub(crate) const fn encoding_kind(&self) -> EncodingKind { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute(((self.0 >> InfoFlags2Consts::ENCODING_SHIFT) & InfoFlags2Consts::ENCODING_MASK) as u8) } + } +} +#[repr(transparent)] +#[derive(Clone, Copy)] +pub(crate) struct InfoFlags1(u32); +impl InfoFlags1 { + pub(crate) const fn op_mask_read_write(&self) -> bool { + (self.0 & InfoFlags1Consts::OP_MASK_READ_WRITE) != 0 + } + + pub(crate) const fn implied_access(&self) -> ImpliedAccess { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute(((self.0 >> InfoFlags1Consts::IMPLIED_ACCESS_SHIFT) & InfoFlags1Consts::IMPLIED_ACCESS_MASK) as u8) } + } + + pub(crate) const fn ignores_index_va(&self) -> bool { + self.0 & InfoFlags1Consts::IGNORES_INDEX_VA != 0 + } + + pub(crate) const fn sign_mask(&self) -> u32 { + (self.0 as i32 >> 31) as u32 + } + + pub(crate) const fn op0_info(&self) -> OpInfo0 { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute(((self.0 >> InfoFlags1Consts::OP_INFO0_SHIFT) & InfoFlags1Consts::OP_INFO0_MASK) as u8) } + } + + pub(crate) const fn op1_info(&self) -> OpInfo1 { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute(((self.0.wrapping_shr(InfoFlags1Consts::OP_INFO1_SHIFT)) & InfoFlags1Consts::OP_INFO1_MASK) as u8) } + } + + pub(crate) const fn op2_info(&self) -> OpInfo2 { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute(((self.0.wrapping_shr(InfoFlags1Consts::OP_INFO2_SHIFT)) & InfoFlags1Consts::OP_INFO2_MASK) as u8) } + } + + pub(crate) const fn op3_info(&self) -> OpInfo3 { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute((self.0 & ((InfoFlags1Consts::OP_INFO3_MASK) << InfoFlags1Consts::OP_INFO3_SHIFT)) != 0) } + } + + pub(crate) const fn op4_info(&self) -> OpInfo4 { + // SAFETY: safety must be guaranteed when calling the constructor + unsafe { mem::transmute((self.0 & ((InfoFlags1Consts::OP_INFO4_MASK) << InfoFlags1Consts::OP_INFO4_SHIFT)) != 0) } + } + + pub const fn op0_access(&self, config: OpAccessOptions) -> OpAccess { + match self.op0_info() { + OpInfo0::None => OpAccess::None, + OpInfo0::Read => OpAccess::Read, + OpInfo0::Write => OpAccess::Write, + OpInfo0::WriteVmm => OpAccess::Write, + OpInfo0::WriteForce | OpInfo0::WriteForceP1 => OpAccess::Write, + OpInfo0::CondWrite => OpAccess::CondWrite, + + // Codes having this OpInfo0: + // Cmovo_r32_rm32 + // Cmovno_r32_rm32 + // Cmovb_r32_rm32 + // Cmovae_r32_rm32 + // Cmove_r32_rm32 + // Cmovne_r32_rm32 + // Cmovbe_r32_rm32 + // Cmova_r32_rm32 + // Cmovs_r32_rm32 + // Cmovns_r32_rm32 + // Cmovp_r32_rm32 + // Cmovnp_r32_rm32 + // Cmovl_r32_rm32 + // Cmovge_r32_rm32 + // Cmovle_r32_rm32 + // Cmovg_r32_rm32 + OpInfo0::CondWrite32_ReadWrite64 => { + if config.is_64_set() { + OpAccess::ReadWrite + } else { + OpAccess::CondWrite + } + } + OpInfo0::ReadWrite => OpAccess::ReadWrite, + OpInfo0::ReadWriteVmm => OpAccess::ReadWrite, + OpInfo0::ReadCondWrite => OpAccess::ReadCondWrite, + OpInfo0::NoMemAccess => OpAccess::NoMemAccess, + + // Codes having this OpInfo0: + // Movss_xmm_xmmm32 + // Movsd_xmm_xmmm64 + // Movss_xmmm32_xmm + // Movsd_xmmm64_xmm + // + // Relevant part from the intel manual for movss + // "Legacy version: When the source and destination operands are XMM registers, bits (MAXVL-1:32) of the corresponding destination register are unmodified. When the source operand is a memory location and destination operand is an XMM registers, Bits (127:32) of the destination operand is cleared to all 0s, bits MAXVL:128 of the destination operand remains unchanged." + // When the operands are availale it is possible to decide whether this is + // `OpAccess::Write` or `OpAccess::ReadWrite` here we return the more general one. + OpInfo0::WriteMem_ReadWriteReg => { + if config.has_memory_operand_set() { + OpAccess::Write + } else { + OpAccess::ReadWrite + } + } + } + } + + pub const fn op1_access(&self) -> OpAccess { + OP_ACCESS_1[self.op1_info() as usize] + } + + pub const fn op2_access(&self) -> OpAccess { + OP_ACCESS_2[self.op2_info() as usize] + } + + pub const fn op3_access(&self) -> OpAccess { + match self.op3_info() { + OpInfo3::Read => OpAccess::Read, + OpInfo3::None => OpAccess::None, + } + } + + pub const fn op4_access(&self) -> OpAccess { + match self.op4_info() { + OpInfo4::Read => OpAccess::Read, + OpInfo4::None => OpAccess::None, + } + } +} + +/// Controls behaviour of [`Code::op_access`] functions. +#[derive(Default, Clone, Copy)] +pub struct OpAccessOptions(u8); + +impl fmt::Debug for OpAccessOptions { + #[allow(clippy::missing_inline_in_public_items)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OpAccessOptions").field("has_memory_operand", &self.has_memory_operand_set()).field("is_64", &self.is_64_set()).finish() + } +} + +impl OpAccessOptions { + const HAS_MEMORY_OPERAND_SHIFT: u8 = 0; + const IS_64_SHIFT: u8 = 1; + + const HAS_MEMORY_OPERAND_MASK: u8 = 1u8 << Self::HAS_MEMORY_OPERAND_SHIFT; + const IS_64_MASK: u8 = 1u8 << Self::IS_64_SHIFT; + + /// Create a new `OpAccessOptions` with options set to `false` + #[must_use] + #[inline] + pub const fn new() -> Self { + Self(0) + } + + /// Set whether it should be assumed that there is at least one memory operand when calculating operand access. Currently this + /// affects operand 0 of the following [`Code`]-s: + /// + /// ``` + /// # use iced_x86::Code; + /// # use iced_x86::Code::*; + /// # use iced_x86::OpAccessOptions; + /// # + /// let affected_codes = [ + /// Movss_xmm_xmmm32, Movsd_xmm_xmmm64, Movss_xmmm32_xmm, Movsd_xmmm64_xmm, + /// ]; + /// let config = OpAccessOptions::default(); + /// let config_memory = OpAccessOptions::default().has_memory_operand(true); + /// for i in 0..=4 { + /// for code in Code::values() { + /// if affected_codes.contains(&code) && i == 0 { + /// assert_ne!(code.op_access(i, config), code.op_access(i, config_memory)); + /// } else { + /// assert_eq!(code.op_access(i, config), code.op_access(i, config_memory)); + /// } + /// } + /// } + /// ``` + /// + /// Relevant part from the intel manual for `movss`: + /// > Legacy version: When the source and destination operands are XMM registers, bits (MAXVL-1:32) of the corresponding destination register are unmodified. When the source operand is a memory location and destination operand is an XMM registers, Bits (127:32) of the destination operand is cleared to all 0s, bits MAXVL:128 of the destination operand remains unchanged. + /// + /// When there are only register operands, operand 0 has `OpAccess::ReadWrite`. If there is a memory operand, the access is `OpAccess::Write` + #[must_use] + #[inline] + pub const fn has_memory_operand(self, value: bool) -> Self { + if value { + Self(self.0 | Self::HAS_MEMORY_OPERAND_MASK) + } else { + Self(self.0 & !Self::HAS_MEMORY_OPERAND_MASK) + } + } + + /// Set whether the bitness should be 64 when calculating operand access. Currently this + /// affects operand 0 the following [`Code`]-s: + /// ``` + /// # use iced_x86::Code; + /// # use iced_x86::Code::*; + /// # use iced_x86::OpAccessOptions; + /// # + /// let affected_codes = [ + /// Cmovo_r32_rm32, Cmovno_r32_rm32, Cmovb_r32_rm32, Cmovae_r32_rm32, + /// Cmove_r32_rm32, Cmovne_r32_rm32, Cmovbe_r32_rm32, Cmova_r32_rm32, + /// Cmovs_r32_rm32, Cmovns_r32_rm32, Cmovp_r32_rm32, Cmovnp_r32_rm32, + /// Cmovl_r32_rm32, Cmovge_r32_rm32, Cmovle_r32_rm32, Cmovg_r32_rm32, + /// ]; + /// let config = OpAccessOptions::default(); + /// let config_64 = OpAccessOptions::default().is_64(true); + /// for i in 0..=4 { + /// for code in Code::values() { + /// if affected_codes.contains(&code) && i == 0 { + /// assert_ne!(code.op_access(i, config), code.op_access(i, config_64)); + /// } else { + /// assert_eq!(code.op_access(i, config), code.op_access(i, config_64)); + /// } + /// } + /// } + /// ``` + /// + /// In 64 bit mode [`OpAccess::ReadWrite`] is returned, since the upper 32 bits are always + /// zeroed. In other modes [`OpAccess::CondWrite`] is returned. + #[must_use] + #[inline] + pub const fn is_64(self, value: bool) -> Self { + if value { + Self(self.0 | Self::IS_64_MASK) + } else { + Self(self.0 & !Self::IS_64_MASK) + } + } + + const fn has_memory_operand_set(&self) -> bool { + self.0 & Self::HAS_MEMORY_OPERAND_MASK != 0 + } + + const fn is_64_set(&self) -> bool { + self.0 & Self::IS_64_MASK != 0 + } +} diff --git a/src/rust/iced-x86/src/info/info_table.rs b/src/rust/iced-x86/src/info/info_table.rs index 287bed100..9c8724d0f 100644 --- a/src/rust/iced-x86/src/info/info_table.rs +++ b/src/rust/iced-x86/src/info/info_table.rs @@ -3,8 +3,10 @@ // ⚠️This file was generated by GENERATOR!🦹‍♂️ +// We must allow this to use `Code::op_access` in const contexts +#[allow(clippy::large_const_arrays)] #[rustfmt::skip] -pub(crate) static TABLE: [(u32, u32); 4936] = [ +pub(crate) const TABLE: [(u32, u32); 4936] = [ (0x0000_0000, 0x0090_0000),// INVALID (0x0000_0000, 0x0090_0000),// DeclareByte (0x0000_0000, 0x0090_0000),// DeclareWord diff --git a/src/rust/iced-x86/src/info/mod.rs b/src/rust/iced-x86/src/info/mod.rs index adaeedd5e..c914f1b70 100644 --- a/src/rust/iced-x86/src/info/mod.rs +++ b/src/rust/iced-x86/src/info/mod.rs @@ -4,11 +4,13 @@ pub(crate) mod cpuid_table; pub(crate) mod enums; pub(crate) mod factory; +pub(crate) mod info_flags; pub(crate) mod info_table; pub(crate) mod rflags_table; #[cfg(test)] mod tests; +pub use self::info_flags::OpAccessOptions; use crate::iced_constants::IcedConstants; pub use crate::info::factory::*; use crate::*;