Skip to content

Commit

Permalink
add OpAccessOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
susitsm authored and Matyas Susits committed Dec 21, 2024
1 parent 2c5b88c commit 66aea9a
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 17 deletions.
26 changes: 13 additions & 13 deletions src/rust/iced-x86/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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};
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};
Expand Down Expand Up @@ -45162,35 +45162,35 @@ impl Code {
/// Gets operand #0's OpAccess
#[must_use]
#[inline]
pub const fn op0_access(&self) -> OpAccess {
self.info_flags1().op0_access()
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) -> OpAccess {
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) -> OpAccess {
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) -> OpAccess {
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) -> OpAccess {
pub const fn op4_access(&self, _options: OpAccessOptions) -> OpAccess {
self.info_flags1().op4_access()
}

Expand All @@ -45200,13 +45200,13 @@ impl Code {
/// * `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) -> OpAccess {
pub const fn op_access(&self, operand: u32, options: OpAccessOptions) -> OpAccess {
match operand {
0 => self.op0_access(),
1 => self.op1_access(),
2 => self.op2_access(),
3 => self.op3_access(),
4 => self.op4_access(),
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,
}
}
Expand Down
123 changes: 119 additions & 4 deletions src/rust/iced-x86/src/info/info_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::enums::{
};
use super::{Code, EncodingKind, OpAccess};
use crate::info_table::TABLE;
use core::mem;
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
Expand Down Expand Up @@ -69,7 +69,7 @@ impl InfoFlags1 {
unsafe { mem::transmute((self.0 & ((InfoFlags1Consts::OP_INFO4_MASK) << InfoFlags1Consts::OP_INFO4_SHIFT)) != 0) }
}

pub const fn op0_access(&self) -> OpAccess {
pub const fn op0_access(&self, config: OpAccessOptions) -> OpAccess {
match self.op0_info() {
OpInfo0::None => OpAccess::None,
OpInfo0::Read => OpAccess::Read,
Expand All @@ -95,7 +95,13 @@ impl InfoFlags1 {
// Cmovge_r32_rm32
// Cmovle_r32_rm32
// Cmovg_r32_rm32
OpInfo0::CondWrite32_ReadWrite64 => OpAccess::CondWrite,
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,
Expand All @@ -111,7 +117,13 @@ impl InfoFlags1 {
// "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 => OpAccess::ReadWrite,
OpInfo0::WriteMem_ReadWriteReg => {
if config.has_memory_operand_set() {
OpAccess::Write
} else {
OpAccess::ReadWrite
}
}
}
}

Expand All @@ -137,3 +149,106 @@ impl InfoFlags1 {
}
}
}

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

/// 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
}
}
2 changes: 2 additions & 0 deletions src/rust/iced-x86/src/info/info_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

// ⚠️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) const TABLE: [(u32, u32); 4936] = [
(0x0000_0000, 0x0090_0000),// INVALID
Expand Down
1 change: 1 addition & 0 deletions src/rust/iced-x86/src/info/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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::*;
Expand Down

0 comments on commit 66aea9a

Please sign in to comment.