use crate::iced_constants::IcedConstants;
use crate::iced_error::IcedError;
#[cfg(feature = "instr_info")]
use crate::info::enums::*;
use crate::instruction_internal;
use crate::*;
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm", feature = "fast_fmt", feature = "serde"))]
use core::fmt;
use core::hash::{Hash, Hasher};
use core::iter::{ExactSizeIterator, FusedIterator};
use core::{mem, slice};
pub(crate) struct InstrFlags1;
#[allow(dead_code)]
impl InstrFlags1 {
pub(crate) const SEGMENT_PREFIX_MASK: u32 = 0x0000_0007;
pub(crate) const SEGMENT_PREFIX_SHIFT: u32 = 0x0000_0005;
pub(crate) const DATA_LENGTH_MASK: u32 = 0x0000_000F;
pub(crate) const DATA_LENGTH_SHIFT: u32 = 0x0000_0008;
pub(crate) const ROUNDING_CONTROL_MASK: u32 = 0x0000_0007;
pub(crate) const ROUNDING_CONTROL_SHIFT: u32 = 0x0000_000C;
pub(crate) const OP_MASK_MASK: u32 = 0x0000_0007;
pub(crate) const OP_MASK_SHIFT: u32 = 0x0000_000F;
pub(crate) const CODE_SIZE_MASK: u32 = 0x0000_0003;
pub(crate) const CODE_SIZE_SHIFT: u32 = 0x0000_0012;
pub(crate) const BROADCAST: u32 = 0x0400_0000;
pub(crate) const SUPPRESS_ALL_EXCEPTIONS: u32 = 0x0800_0000;
pub(crate) const ZEROING_MASKING: u32 = 0x1000_0000;
pub(crate) const REPE_PREFIX: u32 = 0x2000_0000;
pub(crate) const REPNE_PREFIX: u32 = 0x4000_0000;
pub(crate) const LOCK_PREFIX: u32 = 0x8000_0000;
pub(crate) const EQUALS_IGNORE_MASK: u32 = 0x000C_0000;
}
pub(crate) struct MvexInstrFlags;
#[allow(dead_code)]
impl MvexInstrFlags {
pub(crate) const MVEX_REG_MEM_CONV_SHIFT: u32 = 0x0000_0010;
pub(crate) const MVEX_REG_MEM_CONV_MASK: u32 = 0x0000_001F;
pub(crate) const EVICTION_HINT: u32 = 0x8000_0000;
}
#[derive(Debug, Default, Copy, Clone)]
pub struct Instruction {
pub(crate) next_rip: u64,
pub(crate) mem_displ: u64,
pub(crate) flags1: u32, pub(crate) immediate: u32,
pub(crate) code: Code,
pub(crate) mem_base_reg: Register,
pub(crate) mem_index_reg: Register,
pub(crate) regs: [Register; 4],
pub(crate) op_kinds: [OpKind; 4],
pub(crate) scale: InstrScale,
pub(crate) displ_size: u8,
pub(crate) len: u8,
pad: u8,
}
#[cfg(test)]
#[allow(dead_code)]
pub(crate) const INSTRUCTION_TOTAL_SIZE: usize = 40;
#[allow(clippy::len_without_is_empty)]
impl Instruction {
#[must_use]
#[inline]
pub fn new() -> Self {
Instruction::default()
}
#[must_use]
#[allow(trivial_casts)]
#[allow(clippy::missing_inline_in_public_items)]
pub fn eq_all_bits(&self, other: &Self) -> bool {
unsafe {
let a: *const u8 = self as *const Self as *const u8;
let b: *const u8 = other as *const Self as *const u8;
let sa = slice::from_raw_parts(a, mem::size_of::<Self>());
let sb = slice::from_raw_parts(b, mem::size_of::<Self>());
sa == sb
}
}
#[must_use]
#[inline]
pub const fn ip16(&self) -> u16 {
(self.next_rip as u16).wrapping_sub(self.len() as u16)
}
#[inline]
pub fn set_ip16(&mut self, new_value: u16) {
self.next_rip = (new_value as u64).wrapping_add(self.len() as u64);
}
#[must_use]
#[inline]
pub const fn ip32(&self) -> u32 {
(self.next_rip as u32).wrapping_sub(self.len() as u32)
}
#[inline]
pub fn set_ip32(&mut self, new_value: u32) {
self.next_rip = (new_value as u64).wrapping_add(self.len() as u64);
}
#[must_use]
#[inline]
pub const fn ip(&self) -> u64 {
self.next_rip.wrapping_sub(self.len() as u64)
}
#[inline]
pub fn set_ip(&mut self, new_value: u64) {
self.next_rip = new_value.wrapping_add(self.len() as u64);
}
#[must_use]
#[inline]
pub const fn next_ip16(&self) -> u16 {
self.next_rip as u16
}
#[inline]
pub fn set_next_ip16(&mut self, new_value: u16) {
self.next_rip = new_value as u64;
}
#[must_use]
#[inline]
pub const fn next_ip32(&self) -> u32 {
self.next_rip as u32
}
#[inline]
pub fn set_next_ip32(&mut self, new_value: u32) {
self.next_rip = new_value as u64;
}
#[must_use]
#[inline]
pub const fn next_ip(&self) -> u64 {
self.next_rip
}
#[inline]
pub fn set_next_ip(&mut self, new_value: u64) {
self.next_rip = new_value;
}
#[must_use]
#[inline]
pub fn code_size(&self) -> CodeSize {
unsafe { mem::transmute(((self.flags1 >> InstrFlags1::CODE_SIZE_SHIFT) & InstrFlags1::CODE_SIZE_MASK) as CodeSizeUnderlyingType) }
}
#[inline]
pub fn set_code_size(&mut self, new_value: CodeSize) {
self.flags1 = (self.flags1 & !(InstrFlags1::CODE_SIZE_MASK << InstrFlags1::CODE_SIZE_SHIFT))
| (((new_value as u32) & InstrFlags1::CODE_SIZE_MASK) << InstrFlags1::CODE_SIZE_SHIFT);
}
#[must_use]
#[inline]
pub fn is_invalid(&self) -> bool {
self.code == Code::INVALID
}
#[must_use]
#[inline]
pub const fn code(&self) -> Code {
self.code
}
#[inline]
pub fn set_code(&mut self, new_value: Code) {
self.code = new_value
}
#[must_use]
#[inline]
pub fn mnemonic(&self) -> Mnemonic {
self.code().mnemonic()
}
#[must_use]
#[inline]
pub fn op_count(&self) -> u32 {
instruction_op_counts::OP_COUNT[self.code() as usize] as u32
}
#[must_use]
#[inline]
pub const fn len(&self) -> usize {
self.len as usize
}
#[inline]
pub fn set_len(&mut self, new_value: usize) {
self.len = new_value as u8
}
#[inline]
fn is_xacquire_instr(&self) -> bool {
if self.op0_kind() != OpKind::Memory {
false
} else if self.has_lock_prefix() {
self.code() != Code::Cmpxchg16b_m128
} else {
self.mnemonic() == Mnemonic::Xchg
}
}
#[inline]
fn is_xrelease_instr(&self) -> bool {
if self.op0_kind() != OpKind::Memory {
false
} else if self.has_lock_prefix() {
self.code() != Code::Cmpxchg16b_m128
} else {
matches!(
self.code(),
Code::Xchg_rm8_r8
| Code::Xchg_rm16_r16
| Code::Xchg_rm32_r32
| Code::Xchg_rm64_r64
| Code::Mov_rm8_r8 | Code::Mov_rm16_r16
| Code::Mov_rm32_r32 | Code::Mov_rm64_r64
| Code::Mov_rm8_imm8 | Code::Mov_rm16_imm16
| Code::Mov_rm32_imm32
| Code::Mov_rm64_imm32
)
}
}
#[must_use]
#[inline]
pub fn has_xacquire_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::REPNE_PREFIX) != 0 && self.is_xacquire_instr()
}
#[inline]
pub fn set_has_xacquire_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::REPNE_PREFIX;
} else {
self.flags1 &= !InstrFlags1::REPNE_PREFIX;
}
}
#[must_use]
#[inline]
pub fn has_xrelease_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::REPE_PREFIX) != 0 && self.is_xrelease_instr()
}
#[inline]
pub fn set_has_xrelease_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::REPE_PREFIX;
} else {
self.flags1 &= !InstrFlags1::REPE_PREFIX;
}
}
#[must_use]
#[inline]
pub const fn has_rep_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::REPE_PREFIX) != 0
}
#[inline]
pub fn set_has_rep_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::REPE_PREFIX;
} else {
self.flags1 &= !InstrFlags1::REPE_PREFIX;
}
}
#[must_use]
#[inline]
pub const fn has_repe_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::REPE_PREFIX) != 0
}
#[inline]
pub fn set_has_repe_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::REPE_PREFIX;
} else {
self.flags1 &= !InstrFlags1::REPE_PREFIX;
}
}
#[must_use]
#[inline]
pub const fn has_repne_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::REPNE_PREFIX) != 0
}
#[inline]
pub fn set_has_repne_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::REPNE_PREFIX;
} else {
self.flags1 &= !InstrFlags1::REPNE_PREFIX;
}
}
#[must_use]
#[inline]
pub const fn has_lock_prefix(&self) -> bool {
(self.flags1 & InstrFlags1::LOCK_PREFIX) != 0
}
#[inline]
pub fn set_has_lock_prefix(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::LOCK_PREFIX;
} else {
self.flags1 &= !InstrFlags1::LOCK_PREFIX;
}
}
#[must_use]
#[inline]
pub const fn op0_kind(&self) -> OpKind {
self.op_kinds[0]
}
#[inline]
pub fn set_op0_kind(&mut self, new_value: OpKind) {
self.op_kinds[0] = new_value
}
#[must_use]
#[inline]
pub const fn op1_kind(&self) -> OpKind {
self.op_kinds[1]
}
#[inline]
pub fn set_op1_kind(&mut self, new_value: OpKind) {
self.op_kinds[1] = new_value
}
#[must_use]
#[inline]
pub const fn op2_kind(&self) -> OpKind {
self.op_kinds[2]
}
#[inline]
pub fn set_op2_kind(&mut self, new_value: OpKind) {
self.op_kinds[2] = new_value
}
#[must_use]
#[inline]
pub const fn op3_kind(&self) -> OpKind {
self.op_kinds[3]
}
#[inline]
pub fn set_op3_kind(&mut self, new_value: OpKind) {
self.op_kinds[3] = new_value
}
#[allow(clippy::unused_self)]
#[must_use]
#[inline]
pub const fn op4_kind(&self) -> OpKind {
OpKind::Immediate8
}
#[allow(clippy::unused_self)]
#[inline]
pub fn set_op4_kind(&mut self, new_value: OpKind) {
debug_assert_eq!(new_value, OpKind::Immediate8);
}
#[allow(clippy::unused_self)]
#[inline]
#[doc(hidden)]
pub fn try_set_op4_kind(&mut self, new_value: OpKind) -> Result<(), IcedError> {
if new_value != OpKind::Immediate8 {
Err(IcedError::new("Invalid opkind"))
} else {
Ok(())
}
}
#[inline]
pub fn op_kinds(&self) -> impl Iterator<Item = OpKind> + ExactSizeIterator + FusedIterator {
OpKindIterator::new(self)
}
#[must_use]
#[inline]
pub fn op_kind(&self, operand: u32) -> OpKind {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(&op_kind) = self.op_kinds.get(operand as usize) {
op_kind
} else if operand == 4 {
self.op4_kind()
} else {
debug_assert!(false, "Invalid operand: {}", operand);
OpKind::default()
}
}
#[doc(hidden)]
#[inline]
pub fn try_op_kind(&self, operand: u32) -> Result<OpKind, IcedError> {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(&op_kind) = self.op_kinds.get(operand as usize) {
Ok(op_kind)
} else if operand == 4 {
Ok(self.op4_kind())
} else {
Err(IcedError::new("Invalid operand"))
}
}
#[inline]
pub fn set_op_kind(&mut self, operand: u32, op_kind: OpKind) {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(field) = self.op_kinds.get_mut(operand as usize) {
*field = op_kind;
} else if operand == 4 {
self.set_op4_kind(op_kind)
} else {
debug_assert!(false, "Invalid operand: {}", operand);
}
}
#[doc(hidden)]
#[inline]
pub fn try_set_op_kind(&mut self, operand: u32, op_kind: OpKind) -> Result<(), IcedError> {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(field) = self.op_kinds.get_mut(operand as usize) {
*field = op_kind;
Ok(())
} else if operand == 4 {
self.try_set_op4_kind(op_kind)
} else {
Err(IcedError::new("Invalid operand"))
}
}
#[must_use]
#[inline]
pub const fn has_segment_prefix(&self) -> bool {
((self.flags1 >> InstrFlags1::SEGMENT_PREFIX_SHIFT) & InstrFlags1::SEGMENT_PREFIX_MASK).wrapping_sub(1) < 6
}
#[must_use]
#[inline]
pub fn segment_prefix(&self) -> Register {
let index = ((self.flags1 >> InstrFlags1::SEGMENT_PREFIX_SHIFT) & InstrFlags1::SEGMENT_PREFIX_MASK).wrapping_sub(1);
const _: () = assert!(Register::ES as u32 + 1 == Register::CS as u32);
const _: () = assert!(Register::ES as u32 + 2 == Register::SS as u32);
const _: () = assert!(Register::ES as u32 + 3 == Register::DS as u32);
const _: () = assert!(Register::ES as u32 + 4 == Register::FS as u32);
const _: () = assert!(Register::ES as u32 + 5 == Register::GS as u32);
if index < 6 {
unsafe { mem::transmute((Register::ES as u32 + index) as RegisterUnderlyingType) }
} else {
Register::None
}
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn set_segment_prefix(&mut self, new_value: Register) {
debug_assert!(new_value == Register::None || (Register::ES <= new_value && new_value <= Register::GS));
let enc_value =
if new_value == Register::None { 0 } else { (((new_value as u32) - (Register::ES as u32)) + 1) & InstrFlags1::SEGMENT_PREFIX_MASK };
self.flags1 = (self.flags1 & !(InstrFlags1::SEGMENT_PREFIX_MASK << InstrFlags1::SEGMENT_PREFIX_SHIFT))
| (enc_value << InstrFlags1::SEGMENT_PREFIX_SHIFT);
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
pub fn memory_segment(&self) -> Register {
let seg_reg = self.segment_prefix();
if seg_reg != Register::None {
return seg_reg;
}
match self.memory_base() {
Register::BP | Register::EBP | Register::ESP | Register::RBP | Register::RSP => Register::SS,
_ => Register::DS,
}
}
#[must_use]
#[inline]
pub const fn memory_displ_size(&self) -> u32 {
let size = self.displ_size as u32;
if size <= 2 {
size
} else if size == 3 {
4
} else {
8
}
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn set_memory_displ_size(&mut self, new_value: u32) {
self.displ_size = match new_value {
0 => 0,
1 => 1,
2 => 2,
4 => 3,
_ => 4,
};
}
#[must_use]
#[inline]
pub const fn is_broadcast(&self) -> bool {
(self.flags1 & InstrFlags1::BROADCAST) != 0
}
#[inline]
pub fn set_is_broadcast(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::BROADCAST;
} else {
self.flags1 &= !InstrFlags1::BROADCAST;
}
}
#[must_use]
#[inline]
#[cfg(feature = "mvex")]
pub const fn is_mvex_eviction_hint(&self) -> bool {
IcedConstants::is_mvex(self.code()) && (self.immediate & MvexInstrFlags::EVICTION_HINT) != 0
}
#[inline]
#[cfg(feature = "mvex")]
pub fn set_is_mvex_eviction_hint(&mut self, new_value: bool) {
if new_value {
self.immediate |= MvexInstrFlags::EVICTION_HINT;
} else {
self.immediate &= !MvexInstrFlags::EVICTION_HINT;
}
}
#[must_use]
#[inline]
#[cfg(feature = "mvex")]
pub fn mvex_reg_mem_conv(&self) -> MvexRegMemConv {
if !IcedConstants::is_mvex(self.code()) {
MvexRegMemConv::None
} else {
<MvexRegMemConv as core::convert::TryFrom<_>>::try_from(
((self.immediate >> MvexInstrFlags::MVEX_REG_MEM_CONV_SHIFT) & MvexInstrFlags::MVEX_REG_MEM_CONV_MASK) as usize,
)
.ok()
.unwrap_or_default()
}
}
#[inline]
#[cfg(feature = "mvex")]
pub fn set_mvex_reg_mem_conv(&mut self, new_value: MvexRegMemConv) {
self.immediate = (self.immediate & !(MvexInstrFlags::MVEX_REG_MEM_CONV_MASK << MvexInstrFlags::MVEX_REG_MEM_CONV_SHIFT))
| ((new_value as u32) << MvexInstrFlags::MVEX_REG_MEM_CONV_SHIFT);
}
#[must_use]
#[inline]
pub fn memory_size(&self) -> MemorySize {
let code = self.code();
#[cfg(feature = "mvex")]
{
if IcedConstants::is_mvex(code) {
let mvex = crate::mvex::get_mvex_info(code);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 1 == MvexRegMemConv::MemConvBroadcast1 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 2 == MvexRegMemConv::MemConvBroadcast4 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 3 == MvexRegMemConv::MemConvFloat16 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 4 == MvexRegMemConv::MemConvUint8 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 5 == MvexRegMemConv::MemConvSint8 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 6 == MvexRegMemConv::MemConvUint16 as u32);
const _: () = assert!(MvexRegMemConv::MemConvNone as u32 + 7 == MvexRegMemConv::MemConvSint16 as u32);
let sss = ((self.mvex_reg_mem_conv() as u32).wrapping_sub(MvexRegMemConv::MemConvNone as u32) & 7) as usize;
return crate::mvex::mvex_memsz_lut::MVEX_MEMSZ_LUT[(mvex.tuple_type_lut_kind as usize) * 8 + sss];
}
}
if !self.is_broadcast() {
instruction_memory_sizes::SIZES_NORMAL[code as usize]
} else {
instruction_memory_sizes::SIZES_BCST[code as usize]
}
}
#[must_use]
#[inline]
pub const fn memory_index_scale(&self) -> u32 {
1 << (self.scale as u8)
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn set_memory_index_scale(&mut self, new_value: u32) {
match new_value {
1 => self.scale = InstrScale::Scale1,
2 => self.scale = InstrScale::Scale2,
4 => self.scale = InstrScale::Scale4,
_ => {
debug_assert_eq!(new_value, 8);
self.scale = InstrScale::Scale8;
}
}
}
#[must_use]
#[inline]
pub const fn memory_displacement32(&self) -> u32 {
self.mem_displ as u32
}
#[inline]
pub fn set_memory_displacement32(&mut self, new_value: u32) {
self.mem_displ = new_value as u64;
}
#[must_use]
#[inline]
pub const fn memory_displacement64(&self) -> u64 {
self.mem_displ
}
#[inline]
pub fn set_memory_displacement64(&mut self, new_value: u64) {
self.mem_displ = new_value;
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_immediate(&self, operand: u32) -> Result<u64, IcedError> {
Ok(match self.try_op_kind(operand)? {
OpKind::Immediate8 => self.immediate8() as u64,
OpKind::Immediate8_2nd => self.immediate8_2nd() as u64,
OpKind::Immediate16 => self.immediate16() as u64,
OpKind::Immediate32 => self.immediate32() as u64,
OpKind::Immediate64 => self.immediate64(),
OpKind::Immediate8to16 => self.immediate8to16() as u64,
OpKind::Immediate8to32 => self.immediate8to32() as u64,
OpKind::Immediate8to64 => self.immediate8to64() as u64,
OpKind::Immediate32to64 => self.immediate32to64() as u64,
_ => return Err(IcedError::new("Not an immediate operand")),
})
}
#[must_use]
#[inline]
pub fn immediate(&self, operand: u32) -> u64 {
match self.try_immediate(operand) {
Ok(value) => value,
Err(_) => {
debug_assert!(false, "Invalid operand: {}", operand);
0
}
}
}
#[inline]
pub fn set_immediate_i32(&mut self, operand: u32, new_value: i32) {
match self.try_set_immediate_i32(operand, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid operand: {}", operand),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_immediate_i32(&mut self, operand: u32, new_value: i32) -> Result<(), IcedError> {
self.try_set_immediate_u64(operand, new_value as u64)
}
#[inline]
pub fn set_immediate_u32(&mut self, operand: u32, new_value: u32) {
match self.try_set_immediate_u32(operand, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid operand: {}", operand),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_immediate_u32(&mut self, operand: u32, new_value: u32) -> Result<(), IcedError> {
self.try_set_immediate_u64(operand, new_value as u64)
}
#[inline]
pub fn set_immediate_i64(&mut self, operand: u32, new_value: i64) {
match self.try_set_immediate_i64(operand, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid operand: {}", operand),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_immediate_i64(&mut self, operand: u32, new_value: i64) -> Result<(), IcedError> {
self.try_set_immediate_u64(operand, new_value as u64)
}
#[inline]
pub fn set_immediate_u64(&mut self, operand: u32, new_value: u64) {
match self.try_set_immediate_u64(operand, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid operand: {}", operand),
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_set_immediate_u64(&mut self, operand: u32, new_value: u64) -> Result<(), IcedError> {
match self.try_op_kind(operand)? {
OpKind::Immediate8 => self.set_immediate8(new_value as u8),
OpKind::Immediate8to16 => self.set_immediate8to16(new_value as i16),
OpKind::Immediate8to32 => self.set_immediate8to32(new_value as i32),
OpKind::Immediate8to64 => self.set_immediate8to64(new_value as i64),
OpKind::Immediate8_2nd => self.set_immediate8_2nd(new_value as u8),
OpKind::Immediate16 => self.set_immediate16(new_value as u16),
OpKind::Immediate32to64 => self.set_immediate32to64(new_value as i64),
OpKind::Immediate32 => self.set_immediate32(new_value as u32),
OpKind::Immediate64 => self.set_immediate64(new_value),
_ => return Err(IcedError::new("Not an immediate operand")),
}
Ok(())
}
#[must_use]
#[inline]
pub const fn immediate8(&self) -> u8 {
self.immediate as u8
}
#[inline]
pub fn set_immediate8(&mut self, new_value: u8) {
#[cfg(feature = "mvex")]
{
self.immediate = (self.immediate & 0xFFFF_FF00) | (new_value as u32);
}
#[cfg(not(feature = "mvex"))]
{
self.immediate = new_value as u32;
}
}
#[must_use]
#[inline]
pub const fn immediate8_2nd(&self) -> u8 {
self.mem_displ as u8
}
#[inline]
pub fn set_immediate8_2nd(&mut self, new_value: u8) {
self.mem_displ = new_value as u64;
}
#[must_use]
#[inline]
pub const fn immediate16(&self) -> u16 {
self.immediate as u16
}
#[inline]
pub fn set_immediate16(&mut self, new_value: u16) {
self.immediate = new_value as u32;
}
#[must_use]
#[inline]
pub const fn immediate32(&self) -> u32 {
self.immediate
}
#[inline]
pub fn set_immediate32(&mut self, new_value: u32) {
self.immediate = new_value;
}
#[must_use]
#[inline]
pub const fn immediate64(&self) -> u64 {
(self.mem_displ << 32) | (self.immediate as u64)
}
#[inline]
pub fn set_immediate64(&mut self, new_value: u64) {
self.immediate = new_value as u32;
self.mem_displ = new_value >> 32;
}
#[must_use]
#[inline]
pub const fn immediate8to16(&self) -> i16 {
self.immediate as i8 as i16
}
#[inline]
pub fn set_immediate8to16(&mut self, new_value: i16) {
self.immediate = new_value as i8 as u32;
}
#[must_use]
#[inline]
pub const fn immediate8to32(&self) -> i32 {
self.immediate as i8 as i32
}
#[inline]
pub fn set_immediate8to32(&mut self, new_value: i32) {
self.immediate = new_value as i8 as u32;
}
#[must_use]
#[inline]
pub const fn immediate8to64(&self) -> i64 {
self.immediate as i8 as i64
}
#[inline]
pub fn set_immediate8to64(&mut self, new_value: i64) {
self.immediate = new_value as i8 as u32;
}
#[must_use]
#[inline]
pub const fn immediate32to64(&self) -> i64 {
self.immediate as i32 as i64
}
#[inline]
pub fn set_immediate32to64(&mut self, new_value: i64) {
self.immediate = new_value as u32;
}
#[must_use]
#[inline]
pub const fn near_branch16(&self) -> u16 {
self.mem_displ as u16
}
#[inline]
pub fn set_near_branch16(&mut self, new_value: u16) {
self.mem_displ = new_value as u64;
}
#[must_use]
#[inline]
pub const fn near_branch32(&self) -> u32 {
self.mem_displ as u32
}
#[inline]
pub fn set_near_branch32(&mut self, new_value: u32) {
self.mem_displ = new_value as u64;
}
#[must_use]
#[inline]
pub const fn near_branch64(&self) -> u64 {
self.mem_displ
}
#[inline]
pub fn set_near_branch64(&mut self, new_value: u64) {
self.mem_displ = new_value
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
#[allow(unused_mut)]
pub fn near_branch_target(&self) -> u64 {
let mut op_kind = self.op0_kind();
#[cfg(feature = "mvex")]
{
if self.op_count() == 2 {
op_kind = self.op1_kind();
}
}
match op_kind {
OpKind::NearBranch16 => self.near_branch16() as u64,
OpKind::NearBranch32 => self.near_branch32() as u64,
OpKind::NearBranch64 => self.near_branch64(),
_ => 0,
}
}
#[must_use]
#[inline]
pub const fn far_branch16(&self) -> u16 {
self.immediate as u16
}
#[inline]
pub fn set_far_branch16(&mut self, new_value: u16) {
self.immediate = new_value as u32;
}
#[must_use]
#[inline]
pub const fn far_branch32(&self) -> u32 {
self.immediate
}
#[inline]
pub fn set_far_branch32(&mut self, new_value: u32) {
self.immediate = new_value;
}
#[must_use]
#[inline]
pub const fn far_branch_selector(&self) -> u16 {
self.mem_displ as u16
}
#[inline]
pub fn set_far_branch_selector(&mut self, new_value: u16) {
self.mem_displ = new_value as u64;
}
#[must_use]
#[inline]
pub const fn memory_base(&self) -> Register {
self.mem_base_reg
}
#[inline]
pub fn set_memory_base(&mut self, new_value: Register) {
self.mem_base_reg = new_value;
}
#[must_use]
#[inline]
pub const fn memory_index(&self) -> Register {
self.mem_index_reg
}
#[inline]
pub fn set_memory_index(&mut self, new_value: Register) {
self.mem_index_reg = new_value;
}
#[must_use]
#[inline]
pub const fn op0_register(&self) -> Register {
self.regs[0]
}
#[inline]
pub fn set_op0_register(&mut self, new_value: Register) {
self.regs[0] = new_value;
}
#[must_use]
#[inline]
pub const fn op1_register(&self) -> Register {
self.regs[1]
}
#[inline]
pub fn set_op1_register(&mut self, new_value: Register) {
self.regs[1] = new_value;
}
#[must_use]
#[inline]
pub const fn op2_register(&self) -> Register {
self.regs[2]
}
#[inline]
pub fn set_op2_register(&mut self, new_value: Register) {
self.regs[2] = new_value;
}
#[must_use]
#[inline]
pub const fn op3_register(&self) -> Register {
self.regs[3]
}
#[inline]
pub fn set_op3_register(&mut self, new_value: Register) {
self.regs[3] = new_value;
}
#[allow(clippy::unused_self)]
#[must_use]
#[inline]
pub const fn op4_register(&self) -> Register {
Register::None
}
#[allow(clippy::unused_self)]
#[inline]
pub fn set_op4_register(&mut self, new_value: Register) {
debug_assert_eq!(new_value, Register::None);
}
#[allow(clippy::unused_self)]
#[inline]
#[doc(hidden)]
pub fn try_set_op4_register(&mut self, new_value: Register) -> Result<(), IcedError> {
if new_value != Register::None {
Err(IcedError::new("Invalid register"))
} else {
Ok(())
}
}
#[must_use]
#[inline]
pub fn op_register(&self, operand: u32) -> Register {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(®) = self.regs.get(operand as usize) {
reg
} else if operand == 4 {
self.op4_register()
} else {
debug_assert!(false, "Invalid operand: {}", operand);
Register::default()
}
}
#[inline]
#[doc(hidden)]
pub fn try_op_register(&self, operand: u32) -> Result<Register, IcedError> {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
if let Some(®) = self.regs.get(operand as usize) {
Ok(reg)
} else if operand == 4 {
Ok(self.op4_register())
} else {
Err(IcedError::new("Invalid operand"))
}
}
#[inline]
pub fn set_op_register(&mut self, operand: u32, new_value: Register) {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
match operand {
0 => self.set_op0_register(new_value),
1 => self.set_op1_register(new_value),
2 => self.set_op2_register(new_value),
3 => self.set_op3_register(new_value),
4 => self.set_op4_register(new_value),
_ => debug_assert!(false, "Invalid operand: {}", operand),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_op_register(&mut self, operand: u32, new_value: Register) -> Result<(), IcedError> {
const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
match operand {
0 => self.set_op0_register(new_value),
1 => self.set_op1_register(new_value),
2 => self.set_op2_register(new_value),
3 => self.set_op3_register(new_value),
4 => return self.try_set_op4_register(new_value),
_ => return Err(IcedError::new("Invalid operand")),
}
Ok(())
}
#[must_use]
#[inline]
pub fn op_mask(&self) -> Register {
let r = (self.flags1 >> InstrFlags1::OP_MASK_SHIFT) & InstrFlags1::OP_MASK_MASK;
if r == 0 {
Register::None
} else {
const _: () = assert!(InstrFlags1::OP_MASK_MASK == 7);
const _: () = assert!(Register::K0 as u32 + 1 == Register::K1 as u32);
const _: () = assert!(Register::K0 as u32 + 2 == Register::K2 as u32);
const _: () = assert!(Register::K0 as u32 + 3 == Register::K3 as u32);
const _: () = assert!(Register::K0 as u32 + 4 == Register::K4 as u32);
const _: () = assert!(Register::K0 as u32 + 5 == Register::K5 as u32);
const _: () = assert!(Register::K0 as u32 + 6 == Register::K6 as u32);
const _: () = assert!(Register::K0 as u32 + 7 == Register::K7 as u32);
unsafe { mem::transmute((r + Register::K0 as u32) as RegisterUnderlyingType) }
}
}
#[allow(clippy::missing_inline_in_public_items)]
pub fn set_op_mask(&mut self, new_value: Register) {
debug_assert!(new_value == Register::None || (Register::K1 <= new_value && new_value <= Register::K7));
let r = if new_value == Register::None { 0 } else { (new_value as u32 - Register::K0 as u32) & InstrFlags1::OP_MASK_MASK };
self.flags1 = (self.flags1 & !(InstrFlags1::OP_MASK_MASK << InstrFlags1::OP_MASK_SHIFT)) | (r << InstrFlags1::OP_MASK_SHIFT);
}
#[must_use]
#[inline]
pub const fn has_op_mask(&self) -> bool {
(self.flags1 & (InstrFlags1::OP_MASK_MASK << InstrFlags1::OP_MASK_SHIFT)) != 0
}
#[must_use]
#[inline]
pub const fn zeroing_masking(&self) -> bool {
(self.flags1 & InstrFlags1::ZEROING_MASKING) != 0
}
#[inline]
pub fn set_zeroing_masking(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::ZEROING_MASKING;
} else {
self.flags1 &= !InstrFlags1::ZEROING_MASKING;
}
}
#[must_use]
#[inline]
pub const fn merging_masking(&self) -> bool {
(self.flags1 & InstrFlags1::ZEROING_MASKING) == 0
}
#[inline]
pub fn set_merging_masking(&mut self, new_value: bool) {
if new_value {
self.flags1 &= !InstrFlags1::ZEROING_MASKING;
} else {
self.flags1 |= InstrFlags1::ZEROING_MASKING;
}
}
#[must_use]
#[inline]
pub fn rounding_control(&self) -> RoundingControl {
unsafe {
mem::transmute(
((self.flags1 >> InstrFlags1::ROUNDING_CONTROL_SHIFT) & InstrFlags1::ROUNDING_CONTROL_MASK) as RoundingControlUnderlyingType,
)
}
}
#[inline]
pub fn set_rounding_control(&mut self, new_value: RoundingControl) {
self.flags1 = (self.flags1 & !(InstrFlags1::ROUNDING_CONTROL_MASK << InstrFlags1::ROUNDING_CONTROL_SHIFT))
| ((new_value as u32) << InstrFlags1::ROUNDING_CONTROL_SHIFT);
}
#[must_use]
#[inline]
pub const fn declare_data_len(&self) -> usize {
(((self.flags1 >> InstrFlags1::DATA_LENGTH_SHIFT) & InstrFlags1::DATA_LENGTH_MASK) + 1) as usize
}
#[inline]
pub fn set_declare_data_len(&mut self, new_value: usize) {
debug_assert!(1 <= new_value && new_value <= 0x10);
self.flags1 = (self.flags1 & !(InstrFlags1::DATA_LENGTH_MASK << InstrFlags1::DATA_LENGTH_SHIFT))
| ((((new_value as u32) - 1) & InstrFlags1::DATA_LENGTH_MASK) << InstrFlags1::DATA_LENGTH_SHIFT);
}
#[inline]
pub fn set_declare_byte_value_i8(&mut self, index: usize, new_value: i8) {
match self.try_set_declare_byte_value_i8(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[inline]
pub fn try_set_declare_byte_value_i8(&mut self, index: usize, new_value: i8) -> Result<(), IcedError> {
self.try_set_declare_byte_value(index, new_value as u8)
}
#[inline]
pub fn set_declare_byte_value(&mut self, index: usize, new_value: u8) {
match self.try_set_declare_byte_value(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_set_declare_byte_value(&mut self, index: usize, new_value: u8) -> Result<(), IcedError> {
match index {
0 => self.regs[0] = Register::from_u8(new_value),
1 => self.regs[1] = Register::from_u8(new_value),
2 => self.regs[2] = Register::from_u8(new_value),
3 => self.regs[3] = Register::from_u8(new_value),
4 => self.immediate = (self.immediate & 0xFFFF_FF00) | new_value as u32,
5 => self.immediate = (self.immediate & 0xFFFF_00FF) | ((new_value as u32) << 8),
6 => self.immediate = (self.immediate & 0xFF00_FFFF) | ((new_value as u32) << 16),
7 => self.immediate = (self.immediate & 0x00FF_FFFF) | ((new_value as u32) << 24),
8 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_FFFF_FF00) | new_value as u64,
9 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_FFFF_00FF) | ((new_value as u64) << 8),
10 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_FF00_FFFF) | ((new_value as u64) << 16),
11 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_00FF_FFFF) | ((new_value as u64) << 24),
12 => self.mem_displ = (self.mem_displ & 0xFFFF_FF00_FFFF_FFFF) | ((new_value as u64) << 32),
13 => self.mem_displ = (self.mem_displ & 0xFFFF_00FF_FFFF_FFFF) | ((new_value as u64) << 40),
14 => self.mem_displ = (self.mem_displ & 0xFF00_FFFF_FFFF_FFFF) | ((new_value as u64) << 48),
15 => self.mem_displ = (self.mem_displ & 0x00FF_FFFF_FFFF_FFFF) | ((new_value as u64) << 56),
_ => return Err(IcedError::new("Invalid index")),
}
Ok(())
}
#[must_use]
#[inline]
pub fn get_declare_byte_value(&self, index: usize) -> u8 {
match self.try_get_declare_byte_value(index) {
Ok(value) => value,
Err(_) => {
debug_assert!(false, "Invalid index: {}", index);
0
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub const fn try_get_declare_byte_value(&self, index: usize) -> Result<u8, IcedError> {
Ok(match index {
0 => self.regs[0] as u8,
1 => self.regs[1] as u8,
2 => self.regs[2] as u8,
3 => self.regs[3] as u8,
4 => self.immediate as u8,
5 => (self.immediate >> 8) as u8,
6 => (self.immediate >> 16) as u8,
7 => (self.immediate >> 24) as u8,
8 => self.mem_displ as u8,
9 => ((self.mem_displ as u32) >> 8) as u8,
10 => ((self.mem_displ as u32) >> 16) as u8,
11 => ((self.mem_displ as u32) >> 24) as u8,
12 => (self.mem_displ >> 32) as u8,
13 => (self.mem_displ >> 40) as u8,
14 => (self.mem_displ >> 48) as u8,
15 => (self.mem_displ >> 56) as u8,
_ => return Err(IcedError::new("Invalid index")),
})
}
#[inline]
pub fn set_declare_word_value_i16(&mut self, index: usize, new_value: i16) {
match self.try_set_declare_word_value_i16(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_declare_word_value_i16(&mut self, index: usize, new_value: i16) -> Result<(), IcedError> {
self.try_set_declare_word_value(index, new_value as u16)
}
#[inline]
pub fn set_declare_word_value(&mut self, index: usize, new_value: u16) {
match self.try_set_declare_word_value(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_set_declare_word_value(&mut self, index: usize, new_value: u16) -> Result<(), IcedError> {
match index {
0 => {
self.regs[0] = Register::from_u8(new_value as u8);
self.regs[1] = Register::from_u8((new_value >> 8) as u8);
}
1 => {
self.regs[2] = Register::from_u8(new_value as u8);
self.regs[3] = Register::from_u8((new_value >> 8) as u8);
}
2 => self.immediate = (self.immediate & 0xFFFF_0000) | new_value as u32,
3 => self.immediate = self.immediate as u16 as u32 | (new_value as u32) << 16,
4 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_FFFF_0000) | new_value as u64,
5 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_0000_FFFF) | ((new_value as u64) << 16),
6 => self.mem_displ = (self.mem_displ & 0xFFFF_0000_FFFF_FFFF) | ((new_value as u64) << 32),
7 => self.mem_displ = (self.mem_displ & 0x0000_FFFF_FFFF_FFFF) | ((new_value as u64) << 48),
_ => return Err(IcedError::new("Invalid index")),
}
Ok(())
}
#[must_use]
#[inline]
pub fn get_declare_word_value(&self, index: usize) -> u16 {
match self.try_get_declare_word_value(index) {
Ok(value) => value,
Err(_) => {
debug_assert!(false, "Invalid index: {}", index);
0
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub const fn try_get_declare_word_value(&self, index: usize) -> Result<u16, IcedError> {
Ok(match index {
0 => self.regs[0] as u16 | ((self.regs[1] as u16) << 8),
1 => self.regs[2] as u16 | ((self.regs[3] as u16) << 8),
2 => self.immediate as u16,
3 => (self.immediate >> 16) as u16,
4 => self.mem_displ as u16,
5 => ((self.mem_displ as u32) >> 16) as u16,
6 => (self.mem_displ >> 32) as u16,
7 => (self.mem_displ >> 48) as u16,
_ => return Err(IcedError::new("Invalid index")),
})
}
#[inline]
pub fn set_declare_dword_value_i32(&mut self, index: usize, new_value: i32) {
match self.try_set_declare_dword_value_i32(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_declare_dword_value_i32(&mut self, index: usize, new_value: i32) -> Result<(), IcedError> {
self.try_set_declare_dword_value(index, new_value as u32)
}
#[inline]
pub fn set_declare_dword_value(&mut self, index: usize, new_value: u32) {
match self.try_set_declare_dword_value(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_set_declare_dword_value(&mut self, index: usize, new_value: u32) -> Result<(), IcedError> {
match index {
0 => {
self.regs[0] = Register::from_u8(new_value as u8);
self.regs[1] = Register::from_u8((new_value >> 8) as u8);
self.regs[2] = Register::from_u8((new_value >> 16) as u8);
self.regs[3] = Register::from_u8((new_value >> 24) as u8);
}
1 => self.immediate = new_value,
2 => self.mem_displ = (self.mem_displ & 0xFFFF_FFFF_0000_0000) | new_value as u64,
3 => self.mem_displ = (self.mem_displ & 0x0000_0000_FFFF_FFFF) | (new_value as u64) << 32,
_ => return Err(IcedError::new("Invalid index")),
}
Ok(())
}
#[must_use]
#[inline]
pub fn get_declare_dword_value(&self, index: usize) -> u32 {
match self.try_get_declare_dword_value(index) {
Ok(value) => value,
Err(_) => {
debug_assert!(false, "Invalid index: {}", index);
0
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub const fn try_get_declare_dword_value(&self, index: usize) -> Result<u32, IcedError> {
Ok(match index {
0 => self.regs[0] as u32 | ((self.regs[1] as u32) << 8) | ((self.regs[2] as u32) << 16) | ((self.regs[3] as u32) << 24),
1 => self.immediate,
2 => self.mem_displ as u32,
3 => (self.mem_displ >> 32) as u32,
_ => return Err(IcedError::new("Invalid index")),
})
}
#[inline]
pub fn set_declare_qword_value_i64(&mut self, index: usize, new_value: i64) {
match self.try_set_declare_qword_value_i64(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[inline]
#[doc(hidden)]
pub fn try_set_declare_qword_value_i64(&mut self, index: usize, new_value: i64) -> Result<(), IcedError> {
self.try_set_declare_qword_value(index, new_value as u64)
}
#[inline]
pub fn set_declare_qword_value(&mut self, index: usize, new_value: u64) {
match self.try_set_declare_qword_value(index, new_value) {
Ok(()) => {}
Err(_) => debug_assert!(false, "Invalid index: {}", index),
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_set_declare_qword_value(&mut self, index: usize, new_value: u64) -> Result<(), IcedError> {
match index {
0 => {
self.regs[0] = Register::from_u8(new_value as u8);
self.regs[1] = Register::from_u8((new_value >> 8) as u8);
self.regs[2] = Register::from_u8((new_value >> 16) as u8);
self.regs[3] = Register::from_u8((new_value >> 24) as u8);
self.immediate = (new_value >> 32) as u32;
}
1 => self.mem_displ = new_value,
_ => return Err(IcedError::new("Invalid index")),
}
Ok(())
}
#[must_use]
#[inline]
pub fn get_declare_qword_value(&self, index: usize) -> u64 {
match self.try_get_declare_qword_value(index) {
Ok(value) => value,
Err(_) => {
debug_assert!(false, "Invalid index: {}", index);
0
}
}
}
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub const fn try_get_declare_qword_value(&self, index: usize) -> Result<u64, IcedError> {
Ok(match index {
0 => {
self.regs[0] as u64
| ((self.regs[1] as u64) << 8)
| ((self.regs[2] as u64) << 16)
| ((self.regs[3] as u64) << 24)
| ((self.immediate as u64) << 32)
}
1 => self.mem_displ,
_ => return Err(IcedError::new("Invalid index")),
})
}
#[must_use]
#[inline]
pub const fn is_vsib(&self) -> bool {
self.vsib().is_some()
}
#[must_use]
#[inline]
pub const fn is_vsib32(&self) -> bool {
if let Some(is_vsib64) = self.vsib() {
!is_vsib64
} else {
false
}
}
#[must_use]
#[inline]
pub const fn is_vsib64(&self) -> bool {
if let Some(is_vsib64) = self.vsib() {
is_vsib64
} else {
false
}
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::match_single_binding)]
pub const fn vsib(&self) -> Option<bool> {
#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
match self.code() {
Code::VEX_Vpgatherdd_xmm_vm32x_xmm
| Code::VEX_Vpgatherdd_ymm_vm32y_ymm
| Code::VEX_Vpgatherdq_xmm_vm32x_xmm
| Code::VEX_Vpgatherdq_ymm_vm32x_ymm
| Code::EVEX_Vpgatherdd_xmm_k1_vm32x
| Code::EVEX_Vpgatherdd_ymm_k1_vm32y
| Code::EVEX_Vpgatherdd_zmm_k1_vm32z
| Code::EVEX_Vpgatherdq_xmm_k1_vm32x
| Code::EVEX_Vpgatherdq_ymm_k1_vm32x
| Code::EVEX_Vpgatherdq_zmm_k1_vm32y
| Code::VEX_Vgatherdps_xmm_vm32x_xmm
| Code::VEX_Vgatherdps_ymm_vm32y_ymm
| Code::VEX_Vgatherdpd_xmm_vm32x_xmm
| Code::VEX_Vgatherdpd_ymm_vm32x_ymm
| Code::EVEX_Vgatherdps_xmm_k1_vm32x
| Code::EVEX_Vgatherdps_ymm_k1_vm32y
| Code::EVEX_Vgatherdps_zmm_k1_vm32z
| Code::EVEX_Vgatherdpd_xmm_k1_vm32x
| Code::EVEX_Vgatherdpd_ymm_k1_vm32x
| Code::EVEX_Vgatherdpd_zmm_k1_vm32y
| Code::EVEX_Vpscatterdd_vm32x_k1_xmm
| Code::EVEX_Vpscatterdd_vm32y_k1_ymm
| Code::EVEX_Vpscatterdd_vm32z_k1_zmm
| Code::EVEX_Vpscatterdq_vm32x_k1_xmm
| Code::EVEX_Vpscatterdq_vm32x_k1_ymm
| Code::EVEX_Vpscatterdq_vm32y_k1_zmm
| Code::EVEX_Vscatterdps_vm32x_k1_xmm
| Code::EVEX_Vscatterdps_vm32y_k1_ymm
| Code::EVEX_Vscatterdps_vm32z_k1_zmm
| Code::EVEX_Vscatterdpd_vm32x_k1_xmm
| Code::EVEX_Vscatterdpd_vm32x_k1_ymm
| Code::EVEX_Vscatterdpd_vm32y_k1_zmm
| Code::EVEX_Vgatherpf0dps_vm32z_k1
| Code::EVEX_Vgatherpf0dpd_vm32y_k1
| Code::EVEX_Vgatherpf1dps_vm32z_k1
| Code::EVEX_Vgatherpf1dpd_vm32y_k1
| Code::EVEX_Vscatterpf0dps_vm32z_k1
| Code::EVEX_Vscatterpf0dpd_vm32y_k1
| Code::EVEX_Vscatterpf1dps_vm32z_k1
| Code::EVEX_Vscatterpf1dpd_vm32y_k1
| Code::MVEX_Vpgatherdd_zmm_k1_mvt
| Code::MVEX_Vpgatherdq_zmm_k1_mvt
| Code::MVEX_Vgatherdps_zmm_k1_mvt
| Code::MVEX_Vgatherdpd_zmm_k1_mvt
| Code::MVEX_Vpscatterdd_mvt_k1_zmm
| Code::MVEX_Vpscatterdq_mvt_k1_zmm
| Code::MVEX_Vscatterdps_mvt_k1_zmm
| Code::MVEX_Vscatterdpd_mvt_k1_zmm
| Code::MVEX_Undoc_zmm_k1_mvt_512_66_0F38_W0_B0
| Code::MVEX_Undoc_zmm_k1_mvt_512_66_0F38_W0_B2
| Code::MVEX_Undoc_zmm_k1_mvt_512_66_0F38_W0_C0
| Code::MVEX_Vgatherpf0hintdps_mvt_k1
| Code::MVEX_Vgatherpf0hintdpd_mvt_k1
| Code::MVEX_Vgatherpf0dps_mvt_k1
| Code::MVEX_Vgatherpf1dps_mvt_k1
| Code::MVEX_Vscatterpf0hintdps_mvt_k1
| Code::MVEX_Vscatterpf0hintdpd_mvt_k1
| Code::MVEX_Vscatterpf0dps_mvt_k1
| Code::MVEX_Vscatterpf1dps_mvt_k1
=> Some(false),
Code::VEX_Vpgatherqd_xmm_vm64x_xmm
| Code::VEX_Vpgatherqd_xmm_vm64y_xmm
| Code::VEX_Vpgatherqq_xmm_vm64x_xmm
| Code::VEX_Vpgatherqq_ymm_vm64y_ymm
| Code::EVEX_Vpgatherqd_xmm_k1_vm64x
| Code::EVEX_Vpgatherqd_xmm_k1_vm64y
| Code::EVEX_Vpgatherqd_ymm_k1_vm64z
| Code::EVEX_Vpgatherqq_xmm_k1_vm64x
| Code::EVEX_Vpgatherqq_ymm_k1_vm64y
| Code::EVEX_Vpgatherqq_zmm_k1_vm64z
| Code::VEX_Vgatherqps_xmm_vm64x_xmm
| Code::VEX_Vgatherqps_xmm_vm64y_xmm
| Code::VEX_Vgatherqpd_xmm_vm64x_xmm
| Code::VEX_Vgatherqpd_ymm_vm64y_ymm
| Code::EVEX_Vgatherqps_xmm_k1_vm64x
| Code::EVEX_Vgatherqps_xmm_k1_vm64y
| Code::EVEX_Vgatherqps_ymm_k1_vm64z
| Code::EVEX_Vgatherqpd_xmm_k1_vm64x
| Code::EVEX_Vgatherqpd_ymm_k1_vm64y
| Code::EVEX_Vgatherqpd_zmm_k1_vm64z
| Code::EVEX_Vpscatterqd_vm64x_k1_xmm
| Code::EVEX_Vpscatterqd_vm64y_k1_xmm
| Code::EVEX_Vpscatterqd_vm64z_k1_ymm
| Code::EVEX_Vpscatterqq_vm64x_k1_xmm
| Code::EVEX_Vpscatterqq_vm64y_k1_ymm
| Code::EVEX_Vpscatterqq_vm64z_k1_zmm
| Code::EVEX_Vscatterqps_vm64x_k1_xmm
| Code::EVEX_Vscatterqps_vm64y_k1_xmm
| Code::EVEX_Vscatterqps_vm64z_k1_ymm
| Code::EVEX_Vscatterqpd_vm64x_k1_xmm
| Code::EVEX_Vscatterqpd_vm64y_k1_ymm
| Code::EVEX_Vscatterqpd_vm64z_k1_zmm
| Code::EVEX_Vgatherpf0qps_vm64z_k1
| Code::EVEX_Vgatherpf0qpd_vm64z_k1
| Code::EVEX_Vgatherpf1qps_vm64z_k1
| Code::EVEX_Vgatherpf1qpd_vm64z_k1
| Code::EVEX_Vscatterpf0qps_vm64z_k1
| Code::EVEX_Vscatterpf0qpd_vm64z_k1
| Code::EVEX_Vscatterpf1qps_vm64z_k1
| Code::EVEX_Vscatterpf1qpd_vm64z_k1
=> Some(true),
_ => None,
}
}
#[must_use]
#[inline]
pub const fn suppress_all_exceptions(&self) -> bool {
(self.flags1 & InstrFlags1::SUPPRESS_ALL_EXCEPTIONS) != 0
}
#[inline]
pub fn set_suppress_all_exceptions(&mut self, new_value: bool) {
if new_value {
self.flags1 |= InstrFlags1::SUPPRESS_ALL_EXCEPTIONS;
} else {
self.flags1 &= !InstrFlags1::SUPPRESS_ALL_EXCEPTIONS;
}
}
#[must_use]
#[inline]
pub fn is_ip_rel_memory_operand(&self) -> bool {
let base_reg = self.memory_base();
base_reg == Register::RIP || base_reg == Register::EIP
}
#[must_use]
#[inline]
pub fn ip_rel_memory_address(&self) -> u64 {
if self.memory_base() == Register::RIP {
self.memory_displacement64()
} else {
self.memory_displacement32() as u64
}
}
#[must_use]
#[inline]
pub fn virtual_address<F>(&self, operand: u32, element_index: usize, get_register_value: F) -> Option<u64>
where
F: FnMut(Register, usize, usize) -> Option<u64>,
{
self.try_virtual_address(operand, element_index, get_register_value)
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
#[doc(hidden)]
pub fn try_virtual_address<F>(&self, operand: u32, element_index: usize, mut get_register_value: F) -> Option<u64>
where
F: FnMut(Register, usize, usize) -> Option<u64>,
{
let op_kind = self.op_kind(operand);
Some(match op_kind {
OpKind::Register
| OpKind::NearBranch16
| OpKind::NearBranch32
| OpKind::NearBranch64
| OpKind::FarBranch16
| OpKind::FarBranch32
| OpKind::Immediate8
| OpKind::Immediate8_2nd
| OpKind::Immediate16
| OpKind::Immediate32
| OpKind::Immediate64
| OpKind::Immediate8to16
| OpKind::Immediate8to32
| OpKind::Immediate8to64
| OpKind::Immediate32to64 => 0,
OpKind::MemorySegSI => {
get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::SI, 0, 0)? as u16 as u64)
}
OpKind::MemorySegESI => {
get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::ESI, 0, 0)? as u32 as u64)
}
OpKind::MemorySegRSI => get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::RSI, 0, 0)?),
OpKind::MemorySegDI => {
get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::DI, 0, 0)? as u16 as u64)
}
OpKind::MemorySegEDI => {
get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::EDI, 0, 0)? as u32 as u64)
}
OpKind::MemorySegRDI => get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(get_register_value(Register::RDI, 0, 0)?),
OpKind::MemoryESDI => get_register_value(Register::ES, 0, 0)?.wrapping_add(get_register_value(Register::DI, 0, 0)? as u16 as u64),
OpKind::MemoryESEDI => get_register_value(Register::ES, 0, 0)?.wrapping_add(get_register_value(Register::EDI, 0, 0)? as u32 as u64),
OpKind::MemoryESRDI => get_register_value(Register::ES, 0, 0)?.wrapping_add(get_register_value(Register::RDI, 0, 0)?),
OpKind::Memory => {
let base_reg = self.memory_base();
let index_reg = self.memory_index();
let addr_size = instruction_internal::get_address_size_in_bytes(base_reg, index_reg, self.memory_displ_size(), self.code_size());
let mut offset = self.memory_displacement64();
let offset_mask = match addr_size {
8 => u64::MAX,
4 => u32::MAX as u64,
_ => {
debug_assert_eq!(addr_size, 2);
u16::MAX as u64
}
};
match base_reg {
Register::None | Register::EIP | Register::RIP => {}
_ => offset = offset.wrapping_add(get_register_value(base_reg, 0, 0)?),
}
let code = self.code();
if index_reg != Register::None && !code.ignores_index() && !code.is_tile_stride_index() {
if let Some(is_vsib64) = self.vsib() {
if is_vsib64 {
offset = offset.wrapping_add(
get_register_value(index_reg, element_index, 8)? << instruction_internal::internal_get_memory_index_scale(self),
);
} else {
offset = offset.wrapping_add(
(get_register_value(index_reg, element_index, 4)? as i32 as u64)
<< instruction_internal::internal_get_memory_index_scale(self),
);
}
} else {
offset =
offset.wrapping_add(get_register_value(index_reg, 0, 0)? << instruction_internal::internal_get_memory_index_scale(self));
}
}
#[cfg(feature = "mvex")]
{
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 1 == Code::MVEX_Vloadunpackhq_zmm_k1_mt as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 2 == Code::MVEX_Vpackstorehd_mt_k1_zmm as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 3 == Code::MVEX_Vpackstorehq_mt_k1_zmm as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 4 == Code::MVEX_Vloadunpackhps_zmm_k1_mt as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 5 == Code::MVEX_Vloadunpackhpd_zmm_k1_mt as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 6 == Code::MVEX_Vpackstorehps_mt_k1_zmm as u32);
const _: () = assert!(Code::MVEX_Vloadunpackhd_zmm_k1_mt as u32 + 7 == Code::MVEX_Vpackstorehpd_mt_k1_zmm as u32);
if code >= Code::MVEX_Vloadunpackhd_zmm_k1_mt && code <= Code::MVEX_Vpackstorehpd_mt_k1_zmm {
offset = offset.wrapping_sub(0x40);
}
}
offset &= offset_mask;
if !code.ignores_segment() {
get_register_value(self.memory_segment(), 0, 0)?.wrapping_add(offset)
} else {
offset
}
}
})
}
}
#[cfg(feature = "instr_info")]
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
pub struct FpuStackIncrementInfo {
increment: i32,
conditional: bool,
writes_top: bool,
}
#[cfg(feature = "instr_info")]
impl FpuStackIncrementInfo {
#[must_use]
#[inline]
pub const fn new(increment: i32, conditional: bool, writes_top: bool) -> Self {
Self { increment, conditional, writes_top }
}
#[must_use]
#[inline]
pub const fn increment(&self) -> i32 {
self.increment
}
#[must_use]
#[inline]
pub const fn conditional(&self) -> bool {
self.conditional
}
#[must_use]
#[inline]
pub const fn writes_top(&self) -> bool {
self.writes_top
}
}
#[cfg(feature = "instr_info")]
impl Instruction {
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
pub fn stack_pointer_increment(&self) -> i32 {
#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
#[allow(clippy::match_single_binding)]
match self.code() {
Code::Pushad => -32,
Code::Pushaw
| Code::Call_m1664 => -16,
Code::Push_r64
| Code::Pushq_imm32
| Code::Pushq_imm8
| Code::Call_ptr1632
| Code::Pushfq
| Code::Call_rel32_64
| Code::Call_rm64
| Code::Call_m1632
| Code::Push_rm64
| Code::Pushq_FS
| Code::Pushq_GS => -8,
Code::Pushd_ES
| Code::Pushd_CS
| Code::Pushd_SS
| Code::Pushd_DS
| Code::Push_r32
| Code::Pushd_imm32
| Code::Pushd_imm8
| Code::Call_ptr1616
| Code::Pushfd
| Code::Call_rel32_32
| Code::Call_rm32
| Code::Call_m1616
| Code::Push_rm32
| Code::Pushd_FS
| Code::Pushd_GS => -4,
Code::Pushw_ES
| Code::Pushw_CS
| Code::Pushw_SS
| Code::Pushw_DS
| Code::Push_r16
| Code::Push_imm16
| Code::Pushw_imm8
| Code::Pushfw
| Code::Call_rel16
| Code::Call_rm16
| Code::Push_rm16
| Code::Pushw_FS
| Code::Pushw_GS => -2,
Code::Popw_ES
| Code::Popw_CS
| Code::Popw_SS
| Code::Popw_DS
| Code::Pop_r16
| Code::Pop_rm16
| Code::Popfw
| Code::Retnw
| Code::Popw_FS
| Code::Popw_GS => 2,
Code::Popd_ES
| Code::Popd_SS
| Code::Popd_DS
| Code::Pop_r32
| Code::Pop_rm32
| Code::Popfd
| Code::Retnd
| Code::Retfw
| Code::Popd_FS
| Code::Popd_GS => 4,
Code::Pop_r64
| Code::Pop_rm64
| Code::Popfq
| Code::Retnq
| Code::Retfd
| Code::Popq_FS
| Code::Popq_GS => 8,
Code::Popaw
| Code::Retfq => 16,
Code::Uiret => 24,
Code::Popad => 32,
Code::Iretq
| Code::Eretu
| Code::Erets => 40,
Code::Enterw_imm16_imm8 => -(2 + (self.immediate8_2nd() as i32 & 0x1F) * 2 + self.immediate16() as i32),
Code::Enterd_imm16_imm8 => -(4 + (self.immediate8_2nd() as i32 & 0x1F) * 4 + self.immediate16() as i32),
Code::Enterq_imm16_imm8 => -(8 + (self.immediate8_2nd() as i32 & 0x1F) * 8 + self.immediate16() as i32),
Code::Iretw => if self.code_size() == CodeSize::Code64 { 2 * 5 } else { 2 * 3 },
Code::Iretd => if self.code_size() == CodeSize::Code64 { 4 * 5 } else { 4 * 3 },
Code::Retnw_imm16 => 2 + self.immediate16() as i32,
Code::Retnd_imm16
| Code::Retfw_imm16 => 4 + self.immediate16() as i32,
Code::Retnq_imm16
| Code::Retfd_imm16 => 8 + self.immediate16() as i32,
Code::Retfq_imm16 => 16 + self.immediate16() as i32,
_ => 0,
}
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
pub fn fpu_stack_increment_info(&self) -> FpuStackIncrementInfo {
#[cfg_attr(feature = "cargo-fmt", rustfmt::skip)]
#[allow(clippy::match_single_binding)]
match self.code() {
Code::Fld_m32fp
| Code::Fld_sti
| Code::Fld1
| Code::Fldl2t
| Code::Fldl2e
| Code::Fldpi
| Code::Fldlg2
| Code::Fldln2
| Code::Fldz
| Code::Fxtract
| Code::Fdecstp
| Code::Fild_m32int
| Code::Fld_m80fp
| Code::Fld_m64fp
| Code::Fild_m16int
| Code::Fbld_m80bcd
| Code::Fild_m64int
=> FpuStackIncrementInfo { increment: -1, conditional: false, writes_top: true },
Code::Fptan
| Code::Fsincos
=> FpuStackIncrementInfo { increment: -1, conditional: true, writes_top: true },
Code::Fldenv_m14byte
| Code::Fldenv_m28byte
| Code::Fninit
| Code::Finit
| Code::Frstor_m94byte
| Code::Frstor_m108byte
| Code::Fnsave_m94byte
| Code::Fsave_m94byte
| Code::Fnsave_m108byte
| Code::Fsave_m108byte
=> FpuStackIncrementInfo { increment: 0, conditional: false, writes_top: true },
Code::Fcomp_m32fp
| Code::Fcomp_st0_sti
| Code::Fstp_m32fp
| Code::Fstpnce_sti
| Code::Fyl2x
| Code::Fpatan
| Code::Fincstp
| Code::Fyl2xp1
| Code::Ficomp_m32int
| Code::Fisttp_m32int
| Code::Fistp_m32int
| Code::Fstp_m80fp
| Code::Fcomp_m64fp
| Code::Fcomp_st0_sti_DCD8
| Code::Fisttp_m64int
| Code::Fstp_m64fp
| Code::Fstp_sti
| Code::Fucomp_st0_sti
| Code::Ficomp_m16int
| Code::Faddp_sti_st0
| Code::Fmulp_sti_st0
| Code::Fcomp_st0_sti_DED0
| Code::Fsubrp_sti_st0
| Code::Fsubp_sti_st0
| Code::Fdivrp_sti_st0
| Code::Fdivp_sti_st0
| Code::Fisttp_m16int
| Code::Fistp_m16int
| Code::Fbstp_m80bcd
| Code::Fistp_m64int
| Code::Ffreep_sti
| Code::Fstp_sti_DFD0
| Code::Fstp_sti_DFD8
| Code::Fucomip_st0_sti
| Code::Fcomip_st0_sti
| Code::Ftstp
=> FpuStackIncrementInfo { increment: 1, conditional: false, writes_top: true },
Code::Fucompp
| Code::Fcompp
=> FpuStackIncrementInfo { increment: 2, conditional: false, writes_top: true },
_ => FpuStackIncrementInfo::default(),
}
}
#[must_use]
#[inline]
pub fn encoding(&self) -> EncodingKind {
self.code().encoding()
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
pub fn cpuid_features(&self) -> &'static [CpuidFeature] {
self.code().cpuid_features()
}
#[must_use]
#[inline]
pub fn flow_control(&self) -> FlowControl {
self.code().flow_control()
}
#[must_use]
#[inline]
pub fn is_privileged(&self) -> bool {
self.code().is_privileged()
}
#[must_use]
#[inline]
pub fn is_stack_instruction(&self) -> bool {
self.code().is_stack_instruction()
}
#[must_use]
#[inline]
pub fn is_save_restore_instruction(&self) -> bool {
self.code().is_save_restore_instruction()
}
#[must_use]
#[inline]
pub const fn is_string_instruction(&self) -> bool {
self.code().is_string_instruction()
}
#[must_use]
fn rflags_info(&self) -> RflagsInfo {
let flags1 = crate::info::info_table::TABLE[self.code() as usize].0;
let implied_access = (flags1 >> InfoFlags1::IMPLIED_ACCESS_SHIFT) & InfoFlags1::IMPLIED_ACCESS_MASK;
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 + 1 == ImpliedAccess::Shift_Ib_MASK1FMOD11 as u32);
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 + 2 == ImpliedAccess::Shift_Ib_MASK1F as u32);
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 + 3 == ImpliedAccess::Shift_Ib_MASK3F as u32);
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 + 4 == ImpliedAccess::Clear_rflags as u32);
let result = unsafe { mem::transmute(((flags1 >> InfoFlags1::RFLAGS_INFO_SHIFT) & InfoFlags1::RFLAGS_INFO_MASK) as u8) };
let e = implied_access.wrapping_sub(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32);
match e {
0 | 1 => {
#[allow(clippy::eq_op)]
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 - ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 == 0);
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1FMOD11 as u32 - ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 == 1);
let m = if e == 0 { 9 } else { 17 };
match (self.immediate8() & 0x1F) % m {
0 => return RflagsInfo::None,
1 => return RflagsInfo::R_c_W_co,
_ => {}
}
}
2 | 3 => {
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK1F as u32 - ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 == 2);
const _: () = assert!(ImpliedAccess::Shift_Ib_MASK3F as u32 - ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 == 3);
let mask = if e == 2 { 0x1F } else { 0x3F };
match self.immediate8() & mask {
0 => return RflagsInfo::None,
1 => {
if result == RflagsInfo::W_c_U_o {
return RflagsInfo::W_co;
} else if result == RflagsInfo::R_c_W_c_U_o {
return RflagsInfo::R_c_W_co;
} else {
debug_assert_eq!(result, RflagsInfo::W_cpsz_U_ao);
return RflagsInfo::W_copsz_U_a;
}
}
_ => {}
}
}
4 => {
const _: () = assert!(ImpliedAccess::Clear_rflags as u32 - ImpliedAccess::Shift_Ib_MASK1FMOD9 as u32 == 4);
if self.op0_register() == self.op1_register() && self.op0_kind() == OpKind::Register && self.op1_kind() == OpKind::Register {
if self.mnemonic() == Mnemonic::Xor {
return RflagsInfo::C_cos_S_pz_U_a;
} else {
return RflagsInfo::C_acos_S_pz;
}
}
}
_ => {}
}
result
}
#[must_use]
#[inline]
pub fn rflags_read(&self) -> u32 {
crate::info::rflags_table::FLAGS_READ[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub fn rflags_written(&self) -> u32 {
crate::info::rflags_table::FLAGS_WRITTEN[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub fn rflags_cleared(&self) -> u32 {
crate::info::rflags_table::FLAGS_CLEARED[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub fn rflags_set(&self) -> u32 {
crate::info::rflags_table::FLAGS_SET[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub fn rflags_undefined(&self) -> u32 {
crate::info::rflags_table::FLAGS_UNDEFINED[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub fn rflags_modified(&self) -> u32 {
crate::info::rflags_table::FLAGS_MODIFIED[self.rflags_info() as usize] as u32
}
#[must_use]
#[inline]
pub const fn is_jcc_short_or_near(&self) -> bool {
self.code().is_jcc_short_or_near()
}
#[must_use]
#[inline]
pub const fn is_jcc_near(&self) -> bool {
self.code().is_jcc_near()
}
#[must_use]
#[inline]
pub const fn is_jcc_short(&self) -> bool {
self.code().is_jcc_short()
}
#[must_use]
#[inline]
pub const fn is_jmp_short(&self) -> bool {
self.code().is_jmp_short()
}
#[must_use]
#[inline]
pub const fn is_jmp_near(&self) -> bool {
self.code().is_jmp_near()
}
#[must_use]
#[inline]
pub const fn is_jmp_short_or_near(&self) -> bool {
self.code().is_jmp_short_or_near()
}
#[must_use]
#[inline]
pub const fn is_jmp_far(&self) -> bool {
self.code().is_jmp_far()
}
#[must_use]
#[inline]
pub const fn is_call_near(&self) -> bool {
self.code().is_call_near()
}
#[must_use]
#[inline]
pub const fn is_call_far(&self) -> bool {
self.code().is_call_far()
}
#[must_use]
#[inline]
pub const fn is_jmp_near_indirect(&self) -> bool {
self.code().is_jmp_near_indirect()
}
#[must_use]
#[inline]
pub const fn is_jmp_far_indirect(&self) -> bool {
self.code().is_jmp_far_indirect()
}
#[must_use]
#[inline]
pub const fn is_call_near_indirect(&self) -> bool {
self.code().is_call_near_indirect()
}
#[must_use]
#[inline]
pub const fn is_call_far_indirect(&self) -> bool {
self.code().is_call_far_indirect()
}
#[must_use]
#[inline]
#[cfg(feature = "mvex")]
pub const fn is_jkcc_short_or_near(&self) -> bool {
self.code().is_jkcc_short_or_near()
}
#[must_use]
#[inline]
#[cfg(feature = "mvex")]
pub const fn is_jkcc_near(&self) -> bool {
self.code().is_jkcc_near()
}
#[must_use]
#[inline]
#[cfg(feature = "mvex")]
pub const fn is_jkcc_short(&self) -> bool {
self.code().is_jkcc_short()
}
#[must_use]
#[inline]
pub const fn is_jcx_short(&self) -> bool {
self.code().is_jcx_short()
}
#[must_use]
#[inline]
pub const fn is_loopcc(&self) -> bool {
self.code().is_loopcc()
}
#[must_use]
#[inline]
pub const fn is_loop(&self) -> bool {
self.code().is_loop()
}
#[inline]
pub fn negate_condition_code(&mut self) {
self.set_code(self.code().negate_condition_code())
}
#[inline]
pub fn as_short_branch(&mut self) {
self.set_code(self.code().as_short_branch())
}
#[inline]
pub fn as_near_branch(&mut self) {
self.set_code(self.code().as_near_branch())
}
#[must_use]
#[inline]
pub fn condition_code(&self) -> ConditionCode {
self.code().condition_code()
}
}
#[cfg(all(feature = "encoder", feature = "op_code_info"))]
impl Instruction {
#[must_use]
#[inline]
pub fn op_code(&self) -> &'static OpCodeInfo {
self.code().op_code()
}
}
impl Eq for Instruction {}
impl PartialEq<Instruction> for Instruction {
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
fn eq(&self, other: &Self) -> bool {
self.mem_displ == other.mem_displ
&& ((self.flags1 ^ other.flags1) & !InstrFlags1::EQUALS_IGNORE_MASK) == 0
&& self.immediate == other.immediate
&& self.code == other.code
&& self.mem_base_reg == other.mem_base_reg
&& self.mem_index_reg == other.mem_index_reg
&& self.regs == other.regs
&& self.op_kinds == other.op_kinds
&& self.scale == other.scale
&& self.displ_size == other.displ_size
&& self.pad == other.pad
}
}
impl Hash for Instruction {
#[allow(clippy::missing_inline_in_public_items)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.mem_displ.hash(state);
(self.flags1 & !InstrFlags1::EQUALS_IGNORE_MASK).hash(state);
self.immediate.hash(state);
self.code.hash(state);
self.mem_base_reg.hash(state);
self.mem_index_reg.hash(state);
self.regs.hash(state);
self.op_kinds.hash(state);
self.scale.hash(state);
self.displ_size.hash(state);
self.pad.hash(state);
}
}
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm"))]
struct FmtFormatterOutput<'a, 'b> {
f: &'a mut fmt::Formatter<'b>,
result: fmt::Result,
}
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm"))]
impl<'a, 'b> FmtFormatterOutput<'a, 'b> {
fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
Self { f, result: Ok(()) }
}
}
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm"))]
impl FormatterOutput for FmtFormatterOutput<'_, '_> {
fn write(&mut self, text: &str, _kind: FormatterTextKind) {
if self.result.is_ok() {
self.result = self.f.write_str(text);
}
}
}
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm", feature = "fast_fmt"))]
impl fmt::Display for Instruction {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm"))]
{
#[cfg(feature = "masm")]
let mut formatter = MasmFormatter::new();
#[cfg(all(not(feature = "masm"), feature = "nasm"))]
let mut formatter = NasmFormatter::new();
#[cfg(all(not(feature = "masm"), not(feature = "nasm"), feature = "intel"))]
let mut formatter = IntelFormatter::new();
#[cfg(all(not(feature = "masm"), not(feature = "nasm"), not(feature = "intel"), feature = "gas"))]
let mut formatter = GasFormatter::new();
let mut output = FmtFormatterOutput::new(f);
formatter.format(self, &mut output);
output.result
}
#[cfg(not(any(feature = "gas", feature = "intel", feature = "masm", feature = "nasm")))]
{
let mut formatter = FastFormatter::new();
let mut output = alloc::string::String::new();
formatter.format(self, &mut output);
f.write_str(&output)
}
}
}
struct OpKindIterator {
count: u8,
index: u8,
op_kinds: [OpKind; IcedConstants::MAX_OP_COUNT],
}
impl OpKindIterator {
fn new(instruction: &Instruction) -> Self {
const _: () = assert!(IcedConstants::MAX_OP_COUNT <= core::u8::MAX as usize);
OpKindIterator {
count: instruction.op_count() as u8,
index: 0,
op_kinds: [instruction.op0_kind(), instruction.op1_kind(), instruction.op2_kind(), instruction.op3_kind(), instruction.op4_kind()],
}
}
}
impl Iterator for OpKindIterator {
type Item = OpKind;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let index = self.index;
if index < self.count {
self.index = index + 1;
Some(self.op_kinds[index as usize])
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.count as usize - self.index as usize;
(len, Some(len))
}
}
impl ExactSizeIterator for OpKindIterator {}
impl FusedIterator for OpKindIterator {}
#[cfg(feature = "serde")]
const _: () = {
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::marker::PhantomData;
use serde::de;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
const NUM_FIELDS_READABLE: usize = 13;
const NUM_FIELDS_BINARY: usize = 19;
#[inline]
fn is_human_readable(value: bool) -> bool {
if cfg!(feature = "__internal_flip") {
value ^ true
} else {
value
}
}
impl Serialize for Instruction {
#[allow(clippy::missing_inline_in_public_items)]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if is_human_readable(serializer.is_human_readable()) {
let mut serde_state = serializer.serialize_struct("Instruction", NUM_FIELDS_READABLE)?;
let mut fields = 0;
macro_rules! serialize {
($field:ident) => {
serde_state.serialize_field(stringify!($field), &self.$field)?;
fields += 1;
};
}
serialize!(next_rip);
serialize!(mem_displ);
serialize!(flags1);
serialize!(immediate);
serialize!(code);
serialize!(mem_base_reg);
serialize!(mem_index_reg);
serialize!(regs);
serialize!(op_kinds);
serialize!(scale);
serialize!(displ_size);
serialize!(len);
serialize!(pad);
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
serde_state.end()
} else {
let mut serde_state = serializer.serialize_struct("Instruction", NUM_FIELDS_BINARY)?;
let mut fields = 0;
macro_rules! serialize {
($field:ident) => {
serde_state.serialize_field(stringify!($field), &self.$field)?;
fields += 1;
};
($field:ident, $underlying_ty:ty) => {
serde_state.serialize_field(stringify!($field), &(self.$field as $underlying_ty))?;
fields += 1;
};
($field:ident, $index:literal, $underlying_ty:ty) => {
serde_state.serialize_field(concat!(stringify!($field), stringify!($index)), &(self.$field[$index] as $underlying_ty))?;
fields += 1;
};
}
serialize!(next_rip);
serialize!(mem_displ);
serialize!(flags1);
serialize!(immediate);
serialize!(code, CodeUnderlyingType);
serialize!(mem_base_reg, RegisterUnderlyingType);
serialize!(mem_index_reg, RegisterUnderlyingType);
debug_assert_eq!(self.regs.len(), 4);
serialize!(regs, 0, RegisterUnderlyingType);
serialize!(regs, 1, RegisterUnderlyingType);
serialize!(regs, 2, RegisterUnderlyingType);
serialize!(regs, 3, RegisterUnderlyingType);
debug_assert_eq!(self.op_kinds.len(), 4);
serialize!(op_kinds, 0, OpKindUnderlyingType);
serialize!(op_kinds, 1, OpKindUnderlyingType);
serialize!(op_kinds, 2, OpKindUnderlyingType);
serialize!(op_kinds, 3, OpKindUnderlyingType);
serialize!(scale, InstrScaleUnderlyingType);
serialize!(displ_size);
serialize!(len);
serialize!(pad);
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
serde_state.end()
}
}
}
macro_rules! mk_struct_field_visitor {
() => {
struct StructFieldVisitor;
impl<'de> de::Visitor<'de> for StructFieldVisitor {
type Value = StructField;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("field identifier")
}
#[inline]
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
if let Ok(v) = <usize as TryFrom<_>>::try_from(v) {
if let Ok(value) = <StructField as TryFrom<_>>::try_from(v) {
return Ok(value);
}
}
Err(de::Error::invalid_value(de::Unexpected::Unsigned(v), &"Invalid Instruction field value"))
}
#[inline]
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
StructFieldVisitor::deserialize_name(v.as_bytes())
}
#[inline]
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
StructFieldVisitor::deserialize_name(v)
}
}
impl StructFieldVisitor {
#[inline]
fn deserialize_name<E>(v: &[u8]) -> Result<StructField, E>
where
E: de::Error,
{
for (&name, value) in FIELDS.iter().zip(StructField::values()) {
if name.as_bytes() == v {
return Ok(value);
}
}
Err(de::Error::unknown_field(&String::from_utf8_lossy(v), &["Instruction fields"][..]))
}
}
impl<'de> Deserialize<'de> for StructField {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_identifier(StructFieldVisitor)
}
}
};
}
impl<'de> Deserialize<'de> for Instruction {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if is_human_readable(deserializer.is_human_readable()) {
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum StructField {
next_rip,
mem_displ,
flags1,
immediate,
code,
mem_base_reg,
mem_index_reg,
regs,
op_kinds,
scale,
displ_size,
len,
pad,
}
const _: () = assert!(StructField::pad as usize + 1 == NUM_FIELDS_READABLE);
const FIELDS: [&str; NUM_FIELDS_READABLE] = [
"next_rip",
"mem_displ",
"flags1",
"immediate",
"code",
"mem_base_reg",
"mem_index_reg",
"regs",
"op_kinds",
"scale",
"displ_size",
"len",
"pad",
];
impl StructField {
#[inline]
fn values() -> impl Iterator<Item = StructField> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {
(0..NUM_FIELDS_READABLE).map(|x| unsafe { mem::transmute::<u8, StructField>(x as u8) })
}
}
impl TryFrom<usize> for StructField {
type Error = &'static str;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < NUM_FIELDS_READABLE {
Ok(unsafe { mem::transmute(value as u8) })
} else {
Err("Invalid struct field")
}
}
}
mk_struct_field_visitor!();
struct Visitor<'de> {
marker: PhantomData<Instruction>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = Instruction;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct Instruction")
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::mixed_read_write_in_expression)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut fields = 0;
macro_rules! next_element {
($field_name:ident : $field_ty:ty) => {{
fields += 1;
match seq.next_element::<$field_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
}
}};
}
let instruction = Instruction {
next_rip: next_element!(next_rip: u64),
mem_displ: next_element!(mem_displ: u64),
flags1: next_element!(flags1: u32),
immediate: next_element!(immediate: u32),
code: next_element!(code: Code),
mem_base_reg: next_element!(mem_base_reg: Register),
mem_index_reg: next_element!(mem_index_reg: Register),
regs: next_element!(regs: [Register; 4]),
op_kinds: next_element!(op_kinds: [OpKind; 4]),
scale: next_element!(scale: InstrScale),
displ_size: next_element!(displ_size: u8),
len: next_element!(len: u8),
pad: next_element!(pad: u8),
};
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
Ok(instruction)
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::mixed_read_write_in_expression)]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut next_rip: Option<u64> = None;
let mut mem_displ: Option<u64> = None;
let mut flags1: Option<u32> = None;
let mut immediate: Option<u32> = None;
let mut code: Option<Code> = None;
let mut mem_base_reg: Option<Register> = None;
let mut mem_index_reg: Option<Register> = None;
let mut regs: Option<[Register; 4]> = None;
let mut op_kinds: Option<[OpKind; 4]> = None;
let mut scale: Option<InstrScale> = None;
let mut displ_size: Option<u8> = None;
let mut len: Option<u8> = None;
let mut pad: Option<u8> = None;
while let Some(field) = map.next_key::<StructField>()? {
macro_rules! unpack {
($field:ident : $field_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
$field = Some(map.next_value::<$field_ty>()?);
}};
}
match field {
StructField::next_rip => unpack!(next_rip: u64),
StructField::mem_displ => unpack!(mem_displ: u64),
StructField::flags1 => unpack!(flags1: u32),
StructField::immediate => unpack!(immediate: u32),
StructField::code => unpack!(code: Code),
StructField::mem_base_reg => unpack!(mem_base_reg: Register),
StructField::mem_index_reg => unpack!(mem_index_reg: Register),
StructField::regs => unpack!(regs: [Register; 4]),
StructField::op_kinds => unpack!(op_kinds: [OpKind; 4]),
StructField::scale => unpack!(scale: InstrScale),
StructField::displ_size => unpack!(displ_size: u8),
StructField::len => unpack!(len: u8),
StructField::pad => unpack!(pad: u8),
}
}
let mut fields = 0;
macro_rules! unpack_field {
($field:ident) => {{
fields += 1;
match $field {
Some(value) => value,
None => return Err(<A::Error as de::Error>::missing_field(stringify!($field))),
}
}};
}
let instruction = Instruction {
next_rip: unpack_field!(next_rip),
mem_displ: unpack_field!(mem_displ),
flags1: unpack_field!(flags1),
immediate: unpack_field!(immediate),
code: unpack_field!(code),
mem_base_reg: unpack_field!(mem_base_reg),
mem_index_reg: unpack_field!(mem_index_reg),
regs: unpack_field!(regs),
op_kinds: unpack_field!(op_kinds),
scale: unpack_field!(scale),
displ_size: unpack_field!(displ_size),
len: unpack_field!(len),
pad: unpack_field!(pad),
};
debug_assert_eq!(fields, NUM_FIELDS_READABLE);
Ok(instruction)
}
}
deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::<Instruction>, lifetime: PhantomData })
} else {
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
#[allow(dead_code)]
enum StructField {
next_rip,
mem_displ,
flags1,
immediate,
code,
mem_base_reg,
mem_index_reg,
regs0,
regs1,
regs2,
regs3,
op_kinds0,
op_kinds1,
op_kinds2,
op_kinds3,
scale,
displ_size,
len,
pad,
}
const _: () = assert!(StructField::pad as usize + 1 == NUM_FIELDS_BINARY);
const FIELDS: [&str; NUM_FIELDS_BINARY] = [
"next_rip",
"mem_displ",
"flags1",
"immediate",
"code",
"mem_base_reg",
"mem_index_reg",
"regs0",
"regs1",
"regs2",
"regs3",
"op_kinds0",
"op_kinds1",
"op_kinds2",
"op_kinds3",
"scale",
"displ_size",
"len",
"pad",
];
impl StructField {
#[inline]
fn values() -> impl Iterator<Item = StructField> + DoubleEndedIterator + ExactSizeIterator + FusedIterator {
(0..NUM_FIELDS_BINARY).map(|x| unsafe { mem::transmute::<u8, StructField>(x as u8) })
}
}
impl TryFrom<usize> for StructField {
type Error = &'static str;
#[inline]
fn try_from(value: usize) -> Result<Self, Self::Error> {
if value < NUM_FIELDS_BINARY {
Ok(unsafe { mem::transmute(value as u8) })
} else {
Err("Invalid struct field")
}
}
}
mk_struct_field_visitor!();
struct Visitor<'de> {
marker: PhantomData<Instruction>,
lifetime: PhantomData<&'de ()>,
}
impl<'de> de::Visitor<'de> for Visitor<'de> {
type Value = Instruction;
#[inline]
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("struct Instruction")
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::mixed_read_write_in_expression)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut fields = 0;
macro_rules! next_element {
($field_name:ident : $field_ty:ty) => {{
fields += 1;
match seq.next_element::<$field_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
}
}};
($field_name:ident : $field_ty:ty : $underlying_ty:ty) => {{
fields += 1;
let value = match seq.next_element::<$underlying_ty>()? {
Some(value) => value,
None => return Err(de::Error::invalid_length(fields, &"struct Instruction with all its fields")),
};
if let Ok(enum_value) = <$field_ty as TryFrom<usize>>::try_from(value as usize) {
enum_value
} else {
return Err(<A::Error as de::Error>::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"an enum variant in range",
));
}
}};
}
let instruction = Instruction {
next_rip: next_element!(next_rip: u64),
mem_displ: next_element!(mem_displ: u64),
flags1: next_element!(flags1: u32),
immediate: next_element!(immediate: u32),
code: next_element!(code: Code: CodeUnderlyingType),
mem_base_reg: next_element!(mem_base_reg: Register: RegisterUnderlyingType),
mem_index_reg: next_element!(mem_index_reg: Register: RegisterUnderlyingType),
regs: [
next_element!(regs0: Register: RegisterUnderlyingType),
next_element!(regs1: Register: RegisterUnderlyingType),
next_element!(regs2: Register: RegisterUnderlyingType),
next_element!(regs3: Register: RegisterUnderlyingType),
],
op_kinds: [
next_element!(op_kinds0: OpKind: OpKindUnderlyingType),
next_element!(op_kinds1: OpKind: OpKindUnderlyingType),
next_element!(op_kinds2: OpKind: OpKindUnderlyingType),
next_element!(op_kinds3: OpKind: OpKindUnderlyingType),
],
scale: next_element!(scale: InstrScale: InstrScaleUnderlyingType),
displ_size: next_element!(displ_size: u8),
len: next_element!(len: u8),
pad: next_element!(pad: u8),
};
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
Ok(instruction)
}
#[allow(clippy::missing_inline_in_public_items)]
#[allow(clippy::mixed_read_write_in_expression)]
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut next_rip: Option<u64> = None;
let mut mem_displ: Option<u64> = None;
let mut flags1: Option<u32> = None;
let mut immediate: Option<u32> = None;
let mut code: Option<Code> = None;
let mut mem_base_reg: Option<Register> = None;
let mut mem_index_reg: Option<Register> = None;
let mut regs0: Option<Register> = None;
let mut regs1: Option<Register> = None;
let mut regs2: Option<Register> = None;
let mut regs3: Option<Register> = None;
let mut op_kinds0: Option<OpKind> = None;
let mut op_kinds1: Option<OpKind> = None;
let mut op_kinds2: Option<OpKind> = None;
let mut op_kinds3: Option<OpKind> = None;
let mut scale: Option<InstrScale> = None;
let mut displ_size: Option<u8> = None;
let mut len: Option<u8> = None;
let mut pad: Option<u8> = None;
while let Some(field) = map.next_key::<StructField>()? {
macro_rules! unpack {
($field:ident : $field_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
$field = Some(map.next_value::<$field_ty>()?);
}};
($field:ident : $field_ty:ty : $underlying_ty:ty) => {{
if $field.is_some() {
return Err(<A::Error as de::Error>::duplicate_field(stringify!($field)));
}
let value = map.next_value::<$underlying_ty>()?;
if let Ok(enum_value) = <$field_ty as TryFrom<usize>>::try_from(value as usize) {
$field = Some(enum_value);
} else {
return Err(<A::Error as de::Error>::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"an enum variant in range",
));
}
}};
}
match field {
StructField::next_rip => unpack!(next_rip: u64),
StructField::mem_displ => unpack!(mem_displ: u64),
StructField::flags1 => unpack!(flags1: u32),
StructField::immediate => unpack!(immediate: u32),
StructField::code => unpack!(code: Code: CodeUnderlyingType),
StructField::mem_base_reg => unpack!(mem_base_reg: Register: RegisterUnderlyingType),
StructField::mem_index_reg => unpack!(mem_index_reg: Register: RegisterUnderlyingType),
StructField::regs0 => unpack!(regs0: Register: RegisterUnderlyingType),
StructField::regs1 => unpack!(regs1: Register: RegisterUnderlyingType),
StructField::regs2 => unpack!(regs2: Register: RegisterUnderlyingType),
StructField::regs3 => unpack!(regs3: Register: RegisterUnderlyingType),
StructField::op_kinds0 => unpack!(op_kinds0: OpKind: OpKindUnderlyingType),
StructField::op_kinds1 => unpack!(op_kinds1: OpKind: OpKindUnderlyingType),
StructField::op_kinds2 => unpack!(op_kinds2: OpKind: OpKindUnderlyingType),
StructField::op_kinds3 => unpack!(op_kinds3: OpKind: OpKindUnderlyingType),
StructField::scale => unpack!(scale: InstrScale: InstrScaleUnderlyingType),
StructField::displ_size => unpack!(displ_size: u8),
StructField::len => unpack!(len: u8),
StructField::pad => unpack!(pad: u8),
}
}
let mut fields = 0;
macro_rules! unpack_field {
($field:ident) => {{
fields += 1;
match $field {
Some(value) => value,
None => return Err(<A::Error as de::Error>::missing_field(stringify!($field))),
}
}};
}
let instruction = Instruction {
next_rip: unpack_field!(next_rip),
mem_displ: unpack_field!(mem_displ),
flags1: unpack_field!(flags1),
immediate: unpack_field!(immediate),
code: unpack_field!(code),
mem_base_reg: unpack_field!(mem_base_reg),
mem_index_reg: unpack_field!(mem_index_reg),
regs: [unpack_field!(regs0), unpack_field!(regs1), unpack_field!(regs2), unpack_field!(regs3)],
op_kinds: [unpack_field!(op_kinds0), unpack_field!(op_kinds1), unpack_field!(op_kinds2), unpack_field!(op_kinds3)],
scale: unpack_field!(scale),
displ_size: unpack_field!(displ_size),
len: unpack_field!(len),
pad: unpack_field!(pad),
};
debug_assert_eq!(fields, NUM_FIELDS_BINARY);
Ok(instruction)
}
}
deserializer.deserialize_struct("Instruction", &FIELDS[..], Visitor { marker: PhantomData::<Instruction>, lifetime: PhantomData })
}
}
}
};