// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

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

// GENERATOR-BEGIN: InstrFlags1
// ⚠️This was generated by GENERATOR!🦹‍♂️
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;
}
// GENERATOR-END: InstrFlags1

// GENERATOR-BEGIN: MvexInstrFlags
// ⚠️This was generated by GENERATOR!🦹‍♂️
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;
}
// GENERATOR-END: MvexInstrFlags

/// A 16/32/64-bit x86 instruction. Created by [`Decoder`], by [`CodeAssembler`] or by `Instruction::with*()` methods.
///
/// [`CodeAssembler`]: code_asm/struct.CodeAssembler.html
/// [`Decoder`]: struct.Decoder.html
#[derive(Debug, Default, Copy, Clone)]
pub struct Instruction {
	pub(crate) next_rip: u64,
	pub(crate) mem_displ: u64,
	pub(crate) flags1: u32, // InstrFlags1
	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 {
	/// Creates an empty `Instruction` (all fields are cleared). See also the `with_*()` constructor methods.
	#[must_use]
	#[inline]
	pub fn new() -> Self {
		Instruction::default()
	}

	/// Checks if two instructions are equal, comparing all bits, not ignoring anything. `==` ignores some fields.
	#[must_use]
	#[allow(trivial_casts)]
	#[allow(clippy::missing_inline_in_public_items)]
	pub fn eq_all_bits(&self, other: &Self) -> bool {
		// The compiler generated better code than if we compare all fields one at a time.
		// SAFETY: see `slice::from_raw_parts()`
		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
		}
	}

	/// Gets the 16-bit IP of the instruction, see also [`next_ip16()`]
	///
	/// [`next_ip16()`]: #method.next_ip16
	#[must_use]
	#[inline]
	pub const fn ip16(&self) -> u16 {
		(self.next_rip as u16).wrapping_sub(self.len() as u16)
	}

	/// Sets the 16-bit IP of the instruction, see also [`set_next_ip16()`]
	///
	/// [`set_next_ip16()`]: #method.set_next_ip16
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_ip16(&mut self, new_value: u16) {
		self.next_rip = (new_value as u64).wrapping_add(self.len() as u64);
	}

	/// Gets the 32-bit IP of the instruction, see also [`next_ip32()`]
	///
	/// [`next_ip32()`]: #method.next_ip32
	#[must_use]
	#[inline]
	pub const fn ip32(&self) -> u32 {
		(self.next_rip as u32).wrapping_sub(self.len() as u32)
	}

	/// Sets the 32-bit IP of the instruction, see also [`set_next_ip32()`]
	///
	/// [`set_next_ip32()`]: #method.set_next_ip32
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_ip32(&mut self, new_value: u32) {
		self.next_rip = (new_value as u64).wrapping_add(self.len() as u64);
	}

	/// Gets the 64-bit IP of the instruction, see also [`next_ip()`]
	///
	/// [`next_ip()`]: #method.next_ip
	#[must_use]
	#[inline]
	pub const fn ip(&self) -> u64 {
		self.next_rip.wrapping_sub(self.len() as u64)
	}

	/// Sets the 64-bit IP of the instruction, see also [`set_next_ip()`]
	///
	/// [`set_next_ip()`]: #method.set_next_ip
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_ip(&mut self, new_value: u64) {
		self.next_rip = new_value.wrapping_add(self.len() as u64);
	}

	/// Gets the 16-bit IP of the next instruction, see also [`ip16()`]
	///
	/// [`ip16()`]: #method.ip16
	#[must_use]
	#[inline]
	pub const fn next_ip16(&self) -> u16 {
		self.next_rip as u16
	}

	/// Sets the 16-bit IP of the next instruction, see also [`set_ip16()`]
	///
	/// [`set_ip16()`]: #method.set_ip16
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_next_ip16(&mut self, new_value: u16) {
		self.next_rip = new_value as u64;
	}

	/// Gets the 32-bit IP of the next instruction, see also [`ip32()`]
	///
	/// [`ip32()`]: #method.ip32
	#[must_use]
	#[inline]
	pub const fn next_ip32(&self) -> u32 {
		self.next_rip as u32
	}

	/// Sets the 32-bit IP of the next instruction, see also [`set_ip32()`]
	///
	/// [`set_ip32()`]: #method.set_ip32
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_next_ip32(&mut self, new_value: u32) {
		self.next_rip = new_value as u64;
	}

	/// Gets the 64-bit IP of the next instruction, see also [`ip()`]
	///
	/// [`ip()`]: #method.ip
	#[must_use]
	#[inline]
	pub const fn next_ip(&self) -> u64 {
		self.next_rip
	}

	/// Sets the 64-bit IP of the next instruction, see also [`set_ip()`]
	///
	/// [`set_ip()`]: #method.set_ip
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_next_ip(&mut self, new_value: u64) {
		self.next_rip = new_value;
	}

	/// Gets the code size when the instruction was decoded. This value is informational and can
	/// be used by a formatter.
	#[must_use]
	#[inline]
	pub fn code_size(&self) -> CodeSize {
		unsafe { mem::transmute(((self.flags1 >> InstrFlags1::CODE_SIZE_SHIFT) & InstrFlags1::CODE_SIZE_MASK) as CodeSizeUnderlyingType) }
	}

	/// Sets the code size when the instruction was decoded. This value is informational and can
	/// be used by a formatter.
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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);
	}

	/// Checks if it's an invalid instruction ([`code()`] == [`Code::INVALID`])
	///
	/// [`code()`]: #method.code
	/// [`Code::INVALID`]: enum.Code.html#variant.INVALID
	#[must_use]
	#[inline]
	pub fn is_invalid(&self) -> bool {
		self.code == Code::INVALID
	}

	/// Gets the instruction code, see also [`mnemonic()`]
	///
	/// [`mnemonic()`]: #method.mnemonic
	#[must_use]
	#[inline]
	pub const fn code(&self) -> Code {
		self.code
	}

	/// Sets the instruction code
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_code(&mut self, new_value: Code) {
		self.code = new_value
	}

	/// Gets the mnemonic, see also [`code()`]
	///
	/// [`code()`]: #method.code
	#[must_use]
	#[inline]
	pub fn mnemonic(&self) -> Mnemonic {
		self.code().mnemonic()
	}

	/// Gets the operand count. An instruction can have 0-5 operands.
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // add [rax],ebx
	/// let bytes = b"\x01\x18";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// assert_eq!(instr.op_count(), 2);
	/// ```
	#[must_use]
	#[inline]
	pub fn op_count(&self) -> u32 {
		instruction_op_counts::OP_COUNT[self.code() as usize] as u32
	}

	/// Gets the length of the instruction, 0-15 bytes. This is just informational. If you modify the instruction
	/// or create a new one, this method could return the wrong value.
	#[must_use]
	#[inline]
	pub const fn len(&self) -> usize {
		self.len as usize
	}

	/// Sets the length of the instruction, 0-15 bytes. This is just informational. If you modify the instruction
	/// or create a new one, this method could return the wrong value.
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_len(&mut self, new_value: usize) {
		self.len = new_value as u8
	}

	#[inline]
	fn is_xacquire_instr(&self) -> bool {
		// This method can return true even if it's not an xacquire/xrelease instruction. This only happens if
		// it has an invalid LOCK prefix though.
		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 {
		// This method can return true even if it's not an xacquire/xrelease instruction. This only happens if
		// it has an invalid LOCK prefix though.
		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
			)
		}
	}

	/// `true` if the instruction has the `XACQUIRE` prefix (`F2`)
	#[must_use]
	#[inline]
	pub fn has_xacquire_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::REPNE_PREFIX) != 0 && self.is_xacquire_instr()
	}

	/// `true` if the instruction has the `XACQUIRE` prefix (`F2`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// `true` if the instruction has the `XRELEASE` prefix (`F3`)
	#[must_use]
	#[inline]
	pub fn has_xrelease_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::REPE_PREFIX) != 0 && self.is_xrelease_instr()
	}

	/// `true` if the instruction has the `XRELEASE` prefix (`F3`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// `true` if the instruction has the `REPE` or `REP` prefix (`F3`)
	#[must_use]
	#[inline]
	pub const fn has_rep_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::REPE_PREFIX) != 0
	}

	/// `true` if the instruction has the `REPE` or `REP` prefix (`F3`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// `true` if the instruction has the `REPE` or `REP` prefix (`F3`)
	#[must_use]
	#[inline]
	pub const fn has_repe_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::REPE_PREFIX) != 0
	}

	/// `true` if the instruction has the `REPE` or `REP` prefix (`F3`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// `true` if the instruction has the `REPNE` prefix (`F2`)
	#[must_use]
	#[inline]
	pub const fn has_repne_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::REPNE_PREFIX) != 0
	}

	/// `true` if the instruction has the `REPNE` prefix (`F2`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// `true` if the instruction has the `LOCK` prefix (`F0`)
	#[must_use]
	#[inline]
	pub const fn has_lock_prefix(&self) -> bool {
		(self.flags1 & InstrFlags1::LOCK_PREFIX) != 0
	}

	/// `true` if the instruction has the `LOCK` prefix (`F0`)
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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;
		}
	}

	/// Gets operand #0's kind if the operand exists (see [`op_count()`] and [`try_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_op_kind()`]: #method.try_op_kind
	#[must_use]
	#[inline]
	pub const fn op0_kind(&self) -> OpKind {
		self.op_kinds[0]
	}

	/// Sets operand #0's kind if the operand exists (see [`op_count()`] and [`try_set_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_set_op_kind()`]: #method.try_set_op_kind
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_op0_kind(&mut self, new_value: OpKind) {
		self.op_kinds[0] = new_value
	}

	/// Gets operand #1's kind if the operand exists (see [`op_count()`] and [`try_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_op_kind()`]: #method.try_op_kind
	#[must_use]
	#[inline]
	pub const fn op1_kind(&self) -> OpKind {
		self.op_kinds[1]
	}

	/// Sets operand #1's kind if the operand exists (see [`op_count()`] and [`try_set_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_set_op_kind()`]: #method.try_set_op_kind
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_op1_kind(&mut self, new_value: OpKind) {
		self.op_kinds[1] = new_value
	}

	/// Gets operand #2's kind if the operand exists (see [`op_count()`] and [`try_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_op_kind()`]: #method.try_op_kind
	#[must_use]
	#[inline]
	pub const fn op2_kind(&self) -> OpKind {
		self.op_kinds[2]
	}

	/// Sets operand #2's kind if the operand exists (see [`op_count()`] and [`try_set_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_set_op_kind()`]: #method.try_set_op_kind
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_op2_kind(&mut self, new_value: OpKind) {
		self.op_kinds[2] = new_value
	}

	/// Gets operand #3's kind if the operand exists (see [`op_count()`] and [`try_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_op_kind()`]: #method.try_op_kind
	#[must_use]
	#[inline]
	pub const fn op3_kind(&self) -> OpKind {
		self.op_kinds[3]
	}

	/// Sets operand #3's kind if the operand exists (see [`op_count()`] and [`try_set_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_set_op_kind()`]: #method.try_set_op_kind
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[inline]
	pub fn set_op3_kind(&mut self, new_value: OpKind) {
		self.op_kinds[3] = new_value
	}

	/// Gets operand #4's kind if the operand exists (see [`op_count()`] and [`try_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_op_kind()`]: #method.try_op_kind
	#[allow(clippy::unused_self)]
	#[must_use]
	#[inline]
	pub const fn op4_kind(&self) -> OpKind {
		OpKind::Immediate8
	}

	/// Sets operand #4's kind if the operand exists (see [`op_count()`] and [`try_set_op_kind()`])
	///
	/// [`op_count()`]: #method.op_count
	/// [`try_set_op_kind()`]: #method.try_set_op_kind
	///
	/// # Arguments
	///
	/// * `new_value`: new value
	#[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(())
		}
	}

	/// Gets all op kinds ([`op_count()`] values)
	///
	/// [`op_count()`]: #method.op_count
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // add [rax],ebx
	/// let bytes = b"\x01\x18";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// for (i, op_kind) in instr.op_kinds().enumerate() {
	///     println!("op kind #{} = {:?}", i, op_kind);
	/// }
	/// ```
	#[inline]
	pub fn op_kinds(&self) -> impl Iterator<Item = OpKind> + ExactSizeIterator + FusedIterator {
		OpKindIterator::new(self)
	}

	/// Gets an operand's kind if it exists (see [`op_count()`])
	///
	/// [`op_count()`]: #method.op_count
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // add [rax],ebx
	/// let bytes = b"\x01\x18";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// assert_eq!(instr.op_count(), 2);
	/// assert_eq!(instr.op_kind(0), OpKind::Memory);
	/// assert_eq!(instr.memory_base(), Register::RAX);
	/// assert_eq!(instr.memory_index(), Register::None);
	/// assert_eq!(instr.op_kind(1), OpKind::Register);
	/// assert_eq!(instr.op_register(1), Register::EBX);
	/// ```
	#[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"))
		}
	}

	/// Sets an operand's kind
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `op_kind`: Operand kind
	#[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"))
		}
	}

	/// Checks if the instruction has a segment override prefix, see [`segment_prefix()`]
	///
	/// [`segment_prefix()`]: #method.segment_prefix
	#[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
	}

	/// Gets the segment override prefix or [`Register::None`] if none. See also [`memory_segment()`].
	/// Use this method if the operand has kind [`OpKind::Memory`],
	/// [`OpKind::MemorySegSI`], [`OpKind::MemorySegESI`], [`OpKind::MemorySegRSI`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`memory_segment()`]: #method.memory_segment
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	/// [`OpKind::MemorySegSI`]: enum.OpKind.html#variant.MemorySegSI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegRSI`]: enum.OpKind.html#variant.MemorySegRSI
	#[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 {
			// SAFETY: ES+index is a valid enum variant, see above const assert!()'s
			unsafe { mem::transmute((Register::ES as u32 + index) as RegisterUnderlyingType) }
		} else {
			Register::None
		}
	}

	/// Sets the segment override prefix or [`Register::None`] if none. See also [`memory_segment()`].
	/// Use this method if the operand has kind [`OpKind::Memory`],
	/// [`OpKind::MemorySegSI`], [`OpKind::MemorySegESI`], [`OpKind::MemorySegRSI`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`memory_segment()`]: #method.memory_segment
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	/// [`OpKind::MemorySegSI`]: enum.OpKind.html#variant.MemorySegSI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegRSI`]: enum.OpKind.html#variant.MemorySegRSI
	///
	/// # Arguments
	///
	/// * `new_value`: Segment register prefix
	#[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);
	}

	/// Gets the effective segment register used to reference the memory location.
	/// Use this method if the operand has kind [`OpKind::Memory`],
	/// [`OpKind::MemorySegSI`], [`OpKind::MemorySegESI`], [`OpKind::MemorySegRSI`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	/// [`OpKind::MemorySegSI`]: enum.OpKind.html#variant.MemorySegSI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegRSI`]: enum.OpKind.html#variant.MemorySegRSI
	#[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,
		}
	}

	/// Gets the size of the memory displacement in bytes. Valid values are `0`, `1` (16/32/64-bit), `2` (16-bit), `4` (32-bit), `8` (64-bit).
	/// Note that the return value can be 1 and [`memory_displacement64()`] may still not fit in
	/// a signed byte if it's an EVEX/MVEX encoded instruction.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`memory_displacement64()`]: #method.memory_displacement64
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[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
		}
	}

	/// Sets the size of the memory displacement in bytes. Valid values are `0`, `1` (16/32/64-bit), `2` (16-bit), `4` (32-bit), `8` (64-bit).
	/// Note that the return value can be 1 and [`memory_displacement64()`] may still not fit in
	/// a signed byte if it's an EVEX/MVEX encoded instruction.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`memory_displacement64()`]: #method.memory_displacement64
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: Displacement size
	#[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,
		};
	}

	/// `true` if the data is broadcast (EVEX instructions only)
	#[must_use]
	#[inline]
	pub const fn is_broadcast(&self) -> bool {
		(self.flags1 & InstrFlags1::BROADCAST) != 0
	}

	/// Sets the is broadcast flag (EVEX instructions only)
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_is_broadcast(&mut self, new_value: bool) {
		if new_value {
			self.flags1 |= InstrFlags1::BROADCAST;
		} else {
			self.flags1 &= !InstrFlags1::BROADCAST;
		}
	}

	/// `true` if eviction hint bit is set (`{eh}`) (MVEX instructions only)
	#[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
	}

	/// `true` if eviction hint bit is set (`{eh}`) (MVEX instructions only)
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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;
		}
	}

	/// (MVEX) Register/memory operand conversion function
	#[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()
		}
	}

	/// (MVEX) Register/memory operand conversion function
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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);
	}

	/// Gets the size of the memory location that is referenced by the operand. See also [`is_broadcast()`].
	/// Use this method if the operand has kind [`OpKind::Memory`],
	/// [`OpKind::MemorySegSI`], [`OpKind::MemorySegESI`], [`OpKind::MemorySegRSI`],
	/// [`OpKind::MemoryESDI`], [`OpKind::MemoryESEDI`], [`OpKind::MemoryESRDI`]
	///
	/// [`is_broadcast()`]: #method.is_broadcast
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	/// [`OpKind::MemorySegSI`]: enum.OpKind.html#variant.MemorySegSI
	/// [`OpKind::MemorySegESI`]: enum.OpKind.html#variant.MemorySegESI
	/// [`OpKind::MemorySegRSI`]: enum.OpKind.html#variant.MemorySegRSI
	/// [`OpKind::MemoryESDI`]: enum.OpKind.html#variant.MemoryESDI
	/// [`OpKind::MemoryESEDI`]: enum.OpKind.html#variant.MemoryESEDI
	/// [`OpKind::MemoryESRDI`]: enum.OpKind.html#variant.MemoryESRDI
	#[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]
		}
	}

	/// Gets the index register scale value, valid values are `*1`, `*2`, `*4`, `*8`. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[must_use]
	#[inline]
	pub const fn memory_index_scale(&self) -> u32 {
		1 << (self.scale as u8)
	}

	/// Sets the index register scale value, valid values are `*1`, `*2`, `*4`, `*8`. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: New value (1, 2, 4 or 8)
	#[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;
			}
		}
	}

	/// Gets the memory operand's displacement or the 32-bit absolute address if it's
	/// an `EIP` or `RIP` relative memory operand.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[must_use]
	#[inline]
	pub const fn memory_displacement32(&self) -> u32 {
		self.mem_displ as u32
	}

	/// Gets the memory operand's displacement or the 32-bit absolute address if it's
	/// an `EIP` or `RIP` relative memory operand.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_memory_displacement32(&mut self, new_value: u32) {
		self.mem_displ = new_value as u64;
	}

	/// Gets the memory operand's displacement or the 64-bit absolute address if it's
	/// an `EIP` or `RIP` relative memory operand.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[must_use]
	#[inline]
	pub const fn memory_displacement64(&self) -> u64 {
		self.mem_displ
	}

	/// Gets the memory operand's displacement or the 64-bit absolute address if it's
	/// an `EIP` or `RIP` relative memory operand.
	/// Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_memory_displacement64(&mut self, new_value: u64) {
		self.mem_displ = new_value;
	}

	/// Tries to get an operand's immediate value.
	/// Can only be called if the operand has kind [`OpKind::Immediate8`],
	/// [`OpKind::Immediate8_2nd`], [`OpKind::Immediate16`], [`OpKind::Immediate32`],
	/// [`OpKind::Immediate64`], [`OpKind::Immediate8to16`], [`OpKind::Immediate8to32`],
	/// [`OpKind::Immediate8to64`], [`OpKind::Immediate32to64`]
	///
	/// # Errors
	///
	/// - Fails if the operand is not one of those listed above
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	#[allow(clippy::missing_inline_in_public_items)]
	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")),
		})
	}

	/// Gets an operand's immediate value
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	#[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
			}
		}
	}

	/// Sets an operand's immediate value
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `new_value`: Immediate
	#[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)
	}

	/// Sets an operand's immediate value
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `new_value`: Immediate
	#[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)
	}

	/// Sets an operand's immediate value
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `new_value`: Immediate
	#[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)
	}

	/// Sets an operand's immediate value
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `new_value`: Immediate
	#[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(())
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8`]
	///
	/// [`OpKind::Immediate8`]: enum.OpKind.html#variant.Immediate8
	#[must_use]
	#[inline]
	pub const fn immediate8(&self) -> u8 {
		self.immediate as u8
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8`]
	///
	/// [`OpKind::Immediate8`]: enum.OpKind.html#variant.Immediate8
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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;
		}
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8_2nd`]
	///
	/// [`OpKind::Immediate8_2nd`]: enum.OpKind.html#variant.Immediate8_2nd
	#[must_use]
	#[inline]
	pub const fn immediate8_2nd(&self) -> u8 {
		self.mem_displ as u8
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8_2nd`]
	///
	/// [`OpKind::Immediate8_2nd`]: enum.OpKind.html#variant.Immediate8_2nd
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate8_2nd(&mut self, new_value: u8) {
		self.mem_displ = new_value as u64;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate16`]
	///
	/// [`OpKind::Immediate16`]: enum.OpKind.html#variant.Immediate16
	#[must_use]
	#[inline]
	pub const fn immediate16(&self) -> u16 {
		self.immediate as u16
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate16`]
	///
	/// [`OpKind::Immediate16`]: enum.OpKind.html#variant.Immediate16
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate16(&mut self, new_value: u16) {
		self.immediate = new_value as u32;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate32`]
	///
	/// [`OpKind::Immediate32`]: enum.OpKind.html#variant.Immediate32
	#[must_use]
	#[inline]
	pub const fn immediate32(&self) -> u32 {
		self.immediate
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate32`]
	///
	/// [`OpKind::Immediate32`]: enum.OpKind.html#variant.Immediate32
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate32(&mut self, new_value: u32) {
		self.immediate = new_value;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate64`]
	///
	/// [`OpKind::Immediate64`]: enum.OpKind.html#variant.Immediate64
	#[must_use]
	#[inline]
	pub const fn immediate64(&self) -> u64 {
		(self.mem_displ << 32) | (self.immediate as u64)
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate64`]
	///
	/// [`OpKind::Immediate64`]: enum.OpKind.html#variant.Immediate64
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate64(&mut self, new_value: u64) {
		self.immediate = new_value as u32;
		self.mem_displ = new_value >> 32;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to16`]
	///
	/// [`OpKind::Immediate8to16`]: enum.OpKind.html#variant.Immediate8to16
	#[must_use]
	#[inline]
	pub const fn immediate8to16(&self) -> i16 {
		self.immediate as i8 as i16
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to16`]
	///
	/// [`OpKind::Immediate8to16`]: enum.OpKind.html#variant.Immediate8to16
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate8to16(&mut self, new_value: i16) {
		self.immediate = new_value as i8 as u32;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to32`]
	///
	/// [`OpKind::Immediate8to32`]: enum.OpKind.html#variant.Immediate8to32
	#[must_use]
	#[inline]
	pub const fn immediate8to32(&self) -> i32 {
		self.immediate as i8 as i32
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to32`]
	///
	/// [`OpKind::Immediate8to32`]: enum.OpKind.html#variant.Immediate8to32
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate8to32(&mut self, new_value: i32) {
		self.immediate = new_value as i8 as u32;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to64`]
	///
	/// [`OpKind::Immediate8to64`]: enum.OpKind.html#variant.Immediate8to64
	#[must_use]
	#[inline]
	pub const fn immediate8to64(&self) -> i64 {
		self.immediate as i8 as i64
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate8to64`]
	///
	/// [`OpKind::Immediate8to64`]: enum.OpKind.html#variant.Immediate8to64
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate8to64(&mut self, new_value: i64) {
		self.immediate = new_value as i8 as u32;
	}

	/// Gets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate32to64`]
	///
	/// [`OpKind::Immediate32to64`]: enum.OpKind.html#variant.Immediate32to64
	#[must_use]
	#[inline]
	pub const fn immediate32to64(&self) -> i64 {
		self.immediate as i32 as i64
	}

	/// Sets the operand's immediate value. Use this method if the operand has kind [`OpKind::Immediate32to64`]
	///
	/// [`OpKind::Immediate32to64`]: enum.OpKind.html#variant.Immediate32to64
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_immediate32to64(&mut self, new_value: i64) {
		self.immediate = new_value as u32;
	}

	/// Gets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch16`]
	///
	/// [`OpKind::NearBranch16`]: enum.OpKind.html#variant.NearBranch16
	#[must_use]
	#[inline]
	pub const fn near_branch16(&self) -> u16 {
		self.mem_displ as u16
	}

	/// Sets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch16`]
	///
	/// [`OpKind::NearBranch16`]: enum.OpKind.html#variant.NearBranch16
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_near_branch16(&mut self, new_value: u16) {
		self.mem_displ = new_value as u64;
	}

	/// Gets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch32`]
	///
	/// [`OpKind::NearBranch32`]: enum.OpKind.html#variant.NearBranch32
	#[must_use]
	#[inline]
	pub const fn near_branch32(&self) -> u32 {
		self.mem_displ as u32
	}

	/// Sets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch32`]
	///
	/// [`OpKind::NearBranch32`]: enum.OpKind.html#variant.NearBranch32
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_near_branch32(&mut self, new_value: u32) {
		self.mem_displ = new_value as u64;
	}

	/// Gets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch64`]
	///
	/// [`OpKind::NearBranch64`]: enum.OpKind.html#variant.NearBranch64
	#[must_use]
	#[inline]
	pub const fn near_branch64(&self) -> u64 {
		self.mem_displ
	}

	/// Sets the operand's branch target. Use this method if the operand has kind [`OpKind::NearBranch64`]
	///
	/// [`OpKind::NearBranch64`]: enum.OpKind.html#variant.NearBranch64
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_near_branch64(&mut self, new_value: u64) {
		self.mem_displ = new_value
	}

	/// Gets the near branch target if it's a `CALL`/`JMP`/`Jcc` near branch instruction
	/// (i.e., if [`op0_kind()`] is [`OpKind::NearBranch16`], [`OpKind::NearBranch32`] or [`OpKind::NearBranch64`])
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`OpKind::NearBranch16`]: enum.OpKind.html#variant.NearBranch16
	/// [`OpKind::NearBranch32`]: enum.OpKind.html#variant.NearBranch32
	/// [`OpKind::NearBranch64`]: enum.OpKind.html#variant.NearBranch64
	#[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")]
		{
			// Check if JKZD/JKNZD
			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,
		}
	}

	/// Gets the operand's branch target. Use this method if the operand has kind [`OpKind::FarBranch16`]
	///
	/// [`OpKind::FarBranch16`]: enum.OpKind.html#variant.FarBranch16
	#[must_use]
	#[inline]
	pub const fn far_branch16(&self) -> u16 {
		self.immediate as u16
	}

	/// Sets the operand's branch target. Use this method if the operand has kind [`OpKind::FarBranch16`]
	///
	/// [`OpKind::FarBranch16`]: enum.OpKind.html#variant.FarBranch16
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_far_branch16(&mut self, new_value: u16) {
		self.immediate = new_value as u32;
	}

	/// Gets the operand's branch target. Use this method if the operand has kind [`OpKind::FarBranch32`]
	///
	/// [`OpKind::FarBranch32`]: enum.OpKind.html#variant.FarBranch32
	#[must_use]
	#[inline]
	pub const fn far_branch32(&self) -> u32 {
		self.immediate
	}

	/// Sets the operand's branch target. Use this method if the operand has kind [`OpKind::FarBranch32`]
	///
	/// [`OpKind::FarBranch32`]: enum.OpKind.html#variant.FarBranch32
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_far_branch32(&mut self, new_value: u32) {
		self.immediate = new_value;
	}

	/// Gets the operand's branch target selector. Use this method if the operand has kind [`OpKind::FarBranch16`] or [`OpKind::FarBranch32`]
	///
	/// [`OpKind::FarBranch16`]: enum.OpKind.html#variant.FarBranch16
	/// [`OpKind::FarBranch32`]: enum.OpKind.html#variant.FarBranch32
	#[must_use]
	#[inline]
	pub const fn far_branch_selector(&self) -> u16 {
		self.mem_displ as u16
	}

	/// Sets the operand's branch target selector. Use this method if the operand has kind [`OpKind::FarBranch16`] or [`OpKind::FarBranch32`]
	///
	/// [`OpKind::FarBranch16`]: enum.OpKind.html#variant.FarBranch16
	/// [`OpKind::FarBranch32`]: enum.OpKind.html#variant.FarBranch32
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_far_branch_selector(&mut self, new_value: u16) {
		self.mem_displ = new_value as u64;
	}

	/// Gets the memory operand's base register or [`Register::None`] if none. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[must_use]
	#[inline]
	pub const fn memory_base(&self) -> Register {
		self.mem_base_reg
	}

	/// Sets the memory operand's base register or [`Register::None`] if none. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_memory_base(&mut self, new_value: Register) {
		self.mem_base_reg = new_value;
	}

	/// Gets the memory operand's index register or [`Register::None`] if none. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	#[must_use]
	#[inline]
	pub const fn memory_index(&self) -> Register {
		self.mem_index_reg
	}

	/// Sets the memory operand's index register or [`Register::None`] if none. Use this method if the operand has kind [`OpKind::Memory`]
	///
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Memory`]: enum.OpKind.html#variant.Memory
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_memory_index(&mut self, new_value: Register) {
		self.mem_index_reg = new_value;
	}

	/// Gets operand #0's register value. Use this method if operand #0 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	#[must_use]
	#[inline]
	pub const fn op0_register(&self) -> Register {
		self.regs[0]
	}

	/// Sets operand #0's register value. Use this method if operand #0 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_op0_register(&mut self, new_value: Register) {
		self.regs[0] = new_value;
	}

	/// Gets operand #1's register value. Use this method if operand #1 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	#[must_use]
	#[inline]
	pub const fn op1_register(&self) -> Register {
		self.regs[1]
	}

	/// Sets operand #1's register value. Use this method if operand #1 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_op1_register(&mut self, new_value: Register) {
		self.regs[1] = new_value;
	}

	/// Gets operand #2's register value. Use this method if operand #2 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	#[must_use]
	#[inline]
	pub const fn op2_register(&self) -> Register {
		self.regs[2]
	}

	/// Sets operand #2's register value. Use this method if operand #2 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_op2_register(&mut self, new_value: Register) {
		self.regs[2] = new_value;
	}

	/// Gets operand #3's register value. Use this method if operand #3 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	#[must_use]
	#[inline]
	pub const fn op3_register(&self) -> Register {
		self.regs[3]
	}

	/// Sets operand #3's register value. Use this method if operand #3 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[inline]
	pub fn set_op3_register(&mut self, new_value: Register) {
		self.regs[3] = new_value;
	}

	/// Gets operand #4's register value. Use this method if operand #4 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	#[allow(clippy::unused_self)]
	#[must_use]
	#[inline]
	pub const fn op4_register(&self) -> Register {
		Register::None
	}

	/// Sets operand #4's register value. Use this method if operand #4 ([`op0_kind()`]) has kind [`OpKind::Register`], see [`op_count()`] and [`try_op_register()`]
	///
	/// [`op0_kind()`]: #method.op0_kind
	/// [`op_count()`]: #method.op_count
	/// [`try_op_register()`]: #method.try_op_register
	/// [`Register::None`]: enum.Register.html#variant.None
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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(())
		}
	}

	/// Gets the operand's register value. Use this method if the operand has kind [`OpKind::Register`]
	///
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // add [rax],ebx
	/// let bytes = b"\x01\x18";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// assert_eq!(instr.op_count(), 2);
	/// assert_eq!(instr.op_kind(0), OpKind::Memory);
	/// assert_eq!(instr.op_kind(1), OpKind::Register);
	/// assert_eq!(instr.op_register(1), Register::EBX);
	/// ```
	#[must_use]
	#[inline]
	pub fn op_register(&self, operand: u32) -> Register {
		const _: () = assert!(IcedConstants::MAX_OP_COUNT == 5);
		if let Some(&reg) = 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(&reg) = self.regs.get(operand as usize) {
			Ok(reg)
		} else if operand == 4 {
			Ok(self.op4_register())
		} else {
			Err(IcedError::new("Invalid operand"))
		}
	}

	/// Sets the operand's register value. Use this method if the operand has kind [`OpKind::Register`]
	///
	/// [`OpKind::Register`]: enum.OpKind.html#variant.Register
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4
	/// * `new_value`: New value
	#[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(())
	}

	/// Gets the opmask register ([`Register::K1`] - [`Register::K7`]) or [`Register::None`] if none
	///
	/// [`Register::K1`]: enum.Register.html#variant.K1
	/// [`Register::K7`]: enum.Register.html#variant.K7
	/// [`Register::None`]: enum.Register.html#variant.None
	#[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);
			// SAFETY: r+K0 is a valid Register variant since 1<=r<=7
			unsafe { mem::transmute((r + Register::K0 as u32) as RegisterUnderlyingType) }
		}
	}

	/// Sets the opmask register ([`Register::K1`] - [`Register::K7`]) or [`Register::None`] if none
	///
	/// [`Register::K1`]: enum.Register.html#variant.K1
	/// [`Register::K7`]: enum.Register.html#variant.K7
	/// [`Register::None`]: enum.Register.html#variant.None
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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);
	}

	/// Checks if there's an opmask register ([`op_mask()`])
	///
	/// [`op_mask()`]: #method.op_mask
	#[must_use]
	#[inline]
	pub const fn has_op_mask(&self) -> bool {
		(self.flags1 & (InstrFlags1::OP_MASK_MASK << InstrFlags1::OP_MASK_SHIFT)) != 0
	}

	/// `true` if zeroing-masking, `false` if merging-masking.
	/// Only used by most EVEX encoded instructions that use opmask registers.
	#[must_use]
	#[inline]
	pub const fn zeroing_masking(&self) -> bool {
		(self.flags1 & InstrFlags1::ZEROING_MASKING) != 0
	}

	/// `true` if zeroing-masking, `false` if merging-masking.
	/// Only used by most EVEX encoded instructions that use opmask registers.
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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;
		}
	}

	/// `true` if merging-masking, `false` if zeroing-masking.
	/// Only used by most EVEX encoded instructions that use opmask registers.
	#[must_use]
	#[inline]
	pub const fn merging_masking(&self) -> bool {
		(self.flags1 & InstrFlags1::ZEROING_MASKING) == 0
	}

	/// `true` if merging-masking, `false` if zeroing-masking.
	/// Only used by most EVEX encoded instructions that use opmask registers.
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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;
		}
	}

	/// Gets the rounding control (SAE is implied but [`suppress_all_exceptions()`] still returns `false`)
	/// or [`RoundingControl::None`] if the instruction doesn't use it.
	///
	/// [`suppress_all_exceptions()`]: #method.suppress_all_exceptions
	/// [`RoundingControl::None`]: enum.RoundingControl.html#variant.None
	#[must_use]
	#[inline]
	pub fn rounding_control(&self) -> RoundingControl {
		unsafe {
			mem::transmute(
				((self.flags1 >> InstrFlags1::ROUNDING_CONTROL_SHIFT) & InstrFlags1::ROUNDING_CONTROL_MASK) as RoundingControlUnderlyingType,
			)
		}
	}

	/// Sets the rounding control (SAE is implied but [`suppress_all_exceptions()`] still returns `false`)
	/// or [`RoundingControl::None`] if the instruction doesn't use it.
	///
	/// [`suppress_all_exceptions()`]: #method.suppress_all_exceptions
	/// [`RoundingControl::None`]: enum.RoundingControl.html#variant.None
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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);
	}

	/// Gets the number of elements in a `db`/`dw`/`dd`/`dq` directive.
	/// Can only be called if [`code()`] is [`Code::DeclareByte`], [`Code::DeclareWord`], [`Code::DeclareDword`], [`Code::DeclareQword`]
	///
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	/// [`Code::DeclareWord`]: enum.Code.html#variant.DeclareWord
	/// [`Code::DeclareDword`]: enum.Code.html#variant.DeclareDword
	/// [`Code::DeclareQword`]: enum.Code.html#variant.DeclareQword
	#[must_use]
	#[inline]
	pub const fn declare_data_len(&self) -> usize {
		(((self.flags1 >> InstrFlags1::DATA_LENGTH_SHIFT) & InstrFlags1::DATA_LENGTH_MASK) + 1) as usize
	}

	/// Sets the number of elements in a `db`/`dw`/`dd`/`dq` directive.
	/// Can only be called if [`code()`] is [`Code::DeclareByte`], [`Code::DeclareWord`], [`Code::DeclareDword`], [`Code::DeclareQword`]
	///
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	/// [`Code::DeclareWord`]: enum.Code.html#variant.DeclareWord
	/// [`Code::DeclareDword`]: enum.Code.html#variant.DeclareDword
	/// [`Code::DeclareQword`]: enum.Code.html#variant.DeclareQword
	///
	/// # Arguments
	///
	/// * `new_value`: New value: `db`: 1-16; `dw`: 1-8; `dd`: 1-4; `dq`: 1-2
	#[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);
	}

	/// Sets a new `db` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareByte`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	///
	/// # Arguments
	///
	/// * `index`: Index (0-15)
	/// * `new_value`: New value
	#[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),
		}
	}

	/// Sets a new `db` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareByte`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	///
	/// # Errors
	///
	/// - Fails if `index` is invalid
	///
	/// # Arguments
	///
	/// * `index`: Index (0-15)
	/// * `new_value`: New value
	#[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)
	}

	/// Sets a new `db` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareByte`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	///
	/// # Arguments
	///
	/// * `index`: Index (0-15)
	/// * `new_value`: New value
	#[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(())
	}

	/// Gets a `db` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareByte`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareByte`]: enum.Code.html#variant.DeclareByte
	///
	/// # Arguments
	///
	/// * `index`: Index (0-15)
	#[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")),
		})
	}

	/// Sets a new `dw` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareWord`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareWord`]: enum.Code.html#variant.DeclareWord
	///
	/// # Arguments
	///
	/// * `index`: Index (0-7)
	/// * `new_value`: New value
	#[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)
	}

	/// Sets a new `dw` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareWord`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareWord`]: enum.Code.html#variant.DeclareWord
	///
	/// # Arguments
	///
	/// * `index`: Index (0-7)
	/// * `new_value`: New value
	#[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(())
	}

	/// Gets a `dw` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareWord`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareWord`]: enum.Code.html#variant.DeclareWord
	///
	/// # Arguments
	///
	/// * `index`: Index (0-7)
	#[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")),
		})
	}

	/// Sets a new `dd` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareDword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareDword`]: enum.Code.html#variant.DeclareDword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-3)
	/// * `new_value`: New value
	#[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)
	}

	/// Sets a new `dd` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareDword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareDword`]: enum.Code.html#variant.DeclareDword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-3)
	/// * `new_value`: New value
	#[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(())
	}

	/// Gets a `dd` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareDword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareDword`]: enum.Code.html#variant.DeclareDword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-3)
	#[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")),
		})
	}

	/// Sets a new `dq` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareQword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareQword`]: enum.Code.html#variant.DeclareQword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-1)
	/// * `new_value`: New value
	#[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)
	}

	/// Sets a new `dq` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareQword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareQword`]: enum.Code.html#variant.DeclareQword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-1)
	/// * `new_value`: New value
	#[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(())
	}

	/// Gets a `dq` value, see also [`declare_data_len()`].
	/// Can only be called if [`code()`] is [`Code::DeclareQword`]
	///
	/// [`declare_data_len()`]: #method.declare_data_len
	/// [`code()`]: #method.code
	/// [`Code::DeclareQword`]: enum.Code.html#variant.DeclareQword
	///
	/// # Arguments
	///
	/// * `index`: Index (0-1)
	#[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")),
		})
	}

	/// Checks if this is a VSIB instruction, see also [`is_vsib32()`], [`is_vsib64()`]
	///
	/// [`is_vsib32()`]: #method.is_vsib32
	/// [`is_vsib64()`]: #method.is_vsib64
	#[must_use]
	#[inline]
	pub const fn is_vsib(&self) -> bool {
		self.vsib().is_some()
	}

	/// VSIB instructions only ([`is_vsib()`]): `true` if it's using 32-bit indexes, `false` if it's using 64-bit indexes
	///
	/// [`is_vsib()`]: #method.is_vsib
	#[must_use]
	#[inline]
	pub const fn is_vsib32(&self) -> bool {
		if let Some(is_vsib64) = self.vsib() {
			!is_vsib64
		} else {
			false
		}
	}

	/// VSIB instructions only ([`is_vsib()`]): `true` if it's using 64-bit indexes, `false` if it's using 32-bit indexes
	///
	/// [`is_vsib()`]: #method.is_vsib
	#[must_use]
	#[inline]
	pub const fn is_vsib64(&self) -> bool {
		if let Some(is_vsib64) = self.vsib() {
			is_vsib64
		} else {
			false
		}
	}

	/// Checks if it's a vsib instruction.
	///
	/// # Returns
	///
	/// * `Some(true)` if it's a VSIB instruction with 64-bit indexes
	/// * `Some(false)` if it's a VSIB instruction with 32-bit indexes
	/// * `None` if it's not a VSIB instruction.
	#[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() {
			// GENERATOR-BEGIN: Vsib32
			// ⚠️This was generated by GENERATOR!🦹‍♂️
			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),
			// GENERATOR-END: Vsib32

			// GENERATOR-BEGIN: Vsib64
			// ⚠️This was generated by GENERATOR!🦹‍♂️
			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),
			// GENERATOR-END: Vsib64

			_ => None,
		}
	}

	/// Gets the suppress all exceptions flag (EVEX/MVEX encoded instructions). Note that if [`rounding_control()`] is
	/// not [`RoundingControl::None`], SAE is implied but this method will still return `false`.
	///
	/// [`rounding_control()`]: #method.rounding_control
	/// [`RoundingControl::None`]: enum.RoundingControl.html#variant.None
	#[must_use]
	#[inline]
	pub const fn suppress_all_exceptions(&self) -> bool {
		(self.flags1 & InstrFlags1::SUPPRESS_ALL_EXCEPTIONS) != 0
	}

	/// Sets the suppress all exceptions flag (EVEX/MVEX encoded instructions). Note that if [`rounding_control()`] is
	/// not [`RoundingControl::None`], SAE is implied but this method will still return `false`.
	///
	/// [`rounding_control()`]: #method.rounding_control
	/// [`RoundingControl::None`]: enum.RoundingControl.html#variant.None
	///
	/// # Arguments
	///
	/// * `new_value`: New value
	#[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;
		}
	}

	/// Checks if the memory operand is `RIP`/`EIP` relative
	#[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
	}

	/// Gets the `RIP`/`EIP` releative address ([`memory_displacement32()`] or [`memory_displacement64()`]).
	/// This method is only valid if there's a memory operand with `RIP`/`EIP` relative addressing, see [`is_ip_rel_memory_operand()`]
	///
	/// [`memory_displacement32()`]: #method.memory_displacement32
	/// [`memory_displacement64()`]: #method.memory_displacement64
	/// [`is_ip_rel_memory_operand()`]: #method.is_ip_rel_memory_operand
	#[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
		}
	}

	/// Gets the virtual address of a memory operand
	///
	/// # Arguments
	///
	/// * `operand`: Operand number, 0-4, must be a memory operand
	/// * `element_index`: Only used if it's a vsib memory operand. This is the element index of the vector index register.
	/// * `get_register_value`: Function that returns the value of a register or the base address of a segment register, or `None` for unsupported
	///    registers.
	///
	/// # Call-back function args
	///
	/// * Arg 1: `register`: Register (GPR8, GPR16, GPR32, GPR64, XMM, YMM, ZMM, seg). If it's a segment register, the call-back function should return the segment's base address, not the segment's register value.
	/// * Arg 2: `element_index`: Only used if it's a vsib memory operand. This is the element index of the vector index register.
	/// * Arg 3: `element_size`: Only used if it's a vsib memory operand. Size in bytes of elements in vector index register (4 or 8).
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // add [rdi+r12*8-5AA5EDCCh],esi
	/// let bytes = b"\x42\x01\xB4\xE7\x34\x12\x5A\xA5";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// let va = instr.virtual_address(0, 0, |register, _element_index, _element_size| {
	///     match register {
	///         // The base address of ES, CS, SS and DS is always 0 in 64-bit mode
	///         Register::DS => Some(0x0000_0000_0000_0000),
	///         Register::RDI => Some(0x0000_0000_1000_0000),
	///         Register::R12 => Some(0x0000_0004_0000_0000),
	///         _ => None,
	///     }
	/// });
	/// assert_eq!(va, Some(0x0000_001F_B55A_1234));
	/// ```
	#[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
				}
			}
		})
	}
}

/// Contains the FPU `TOP` increment, whether it's conditional and whether the instruction writes to `TOP`
#[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 {
	/// Constructor
	#[must_use]
	#[inline]
	pub const fn new(increment: i32, conditional: bool, writes_top: bool) -> Self {
		Self { increment, conditional, writes_top }
	}

	/// Used if [`writes_top()`] is `true`:
	///
	/// Value added to `TOP`.
	///
	/// This is negative if it pushes one or more values and positive if it pops one or more values
	/// and `0` if it writes to `TOP` (eg. `FLDENV`, etc) without pushing/popping anything.
	///
	/// [`writes_top()`]: #method.writes_top
	#[must_use]
	#[inline]
	pub const fn increment(&self) -> i32 {
		self.increment
	}

	/// `true` if it's a conditional push/pop (eg. `FPTAN` or `FSINCOS`)
	#[must_use]
	#[inline]
	pub const fn conditional(&self) -> bool {
		self.conditional
	}

	/// `true` if `TOP` is written (it's a conditional/unconditional push/pop, `FNSAVE`, `FLDENV`, etc)
	#[must_use]
	#[inline]
	pub const fn writes_top(&self) -> bool {
		self.writes_top
	}
}

#[cfg(feature = "instr_info")]
impl Instruction {
	/// Gets the number of bytes added to `SP`/`ESP`/`RSP` or 0 if it's not an instruction that pushes or pops data. This method assumes
	/// the instruction doesn't change the privilege level (eg. `IRET/D/Q`). If it's the `LEAVE` instruction, this method returns 0.
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // pushfq
	/// let bytes = b"\x9C";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// assert!(instr.is_stack_instruction());
	/// assert_eq!(instr.stack_pointer_increment(), -8);
	/// ```
	#[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() {
			// GENERATOR-BEGIN: StackPointerIncrementTable
			// ⚠️This was generated by GENERATOR!🦹‍♂️
			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 => 40,
			Code::Eretu
			| Code::Erets => 48,
			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,
			// GENERATOR-END: StackPointerIncrementTable
			_ => 0,
		}
	}

	/// Gets the FPU status word's `TOP` increment value and whether it's a conditional or unconditional push/pop
	/// and whether `TOP` is written.
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // ficomp dword ptr [rax]
	/// let bytes = b"\xDA\x18";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// let info = instr.fpu_stack_increment_info();
	/// // It pops the stack once
	/// assert_eq!(info.increment(), 1);
	/// assert!(!info.conditional());
	/// assert!(info.writes_top());
	/// ```
	#[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() {
			// GENERATOR-BEGIN: FpuStackIncrementInfoTable
			// ⚠️This was generated by GENERATOR!🦹‍♂️
			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 },
			// GENERATOR-END: FpuStackIncrementInfoTable
			_ => FpuStackIncrementInfo::default(),
		}
	}

	/// Instruction encoding, eg. Legacy, 3DNow!, VEX, EVEX, XOP
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // vmovaps xmm1,xmm5
	/// let bytes = b"\xC5\xF8\x28\xCD";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	/// let instr = decoder.decode();
	///
	/// assert_eq!(instr.encoding(), EncodingKind::VEX);
	/// ```
	#[must_use]
	#[inline]
	pub fn encoding(&self) -> EncodingKind {
		self.code().encoding()
	}

	/// Gets the CPU or CPUID feature flags
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // vmovaps xmm1,xmm5
	/// // vmovaps xmm10{k3}{z},xmm19
	/// let bytes = b"\xC5\xF8\x28\xCD\x62\x31\x7C\x8B\x28\xD3";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // vmovaps xmm1,xmm5
	/// let instr = decoder.decode();
	/// let cpuid = instr.cpuid_features();
	/// assert_eq!(cpuid.len(), 1);
	/// assert_eq!(cpuid[0], CpuidFeature::AVX);
	///
	/// // vmovaps xmm10{k3}{z},xmm19
	/// let instr = decoder.decode();
	/// let cpuid = instr.cpuid_features();
	/// assert_eq!(cpuid.len(), 2);
	/// assert_eq!(cpuid[0], CpuidFeature::AVX512VL);
	/// assert_eq!(cpuid[1], CpuidFeature::AVX512F);
	/// ```
	#[must_use]
	#[allow(clippy::missing_inline_in_public_items)]
	pub fn cpuid_features(&self) -> &'static [CpuidFeature] {
		self.code().cpuid_features()
	}

	/// Control flow info
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // or ecx,esi
	/// // ud0 rcx,rsi
	/// // call rcx
	/// let bytes = b"\x0B\xCE\x48\x0F\xFF\xCE\xFF\xD1";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // or ecx,esi
	/// let instr = decoder.decode();
	/// assert_eq!(instr.flow_control(), FlowControl::Next);
	///
	/// // ud0 rcx,rsi
	/// let instr = decoder.decode();
	/// assert_eq!(instr.flow_control(), FlowControl::Exception);
	///
	/// // call rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.flow_control(), FlowControl::IndirectCall);
	/// ```
	#[must_use]
	#[inline]
	pub fn flow_control(&self) -> FlowControl {
		self.code().flow_control()
	}

	/// `true` if it's a privileged instruction (all CPL=0 instructions (except `VMCALL`) and IOPL instructions `IN`, `INS`, `OUT`, `OUTS`, `CLI`, `STI`)
	#[must_use]
	#[inline]
	pub fn is_privileged(&self) -> bool {
		self.code().is_privileged()
	}

	/// `true` if this is an instruction that implicitly uses the stack pointer (`SP`/`ESP`/`RSP`), eg. `CALL`, `PUSH`, `POP`, `RET`, etc.
	/// See also [`stack_pointer_increment()`]
	///
	/// [`stack_pointer_increment()`]: #method.stack_pointer_increment
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // or ecx,esi
	/// // push rax
	/// let bytes = b"\x0B\xCE\x50";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // or ecx,esi
	/// let instr = decoder.decode();
	/// assert!(!instr.is_stack_instruction());
	///
	/// // push rax
	/// let instr = decoder.decode();
	/// assert!(instr.is_stack_instruction());
	/// assert_eq!(instr.stack_pointer_increment(), -8);
	/// ```
	#[must_use]
	#[inline]
	pub fn is_stack_instruction(&self) -> bool {
		self.code().is_stack_instruction()
	}

	/// `true` if it's an instruction that saves or restores too many registers (eg. `FXRSTOR`, `XSAVE`, etc).
	#[must_use]
	#[inline]
	pub fn is_save_restore_instruction(&self) -> bool {
		self.code().is_save_restore_instruction()
	}

	/// `true` if it's a "string" instruction, such as `MOVS`, `LODS`, `SCAS`, etc.
	#[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);
		// SAFETY: The table is generated and only contains valid enum variants
		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
	}

	/// All flags that are read by the CPU when executing the instruction.
	/// This method returns an [`RflagsBits`] value. See also [`rflags_modified()`].
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	/// [`rflags_modified()`]: #method.rflags_modified
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_read(&self) -> u32 {
		crate::info::rflags_table::FLAGS_READ[self.rflags_info() as usize] as u32
	}

	/// All flags that are written by the CPU, except those flags that are known to be undefined, always set or always cleared.
	/// This method returns an [`RflagsBits`] value. See also [`rflags_modified()`].
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	/// [`rflags_modified()`]: #method.rflags_modified
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_written(&self) -> u32 {
		crate::info::rflags_table::FLAGS_WRITTEN[self.rflags_info() as usize] as u32
	}

	/// All flags that are always cleared by the CPU.
	/// This method returns an [`RflagsBits`] value. See also [`rflags_modified()`].
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	/// [`rflags_modified()`]: #method.rflags_modified
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_cleared(&self) -> u32 {
		crate::info::rflags_table::FLAGS_CLEARED[self.rflags_info() as usize] as u32
	}

	/// All flags that are always set by the CPU.
	/// This method returns an [`RflagsBits`] value. See also [`rflags_modified()`].
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	/// [`rflags_modified()`]: #method.rflags_modified
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_set(&self) -> u32 {
		crate::info::rflags_table::FLAGS_SET[self.rflags_info() as usize] as u32
	}

	/// All flags that are undefined after executing the instruction.
	/// This method returns an [`RflagsBits`] value. See also [`rflags_modified()`].
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	/// [`rflags_modified()`]: #method.rflags_modified
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_undefined(&self) -> u32 {
		crate::info::rflags_table::FLAGS_UNDEFINED[self.rflags_info() as usize] as u32
	}

	/// All flags that are modified by the CPU. This is `rflags_written() + rflags_cleared() + rflags_set() + rflags_undefined()`. This method returns an [`RflagsBits`] value.
	///
	/// [`RflagsBits`]: struct.RflagsBits.html
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // adc rsi,rcx
	/// // xor rdi,5Ah
	/// let bytes = b"\x48\x11\xCE\x48\x83\xF7\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // adc rsi,rcx
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::CF);
	/// assert_eq!(instr.rflags_written(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	///
	/// // xor rdi,5Ah
	/// let instr = decoder.decode();
	/// assert_eq!(instr.rflags_read(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_written(), RflagsBits::SF | RflagsBits::ZF | RflagsBits::PF);
	/// assert_eq!(instr.rflags_cleared(), RflagsBits::OF | RflagsBits::CF);
	/// assert_eq!(instr.rflags_set(), RflagsBits::NONE);
	/// assert_eq!(instr.rflags_undefined(), RflagsBits::AF);
	/// assert_eq!(instr.rflags_modified(), RflagsBits::OF | RflagsBits::SF | RflagsBits::ZF | RflagsBits::AF | RflagsBits::CF | RflagsBits::PF);
	/// ```
	#[must_use]
	#[inline]
	pub fn rflags_modified(&self) -> u32 {
		crate::info::rflags_table::FLAGS_MODIFIED[self.rflags_info() as usize] as u32
	}

	/// Checks if it's a `Jcc SHORT` or `Jcc NEAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_jcc_short_or_near(&self) -> bool {
		self.code().is_jcc_short_or_near()
	}

	/// Checks if it's a `Jcc NEAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_jcc_near(&self) -> bool {
		self.code().is_jcc_near()
	}

	/// Checks if it's a `Jcc SHORT` instruction
	#[must_use]
	#[inline]
	pub const fn is_jcc_short(&self) -> bool {
		self.code().is_jcc_short()
	}

	/// Checks if it's a `JMP SHORT` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_short(&self) -> bool {
		self.code().is_jmp_short()
	}

	/// Checks if it's a `JMP NEAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_near(&self) -> bool {
		self.code().is_jmp_near()
	}

	/// Checks if it's a `JMP SHORT` or a `JMP NEAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_short_or_near(&self) -> bool {
		self.code().is_jmp_short_or_near()
	}

	/// Checks if it's a `JMP FAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_far(&self) -> bool {
		self.code().is_jmp_far()
	}

	/// Checks if it's a `CALL NEAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_call_near(&self) -> bool {
		self.code().is_call_near()
	}

	/// Checks if it's a `CALL FAR` instruction
	#[must_use]
	#[inline]
	pub const fn is_call_far(&self) -> bool {
		self.code().is_call_far()
	}

	/// Checks if it's a `JMP NEAR reg/[mem]` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_near_indirect(&self) -> bool {
		self.code().is_jmp_near_indirect()
	}

	/// Checks if it's a `JMP FAR [mem]` instruction
	#[must_use]
	#[inline]
	pub const fn is_jmp_far_indirect(&self) -> bool {
		self.code().is_jmp_far_indirect()
	}

	/// Checks if it's a `CALL NEAR reg/[mem]` instruction
	#[must_use]
	#[inline]
	pub const fn is_call_near_indirect(&self) -> bool {
		self.code().is_call_near_indirect()
	}

	/// Checks if it's a `CALL FAR [mem]` instruction
	#[must_use]
	#[inline]
	pub const fn is_call_far_indirect(&self) -> bool {
		self.code().is_call_far_indirect()
	}

	/// Checks if it's a `JKccD SHORT` or `JKccD NEAR` instruction
	#[must_use]
	#[inline]
	#[cfg(feature = "mvex")]
	pub const fn is_jkcc_short_or_near(&self) -> bool {
		self.code().is_jkcc_short_or_near()
	}

	/// Checks if it's a `JKccD NEAR` instruction
	#[must_use]
	#[inline]
	#[cfg(feature = "mvex")]
	pub const fn is_jkcc_near(&self) -> bool {
		self.code().is_jkcc_near()
	}

	/// Checks if it's a `JKccD SHORT` instruction
	#[must_use]
	#[inline]
	#[cfg(feature = "mvex")]
	pub const fn is_jkcc_short(&self) -> bool {
		self.code().is_jkcc_short()
	}

	/// Checks if it's a `JCXZ SHORT`, `JECXZ SHORT` or `JRCXZ SHORT` instruction
	#[must_use]
	#[inline]
	pub const fn is_jcx_short(&self) -> bool {
		self.code().is_jcx_short()
	}

	/// Checks if it's a `LOOPcc SHORT` instruction
	#[must_use]
	#[inline]
	pub const fn is_loopcc(&self) -> bool {
		self.code().is_loopcc()
	}

	/// Checks if it's a `LOOP SHORT` instruction
	#[must_use]
	#[inline]
	pub const fn is_loop(&self) -> bool {
		self.code().is_loop()
	}

	/// Negates the condition code, eg. `JE` -> `JNE`. Can be used if it's `Jcc`, `SETcc`, `CMOVcc`, `CMPccXADD`, `LOOPcc`
	/// and does nothing if the instruction doesn't have a condition code.
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // setbe al
	/// let bytes = b"\x0F\x96\xC0";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// let mut instr = decoder.decode();
	/// assert_eq!(instr.code(), Code::Setbe_rm8);
	/// assert_eq!(instr.condition_code(), ConditionCode::be);
	/// instr.negate_condition_code();
	/// assert_eq!(instr.code(), Code::Seta_rm8);
	/// assert_eq!(instr.condition_code(), ConditionCode::a);
	/// ```
	#[inline]
	pub fn negate_condition_code(&mut self) {
		self.set_code(self.code().negate_condition_code())
	}

	/// Converts `Jcc/JMP NEAR` to `Jcc/JMP SHORT` and does nothing if it's not a `Jcc/JMP NEAR` instruction
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // jbe near ptr label
	/// let bytes = b"\x0F\x86\x5A\xA5\x12\x34";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// let mut instr = decoder.decode();
	/// assert_eq!(instr.code(), Code::Jbe_rel32_64);
	/// instr.as_short_branch();
	/// assert_eq!(instr.code(), Code::Jbe_rel8_64);
	/// instr.as_short_branch();
	/// assert_eq!(instr.code(), Code::Jbe_rel8_64);
	/// ```
	#[inline]
	pub fn as_short_branch(&mut self) {
		self.set_code(self.code().as_short_branch())
	}

	/// Converts `Jcc/JMP SHORT` to `Jcc/JMP NEAR` and does nothing if it's not a `Jcc/JMP SHORT` instruction
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // jbe short label
	/// let bytes = b"\x76\x5A";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// let mut instr = decoder.decode();
	/// assert_eq!(instr.code(), Code::Jbe_rel8_64);
	/// instr.as_near_branch();
	/// assert_eq!(instr.code(), Code::Jbe_rel32_64);
	/// instr.as_near_branch();
	/// assert_eq!(instr.code(), Code::Jbe_rel32_64);
	/// ```
	#[inline]
	pub fn as_near_branch(&mut self) {
		self.set_code(self.code().as_near_branch())
	}

	/// Gets the condition code if it's `Jcc`, `SETcc`, `CMOVcc`, `CMPccXADD`, `LOOPcc` else [`ConditionCode::None`] is returned
	///
	/// [`ConditionCode::None`]: enum.ConditionCode.html#variant.None
	///
	/// # Examples
	///
	/// ```
	/// use iced_x86::*;
	///
	/// // setbe al
	/// // jl short label
	/// // cmovne ecx,esi
	/// // nop
	/// let bytes = b"\x0F\x96\xC0\x7C\x5A\x0F\x45\xCE\x90";
	/// let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
	///
	/// // setbe al
	/// let instr = decoder.decode();
	/// assert_eq!(instr.condition_code(), ConditionCode::be);
	///
	/// // jl short label
	/// let instr = decoder.decode();
	/// assert_eq!(instr.condition_code(), ConditionCode::l);
	///
	/// // cmovne ecx,esi
	/// let instr = decoder.decode();
	/// assert_eq!(instr.condition_code(), ConditionCode::ne);
	///
	/// // nop
	/// let instr = decoder.decode();
	/// assert_eq!(instr.condition_code(), ConditionCode::None);
	/// ```
	#[must_use]
	#[inline]
	pub fn condition_code(&self) -> ConditionCode {
		self.code().condition_code()
	}
}

#[cfg(all(feature = "encoder", feature = "op_code_info"))]
impl Instruction {
	/// Gets the [`OpCodeInfo`]
	///
	/// [`OpCodeInfo`]: struct.OpCodeInfo.html
	#[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 {
		//
		// if the order of #[cfg()] checks gets updated, also update the `display_trait()` test method
		//
		#[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 {
		// `index`/`count` are `u8`
		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 {}

// We've documented that we only support serializing and deserializing data created by the same version of iced.
// That means we can optimize bincode serialized instructions. It's wasting 35 bytes per serialized instruction
// because it stores each enum variant in a u32. That's almost a full instruction instance wasted with padding.
#[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};

	// eg. json
	const NUM_FIELDS_READABLE: usize = 13;
	// eg. bincode
	const NUM_FIELDS_BINARY: usize = 19;

	#[inline]
	const fn is_human_readable(value: bool) -> bool {
		// This feature is used to test the other code paths
		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 {
						// SAFETY: all values 0-max are valid enum values
						(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 {
							// SAFETY: all values 0-max are valid enum values
							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 {
						// SAFETY: all values 0-max are valid enum values
						(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 {
							// SAFETY: all values 0-max are valid enum values
							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 })
			}
		}
	}
};
