Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add op access for OpCodeInfo and Code #614

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)}");
Expand Down Expand Up @@ -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);
Expand Down
69 changes: 69 additions & 0 deletions src/rust/iced-x86/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
use crate::mnemonics;
use crate::*;
use core::iter::{ExactSizeIterator, FusedIterator, Iterator};
Expand Down Expand Up @@ -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) -> OpAccess {
self.info_flags1().op0_access()
}

/// Gets operand #1's OpAccess
#[must_use]
#[inline]
pub const fn op1_access(&self) -> OpAccess {
self.info_flags1().op1_access()
}

/// Gets operand #2's OpAccess
#[must_use]
#[inline]
pub const fn op2_access(&self) -> OpAccess {
self.info_flags1().op2_access()
}

/// Gets operand #3's OpAccess
#[must_use]
#[inline]
pub const fn op3_access(&self) -> OpAccess {
self.info_flags1().op3_access()
}

/// Gets operand #4's OpAccess
#[must_use]
#[inline]
pub const fn op4_access(&self) -> 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) -> OpAccess {
match operand {
0 => self.op0_access(),
1 => self.op1_access(),
2 => self.op2_access(),
3 => self.op3_access(),
4 => self.op4_access(),
_ => OpAccess::None,
}
}
}
4 changes: 2 additions & 2 deletions src/rust/iced-x86/src/info/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
60 changes: 29 additions & 31 deletions src/rust/iced-x86/src/info/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
}
Expand Down
139 changes: 139 additions & 0 deletions src/rust/iced-x86/src/info/info_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
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::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) -> 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 => 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 => 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,
}
}
}
2 changes: 1 addition & 1 deletion src/rust/iced-x86/src/info/info_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// ⚠️This file was generated by GENERATOR!🦹‍♂️

#[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
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 @@ -4,6 +4,7 @@
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)]
Expand Down