aboutsummaryrefslogtreecommitdiff
path: root/src/protected_mode
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-05-18 07:06:50 +0000
committeriximeow <me@iximeow.net>2026-05-25 06:37:09 +0000
commit340d89e4efd6200e733db8f67e787a6625a0a128 (patch)
tree50dab8c6677995118cc26a87551a13e2ae12cdb5 /src/protected_mode
parent87dc48adcce4e80aa98a2867edacc023579fc4c4 (diff)
adapt long-mode behavior support to protected mode and real mode
along the way, fix an error: maskmov is memory read-write. additionally, operand information about {push,pop}a{,d}.
Diffstat (limited to 'src/protected_mode')
-rw-r--r--src/protected_mode/behavior.rs6205
-rw-r--r--src/protected_mode/mod.rs3
2 files changed, 6208 insertions, 0 deletions
diff --git a/src/protected_mode/behavior.rs b/src/protected_mode/behavior.rs
new file mode 100644
index 0000000..43bace0
--- /dev/null
+++ b/src/protected_mode/behavior.rs
@@ -0,0 +1,6205 @@
+//! behavior information for x86-32 instructions.
+//!
+//! this module allows users of yaxpeax-x86 to collect operand read/write information about
+//! instructions ([`InstBehavior::operand_access()`]), as well as allowed execution level
+//! ([`InstBehavior::privilege_level()`]), potential exceptions ([`InstBehavior::exceptions()`]),
+//! and iterating all explicit and implicit operands ([`InstBehavior::all_operands()`]).
+//!
+//! a concise example of the information provided by this model:
+//! ```
+//! extern crate alloc;
+//!
+//! use yaxpeax_x86::Access;
+//! use yaxpeax_x86::protected_mode::{InstDecoder, Operand, RegSpec};
+//!
+//! // `3300` is "xor eax, dword [eax]"
+//! let bytes = &[0x33, 0x00];
+//! let inst = InstDecoder::default().decode_slice(bytes).expect("can decode trivial instructions");
+//!
+//! #[cfg(feature="fmt")]
+//! assert_eq!(inst.to_string(), "xor eax, dword [eax]");
+//!
+//! let behavior = inst.behavior();
+//! let operands = behavior.all_operands().expect("xor eax, eax is not complex");
+//!
+//! let collected: alloc::vec::Vec<(Operand, Access)> = operands.iter().collect();
+//! let expected = alloc::vec![
+//! (Operand::Register { reg: RegSpec::eflags() }, Access::Write),
+//! (Operand::Register { reg: RegSpec::eax() }, Access::ReadWrite),
+//! (Operand::MemDeref { base: RegSpec::eax() }, Access::Read),
+//! ];
+//! assert_eq!(collected, expected);
+//!
+//! #[cfg(feature="unstable")]
+//! {
+//! use yaxpeax_x86::{Exception, PrivilegeLevel};
+//!
+//! assert_eq!(behavior.privilege_level(), Some(PrivilegeLevel::Any));
+//! let exceptions = behavior.exceptions();
+//! assert!(!exceptions.none());
+//! assert!(exceptions.may(Exception::PF));
+//! }
+//! ```
+//!
+//! [`InstBehavior::all_operands()`] and [`InstBehavior::visit_accesses()`] may error with a
+//! [`ComplexOp`]; this enum enumerates instructions either involving architectural state not
+//! expressed in yaxpeax-x86' API, or otherwise related to processor state in a way that simply
+//! considering the operands as-presented would be inaccurate. where possible, documentation on
+//! `ComplexOp` tries to guide users towards how to handle such instructions.
+//!
+//! some behavior information in this module is "unstable", meaning it must be opted into with
+//! `feature = ["unstable"]` on yaxpeax-x86; information from "unstable" interfaces may be
+//! less-tested and change across semver-compatible releases! if you want to use unstable
+//! interfaces here, first: thank you!! please report any issues, and second: consider pinning to a
+//! specific minor version while setting `feature = ["unstable"]` if instruction behavior becoming
+//! more correct might present an issue in your application.
+
+use super::{Instruction, Opcode, Operand, OperandSpec};
+use super::RegSpec;
+
+use crate::behavior::Access;
+#[cfg(any(doc, feature = "unstable"))]
+use crate::behavior::{Exception, ExceptionInfo, PrivilegeLevel};
+
+/// an accessor for run-time characteristics of instructions.
+///
+/// generally, behavior accessors across architectures are expected to have a `behavior()`
+/// entrypoint on a decoded instruction. it is not clear which properties of `behavior()`
+/// generalize across architectures (yet!) but presumably something like `all_operands()` and
+/// `Access` do.
+///
+/// additionally, of note for x86:
+///
+/// * x86 has privilege levels, where some instructions raise an exception when executed in
+/// inappropriate privilege levels. this is expressed by [`InstBehavior::privilege_level()`] and
+/// [`InstBehavior::exceptions()`].
+/// * x86 instructions have the familiar operands from textual disassembly, but many instructions
+/// have other implied operands. in some cases the implied operand is a second memory-accessing
+/// operation (consider `call qword [ecx]`; `qword [ecx]` is one memory access, but the implied
+/// push of a return address is a second memory operation).
+/// * `{,e}flags` is often written and sometimes read, but almost never as an explicit source or
+/// destination operand. this can be queried with [`InstBehavior::flags_access()`].
+///
+/// it's also useful to know if implicit and explicit operands are reads, writes, or both, such as
+/// when diagnosing a run-time fault. to iterate over this information, `all_operands().iter()`. or
+/// `visit_accesses(&mut ..)` to collect all operand/access information for this instruction.
+#[derive(Copy, Clone)]
+pub struct InstBehavior<'inst> {
+ inst: &'inst Instruction,
+ behavior: BehaviorDigest,
+}
+
+impl Instruction {
+ /// get a struct to query behaviors of an instruction.
+ ///
+ /// "behaviors" is broad! as of writing, "behavior" covers "implicit and explicit operand
+ /// reads/writes", "possible exceptions", "allowed privilege levels", and "instruction has
+ /// additional semantics not easily expressed by this library".
+ ///
+ /// see the documentation for [`InstBehavior`] as well as the
+ /// [`behavior`][crate::protected_mode::behavior] module for more information.
+ pub fn behavior<'inst>(&'inst self) -> InstBehavior<'inst> {
+ let mut behavior = opcode2behavior(&self.opcode);
+
+ if behavior.is_nontrivial() {
+ // mul and imul are incredibly frustrating, with multiple behaviors corresponding to
+ // different encodings with different opcode counts. fix up behaviors here..
+ if self.opcode == Opcode::MUL || (self.opcode == Opcode::IMUL && self.operand_count == 1) {
+ let op_width = if self.operands[0] == OperandSpec::RegMMM {
+ self.regs[1].width()
+ } else {
+ self.mem_size
+ };
+ let ops_idx = match op_width {
+ 1 => MUL_IDX_1OP_BYTE,
+ 2 => MUL_IDX_1OP_WORD,
+ 4 => MUL_IDX_1OP_DWORD,
+ _ /* 8 */ => MUL_IDX_1OP_QWORD,
+ };
+ behavior = behavior
+ .set_implicit_ops(ops_idx);
+ } else if self.opcode == Opcode::IMUL {
+ if self.operand_count == 2 {
+ behavior = behavior
+ .set_operand(0, Access::ReadWrite)
+ .set_operand(1, Access::Read);
+ } else if self.operand_count == 3 {
+ behavior = behavior
+ .set_operand(0, Access::ReadWrite)
+ .set_operand(1, Access::Read)
+ .set_operand(2, Access::Read);
+ }
+ } else if self.opcode == Opcode::PUSHA {
+ if !self.prefixes.operand_size() {
+ behavior = behavior
+ .set_implicit_ops(PUSHAD_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(PUSHA_IDX);
+ }
+ } else if self.opcode == Opcode::POPA {
+ if !self.prefixes.operand_size() {
+ behavior = behavior
+ .set_implicit_ops(POPAD_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(POPA_IDX);
+ }
+ } else if self.opcode == Opcode::DIV || self.opcode == Opcode::IDIV {
+ let op_width = if self.operands[0] == OperandSpec::RegMMM {
+ self.regs[1].width()
+ } else {
+ self.mem_size
+ };
+ let ops_idx = match op_width {
+ 1 => DIV_IDX_1OP_BYTE,
+ 2 => DIV_IDX_1OP_WORD,
+ 4 => DIV_IDX_1OP_DWORD,
+ _ /* 8 */ => DIV_IDX_1OP_QWORD,
+ };
+ behavior = behavior
+ .set_implicit_ops(ops_idx);
+ } else if self.opcode == Opcode::NOP {
+ if self.operand_count == 1 {
+ behavior = behavior
+ .set_operand(0, Access::None);
+ }
+ } else if self.opcode == Opcode::CMPXCHG {
+ let op_width = if self.operands[0] == OperandSpec::RegMMM {
+ self.regs[1].width()
+ } else {
+ self.mem_size
+ };
+ let ops_idx = match op_width {
+ 1 => CMPXCHG_IDX_BYTE,
+ 2 => CMPXCHG_IDX_WORD,
+ 4 => CMPXCHG_IDX_DWORD,
+ _ /* 8 */ => CMPXCHG_IDX_QWORD,
+ };
+ behavior = behavior
+ .set_implicit_ops(ops_idx);
+ } else if self.opcode == Opcode::VMOVLPS || self.opcode == Opcode::VMOVHPS ||
+ self.opcode == Opcode::VMOVLPD || self.opcode == Opcode::VMOVHPD {
+ if self.operand_count == 2 {
+ behavior = behavior
+ .set_operand(0, Access::ReadWrite);
+ } else {
+ behavior = behavior
+ .set_operand(0, Access::Write)
+ .set_operand(2, Access::Read);
+ }
+ } else if self.opcode() == Opcode::EXTRQ {
+ if self.operand_count > 2 {
+ behavior = behavior
+ .set_operand(2, Access::Read);
+ }
+ } else if self.opcode() == Opcode::INSERTQ {
+ if self.operand_count > 2 {
+ behavior = behavior
+ .set_operand(2, Access::Read)
+ .set_operand(3, Access::Read);
+ }
+ } else if self.opcode() == Opcode::RETURN {
+ if self.operand_count != 0 {
+ behavior = behavior
+ .set_operand(0, Access::Read);
+ }
+ } else if self.opcode() == Opcode::RETF {
+ if self.operand_count != 0 {
+ behavior = behavior
+ .set_operand(0, Access::Read);
+ }
+ } else if self.opcode() == Opcode::MULX {
+ // `mulx` is always vex-encoded.
+ //
+ // TODO: vex.w is UD in 32-bit code, right?
+ if self.prefixes.vex_unchecked().w() {
+ behavior = behavior
+ .set_implicit_ops(MULX_64B_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(MULX_32B_IDX);
+ }
+ } else if self.opcode() == Opcode::VMASKMOVDQU {
+ // in 64-bit mode, 67 overrides 64-bit addressing down to 32-bit.
+ if self.prefixes.address_size() {
+ behavior = behavior
+ .set_implicit_ops(EDI_MEMWRITE_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(RDI_MEMWRITE_IDX);
+ }
+ } else if self.opcode() == Opcode::PCMPESTRI || self.opcode() == Opcode::VPCMPESTRI {
+ if self.prefixes.vex_unchecked().w() {
+ behavior = behavior
+ .set_implicit_ops(PCMPESTRI_64B_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(PCMPESTRI_32B_IDX);
+ }
+ } else if self.opcode() == Opcode::PCMPESTRM || self.opcode() == Opcode::VPCMPESTRM {
+ if self.prefixes.vex_unchecked().w() {
+ behavior = behavior
+ .set_implicit_ops(PCMPESTRM_64B_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(PCMPESTRM_32B_IDX);
+ }
+ } else if self.opcode() == Opcode::LOOPNZ
+ || self.opcode() == Opcode::LOOPZ
+ || self.opcode() == Opcode::LOOP
+ || self.opcode() == Opcode::JECXZ {
+ if self.prefixes.operand_size() {
+ behavior = behavior
+ .set_implicit_ops(RW_CX_IDX);
+ } else {
+ behavior = behavior
+ .set_implicit_ops(RW_ECX_IDX);
+ }
+ } else {
+ // there should never be an unhandled nontrivial opcode, but leave this in so
+ // fuzzing and testing can make sure. this way in normal builds the branch is empty
+ // and compilers can forget all about it for us.
+ #[cfg(feature = "_debug_internal_asserts")]
+ unreachable!();
+ }
+ }
+
+ InstBehavior {
+ inst: self,
+ behavior
+ }
+ }
+}
+
+/// a handle for an instruction, its behavior, and any related implicit operands.
+///
+/// this is only useful for [`InstOperands::iter()`].
+#[derive(Copy, Clone)]
+pub struct InstOperands<'inst> {
+ inst: InstBehavior<'inst>,
+ implicit_ops: &'static [ImplicitOperand],
+}
+
+impl<'inst> InstOperands<'inst> {
+ /// establish an iterator over the operands described in this `InstOperands`.
+ pub fn iter(self) -> AccessIter<'inst> {
+ AccessIter::new(self)
+ }
+}
+
+/// this struct implements [`Iterator`] to allow library users to walk all explicit and implicit
+/// operands for the corresponding instruction, along with if they are used for reading or for
+/// writing.
+///
+/// implicit operands are always walked first, explicit operands are walked last.
+pub struct AccessIter<'inst> {
+ operands: InstOperands<'inst>,
+ explicit: bool,
+ next: u8,
+}
+
+impl<'inst> AccessIter<'inst> {
+ fn new(operands: InstOperands<'inst>) -> Self {
+ Self {
+ operands,
+ explicit: false,
+ next: 0,
+ }
+ }
+
+ /// weaken this iterator to only returning the operands corresponding to this instruction,
+ /// without specific access information.
+ pub fn operands(self) -> OperandIter<'inst> {
+ OperandIter { inner: self }
+ }
+}
+
+impl<'inst> Iterator for AccessIter<'inst> {
+ type Item = (Operand, Access);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // iteration order is:
+ // * if the instruction accesses flags, report that
+ // * then, walk the implicit operand list
+ // * finally, walk explicit operands
+ // * fin
+
+ // the implicit operand list might be empty, there may be no flags, etc. so we need to
+ // consider the implicit operand iterator states up to two times before falling through to
+ // the first is this the first reported case of buttelement of the explicit operand list (if there is one). using a loop here
+ // seems like the least-gross way to go...
+ while !self.explicit {
+ if self.next == 0 {
+ // we only consider flags at most once; either we're returning a value here or
+ // we're going to keep searching through the loop.
+ self.next += 1;
+ if let Some(acc) = self.operands.inst.flags_access() {
+ if acc != Access::None {
+ return Some((Operand::Register { reg: RegSpec::eflags() }, acc));
+ }
+ }
+ } else {
+ let implicit_idx = self.next - 1;
+ self.next += 1;
+
+ if let Some(entry) = self.operands.implicit_ops.get(implicit_idx as usize) {
+ let access = if entry.write {
+ Access::Write
+ } else {
+ Access::Read
+ };
+ return Some((entry.into_operand(), access));
+ } else {
+ // we've gotten to the end of implicit operands. flip to explicit operands,
+ // reset `next`, and continue searching.
+ self.explicit = true;
+ self.next = 0;
+ }
+ }
+ }
+
+
+ if self.next < self.operands.inst.inst.operand_count() {
+ let op = self.operands.inst.inst.operand(self.next);
+ let access = self.operands.inst.operand_access(self.next)
+ .expect("defined operand has defined access");
+ debug_assert!(
+ access != Access::None || (
+ self.operands.inst.inst.opcode == Opcode::NOP ||
+ self.operands.inst.inst.opcode == Opcode::UD1 ||
+ self.operands.inst.inst.opcode == Opcode::UD0
+ )
+ );
+ let res = Some((op, access));
+ self.next += 1;
+ res
+ } else {
+ None
+ }
+ }
+}
+
+/// a reduced-strength iterator of an instruction's implicit and explicit operands.
+///
+/// unlike `AccessIter`, this iterator does not provide read/write information, simply that
+/// operands are or are not present. this is more likely useful for some kinds of instruction
+/// printing than automated instruction analysis.
+pub struct OperandIter<'inst> {
+ inner: AccessIter<'inst>,
+}
+
+/// enough structure to describe any implicitly-present operand in an x86_64 instruction.
+///
+/// this is (maybe surprisingly, compared to the rest of the isa) relatively tiny: the only
+/// implicit operands to date are register reads/writes, and simple dereference of a register (such
+/// as `[rsp - 8] = ...` in a push).
+// TODO: this needs accessors for the elements or something.
+#[derive(Copy, Clone)]
+pub struct ImplicitOperand {
+ // TODO: not suitable for public API!
+ spec: OperandSpec,
+ reg: RegSpec,
+ disp: i32,
+ write: bool,
+}
+
+impl ImplicitOperand {
+ fn into_operand(self) -> Operand {
+ match self.spec {
+ OperandSpec::RegRRR => {
+ Operand::Register { reg: self.reg }
+ },
+ OperandSpec::Deref => {
+ Operand::MemDeref { base: self.reg }
+ }
+ OperandSpec::Disp => {
+ Operand::Disp { base: self.reg, disp: self.disp }
+ }
+ OperandSpec::Deref_edi => {
+ Operand::MemDeref { base: RegSpec::edi() }
+ }
+ // from `xlat` specifically... `base` specifies ebx, infer ax as the index here.
+ OperandSpec::MemIndexScale => {
+ Operand::MemBaseIndexScale {
+ base: self.reg,
+ index: RegSpec::al(),
+ scale: 1
+ }
+ }
+ spec => {
+ panic!("unexpected implicit op: {:?}", spec);
+ }
+ }
+ }
+}
+
+impl<'inst> Iterator for OperandIter<'inst> {
+ type Item = Operand;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.inner.next().map(|(op, _acc)| op)
+ }
+}
+
+impl<'inst> InstBehavior<'inst> {
+ #[cfg(any(doc, feature = "unstable"))]
+ /// get the [`PrivilegeLevel`] for this instruction.
+ ///
+ /// returns `None` if no privilege level information is recorded for the instruction. such
+ /// cases are a bug, please report if you see them.
+ pub fn privilege_level(&self) -> Option<PrivilegeLevel> {
+ let pl_bits = self.behavior.behavior & 0b11;
+ const LUT: [Option<PrivilegeLevel>; 4] = [
+ Some(PrivilegeLevel::Any), Some(PrivilegeLevel::PL0),
+ Some(PrivilegeLevel::Special), None,
+ ];
+
+ LUT[pl_bits as usize]
+ }
+
+ #[cfg(any(doc, feature = "unstable"))]
+ /// get the [`ExceptionInfo`] for this instruction.
+ ///
+ /// this is very much best-effort and poorly tested. it is behind the `unstable` feature for a
+ /// reason!
+ pub fn exceptions(&self) -> ExceptionInfo {
+ let mut exceptions = ExceptionInfo::empty();
+ if self.privilege_level() != Some(PrivilegeLevel::Any) {
+ // TODO: is it correct that executing with incorrect CPL always yields #GP?
+ exceptions.set(Exception::GP, true);
+ }
+
+ match self.all_operands() {
+ Ok(op_info) => {
+ exceptions.set(Exception::PF, op_info.iter().operands().any(|x| x.is_memory()));
+ }
+ Err(_complex) => {
+ // TODO: is it correct that all complex ops can page fault? no: wrmsr/rdmsr do not
+ // #PF.
+ exceptions.set(Exception::PF, true);
+ }
+ }
+
+ exceptions
+ }
+
+ /// transform this instruction's [`Opcode`] into a [`ComplexOp`], if the instruction is
+ /// "complex".
+ ///
+ /// documentation on [`ComplexOp`] covers what instructions are considered "complex" by
+ /// yaxpeax-x86 and why in more detail. correct analysis of a function (or program!) in the
+ /// presence of complex instructions may require consulting the Intel Software Developer's
+ /// Manual or AMD Architecture Programmer's Manual.
+ pub fn as_complex_op(&self) -> Option<ComplexOp> {
+ // if the behavior is not complex, it is *definitely* not a complex op. if the behavior is
+ // complex, it's really a "depending on the specific instruction and operands it might
+ // be"...
+ if !self.behavior.is_complex() {
+ return None;
+ }
+
+ if self.inst.opcode == Opcode::BT {
+ if self.inst.operands[0] != OperandSpec::RegMMM {
+ Some(ComplexOp::BT)
+ } else {
+ None
+ }
+ } else if self.inst.opcode == Opcode::BTS {
+ if self.inst.operands[0] != OperandSpec::RegMMM {
+ Some(ComplexOp::BTS)
+ } else {
+ None
+ }
+ } else if self.inst.opcode == Opcode::BTR {
+ if self.inst.operands[0] != OperandSpec::RegMMM {
+ Some(ComplexOp::BTR)
+ } else {
+ None
+ }
+ } else if self.inst.opcode == Opcode::BTC {
+ if self.inst.operands[0] != OperandSpec::RegMMM {
+ Some(ComplexOp::BTC)
+ } else {
+ None
+ }
+ } else {
+ // Safety: every `Opcode` with a `BehaviorDigest` that is `set_complex(true)` has a
+ // corresponding `ComplexOp` variant set to the same integer value, and the two types
+ // agree on repr.
+ let comp: ComplexOp = unsafe { core::mem::transmute::<Opcode, ComplexOp>(self.inst.opcode) };
+ Some(comp)
+ }
+ }
+
+ /// produce an `InstOperands` describing the explicit and implicit operands of this
+ /// instruction.
+ ///
+ /// "explicit" operands are ones that are displayed as part of the instruction's textual
+ /// assembly/disassembly, while "implicit" operands are operands describing the reset of the
+ /// instruction's behavior.
+ ///
+ /// this notion of "implicit operands" does not map precisely to terminology from either the
+ /// Intel SDM or AMD APM. instead, it is provided by `yaxpeax-x86` to try providing an answer
+ /// to some common queries about instructions .
+ pub fn all_operands(&self) -> Result<InstOperands<'inst>, ComplexOp> {
+ if let Some(op) = self.as_complex_op() {
+ return Err(op);
+ }
+
+ let implicit_ops = if let Some(ops) = self.implicit_oplist() {
+ ops
+ } else {
+ &[]
+ };
+
+ Ok(InstOperands {
+ inst: *self,
+ implicit_ops,
+ })
+ }
+
+ /// get the `Access` behavior this instruction has for `rflags`.
+ ///
+ /// note that as the documentation for [`Access`] describes, "read" and "write" have slightly
+ /// different meanings for the flags register than other locations.
+ // this implies that `rflags` must never appear in an implicit operand list.
+ pub fn flags_access(&self) -> Option<Access> {
+ let flag_acc = (self.behavior.behavior >> 2) & 0b11;
+ Access::from_bits(flag_acc)
+ }
+
+ fn implicit_oplist(&self) -> Option<&'static [ImplicitOperand]> {
+ let ops_idx = self.behavior.extra;
+ if ops_idx == 0 {
+ return None;
+ }
+
+ // TODO: ops_idx cannot be out of bounds, so maybe kinda-unchecked here..?
+ Some(&IMPLICIT_OPS_LIST[ops_idx as usize])
+ }
+
+ /// get the `Access` behavor for an explicit operand of this instruction.
+ ///
+ /// `None` means that there is no operand at the given index, while `Some(Access::None)` means
+ /// there is an operand, and the instruction does not actually access it (as for `nop`, `ud0`,
+ /// and `ud1`)
+ pub fn operand_access(&self, idx: u8) -> Option<Access> {
+ if idx >= self.inst.operand_count {
+ return None;
+ }
+
+ let op_acc = (self.behavior.operand_access >> (2 * idx)) & 0b11;
+ Access::from_bits(op_acc)
+ }
+
+ /// iterate all operands in the instruction and report them to the provided `AccessVisitor`.
+ ///
+ /// this is a more informative, but somewhat more specialized, interface than simply iterating
+ /// [`InstBehavior::all_operands()`]. for memory operands, address calculations are reported to
+ /// the access visitor as reads of the relevant registers. if all dependent values are
+ /// available, the resulting effective address is computed and reported as part of the memory
+ /// access.
+ ///
+ /// `visit_accesses()` is slightly more efficient in this than iterating `all_operands()` as
+ /// well, as it uses unstable internal representations directly, rather than converting to API
+ /// types and back for every operand.
+ pub fn visit_accesses<T: AccessVisitor>(&self, v: &mut T) -> Result<(), ComplexOp> {
+ if let Some(op) = self.as_complex_op() {
+ return Err(op);
+ }
+
+ fn compute_addr<T: AccessVisitor>(v: &mut T, inst: &Instruction, op_spec: OperandSpec) -> Option<u32> {
+ #[cfg(feature = "_debug_internal_asserts")]
+ if !op_spec.is_memory() {
+ panic!("expected memory operand but got {:?}", op_spec);
+ }
+
+ match op_spec {
+ OperandSpec::Deref |
+ OperandSpec::Deref_mask => {
+ v.get_register(inst.regs[1])
+ }
+ OperandSpec::Deref_edi => {
+ v.get_register(RegSpec::edi())
+ }
+ OperandSpec::Deref_esi => {
+ v.get_register(RegSpec::esi())
+ }
+ OperandSpec::Deref_di => {
+ v.get_register(RegSpec::di())
+ }
+ OperandSpec::Deref_si => {
+ v.get_register(RegSpec::si())
+ }
+ OperandSpec::Disp => {
+ let base = v.get_register(inst.regs[1]);
+ base.map(|addr| addr.wrapping_add(inst.disp))
+ }
+ OperandSpec::Disp_mask => {
+ let base = v.get_register(inst.regs[1]);
+ base.map(|addr| addr.wrapping_add(inst.disp))
+ }
+ OperandSpec::MemIndexScale => {
+ let index = v.get_register(inst.regs[2]);
+ index.map(|addr| {
+ addr
+ .wrapping_mul(inst.scale as u32)
+ })
+ }
+ OperandSpec::MemIndexScale_mask => {
+ let index = v.get_register(inst.regs[2]);
+ index.map(|addr| {
+ addr
+ .wrapping_mul(inst.scale as u32)
+ })
+ }
+ OperandSpec::MemIndexScaleDisp => {
+ let index = v.get_register(inst.regs[2]);
+ index.map(|addr| {
+ addr
+ .wrapping_mul(inst.scale as u32)
+ .wrapping_add(inst.disp)
+ })
+ }
+ OperandSpec::MemIndexScaleDisp_mask => {
+ let index = v.get_register(inst.regs[2]);
+ index.map(|addr| {
+ addr
+ .wrapping_mul(inst.scale as u32)
+ .wrapping_add(inst.disp)
+ })
+ }
+ OperandSpec::MemBaseIndexScale => {
+ let base = v.get_register(inst.regs[1]);
+ let index = v.get_register(inst.regs[2]);
+ base.and_then(|base| {
+ index.map(|index| {
+ base
+ .wrapping_add(index.wrapping_mul(inst.scale as u32))
+ })
+ })
+ }
+ OperandSpec::MemBaseIndexScale_mask => {
+ let base = v.get_register(inst.regs[1]);
+ let index = v.get_register(inst.regs[2]);
+ base.and_then(|base| {
+ index.map(|index| {
+ base
+ .wrapping_add(index.wrapping_mul(inst.scale as u32))
+ })
+ })
+ }
+ OperandSpec::MemBaseIndexScaleDisp => {
+ let base = v.get_register(inst.regs[1]);
+ let index = v.get_register(inst.regs[2]);
+ base.and_then(|base| {
+ index.map(|index| {
+ base
+ .wrapping_add(index.wrapping_mul(inst.scale as u32))
+ .wrapping_add(inst.disp)
+ })
+ })
+ }
+ OperandSpec::MemBaseIndexScaleDisp_mask => {
+ let base = v.get_register(inst.regs[1]);
+ let index = v.get_register(inst.regs[2]);
+ base.and_then(|base| {
+ index.map(|index| {
+ base
+ .wrapping_add(index.wrapping_mul(inst.scale as u32))
+ .wrapping_add(inst.disp)
+ })
+ })
+ }
+ OperandSpec::DispU32 => {
+ Some(inst.disp)
+ }
+ OperandSpec::DispU16 => {
+ Some(inst.disp as u16 as u32)
+ }
+ other => {
+ // this could be `_debug_internal_assertions`-gated, but i'm not quite that
+ // confident yet..
+ panic!("not-yet-handled memory operand: {:?}", other);
+ }
+ }
+ }
+
+ if let Some(implicit_oplist) = self.implicit_oplist() {
+ for op in implicit_oplist.iter() {
+ if op.spec == OperandSpec::RegRRR {
+ if op.write {
+ v.register_write(op.reg);
+ } else {
+ v.register_read(op.reg);
+ }
+ } else if op.spec == OperandSpec::Deref_edi {
+ // Deref_edi is used for string instructions; operand-size overrides apply here
+ // and so the register that is incremented (or decremented!) depends on the
+ // operand-size prefix. the register is correct for the operands, so we'll just
+ // steal it.
+ let reg = match self.inst.operands[op.disp as usize] {
+ OperandSpec::Deref_edi => RegSpec::edi(),
+ OperandSpec::Deref_esi => RegSpec::esi(),
+ OperandSpec::Deref_di => RegSpec::di(),
+ OperandSpec::Deref_si => RegSpec::si(),
+ OperandSpec::Deref => self.inst.regs[1],
+ other => {
+ // this could be `_debug_internal_assertions`-gated, but i'm not quite
+ // that confident yet..
+ panic!("TODO: unreachable {:?}", other);
+ }
+ };
+ if op.write {
+ v.register_write(reg);
+ } else {
+ v.register_read(reg);
+ }
+ } else {
+ let addr = match op.spec {
+ OperandSpec::Deref => {
+ v.get_register(op.reg)
+ },
+ OperandSpec::Disp => {
+ if let Some(base) = v.get_register(op.reg) {
+ Some(base.wrapping_add(op.disp as u32))
+ } else {
+ None
+ }
+ }
+ OperandSpec::MemIndexScale => {
+ // HACK HACK HACK this is just how i've decided to interpret
+ // `MemIndexScale` as an operand spec; it's only for xlat. adding
+ // another field to implicit operands just for this is a little silly..
+ let base = v.get_register(op.reg);
+ let index = v.get_register(RegSpec::al());
+ if let (Some(base), Some(index)) = (base, index) {
+ Some(base.wrapping_add(index))
+ } else {
+ None
+ }
+ }
+ other => {
+ // this could be `_debug_internal_assertions`-gated, but i'm not quite
+ // that confident yet..
+ panic!("impossible operand spec {:?}", other);
+ }
+ };
+
+ let size = self.inst.mem_size().expect("memory operand implies memory access size")
+ .bytes_size().expect("non-complex instructions have well-defined bytes_size()");
+
+ if op.write {
+ v.memory_write(addr, size as u32);
+ } else {
+ v.memory_read(addr, size as u32);
+ }
+ }
+ }
+ }
+
+ if let Some(acc) = self.flags_access() {
+ if acc.is_read() {
+ v.register_read(RegSpec::eflags());
+ }
+ if acc.is_write() {
+ v.register_write(RegSpec::eflags());
+ }
+ }
+
+ let operand_access = self.behavior.operand_access;
+
+ for i in 0..self.inst.operand_count {
+ let access = Access::from_bits((operand_access >> (i * 2)) & 0b11)
+ .expect("selected only low two bits");
+ let op_spec = self.inst.operands[i as usize];
+
+ if access.is_read() {
+ match op_spec {
+ OperandSpec::RegRRR => {
+ v.register_read(self.inst.regs[0]);
+ }
+ OperandSpec::RegMMM => {
+ v.register_read(self.inst.regs[1]);
+ }
+ OperandSpec::RegVex => {
+ v.register_read(self.inst.regs[3]);
+ }
+ OperandSpec::Reg4 => {
+ let spec = RegSpec {
+ num: self.inst.imm as u8,
+ bank: self.inst.regs[3].bank,
+ };
+ v.register_read(spec);
+ }
+ OperandSpec::RegRRR_maskmerge |
+ OperandSpec::RegRRR_maskmerge_sae |
+ OperandSpec::RegRRR_maskmerge_sae_noround => {
+ v.register_read(self.inst.regs[0]);
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ }
+ OperandSpec::RegMMM_maskmerge |
+ OperandSpec::RegMMM_maskmerge_sae_noround => {
+ v.register_read(self.inst.regs[1]);
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ }
+ OperandSpec::RegVex_maskmerge => {
+ v.register_read(self.inst.regs[3]);
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ }
+ OperandSpec::AbsoluteFarAddress |
+ OperandSpec::ImmI8 |
+ OperandSpec::ImmU8 |
+ OperandSpec::ImmI16 |
+ OperandSpec::ImmU16 |
+ OperandSpec::ImmI32 |
+ OperandSpec::ImmInDispField => {
+ // no register/memory access to report.
+ }
+ other => {
+ // compute effective address...
+ let addr = compute_addr(v, &self.inst, op_spec);
+ let size = self.inst.mem_size().expect("memory operand implies memory access size")
+ .bytes_size().expect("non-complex instructions have well-defined bytes_size()");
+ if other.is_masked() && self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ // `lea` *just* computes the effective address, which we've done above.
+ // othrwise, the instruction will actually read this memory operand.
+ if self.inst.opcode != Opcode::LEA {
+ v.memory_read(addr, size as u32);
+ }
+ }
+ }
+ }
+
+ if access.is_write() {
+ match op_spec {
+ OperandSpec::RegRRR => {
+ v.register_write(self.inst.regs[0]);
+ }
+ OperandSpec::RegMMM => {
+ v.register_write(self.inst.regs[1]);
+ }
+ OperandSpec::RegVex => {
+ v.register_write(self.inst.regs[3]);
+ }
+ OperandSpec::Reg4 => {
+ let spec = RegSpec {
+ num: self.inst.imm as u8,
+ bank: self.inst.regs[3].bank,
+ };
+ v.register_write(spec);
+ }
+ OperandSpec::RegRRR_maskmerge |
+ OperandSpec::RegRRR_maskmerge_sae |
+ OperandSpec::RegRRR_maskmerge_sae_noround => {
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ v.register_write(self.inst.regs[0]);
+ }
+ OperandSpec::RegMMM_maskmerge |
+ OperandSpec::RegMMM_maskmerge_sae_noround => {
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ v.register_write(self.inst.regs[1]);
+ }
+ OperandSpec::RegVex_maskmerge => {
+ if self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ v.register_write(self.inst.regs[3]);
+ }
+ OperandSpec::ImmI8 |
+ OperandSpec::ImmU8 |
+ OperandSpec::ImmI16 |
+ OperandSpec::ImmU16 |
+ OperandSpec::ImmI32 |
+ OperandSpec::ImmInDispField => {
+ // no register/memory access to report.
+ }
+ other => {
+ // compute effective address...
+ let addr = compute_addr(v, &self.inst, op_spec);
+ let size = self.inst.mem_size().expect("memory operand implies memory access size")
+ .bytes_size().expect("non-complex instructions have well-defined bytes_size()");
+ if other.is_masked() && self.inst.prefixes.evex_unchecked().mask_reg() != 0 {
+ v.register_read(RegSpec::mask(self.inst.prefixes.evex_unchecked().mask_reg()));
+ }
+ // no lea check necessary: its memory access is coded as a read and no
+ // instruction has a similar "fake" memory write.
+ v.memory_write(addr, size as u32);
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+struct BehaviorDigest {
+ // laid out like:
+ //
+ // |7 6|5 4|3 2|1 0|
+ // |_ _ C N|FL |PL |
+ //
+ // C: complex (this instruction *may* N: non-trivial (implementation detail of constructing
+ // the instruction's behavior digest)
+ // N: non-trivial (implementation detail of constructing the instruction's behavior digest)
+ // FL: access bits for {,e,r}flags
+ // PL: privilege level this instruction can be executed.
+ // 00 -> all levels
+ // 01 -> CPL=0 only
+ // 10 -> something more complicated (instruction-specific)
+ // 11 -> reserved
+ behavior: u8,
+ // for the up-to four explicit operands in an x86 instruction.
+ //
+ // bits are pairs interpreted as described in `enum Access`. operand count on the instruction
+ // describes validity of these bits: fields left `00` must not have a corresponding operand at
+ // that offset. fields with no corresponding operand may have bits set.
+ operand_access: u8,
+ // selector for a `&'static [Operand]` of additional "implicit" operands for the instruction.
+ extra: u16,
+}
+
+impl BehaviorDigest {
+ const fn empty() -> BehaviorDigest {
+ BehaviorDigest {
+ behavior: 0,
+ operand_access: 0,
+ extra: 0
+ }
+ }
+
+ const fn set_pl0(mut self) -> Self {
+ self.behavior &= 0b11_11_11_00;
+ self.behavior |= 0b00_00_00_01;
+ self
+ }
+
+ const fn set_pl_any(mut self) -> Self {
+ self.behavior &= 0b11_11_11_00;
+ self.behavior |= 0b00_00_00_00;
+ self
+ }
+
+ const fn set_pl_special(mut self) -> Self {
+ self.behavior &= 0b11_11_11_00;
+ self.behavior |= 0b00_00_00_10;
+ self
+ }
+
+ const fn set_flags_access(mut self, access: Access) -> Self {
+ self.behavior &= 0b11_11_00_11;
+ self.behavior |= (access as u8) << 2;
+ self
+ }
+
+ const fn set_operand(mut self, idx: u8, access: Access) -> Self {
+ assert!(idx < 4);
+ let offset = idx * 2;
+ self.operand_access &= !(0b11 << offset);
+ self.operand_access |= (access as u8) << offset;
+ self
+ }
+
+ const fn set_implicit_ops(mut self, ops_idx: u16) -> Self {
+ // TODO: this needs much less than a full u16 (much less than |Opcode| even)
+ self.extra = ops_idx;
+ self
+ }
+
+ const fn set_nontrivial(mut self, state: bool) -> Self {
+ self.behavior &= 0b11_10_11_11;
+ self.behavior |= (state as u8) << 4;
+ self
+ }
+
+ const fn is_nontrivial(&self) -> bool {
+ (self.behavior & (1 << 4)) != 0
+ }
+
+ const fn set_complex(mut self, state: bool) -> Self {
+ self.behavior &= 0b11_01_11_11;
+ self.behavior |= (state as u8) << 5;
+ self
+ }
+
+ const fn is_complex(&self) -> bool {
+ (self.behavior & (1 << 5)) != 0
+ }
+}
+
+/// a subset of [`Opcode`] where access patterns cannot be expressed as a simple stream of reads or
+/// writes. in many cases these instructions are only executable when `CPL=0` but notable
+/// exceptions are `rep`-prefixed string instructions (`rep movs`, `rep stos`, etc).
+///
+/// complex instructions and appropriate handling are documented on a best-effort basis below.
+///
+/// ### `rdmsr`, `wrmsr`, and all other instructions that directly read or write MSRs
+///
+/// the library API has no way to express MSRs as read or written locations, and must defer to user
+/// code to track reads or writes of this state.
+///
+/// "other instructions" include `wrfsbase`, `wrgsbase`, `rdfsbase`, `rdgsbase`, `syscall`,
+/// `sysenter`, `rdpru`, `rdtsc`, and others.
+///
+/// ### the `xsave` family
+///
+/// this section applies for all of `xsave`, `xsaveopt`, `xsavec`, `xsavec64`, `xsaves`,
+/// `xsaves64`, `xrstor`, `xrstors`, `xrstors64`, as well as many other instructions operating on
+/// bulk processor state and `mxcsr`.
+///
+/// these instructions are considered "complex" because the actual amount of data read or written
+/// depends on dynamic processor state, specifically, bits in `xcr0`. further, the upper bound of
+/// data read or written by these instructions is *also* processor-dependent - each architecture
+/// extension that adds processor state tends to have a corresponding bit opting it in or out of
+/// xsave state with these instructions.
+///
+/// gaps between enabled feature bits are possible and it would be legal for processors to change
+/// xsave layout based on enabled feature bits as well. no processor *does* this, but i'm not
+/// assuming this layout is fixed only to leave you holding the bag!
+///
+/// see the Intel SDM chapter `13.1 XSAVE-Supported Features And State-Component Bitmaps` for more
+/// details.
+///
+/// other related instructions, like `fnstenv`, `frstor`, and others, simply save and restore
+/// architectural state that is not expressed in the library API and cannot be included in implicit
+/// operand lists (such as `mxcsr`).
+///
+/// ### `in`, `out`, including rep-prefixed forms
+///
+/// port I/O instructions use a register or immedate to select an I/O port, meaning the literal
+/// operand and architectural operation are totally distinct. the library API does not currently
+/// have an operand form for I/O ports, so these instructions are "complex".
+///
+/// ### rep-prefixed string instructions
+///
+/// this section applies for all of `rep movs`, `rep stos`, `rep lods`, `rep scas`, and `rep cmps`.
+///
+/// these instructions are considered "complex" for two reasons. first, while these are single
+/// instructions, they do not execute atomically. then even if they were executed in a single
+/// architectural state update, the size of the memory access is a function of `ecx`.
+///
+/// worse, different processors can execute these instructions somewhat differently. from the Intel
+/// SDM:
+///
+/// > 7.3.9.3 Fast-String Operation
+/// >
+/// > [...] Instructions using fast-string operation effectively operate on the string in groups
+/// > that may include multiple elements of the native data size (byte, word, doubleword, or
+/// > quadword). With fast-string operation, the processor recognizes interrupts and data
+/// > breakpoints only on boundaries between these groups. [...]
+///
+/// the number of multiple elements is not defined, nor is the unit size of a fast string operation
+/// group, and that interrupts and breakpoints are only recognized on boundaries between these
+/// groups of implementation-defined size. even considering `rep`-prefixed string instructions as a
+/// series of the instruction data size is incorrect if the instruction's initial conditions are
+/// eligible for `Fast-String Operation` acceleration.
+///
+/// correctly interpreting the access pattern of these instructions depends heavily on the
+/// application needing this information.
+///
+/// ### AVX512 scatter/gather instructions
+///
+/// this section applies for all of `vpscatter{dd,dq,qd,qq}` and `vpgather{dd,dq,qd,qq}`.
+/// additionally, the vector scatter/gather prefetch instructions
+/// `v{gather,scatter}pf0{dps,dpd,qps,qpd}` are complex in part for these reasons.
+///
+/// these instructions are considered "complex" because their memory access characteristics are
+/// actually to many memory addresses using the lanes of the vector register used as an index in
+/// the memory operand. consider `vpscatterdd [r15 + ymm25], k6, ymm10`; this instruction has
+/// accesses four memory locations, one for each dword lane in `ymm25` as an offset to `r15`.
+///
+/// `yaxpeax-x86` does not have a way to name individual lanes of vector registers and may not ever
+/// add one if this is the only use. therefore there is no way to express these memory accesses and
+/// the instructions are considered complex.
+///
+/// the vector scatter/gather instructions additionally are complex for the same reasons as
+/// prefetch instructions described below.
+///
+/// ### `monitor`, `monitorx`, `mwait`, `mwaitx`
+///
+/// these instructions reference memory but neither read nor write it. instead, `monitor` primes
+/// hardware to watch for accesses to the specified address, while `mwait` waits for an access to
+/// some earlier `monitor`-primed adddress. this address-monitoring hardware is not expressed in
+/// the library API and makes this family of instructions "complex" due to reading or writing
+/// unrepresented state.
+///
+/// arguably `monitor` could be described as a load; it sets the A-bit in page tables, is ordered
+/// as a load, and is subject to the permission checking associated with a byte load.
+///
+/// ### `syscall/sysret`, `sysenter/sysexit`
+///
+/// these instructions are considered "complex" because they include implicit reads and possibly
+/// writes to various MSRs. further, depending on dynamic processor state (i.e. "is FRED enabled")
+/// these instructions may behave quite differently than a "normal" shuffling of
+/// `rip`/`rflags`/`cs`.
+///
+/// ### `vmread`, `vmwrite`, `vmrun`, `vmsave`, `vmload`, and SVM/VMX generally
+///
+/// for instructions that *have* an operand, their operand's semantics differs substantially from a
+/// "normal" understanding of the literal operand.
+///
+/// for `vmread` and `vmwrite`, the memory operand may be `[eax]`, but it is implicitly an access
+/// to the current VMCS - and, indeed, not even an access to "memory".
+///
+/// for `vmrun`, `vmsave`, and `vmload`, the operand is "`eax`", but expects `eax` to carry a
+/// physical address to a VMCB which is then loaded from or stored into.
+///
+/// generally, SVM/VMX instructions operate on a hidden VMCB/VMCS structure and are "complex" for
+/// interacting with architectural state that is not expressed in library APIs.
+///
+/// ### `vzeroupper`, `vzeroall`
+///
+/// these instructions are considered "complex" because their actual effect varies by processor
+/// implementation. when AVX512 is supported, these operate on the `zmm*` registers, otherwise they
+/// operate on `ymm*`.
+///
+/// ### `prefetchnta`, `prefetcht2`, `prefetcht1`, `prefetcht0`
+///
+/// these instructions are considered "complex" because they are hints, but have effects on
+/// microarchitectural state. the memory operand is documented as a 1-byte access, reported as a
+/// 32-byte access, but practically is implementation-defined and "a minimum of 32 bytes". the
+/// memory operand needs not be a valid address, either, and if it is not a mapped address then the
+/// `prefetch*` instructions do not raise a #PF.
+///
+/// in architectural terms, these instructions could have an operand access form of `Access::None`,
+/// but due to the microarchitectural effects this would be misleading. so, these are "complex" and
+/// should be handled by user code as a no-op, or read, or access hint, etc.
+///
+/// ### `clzero`, `clflush`, `clflushopt`, `clwb`
+///
+/// these instructions are "complex" because the amount of memory that is operated on is
+/// processor-dependent and the accessed address is *not* simply the effective address of the
+/// memory operand.
+///
+/// the size of an x86 cache line is _typically_ 64 bytes, but is reported per-processor in CPUID
+/// information (leaf `eax=1`: `clflush line size`, AMD leaf `eax=8000_0005`: `cache line size`).
+///
+/// some x86 processors have had 32-byte cache lines.
+///
+/// `clflush`, `clflushopt`, and `clwb` are closer to a no-op in terms of architectural state. they
+/// are included as "complex" for the reasons above and in support of library uses which want to
+/// precisely model memory, such as in modeling the execution of multi-processor systems.
+///
+/// ### `bts`, `btc`, `bt`
+///
+/// these instructions are *conditionally* "complex". when the destination is a memory operand they
+/// are complex because the effective address of the modified word/dword/qword is a function of
+/// both operands of the instruction.
+///
+/// in particular, the accessed location is the word/dword/qword at the first operand's effective
+/// address *plus* the second operand divided by the access size. as a worked example with a dword
+/// access:
+/// ```text
+/// eax := 0x1000_0100
+/// ecx := 0x203
+///
+/// // bts dword [eax], ecx
+/// ptr = eax + (ecx / 32) ; 0x1000_0303
+/// bit = ecx % 32 ; 3
+/// cf := (*ptr >> bit) & 1
+/// *ptr |= (1 << bit)
+///
+/// this was very dismaying to learn! the library API has no hope of expressing this! but the fact
+/// that the test harness detected this is strong evidence it works...
+/// ```
+///
+/// ### `enqcmd`, `enqcmds`
+///
+/// these instructions use "enqueue stores" to write to what are expected to be "enqueue registers"
+/// via MMIO. additionally, i do not have hardware to test these against "normal" memory, so these
+/// are "complex" out of caution.
+///
+/// ### CET-related instructions (`wrss`, `incssp`, `clrssbsy`, etc)
+///
+/// CET-related instructions manipulate shadow stack state, which is a kind-of-hidden architectural
+/// state that is not expressed in the library API. these instructions are considered "complex" due
+/// to reading or writing that state.
+///
+/// ### `ltr`, `str`, `lldt`, `sldt`, `lidt`, `sidt`, `lgdt`, `sgdt`, (AMD: `clgi`, `stgi`)
+///
+/// these instructions all directly manage architectural state which is not expressed in the
+/// library API.
+///
+/// ### `xgetbv`/`xsetbv`
+///
+/// these instructions operate on `xcr*` registers (namely, `xcr0`), which is not currently
+/// expressible in the library API, so these are considered "complex".
+///
+/// ### `v4f*madd`
+///
+/// `v4f*` family multiply-add instructions operate on ranges of registers that are not (currently)
+/// expressed precisely in the library API; the {x,y,z}mm register set these operate on is obtained by
+/// "mask the low two bits of the SIMD register, the result is the base of and the next three are
+/// the rest of the bank". this *could* be expressed in the library API but seems like it would be
+/// awkward. the instructions seem uncommon, so they are "complex" for expediency.
+///
+/// ### `movdir64b`
+///
+/// movdir64b is considered complex primarily because it has two memory operands, but the
+/// destination operand (first, in Intel syntax) is expressly *not* a memory operand so far as
+/// syntax is concerned.
+///
+/// ### `hreset`
+///
+/// `hreset` manages microarchitectural processor history, but is considered "complex" somewhat
+/// arbitrarily as its sole responsibility is to operate on state that is not expressed in the
+/// library API.
+///
+/// ### `psmash`, `pvalidate`, `rmpadjust`, `rmpupdate`
+///
+/// `psmash`-related instructions depend on architectural state which is described in more depth
+/// above, but not currently expressed in the library API, so they are "complex".
+///
+/// ### `ptwrite`
+///
+/// `ptwrite` modifies processor state that is not expressed in the library API currently, so it
+/// is "complex".
+///
+/// ### Restricted Transactional Memory (RTM)
+///
+/// `xbegin`, `xend`, `xtest`, and `xabort` are all "complex" because the RTM instructions relate
+/// to architectural state for memory transactions which are not expressed in the library API.
+/// additionally, these instructions have consequences for control flow that are not easily
+/// expressed in the library API.
+///
+/// ### `pconfig`
+///
+/// `pconfig` is "complex" because it alters architectural state and has complex semantics. the
+/// instruction is similar to `getsec` or `cpuid` in intended breadth and like `cpuid` could
+/// perhaps be made non-complex on the expectation that library users interested in *this
+/// instruction* would look for the opcode instead.
+///
+/// out of caution, and because this is a CPL=0-only relatively-rare instruction, this is still
+/// "complex".
+///
+/// ### `bndldx`, `bndstx`
+///
+/// these MPX instructions are "complex" because the interpretation of their operands differs
+/// substantially from the typical meaning, and they interact with architectural state (bounds
+/// table entries, "BTEs") that is not expressed in the library API.
+///
+/// ### `iret`, `iretd`, `iretq`
+///
+/// interrupt return instructions are considered "complex" purely for their semantics being, well,
+/// complex. they interact with the current execution mode, privilege level, requested privilege
+/// level of returned-to segments, and shadow stacks.
+///
+/// most architectural state they interact with is expressed in the library API. these are
+/// difficult to consider "complex" by the general guidelines above. in truth, they are complex
+/// mostly because they are uncommon, typically executed at CPL=0, and more difficult to
+/// comprehensively test. these may stop being considered complex in a future release.
+///
+/// ### OSPKE
+///
+/// `rdpkru` and `wrpkru` are considered complex because these instructions operate on the `pkru`
+/// register, which is not expressed in the library API today. these may stop being considered
+/// complex in a future release, at which point `pkru` would be an implicit operand as appropriate.
+///
+/// ### `rsm`
+///
+/// this instruction is considered complex for a few related reasons:
+///
+/// * yours truly does not really know much about SMM at all, so it's not clear if there are
+/// architectural state gotchas involved in transitioning to/from SMM
+/// * yours truly is not sure how much state is covered by the processor state save/restore on SMM
+/// transition, and has no way to validate if any implicit operand list describing the
+/// reads/writes is correct.
+///
+/// you know how to test SMM transitions and returns, please write me!
+///
+/// ### WAITPKG
+///
+/// `tpause`, `umonitor`, and `umwait` are considered complex for different reasons:
+///
+/// * `umonitor` and `umwait` are complex in similar ways to `monitor` and `mwait`.
+/// * `tpause` is considered "complex" because the implicit operands are compared with the TSC; one
+/// might imagine the library would report an implicit read of the TSC MSR, but there is no
+/// library API to describe MSR accesses yet.
+///
+/// ### UINTR
+///
+/// UINTR-related instructions are considered complex for varied reasons:
+///
+/// * `stui`, `clui`, `testui`: these instructions manipulate a bit in `rflags` and probably do not
+/// need to be complex (similar to `sti`, `cli`). these may lose their "complex" status in a future
+/// release.
+/// * `senduipi`: this instruction is "complex" because the user-IPI mechanism involves the
+/// user-interrupt target table (UITT) and referenced user posted-interrupt descriptor (UPID).
+/// * `uiret`: this instruction is only "complex" because it is considered uncommon (for now?),
+/// this author has no hardware to test it on, and it's not immediately clear how this relates to
+/// a corresponding UPID (if i've even read the documentation correctly!)
+///
+/// ### TDX
+///
+/// TDX-related instructions are considered complex because they are not more precisely tested and
+/// are assumed as-complex-as-VMX in the first place.
+// TODO: this could be declared through a macro that does something like:
+// "declare_opcode_subset! { }" which gets a list of identifiers and generates the
+// `Opcode::<ident> as u32` rhs. but a vim macro will do for now.
+#[non_exhaustive]
+#[repr(u32)] // same repr as `Opcode`
+#[derive(Copy, Clone, Debug)]
+#[allow(missing_docs)]
+pub enum ComplexOp {
+ /// rdmsr/wrmsr are considered "complex" for reasons in the enum doc comment.
+ RDMSR = (Opcode::RDMSR as u32),
+ WRMSR = (Opcode::WRMSR as u32),
+
+ /// `rdtsc` and `rdtscp` read MSRs and can be modeled as a special form of `rdmsr`; they are
+ /// "complex" in the same way.
+ RDTSC = (Opcode::RDTSC as u32),
+ RDTSCP = (Opcode::RDTSCP as u32),
+
+ /// `rdpru` reads MSRs and can be modeled as a special form of `rdmsr`; it is "complex" in the
+ /// same way.
+ RDPRU = (Opcode::RDPRU as u32),
+
+ /// instructions interacting with MSRs, such as these (`IA32_FS_BASE`, `IA32_GS_BASE`,
+ /// `IA32_KERNEL_GS_BASE`) are complex for the moment.
+ SWAPGS = (Opcode::SWAPGS as u32),
+ RDFSBASE = (Opcode::RDFSBASE as u32),
+ WRFSBASE = (Opcode::WRFSBASE as u32),
+ RDGSBASE = (Opcode::RDGSBASE as u32),
+ WRGSBASE = (Opcode::WRGSBASE as u32),
+
+ /// the bulk processor state save/restore instructions, as well as `mxcsr`-related
+ /// instructions, are considered complex for reasons described under `fxsave` in the enum doc
+ /// comment above.
+ FRSTOR = (Opcode::FRSTOR as u32),
+ FLDENV = (Opcode::FLDENV as u32),
+ FNSTENV = (Opcode::FNSTENV as u32),
+ FNSAVE = (Opcode::FNSAVE as u32),
+ FNSTCW = (Opcode::FNSTCW as u32),
+ FNSTSW = (Opcode::FNSTSW as u32),
+ FXSAVE = (Opcode::FXSAVE as u32),
+ FXRSTOR = (Opcode::FXRSTOR as u32),
+ LDMXCSR = (Opcode::LDMXCSR as u32),
+ VLDMXCSR = (Opcode::VLDMXCSR as u32),
+ STMXCSR = (Opcode::STMXCSR as u32),
+ VSTMXCSR = (Opcode::VSTMXCSR as u32),
+ XSAVE = (Opcode::XSAVE as u32),
+ XSAVEC = (Opcode::XSAVEC as u32),
+ XSAVES = (Opcode::XSAVES as u32),
+ XSAVEC64 = (Opcode::XSAVEC64 as u32),
+ XSAVES64 = (Opcode::XSAVES64 as u32),
+ XRSTOR = (Opcode::XRSTOR as u32),
+ XRSTORS = (Opcode::XRSTORS as u32),
+ XRSTORS64 = (Opcode::XRSTORS64 as u32),
+ XSAVEOPT = (Opcode::XSAVEOPT as u32),
+
+ /// in/out are considered "complex" for reasons in the enum doc comment.
+ IN = (Opcode::IN as u32),
+ OUT = (Opcode::OUT as u32),
+
+ /// string instructions are considered "complex" for reasons in the enum doc comment.
+ MOVS = (Opcode::MOVS as u32),
+ STOS = (Opcode::STOS as u32),
+ LODS = (Opcode::LODS as u32),
+ SCAS = (Opcode::SCAS as u32),
+ CMPS = (Opcode::CMPS as u32),
+
+ /// scatter/gather instructions are considered "complex" for reasons in the enum doc comment.
+ VPGATHERDD = (Opcode::VPGATHERDD as u32),
+ VPGATHERDQ = (Opcode::VPGATHERDQ as u32),
+ VPGATHERQD = (Opcode::VPGATHERQD as u32),
+ VPGATHERQQ = (Opcode::VPGATHERQQ as u32),
+ VGATHERDPD = (Opcode::VGATHERDPD as u32),
+ VGATHERDPS = (Opcode::VGATHERDPS as u32),
+ VGATHERQPD = (Opcode::VGATHERQPD as u32),
+ VGATHERQPS = (Opcode::VGATHERQPS as u32),
+
+ VPSCATTERDD = (Opcode::VPSCATTERDD as u32),
+ VPSCATTERDQ = (Opcode::VPSCATTERDQ as u32),
+ VPSCATTERQD = (Opcode::VPSCATTERQD as u32),
+ VPSCATTERQQ = (Opcode::VPSCATTERQQ as u32),
+
+ /// monitor/mwait instructions are considered "complex" for reasons in the enum doc comment.
+ MONITOR = (Opcode::MONITOR as u32),
+ MONITORX = (Opcode::MONITORX as u32),
+ MWAIT = (Opcode::MWAIT as u32),
+ MWAITX = (Opcode::MWAITX as u32),
+
+ /// the syscall/systenter and sysexit/sysret instructions are considered complex because of
+ /// their interaction with architectural state that is not expressible purely as register or
+ /// memory accesses.
+ SYSCALL = (Opcode::SYSCALL as u32),
+ SYSRET = (Opcode::SYSRET as u32),
+ SYSENTER = (Opcode::SYSENTER as u32),
+ SYSEXIT = (Opcode::SYSEXIT as u32),
+
+ /// SVM instructions generally are considered "complex" for reasons in the doc comment above.
+ SKINIT = (Opcode::SKINIT as u32),
+ VMLOAD = (Opcode::VMLOAD as u32),
+ VMMCALL = (Opcode::VMMCALL as u32),
+ VMSAVE = (Opcode::VMSAVE as u32),
+ VMRUN = (Opcode::VMRUN as u32),
+ VMPTRLD = (Opcode::VMPTRLD as u32),
+ VMPTRST = (Opcode::VMPTRST as u32),
+
+ /// VMX instructions, too, are considered "complex" for similar reasons as SVM.
+ VMXON = (Opcode::VMXON as u32),
+ VMXOFF = (Opcode::VMXOFF as u32),
+ VMREAD = (Opcode::VMREAD as u32),
+ VMWRITE = (Opcode::VMWRITE as u32),
+ VMCLEAR = (Opcode::VMCLEAR as u32),
+ VMCALL = (Opcode::VMCALL as u32),
+ VMLAUNCH = (Opcode::VMLAUNCH as u32),
+ VMRESUME = (Opcode::VMRESUME as u32),
+ VMFUNC = (Opcode::VMFUNC as u32),
+
+ /// vzeroupper/vzeroall are considered "complex" for reasons in the doc comment above.
+ VZEROUPPER = (Opcode::VZEROUPPER as u32),
+ VZEROALL = (Opcode::VZEROALL as u32),
+
+ /// clzero, clflush, clflushopt, and clwb are considered "complex" for reasons in the enum doc
+ /// comment.
+ CLZERO = (Opcode::CLZERO as u32),
+ CLFLUSH = (Opcode::CLFLUSH as u32),
+ CLFLUSHOPT = (Opcode::CLFLUSHOPT as u32),
+ CLWB = (Opcode::CLWB as u32),
+
+ /// prefetch instructions are considered "complex" for reasons in the enum doc comment.
+ PREFETCHNTA = (Opcode::PREFETCHNTA as u32),
+ PREFETCHT2 = (Opcode::PREFETCH2 as u32),
+ PREFETCHT1 = (Opcode::PREFETCH1 as u32),
+ PREFETCHT0 = (Opcode::PREFETCH0 as u32),
+
+ /// bit test/set/reset/complement instructions are conditionally complex depending on their
+ /// destination operand form, as described in the enum doc comment.
+ BT = (Opcode::BT as u32),
+ BTC = (Opcode::BTC as u32),
+ BTR = (Opcode::BTR as u32),
+ BTS = (Opcode::BTS as u32),
+
+ /// enqueue stores in an archtecturally interesting way, and write to
+ /// architecturally-interesting non-memory locations, so they are "complex".
+ ENQCMD = (Opcode::ENQCMD as u32),
+ ENQCMDS = (Opcode::ENQCMDS as u32),
+
+ /// shadow stacks and other CET machinery involve modifies processor state that cannot be
+ /// expressed by `yaxpeax-x86` as any particular location currently, so it is "complex".
+ WRUSS = (Opcode::WRUSS as u32),
+ WRSS = (Opcode::WRSS as u32),
+ INCSSP = (Opcode::INCSSP as u32),
+ SAVEPREVSSP = (Opcode::SAVEPREVSSP as u32),
+ SETSSBSY = (Opcode::SETSSBSY as u32),
+ CLRSSBSY = (Opcode::CLRSSBSY as u32),
+ RSTORSSP = (Opcode::RSTORSSP as u32),
+ ENDBR64 = (Opcode::ENDBR64 as u32),
+ ENDBR32 = (Opcode::ENDBR32 as u32),
+
+ /// str/ldr and sldt/lldt are considered complex because of their interaction with
+ /// architectural state that is not expressible purely as register or memory accesses.
+ STR = (Opcode::STR as u32),
+ LTR = (Opcode::LTR as u32),
+ SLDT = (Opcode::SLDT as u32),
+ LLDT = (Opcode::LLDT as u32),
+
+ /// likewise, the AMD global interrupt flag (GIF) is not expressible as an architectural
+ /// location by `yaxpeax-x86`, and so instructions operating on it are "complex".
+ CLGI = (Opcode::CLGI as u32),
+ STGI = (Opcode::STGI as u32),
+
+ /// `xgetbv`/`xsetbv` are "complex" because the library API does not have a way to express
+ /// extended control registers (xcr0 and the like).
+ XGETBV = (Opcode::XGETBV as u32),
+ XSETBV = (Opcode::XSETBV as u32),
+
+ /// `v4f*` family multiply-add instructions operate on ranges of registers that are not
+ /// (currently) expressed precisely in the library API
+ V4FNMADDSS = (Opcode::V4FNMADDSS as u32),
+ V4FNMADDPS = (Opcode::V4FNMADDPS as u32),
+ V4FMADDSS = (Opcode::V4FMADDSS as u32),
+ V4FMADDPS = (Opcode::V4FMADDPS as u32),
+
+ /// movdir64b is considered complex primarily because it has two memory operands, but the
+ /// destination operand (first, in Intel syntax) is expressly *not* a memory operand so far as
+ /// syntax is concerned.
+ MOVDIR64B = (Opcode::MOVDIR64B as u32),
+
+ /// `hreset` manages microarchitectural processor history, but is considered "complex" somewhat
+ /// arbitrarily as its sole responsibility is to operate on state that is not expressed in the
+ /// library API.
+ HRESET = (Opcode::HRESET as u32),
+
+ /// `psmash`-related instructions depend on architectural state which is described in more depth
+ /// above, but not currently expressed in the library API, so they are "complex".
+ PSMASH = (Opcode::PSMASH as u32),
+ PVALIDATE = (Opcode::PVALIDATE as u32),
+ RMPADJUST = (Opcode::RMPADJUST as u32),
+ RMPUPDATE = (Opcode::RMPUPDATE as u32),
+
+ /// `ptwrite` modifies processor state that is not expressed in the library API currently, so it
+ /// is "complex".
+ PTWRITE = (Opcode::PTWRITE as u32),
+
+ /// these instructions are all documented as complex for the reasons under `Restricted
+ /// Transactional Memory` (RTM) above.
+ XABORT = (Opcode::XABORT as u32),
+ XBEGIN = (Opcode::XBEGIN as u32),
+ XEND = (Opcode::XEND as u32),
+ XTEST = (Opcode::XTEST as u32),
+
+ /// `pconfig` is "complex" because it alters architectural state and has complex semantics.
+ PCONFIG = (Opcode::PCONFIG as u32),
+
+ /// some MPX-related instructions are considered complex for the reasons described in the enum
+ /// doc comment above.
+ BNDLDX = (Opcode::BNDLDX as u32),
+ BNDSTX = (Opcode::BNDSTX as u32),
+
+ /// `iret*` instructions are considered complex for the reasons described in the enum doc
+ /// comment above.
+ IRET = (Opcode::IRET as u32),
+ IRETD = (Opcode::IRETD as u32),
+ IRETQ = (Opcode::IRETQ as u32),
+
+ /// enclave-related instructions are considered complex for the reasons described in the enum
+ /// doc comment above.
+ ENCLS = (Opcode::ENCLS as u32),
+ ENCLV = (Opcode::ENCLV as u32),
+ ENCLU = (Opcode::ENCLU as u32),
+
+ /// OSPKE-related instructions are considered complex for the reasons described in the enum doc
+ /// comment above.
+ RDPKRU = (Opcode::RDPKRU as u32),
+ WRPKRU = (Opcode::WRPKRU as u32),
+
+ /// `rsm` is considered complex for the reasons related to SMM described in the enum doc
+ /// comment above.
+ RSM = (Opcode::RSM as u32),
+
+ /// WAITPKG-related instructions are considered complex for the reasons described in the enum
+ /// doc comment above.
+ TPAUSE = (Opcode::TPAUSE as u32),
+ UMONITOR = (Opcode::UMONITOR as u32),
+ UMWAIT = (Opcode::UMWAIT as u32),
+
+ /// UINTR-related instructions are considered complex for the reasons described in the enum
+ /// doc comment above.
+ UIRET = (Opcode::UIRET as u32),
+ TESTUI = (Opcode::TESTUI as u32),
+ CLUI = (Opcode::CLUI as u32),
+ STUI = (Opcode::STUI as u32),
+ SENDUIPI = (Opcode::SENDUIPI as u32),
+
+ /// TDX-related instructions are considered complex for the reasons described in the enum
+ /// doc comment above.
+ TDCALL = (Opcode::TDCALL as u32),
+ SEAMRET = (Opcode::SEAMRET as u32),
+ SEAMOPS = (Opcode::SEAMOPS as u32),
+ SEAMCALL = (Opcode::SEAMCALL as u32),
+
+ /// vector scatter/gather prefetch instructions are considered complex for the reasons "normal"
+ /// scatter/gather are complex, as well as the reasons "normal" prefetch instructions are
+ /// complex.
+ VGATHERPF0DPD = (Opcode::VGATHERPF0DPD as u32),
+ VGATHERPF0DPS = (Opcode::VGATHERPF0DPS as u32),
+ VGATHERPF0QPD = (Opcode::VGATHERPF0QPD as u32),
+ VGATHERPF0QPS = (Opcode::VGATHERPF0QPS as u32),
+ VGATHERPF1DPD = (Opcode::VGATHERPF1DPD as u32),
+ VGATHERPF1DPS = (Opcode::VGATHERPF1DPS as u32),
+ VGATHERPF1QPD = (Opcode::VGATHERPF1QPD as u32),
+ VGATHERPF1QPS = (Opcode::VGATHERPF1QPS as u32),
+ VSCATTERPF0DPD = (Opcode::VSCATTERPF0DPD as u32),
+ VSCATTERPF0DPS = (Opcode::VSCATTERPF0DPS as u32),
+ VSCATTERPF0QPD = (Opcode::VSCATTERPF0QPD as u32),
+ VSCATTERPF0QPS = (Opcode::VSCATTERPF0QPS as u32),
+ VSCATTERPF1DPD = (Opcode::VSCATTERPF1DPD as u32),
+ VSCATTERPF1DPS = (Opcode::VSCATTERPF1DPS as u32),
+ VSCATTERPF1QPD = (Opcode::VSCATTERPF1QPD as u32),
+ VSCATTERPF1QPS = (Opcode::VSCATTERPF1QPS as u32),
+}
+
+/// a visitor for collecting architectural accesses for an `Instruction`. used with
+/// [`InstBehavior::visit_accesses`].
+///
+/// ## address calculation
+///
+/// [`memory_read()`][AccessVisitor::memory_read] and
+/// [`memory_write()`][AccessVisitor::memory_write] take an optional parameter for an effective
+/// address that is either read or written. by default, the address provided is typically `None`,
+/// but with appropriate implementations of this trait, `yaxpeax-x86` will calculate and report the
+/// effective addresses of memory acceses. when visiting a memory operand, the library will call
+/// [`get_register()`][AccessVisitor::get_register] on each register used in an operand's address
+/// calculation; if all calls return a value, then the library will compute an address and provide
+/// it in the corresponding `memory_read()` or `memory_write()`.
+///
+/// the default `get_register()` implementation does not return register values, but does call
+/// `register_read()`. this means that `register_read()` is called for each register that may be
+/// used by the instruction in question. if this is desirable and you are providing a custom
+/// implementation of `get_register()`, be sure to include a `register_read()`! alternatively, if
+/// `get_register()` is made to not call `register_read()`, then the other functions in this trait
+/// (`{register,memory}_{read,write}`()) are called one-to-one for implicit or explicit operands of
+/// this instruction.
+pub trait AccessVisitor {
+ /// record that the instruction reads a register. note that the default implementation of
+ /// [`AccessVisitor::get_register`] also calls `register_read`; registers used as part of an
+ /// address calculation for memory accesses are recorded via `register_read()` by default!
+ fn register_read(&mut self, reg: RegSpec);
+ /// record that the instruction writes a register.
+ fn register_write(&mut self, reg: RegSpec);
+ /// get a numeric value for `reg`, if possible. this is called as part of computing effective
+ /// addresses used in [`AccessVisitor::memory_read`] and [`AccessVisitor::memory_write`], for
+ /// each register involved in an address calculation.
+ ///
+ /// if any `get_register()` returns `None` in an address calculation, the subsequent
+ /// `memory_read()` or `memory_write()` for that operand will be given an `address` of `None`.
+ ///
+ /// `get_register()` may be implemented withhout calling `register_read()`, in which case when
+ /// used with `visit_accesses` the register/memory read/writes will all correspond directly to
+ /// implicit and explicit operands.
+ fn get_register(&mut self, reg: RegSpec) -> Option<u32> {
+ self.register_read(reg);
+ None
+ }
+ /// record that the instruction reads a memory location.
+ ///
+ /// when used with `visit_accesses`, an address is only provided when yaxpeax-x86 can calculate
+ /// an effective address (i.e. `get_register()` calls for all dependent registers return a
+ /// value). all non-`ComplexOp` instructions have a known memory access size, so this is always
+ /// reported regardless of if *where* is not known.
+ ///
+ /// some instructions can both read and write memory (consider `call [addr]`).
+ fn memory_read(&mut self, address: Option<u32>, size: u32);
+ /// record that the instruction writes a memory location.
+ ///
+ /// when used with `visit_accesses`, an address is only provided when yaxpeax-x86 can calculate
+ /// an effective address (i.e. `get_register()` calls for all dependent registers return a
+ /// value). all non-`ComplexOp` instructions have a known memory access size, so this is always
+ /// reported regardless of if *where* is not known.
+ ///
+ /// some instructions can both read and write memory (consider `call [addr]`).
+ fn memory_write(&mut self, address: Option<u32>, size: u32);
+}
+
+#[cfg(all(test, feature = "std"))]
+mod test {
+ use super::*;
+ use crate::protected_mode::InstDecoder;
+
+ use alloc::vec;
+ use alloc::vec::Vec;
+
+ #[test]
+ fn access_visitor_works() {
+ // xor eax, dword [ecx]
+ let bytes = &[0x33, 0x01];
+ let inst = InstDecoder::default().decode_slice(bytes).expect("can decode trivial instructions");
+
+ struct AccessCtx {
+ ecx: u32,
+
+ accesses: Vec<(RegSpec, Access)>,
+ mem_accesses: Vec<((Option<u32>, u32), Access)>,
+ }
+
+ let mut ctx = AccessCtx {
+ ecx: 0x10000,
+ accesses: Vec::new(),
+ mem_accesses: Vec::new(),
+ };
+
+ impl AccessVisitor for AccessCtx {
+ fn register_read(&mut self, reg: RegSpec) {
+ self.accesses.push((reg, Access::Read));
+ }
+
+ fn register_write(&mut self, reg: RegSpec) {
+ self.accesses.push((reg, Access::Write));
+ }
+
+ fn get_register(&mut self, reg: RegSpec) -> Option<u32> {
+ self.register_read(reg);
+
+ if reg == RegSpec::ecx() {
+ Some(self.ecx)
+ } else {
+ None
+ }
+ }
+
+ fn memory_read(&mut self, address: Option<u32>, size: u32) {
+ self.mem_accesses.push(((address, size), Access::Read));
+ }
+
+ fn memory_write(&mut self, address: Option<u32>, size: u32) {
+ self.mem_accesses.push(((address, size), Access::Write));
+ }
+ }
+
+ let behavior = inst.behavior();
+ behavior.visit_accesses(&mut ctx).expect("xor eax, [ecx] is not complex");
+
+ assert_eq!(ctx.accesses, vec![
+ (RegSpec::eflags(), Access::Write),
+ (RegSpec::eax(), Access::Read),
+ (RegSpec::eax(), Access::Write),
+ (RegSpec::ecx(), Access::Read)
+ ]);
+ assert_eq!(ctx.mem_accesses, vec![((Some(0x10000), 4), Access::Read)]);
+ }
+
+ #[test]
+ fn operand_iter_basically_works() {
+ // xor eax, eax
+ let bytes = &[0x33, 0xc0];
+ let inst = InstDecoder::default().decode_slice(bytes).expect("can decode trivial instructions");
+
+ // uwu whats this...
+ let behavior = inst.behavior();
+
+ // owo hewwo there
+ let operands = behavior.all_operands().expect("xor eax, eax is not complex");
+
+ // OwO waowwww
+ let collected: alloc::vec::Vec<(Operand, Access)> = operands.iter().collect();
+ let expected = alloc::vec![
+ (Operand::Register { reg: RegSpec::eflags() }, Access::Write),
+ (Operand::Register { reg: RegSpec::eax() }, Access::ReadWrite),
+ (Operand::Register { reg: RegSpec::eax() }, Access::Read),
+ ];
+ assert_eq!(collected, expected);
+
+ #[cfg(feature = "unstable")]
+ {
+ assert_eq!(behavior.privilege_level(), Some(PrivilegeLevel::Any));
+ let exceptions = behavior.exceptions();
+ assert!(exceptions.none());
+ }
+
+ // but if an operand does a memory access, that can fault:
+ // xor eax, [eax]
+ let bytes = &[0x33, 0x00];
+ let inst = InstDecoder::default().decode_slice(bytes).expect("can decode trivial instructions");
+ let behavior = inst.behavior();
+ let operands = behavior.all_operands().expect("xor eax, eax is not complex");
+
+ let collected: alloc::vec::Vec<(Operand, Access)> = operands.iter().collect();
+ let expected = alloc::vec![
+ (Operand::Register { reg: RegSpec::eflags() }, Access::Write),
+ (Operand::Register { reg: RegSpec::eax() }, Access::ReadWrite),
+ (Operand::MemDeref { base: RegSpec::eax() }, Access::Read),
+ ];
+ assert_eq!(collected, expected);
+
+ #[cfg(feature="unstable")]
+ {
+ assert_eq!(behavior.privilege_level(), Some(PrivilegeLevel::Any));
+ let exceptions = behavior.exceptions();
+ assert!(!exceptions.none());
+ assert!(exceptions.may(Exception::PF));
+ }
+ }
+}
+
+/// no operations, but you can run it anywhere.
+const GENERAL: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any();
+
+/// instructions that can execute at all privilege levels, have two operands, read/write the first,
+/// and read the second.
+const GENERAL_RW_R: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::ReadWrite)
+ .set_operand(1, Access::Read);
+
+/// same as above, but writes flags. this is most arithmetic instructions.
+const GENERAL_RW_R_FLAGWRITE: BehaviorDigest = GENERAL_RW_R
+ .set_flags_access(Access::Write);
+
+/// popcnt and maybe others?
+const GENERAL_W_R_FLAGWRITE: BehaviorDigest = GENERAL_RW_R
+ .set_operand(0, Access::Write)
+ .set_flags_access(Access::Write);
+
+/// test, cmp, with no write but to flags.
+const GENERAL_R_R_FLAGWRITE: BehaviorDigest = GENERAL_RW_R
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::Write);
+
+/// `sbb`, `adc`, etc both read flags and write them.
+const GENERAL_RW_R_FLAGRW: BehaviorDigest = GENERAL_RW_R
+ .set_flags_access(Access::ReadWrite);
+
+/// `xadd` reads everything and writes everything, even flags!
+const GENERAL_RW_RW_FLAGRW: BehaviorDigest = GENERAL_RW_R_FLAGRW
+ .set_operand(1, Access::ReadWrite);
+
+/// setcc and friends read flags to maybe write their operand.
+const GENERAL_RW_FLAGREAD: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::ReadWrite)
+ .set_flags_access(Access::Read);
+
+/// cmov reads from a second operand and (may) writes to the first.
+const GENERAL_W_R_FLAGREAD: BehaviorDigest = GENERAL_RW_FLAGREAD
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read);
+
+/// cmc, clc, sti, cli, etc that toggle individual bits in flags
+const GENERAL_FLAGRW: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::ReadWrite);
+
+/// `inc`, `dec`, and `neg` have one operand and modify flags.
+const GENERAL_RW_FLAGWRITE: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::ReadWrite)
+ .set_flags_access(Access::Write);
+
+/// `inc`, `dec`, and `neg` have one operand and modify flags.
+const GENERAL_RW: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::ReadWrite);
+
+const GENERAL_R_R: BehaviorDigest = GENERAL_RW_R
+ .set_operand(0, Access::Read);
+
+/// `ins` writes to the memory operand, reads from `rdx` (second operand)
+const GENERAL_W_R: BehaviorDigest = GENERAL_RW_R
+ .set_operand(0, Access::Write);
+
+/// many vex/evex-encoded instructions
+const GENERAL_W_R_R: BehaviorDigest = GENERAL_W_R
+ .set_operand(2, Access::Read);
+
+/// and for vex/evex-encoded instructions with an imm8 suffix
+///
+/// this is not distinct from a `GENERAL_W_R_R_R`, but is named distinctly in case yaxpeax-x86
+/// should report imm8 operands differently from "read" or "write".
+const GENERAL_W_R_R_IMM8: BehaviorDigest = GENERAL_W_R_R
+ .set_operand(3, Access::Read);
+
+/// shld
+const GENERAL_RW_R_R: BehaviorDigest = GENERAL_W_R_R
+ .set_operand(0, Access::ReadWrite);
+
+const GENERAL_RW_RW: BehaviorDigest = GENERAL_RW_R
+ .set_operand(1, Access::ReadWrite);
+
+const GENERAL_RW_RW_FLAGWRITE: BehaviorDigest = GENERAL_RW_RW
+ .set_flags_access(Access::Write);
+
+const GENERAL_R: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read);
+
+const GENERAL_W: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write);
+
+const GENERAL_R_FLAGREAD: BehaviorDigest = GENERAL_R
+ .set_flags_access(Access::Read);
+
+const GENERAL_R_FLAGWRITE: BehaviorDigest = GENERAL_R
+ .set_flags_access(Access::Write);
+
+// TODO: seems incredibly funky that jcc's operand is an immediate, when written like this..
+const JCC: BehaviorDigest = BehaviorDigest::empty()
+ .set_implicit_ops(JCC_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read);
+
+const CMOVCC: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Read)
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read);
+
+const SETCC: BehaviorDigest = BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Read)
+ .set_operand(0, Access::Write);
+
+static PUSH_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -8i32,
+ write: true,
+ },
+ // push.. pushes the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static POP_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::esp(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static JCC_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CBW_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CWDE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CDQE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ }
+];
+
+// TODO: these need ... something.
+//
+// note CQD, CDQ, CQO:
+//
+// these are writes to dx/edx/rdx but *not* `*ax`. this is because while these registers "write"
+// sign-extended *ax to *ax:*dx, "writes" to eax:edx do not modify the upper 32 bits of rax. that
+// is to say, that if `rax` is 0x8000_1234_c000_0000 and a `cdq` is executed, the result is:
+// ```
+// rax = 0x8000_1234_c000_0000
+// rdx = 0x0000_0000_ffff_ffff
+// ```
+//
+// cool, huh!!
+static CWD_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CDQ_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CQO_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static PUSHF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -8i32,
+ write: true,
+ },
+ // push.. pushes the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static POPF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::esp(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static SAHF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ah(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static LAHF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ah(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static MOVS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 1,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 1,
+ write: true,
+ },
+];
+
+static LODS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 1,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 1,
+ write: true,
+ },
+];
+
+static STOS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static SCAS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref_edi,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eflags(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static RETURN_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::esp(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static LEAVE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::esp(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static ENTER_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -8i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static XLAT_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ // xlat is the only implicit operand to use a base/index addressing scheme, so note the
+ // base (ebx) and handle the implicit al index in code..?
+ spec: OperandSpec::MemIndexScale,
+ reg: RegSpec::ebx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CLTS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cr2(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cr2(),
+ disp: 0,
+ write: true,
+ },
+];
+
+// the actual implicit operands of `{i,}mul` are broken out by operand count and operation size..
+static MUL_OPS_1OP_BYTE: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_WORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_DWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static MUL_OPS_1OP_QWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
+// the actual implicit operands of `{i,}div` are broken out by operation size..
+static DIV_OPS_1OP_BYTE: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ },
+];
+static DIV_OPS_1OP_WORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static DIV_OPS_1OP_DWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+static DIV_OPS_1OP_QWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
+static RDTSC_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
+static RDTSCP_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
+static RDPMC_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ }
+];
+
+static CPUID_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebx(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static CALL_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -8i32,
+ write: true,
+ },
+ // push.. pushes the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static JMP_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static JMPF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cs(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CALLF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cs(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -10i32,
+ write: true,
+ },
+ // push.. pushes the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static RETF_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::esp(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eip(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cs(),
+ disp: 0,
+ write: true,
+ },
+ // pop.. pops the value (above), then does a RMW on rsp.
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static LFS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::fs(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static LGS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::gs(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static LSS_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ss(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static CMPXCHG_OPS_BYTE: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CMPXCHG_OPS_WORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CMPXCHG_OPS_DWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CMPXCHG_OPS_QWORD: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CMPXCHG8B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static CMPXCHG16B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static MASKMOVQ_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static MONITOR_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static XMM0_READ_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm0(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static MULX_64B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static MULX_32B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static EDI_MEMWRITE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static RDI_MEMWRITE_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::Deref,
+ reg: RegSpec::edi(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPESTRI_64B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPESTRI_32B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPESTRM_64B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm0(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPESTRM_32B_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm0(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPISTRI_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static PCMPISTRM_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm0(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static READ_EDX_EAX_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static LMSW_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cr0(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cr0(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static SMSW_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cr0(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static READ_EAX_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static WRITE_AL_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static RW_XMM0TO7_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(0),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(1),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(2),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(3),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(4),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(5),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(6),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(7),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(0),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(1),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(2),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(3),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(4),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(5),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(6),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(7),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static ENCODEKEY_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(0),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(0),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(1),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(2),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(4),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(5),
+ disp: 0i32,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(6),
+ disp: 0i32,
+ write: true,
+ },
+];
+
+static LOADIWKEY_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0i32,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::xmm(0),
+ disp: 0i32,
+ write: false,
+ },
+];
+
+static RW_ECX_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static RW_CX_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cx(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static RW_AX_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static RW_AL_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::al(),
+ disp: 0,
+ write: true,
+ },
+];
+
+static PUSHAD_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esi(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edi(),
+ disp: 0,
+ write: false,
+ },
+ // actually .. push values
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: -4i32 * 8,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static POPAD_OPS: &'static [ImplicitOperand] = &[
+ // read "the stack"
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: false,
+ },
+ // and write registers
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::eax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ecx(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edx(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebx(),
+ disp: 0,
+ write: true,
+ },
+ // no pop to sp
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ebp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esi(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::edi(),
+ disp: 0,
+ write: true,
+ },
+ // and finally write back modified sp
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::esp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static PUSHA_OPS: &'static [ImplicitOperand] = &[
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::sp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::bx(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::bp(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::si(),
+ disp: 0,
+ write: false,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::di(),
+ disp: 0,
+ write: false,
+ },
+ // actually .. push values
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::sp(),
+ disp: -2i32 * 8,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::sp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+static POPA_OPS: &'static [ImplicitOperand] = &[
+ // read "the stack"
+ ImplicitOperand {
+ spec: OperandSpec::Disp,
+ reg: RegSpec::sp(),
+ disp: 0,
+ write: false,
+ },
+ // and write registers
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::ax(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::cx(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::dx(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::bx(),
+ disp: 0,
+ write: true,
+ },
+ // no pop to sp
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::bp(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::si(),
+ disp: 0,
+ write: true,
+ },
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::di(),
+ disp: 0,
+ write: true,
+ },
+ // and finally write back modified sp
+ ImplicitOperand {
+ spec: OperandSpec::RegRRR,
+ reg: RegSpec::sp(),
+ disp: 0,
+ write: true,
+ }
+];
+
+const PUSH_OPS_IDX: u16 = 1;
+const POP_OPS_IDX: u16 = 2;
+const JCC_OPS_IDX: u16 = 3;
+const CBW_IDX: u16 = 4;
+const CWDE_IDX: u16 = 5;
+const CDQE_IDX: u16 = 6;
+const CWD_IDX: u16 = 7;
+const CDQ_IDX: u16 = 8;
+const CQO_IDX: u16 = 9;
+const PUSHF_IDX: u16 = 10;
+const POPF_IDX: u16 = 11;
+const SAHF_IDX: u16 = 12;
+const LAHF_IDX: u16 = 13;
+const MOVS_IDX: u16 = 14;
+const LODS_IDX: u16 = 15;
+const STOS_IDX: u16 = 16;
+const SCAS_IDX: u16 = 17;
+const RETURN_IDX: u16 = 18;
+const LEAVE_IDX: u16 = 19;
+const XLAT_IDX: u16 = 20;
+const CLTS_IDX: u16 = 21;
+const MUL_IDX_1OP_BYTE: u16 = 22;
+const MUL_IDX_1OP_WORD: u16 = 23;
+const MUL_IDX_1OP_DWORD: u16 = 24;
+const MUL_IDX_1OP_QWORD: u16 = 25;
+const DIV_IDX_1OP_BYTE: u16 = 26;
+const DIV_IDX_1OP_WORD: u16 = 27;
+const DIV_IDX_1OP_DWORD: u16 = 28;
+const DIV_IDX_1OP_QWORD: u16 = 29;
+const RDTSC_IDX: u16 = 30;
+const RDPMC_IDX: u16 = 31;
+const CPUID_IDX: u16 = 32;
+const CALL_OPS_IDX: u16 = 33;
+const JMP_OPS_IDX: u16 = 34;
+const CALLF_OPS_IDX: u16 = 35;
+const JMPF_OPS_IDX: u16 = 36;
+const LFS_IDX: u16 = 37;
+const LGS_IDX: u16 = 38;
+const LSS_IDX: u16 = 39;
+const CMPXCHG_IDX_BYTE: u16 = 40;
+const CMPXCHG_IDX_WORD: u16 = 41;
+const CMPXCHG_IDX_DWORD: u16 = 42;
+const CMPXCHG_IDX_QWORD: u16 = 43;
+const CMPXCHG8B_IDX: u16 = 44;
+const CMPXCHG16B_IDX: u16 = 45;
+const RDTSCP_IDX: u16 = 46;
+const MASKMOVQ_IDX: u16 = 47;
+const MONITOR_IDX: u16 = 48;
+const XMM0_READ_IDX: u16 = 49;
+const MULX_64B_IDX: u16 = 50;
+const MULX_32B_IDX: u16 = 51;
+const EDI_MEMWRITE_IDX: u16 = 52;
+const RDI_MEMWRITE_IDX: u16 = 53;
+const PCMPESTRI_64B_IDX: u16 = 54;
+const PCMPESTRI_32B_IDX: u16 = 55;
+const PCMPESTRM_64B_IDX: u16 = 56;
+const PCMPESTRM_32B_IDX: u16 = 57;
+const PCMPISTRI_IDX: u16 = 58;
+const PCMPISTRM_IDX: u16 = 59;
+const READ_EDX_EAX_IDX: u16 = 60;
+const RETF_IDX: u16 = 61;
+const LMSW_IDX: u16 = 62;
+const SMSW_IDX: u16 = 63;
+const READ_EAX_IDX: u16 = 64;
+const WRITE_AL_IDX: u16 = 65;
+const RW_XMM0TO7_IDX: u16 = 66;
+const ENCODEKEY_IDX: u16 = 67;
+const LOADIWKEY_IDX: u16 = 68;
+const RW_ECX_IDX: u16 = 69;
+const RW_CX_IDX: u16 = 70;
+const ENTER_IDX: u16 = 71;
+const RW_AX_IDX: u16 = 72;
+const RW_AL_IDX: u16 = 73;
+const PUSHA_IDX: u16 = 74;
+const POPA_IDX: u16 = 75;
+const PUSHAD_IDX: u16 = 76;
+const POPAD_IDX: u16 = 77;
+
+static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 78] = [
+ &[], // implicit ops list 0 is not used
+ PUSH_OPS,
+ POP_OPS,
+ JCC_OPS,
+ CBW_OPS,
+ CWDE_OPS,
+ CDQE_OPS,
+ CWD_OPS,
+ CDQ_OPS,
+ CQO_OPS,
+ PUSHF_OPS,
+ POPF_OPS,
+ SAHF_OPS,
+ LAHF_OPS,
+ MOVS_OPS,
+ LODS_OPS,
+ STOS_OPS,
+ SCAS_OPS,
+ RETURN_OPS,
+ LEAVE_OPS,
+ XLAT_OPS,
+ CLTS_OPS,
+ MUL_OPS_1OP_BYTE,
+ MUL_OPS_1OP_WORD,
+ MUL_OPS_1OP_DWORD,
+ MUL_OPS_1OP_QWORD,
+ DIV_OPS_1OP_BYTE,
+ DIV_OPS_1OP_WORD,
+ DIV_OPS_1OP_DWORD,
+ DIV_OPS_1OP_QWORD,
+ RDTSC_OPS,
+ RDPMC_OPS,
+ CPUID_OPS,
+ CALL_OPS,
+ JMP_OPS,
+ CALLF_OPS,
+ JMPF_OPS,
+ LFS_OPS,
+ LGS_OPS,
+ LSS_OPS,
+ CMPXCHG_OPS_BYTE,
+ CMPXCHG_OPS_WORD,
+ CMPXCHG_OPS_DWORD,
+ CMPXCHG_OPS_QWORD,
+ CMPXCHG8B_OPS,
+ CMPXCHG16B_OPS,
+ RDTSCP_OPS,
+ MASKMOVQ_OPS,
+ MONITOR_OPS,
+ XMM0_READ_OPS,
+ MULX_64B_OPS,
+ MULX_32B_OPS,
+ EDI_MEMWRITE_OPS,
+ RDI_MEMWRITE_OPS,
+ PCMPESTRI_64B_OPS,
+ PCMPESTRI_32B_OPS,
+ PCMPESTRM_64B_OPS,
+ PCMPESTRM_32B_OPS,
+ PCMPISTRI_OPS,
+ PCMPISTRM_OPS,
+ READ_EDX_EAX_OPS,
+ RETF_OPS,
+ LMSW_OPS,
+ SMSW_OPS,
+ READ_EAX_OPS,
+ WRITE_AL_OPS,
+ RW_XMM0TO7_OPS,
+ ENCODEKEY_OPS,
+ LOADIWKEY_OPS,
+ RW_ECX_OPS,
+ RW_CX_OPS,
+ ENTER_OPS,
+ RW_AX_OPS,
+ RW_AL_OPS,
+ PUSHA_OPS,
+ POPA_OPS,
+ PUSHAD_OPS,
+ POPAD_OPS,
+];
+
+fn opcode2behavior(opc: &Opcode) -> BehaviorDigest {
+ let idx = (*opc as u32) & 0xfff;
+ TABLE[idx as usize]
+}
+
+#[cfg(feature = "_debug_internal_asserts")]
+#[test]
+fn behavior_table_size_is_right() {
+ use strum::EnumCount;
+ assert_eq!(TABLE.len(), super::Opcode::COUNT);
+
+ assert_eq!(opcode2behavior(&Opcode::VMOVLHPS), GENERAL_W_R_R);
+}
+
+/// this table MUST line up with Opcode declaration order in `mod.rs`.
+static TABLE: [BehaviorDigest; 1425] = [
+ /* ADD => */ GENERAL_RW_R_FLAGWRITE,
+ /* OR => */ GENERAL_RW_R_FLAGWRITE,
+ /* ADC => */ GENERAL_RW_R_FLAGRW,
+ /* SBB => */ GENERAL_RW_R_FLAGRW,
+ /* AND => */ GENERAL_RW_R_FLAGWRITE,
+ /* SUB => */ GENERAL_RW_R_FLAGWRITE,
+ /* XOR => */ GENERAL_RW_R_FLAGWRITE,
+ /* CMP => */ GENERAL_R_R_FLAGWRITE,
+ /* ROL => */ GENERAL_RW_R_FLAGWRITE,
+ /* ROR => */ GENERAL_RW_R_FLAGWRITE,
+ /* RCL => */ GENERAL_RW_R_FLAGRW,
+ /* RCR => */ GENERAL_RW_R_FLAGRW,
+ /* SHL => */ GENERAL_RW_R_FLAGWRITE,
+ /* SHR => */ GENERAL_RW_R_FLAGWRITE,
+ /* SAL => */ GENERAL_RW_R_FLAGWRITE,
+ /* SAR => */ GENERAL_RW_R_FLAGWRITE,
+ /* BTC => */ GENERAL_RW_R_FLAGWRITE
+ .set_complex(true),
+ /* BTR => */ GENERAL_RW_R_FLAGWRITE
+ .set_complex(true),
+ /* BTS => */ GENERAL_RW_R_FLAGWRITE
+ .set_complex(true),
+ /* CMPXCHG => */ GENERAL_RW_R_FLAGWRITE
+ .set_nontrivial(true),
+ /* CMPXCHG8B => */ GENERAL_RW_R_FLAGWRITE
+ .set_implicit_ops(CMPXCHG8B_IDX),
+ /* CMPXCHG16B => */ GENERAL_RW_R_FLAGWRITE
+ .set_implicit_ops(CMPXCHG16B_IDX),
+ /* DEC => */ GENERAL_RW_FLAGWRITE,
+ /* INC => */ GENERAL_RW_FLAGWRITE,
+ /* NEG => */ GENERAL_RW_FLAGWRITE,
+ /* NOT => */ GENERAL_RW,
+ /* XADD => */ GENERAL_RW_RW_FLAGRW,
+ /* XCHG => */ GENERAL_RW_RW,
+
+ /* CMPS => */ GENERAL_RW_RW_FLAGWRITE
+ .set_implicit_ops(MOVS_IDX),
+ /* SCAS => */ GENERAL_W_R_FLAGREAD
+ .set_implicit_ops(SCAS_IDX), // TODO: second operand is `aX`, right?
+ /* MOVS => */ GENERAL_W_R_FLAGREAD
+ .set_implicit_ops(MOVS_IDX),
+ /* LODS => */ GENERAL_W_R_FLAGREAD
+ .set_implicit_ops(LODS_IDX),
+ /* STOS => */ GENERAL_W_R_FLAGREAD
+ .set_implicit_ops(STOS_IDX),
+ /* INS => */ GENERAL_W_R,
+ /* OUTS => */ GENERAL_R_R,
+
+ // "Invalid" should never be a publicly-visible Opcode variant..
+ /* Invalid => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* BT => */ GENERAL_R_R_FLAGWRITE
+ .set_complex(true),
+ /* BSF => */ GENERAL_RW_R_FLAGWRITE,
+ /* BSR => */ GENERAL_RW_R_FLAGWRITE,
+ /* TZCNT => */ GENERAL_RW_R_FLAGWRITE,
+ /* MOVSS => */ GENERAL_RW_R,
+ /* ADDSS => */ GENERAL_RW_R,
+ /* SUBSS => */ GENERAL_RW_R,
+ /* MULSS => */ GENERAL_RW_R,
+ /* DIVSS => */ GENERAL_RW_R,
+ /* MINSS => */ GENERAL_RW_R,
+ /* MAXSS => */ GENERAL_RW_R,
+ /* SQRTSS => */ GENERAL_RW_R,
+ /* MOVSD => */ GENERAL_RW_R,
+ /* SQRTSD => */ GENERAL_RW_R,
+ /* ADDSD => */ GENERAL_RW_R,
+ /* SUBSD => */ GENERAL_RW_R,
+ /* MULSD => */ GENERAL_RW_R,
+ /* DIVSD => */ GENERAL_RW_R,
+ /* MINSD => */ GENERAL_RW_R,
+ /* MAXSD => */ GENERAL_RW_R,
+ /* MOVSLDUP => */ GENERAL_W_R,
+ /* MOVSHDUP => */ GENERAL_W_R,
+ /* MOVDDUP => */ GENERAL_W_R,
+ /* HADDPS => */ GENERAL_RW_R,
+ /* HSUBPS => */ GENERAL_RW_R,
+ /* ADDSUBPD => */ GENERAL_RW_R,
+ /* ADDSUBPS => */ GENERAL_RW_R,
+ /* CVTSI2SS => */ GENERAL_W_R,
+ /* CVTSI2SD => */ GENERAL_W_R,
+ /* CVTTSD2SI => */ GENERAL_W_R,
+ /* CVTTPS2DQ => */ GENERAL_W_R,
+ /* CVTPD2DQ => */ GENERAL_W_R,
+ /* CVTPD2PS => */ GENERAL_W_R,
+ /* CVTPS2DQ => */ GENERAL_W_R,
+ /* CVTSD2SI => */ GENERAL_RW_R,
+ /* CVTSD2SS => */ GENERAL_RW_R,
+ /* CVTTSS2SI => */ GENERAL_RW_R,
+ /* CVTSS2SI => */ GENERAL_RW_R,
+ /* CVTSS2SD => */ GENERAL_RW_R,
+ /* CVTDQ2PD => */ GENERAL_W_R,
+ /* LDDQU => */ GENERAL_W_R,
+ /* MOVZX => */ GENERAL_RW_R,
+ /* MOVSX => */ GENERAL_RW_R,
+ /* MOVSXD => */ GENERAL_RW_R,
+ /* SHRD => */ GENERAL_RW_R_FLAGWRITE
+ .set_operand(2, Access::Read),
+ /* HLT => */ BehaviorDigest::empty()
+ .set_pl0(),
+ /* CALL => */ BehaviorDigest::empty()
+ .set_implicit_ops(CALL_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* CALLF => */ BehaviorDigest::empty()
+ .set_implicit_ops(CALLF_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* JMP => */ BehaviorDigest::empty()
+ .set_implicit_ops(JMP_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* JMPF => */ BehaviorDigest::empty()
+ .set_implicit_ops(JMPF_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* PUSH => */ BehaviorDigest::empty()
+ .set_implicit_ops(PUSH_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* POP => */ BehaviorDigest::empty()
+ .set_implicit_ops(POP_OPS_IDX)
+ .set_pl_any()
+ .set_operand(0, Access::Write),
+ /* LEA => */ GENERAL_W_R,
+ /* NOP => */ GENERAL,
+ /* PREFETCHNTA => */ GENERAL_R,
+ /* PREFETCH0 => */ GENERAL_R,
+ /* PREFETCH1 => */ GENERAL_R,
+ /* PREFETCH2 => */ GENERAL_R,
+ /* POPF => */ BehaviorDigest::empty()
+ .set_implicit_ops(POPF_IDX)
+ .set_pl_any(),
+ /* INT => */ GENERAL_R,
+ /* INTO => */ GENERAL_R_FLAGREAD,
+ // TODO: should there be implicit operands for the iret instructions? they're complex
+ // anyway..
+ /* IRET => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* IRETD => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* IRETQ => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* RETF => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_nontrivial(true)
+ .set_implicit_ops(RETF_IDX),
+ /* ENTER => */ BehaviorDigest::empty()
+ .set_implicit_ops(ENTER_IDX)
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read)
+ .set_pl_any(),
+ /* LEAVE => */ BehaviorDigest::empty()
+ .set_implicit_ops(LEAVE_IDX)
+ .set_pl_any(),
+ /* MOV => */ GENERAL_RW_R,
+ /* RETURN => */ BehaviorDigest::empty()
+ .set_implicit_ops(RETURN_IDX)
+ .set_nontrivial(true)
+ .set_pl_any(),
+ /* PUSHF => */ BehaviorDigest::empty()
+ .set_implicit_ops(PUSHF_IDX)
+ .set_pl_any(),
+ /* WAIT => */ GENERAL,
+ /* CBW => */ BehaviorDigest::empty()
+ .set_implicit_ops(CBW_IDX)
+ .set_pl_any(),
+ /* CWDE => */ BehaviorDigest::empty()
+ .set_implicit_ops(CWDE_IDX)
+ .set_pl_any(),
+ /* CDQE => */ BehaviorDigest::empty()
+ .set_implicit_ops(CDQE_IDX)
+ .set_pl_any(),
+ /* CWD => */ BehaviorDigest::empty()
+ .set_implicit_ops(CWD_IDX)
+ .set_pl_any(),
+ /* CDQ => */ BehaviorDigest::empty()
+ .set_implicit_ops(CDQ_IDX)
+ .set_pl_any(),
+ /* CQO => */ BehaviorDigest::empty()
+ .set_implicit_ops(CQO_IDX)
+ .set_pl_any(),
+ /* LAHF => */ BehaviorDigest::empty()
+ .set_implicit_ops(LAHF_IDX)
+ .set_pl_any(),
+ /* SAHF => */ BehaviorDigest::empty()
+ .set_implicit_ops(SAHF_IDX)
+ .set_pl_any(),
+ /* TEST => */ GENERAL_R_R_FLAGWRITE,
+ /* IN => */ BehaviorDigest::empty()
+ .set_complex(true)
+ .set_pl_special()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read),
+ /* OUT => */ BehaviorDigest::empty()
+ .set_complex(true)
+ .set_pl_special()
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read),
+ /* IMUL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_operand(0, Access::Read) // operands are adjusted via non_trivial
+ .set_nontrivial(true),
+ /* JO => */ JCC,
+ /* JNO => */ JCC,
+ /* JB => */ JCC,
+ /* JNB => */ JCC,
+ /* JZ => */ JCC,
+ /* JNZ => */ JCC,
+ /* JA => */ JCC,
+ /* JNA => */ JCC,
+ /* JS => */ JCC,
+ /* JNS => */ JCC,
+ /* JP => */ JCC,
+ /* JNP => */ JCC,
+ /* JL => */ JCC,
+ /* JGE => */ JCC,
+ /* JLE => */ JCC,
+ /* JG => */ JCC,
+ /* CMOVA => */ CMOVCC,
+ /* CMOVB => */ CMOVCC,
+ /* CMOVG => */ CMOVCC,
+ /* CMOVGE => */ CMOVCC,
+ /* CMOVL => */ CMOVCC,
+ /* CMOVLE => */ CMOVCC,
+ /* CMOVNA => */ CMOVCC,
+ /* CMOVNB => */ CMOVCC,
+ /* CMOVNO => */ CMOVCC,
+ /* CMOVNP => */ CMOVCC,
+ /* CMOVNS => */ CMOVCC,
+ /* CMOVNZ => */ CMOVCC,
+ /* CMOVO => */ CMOVCC,
+ /* CMOVP => */ CMOVCC,
+ /* CMOVS => */ CMOVCC,
+ /* CMOVZ => */ CMOVCC,
+ /* DIV => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_operand(0, Access::Read)
+ .set_nontrivial(true),
+ /* IDIV => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_operand(0, Access::Read)
+ .set_nontrivial(true),
+ /* MUL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_operand(0, Access::Read)
+ .set_nontrivial(true),
+ /* SETO => */ SETCC,
+ /* SETNO => */ SETCC,
+ /* SETB => */ SETCC,
+ /* SETAE => */ SETCC,
+ /* SETZ => */ SETCC,
+ /* SETNZ => */ SETCC,
+ /* SETBE => */ SETCC,
+ /* SETA => */ SETCC,
+ /* SETS => */ SETCC,
+ /* SETNS => */ SETCC,
+ /* SETP => */ SETCC,
+ /* SETNP => */ SETCC,
+ /* SETL => */ SETCC,
+ /* SETGE => */ SETCC,
+ /* SETLE => */ SETCC,
+ /* SETG => */ SETCC,
+ /* CPUID => */ BehaviorDigest::empty()
+ .set_implicit_ops(CPUID_IDX)
+ .set_pl_any(),
+ /* UD0 => */ GENERAL,
+ /* UD1 => */ GENERAL
+ .set_operand(0, Access::None)
+ .set_operand(1, Access::None),
+ /* UD2 => */ GENERAL,
+ /* WBINVD => */ BehaviorDigest::empty()
+ .set_pl0(),
+ /* INVD => */ BehaviorDigest::empty()
+ .set_pl0(),
+ /* SYSRET => */ BehaviorDigest::empty()
+ .set_pl0(),
+ /* CLTS => */ BehaviorDigest::empty()
+ .set_implicit_ops(CLTS_IDX)
+ .set_pl0(),
+ /* SYSCALL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* LSL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read)
+ .set_flags_access(Access::Write),
+ /* LAR => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read)
+ .set_flags_access(Access::Write),
+ /* LES => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read),
+ /* LDS => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read),
+ /* SGDT => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::Write),
+ /* SIDT => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::Write),
+ /* LGDT => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read),
+ /* LIDT => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read),
+ /* SMSW => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Write)
+ .set_implicit_ops(SMSW_IDX),
+ /* LMSW => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_implicit_ops(LMSW_IDX),
+ /* SWAPGS => */ BehaviorDigest::empty()
+ .set_pl0(),
+ /* RDTSCP => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_implicit_ops(RDTSCP_IDX)
+ .set_complex(true),
+ // TODO: invlpg does not generate a page fault, so it's "memory" only in generating an
+ // address.
+ /* INVLPG => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read),
+ // TODO: this is only complex because while the memory access is 512 bytes,
+ // `MemoryAccessSize::bytes_size()` does not report it as such.
+ /* FXSAVE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ // TODO: this is only complex because while the memory access is 512 bytes,
+ // `MemoryAccessSize::bytes_size()` does not report it as such.
+ /* FXRSTOR => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* LDMXCSR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true)
+ .set_pl_any(),
+ /* STMXCSR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_complex(true)
+ .set_pl_any(),
+ /* XSAVE => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_pl_any()
+ .set_complex(true),
+ /* XRSTOR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_pl_any()
+ .set_complex(true),
+ /* XSAVEOPT => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_pl_any()
+ .set_complex(true),
+ /* LFENCE => */ GENERAL,
+ /* MFENCE => */ GENERAL,
+ /* SFENCE => */ GENERAL,
+ // in almost all cases `clflush` does not "write" anything, but it is more of a write than
+ // a read; from any other processor's perspective, the cache coherency protocol would
+ // ensure that other processors' caches "are" memory and this would be a no-op for
+ // architectural state. but for some kinds of memory (WC, for example), cache coherency is
+ // more lax and the executing processor's cache is in fact writing up to 64 bytes of novel
+ // data to main memory.
+ /* CLFLUSH => */ GENERAL_W
+ .set_complex(true),
+ // same argument as `clflush`.
+ /* CLFLUSHOPT => */ GENERAL_W
+ .set_complex(true),
+ // same argument as `clflush`.
+ /* CLWB => */ GENERAL_W
+ .set_complex(true),
+ /* WRMSR => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* RDTSC => */ BehaviorDigest::empty()
+ .set_implicit_ops(RDTSC_IDX)
+ .set_pl_special()
+ .set_complex(true),
+ /* RDMSR => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* RDPMC => */ BehaviorDigest::empty()
+ .set_implicit_ops(RDPMC_IDX)
+ .set_pl_special(),
+ /* SLDT => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ /* STR => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ /* LLDT => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* LTR => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VERR => */ GENERAL_R_FLAGWRITE,
+ /* VERW => */ GENERAL_R_FLAGWRITE,
+ /* CMC => */ GENERAL_FLAGRW,
+ /* CLC => */ GENERAL_FLAGRW,
+ /* STC => */ GENERAL_FLAGRW,
+ /* CLI => */ GENERAL_FLAGRW
+ .set_pl_special(),
+ /* STI => */ GENERAL_FLAGRW
+ .set_pl_special(),
+ /* CLD => */ GENERAL_FLAGRW,
+ /* STD => */ GENERAL_FLAGRW,
+ /* JMPE => */ BehaviorDigest::empty()
+ .set_pl_any() // TODO: don't have a processor with jmpe to validate
+ .set_operand(0, Access::Read)
+ .set_implicit_ops(JMP_OPS_IDX),
+ /* POPCNT => */ GENERAL_W_R_FLAGWRITE,
+ /* MOVDQU => */ GENERAL_W_R,
+ /* MOVDQA => */ GENERAL_W_R,
+ /* MOVQ => */ GENERAL_W_R,
+ /* CMPSS => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* CMPSD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* UNPCKLPS => */ GENERAL_RW_R,
+ /* UNPCKLPD => */ GENERAL_RW_R,
+ /* UNPCKHPS => */ GENERAL_RW_R,
+ /* UNPCKHPD => */ GENERAL_RW_R,
+ /* PSHUFHW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PSHUFLW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* MOVUPS => */ GENERAL_W_R,
+ /* MOVQ2DQ => */ GENERAL_W_R,
+ /* MOVDQ2Q => */ GENERAL_W_R,
+ /* RSQRTSS => */ GENERAL_RW_R,
+ /* RCPSS => */ GENERAL_RW_R,
+
+ /* ANDN => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* BEXTR => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* BLSI => */ GENERAL_W_R_FLAGWRITE,
+ /* BLSMSK => */ GENERAL_W_R_FLAGWRITE,
+ /* BLSR => */ GENERAL_W_R_FLAGWRITE,
+ /* VMCLEAR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VMXON => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* VMCALL => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VMLAUNCH => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* VMRESUME => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* VMXOFF => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* PCONFIG => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* MONITOR => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_implicit_ops(MONITOR_IDX)
+ .set_complex(true),
+ /* MWAIT => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* MONITORX => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_implicit_ops(MONITOR_IDX)
+ .set_complex(true),
+ /* MWAITX => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* CLAC => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write),
+ /* STAC => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write),
+ /* ENCLS => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* ENCLV => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* XGETBV => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* XSETBV => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* VMFUNC => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* XABORT => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* XBEGIN => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* XEND => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* XTEST => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* ENCLU => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* RDPKRU => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* WRPKRU => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ /* RDPRU => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* CLZERO => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ /* RDSEED => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_flags_access(Access::Write)
+ .set_pl_any(),
+ /* RDRAND => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_flags_access(Access::Write)
+ .set_pl_any(),
+
+ /* ADDPS => */ GENERAL_RW_R,
+ /* ADDPD => */ GENERAL_RW_R,
+ /* ANDNPS => */ GENERAL_RW_R,
+ /* ANDNPD => */ GENERAL_RW_R,
+ /* ANDPS => */ GENERAL_RW_R,
+ /* ANDPD => */ GENERAL_RW_R,
+ /* BSWAP => */ GENERAL_RW,
+ /* CMPPD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* CMPPS => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* COMISD => */ GENERAL_R_R_FLAGWRITE,
+ /* COMISS => */ GENERAL_R_R_FLAGWRITE,
+ /* CVTDQ2PS => */ GENERAL_W_R,
+ /* CVTPI2PS => */ GENERAL_RW_R,
+ // TODO: are these cvtp*2p* instructions targeting mmx actually read-write on the
+ // destination? what happens to the top 16 bits of the destination?
+ /* CVTPI2PD => */ GENERAL_W_R,
+ /* CVTPS2PD => */ GENERAL_W_R,
+ /* CVTPS2PI => */ GENERAL_W_R,
+ /* CVTPD2PI => */ GENERAL_W_R,
+ /* CVTTPS2PI => */ GENERAL_W_R,
+ /* CVTTPD2PI => */ GENERAL_W_R,
+ // exciting: zeroes the upper half of the xmm register, but leaves ymm/zmm unmodified
+ /* CVTTPD2DQ => */ GENERAL_W_R,
+ /* DIVPS => */ GENERAL_RW_R,
+ /* DIVPD => */ GENERAL_RW_R,
+ /* EMMS => */ GENERAL,
+ // TODO: untested, don't have relevant hardware..
+ /* GETSEC => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* LFS => */ GENERAL_W_R
+ .set_implicit_ops(LFS_IDX),
+ /* LGS => */ GENERAL_W_R
+ .set_implicit_ops(LGS_IDX),
+ /* LSS => */ GENERAL_W_R
+ .set_implicit_ops(LSS_IDX),
+ /* MASKMOVQ => */ GENERAL_R_R
+ .set_implicit_ops(MASKMOVQ_IDX),
+ /* MASKMOVDQU => */ GENERAL_R_R
+ .set_implicit_ops(MASKMOVQ_IDX),
+ /* MAXPS => */ GENERAL_RW_R,
+ /* MAXPD => */ GENERAL_RW_R,
+ /* MINPS => */ GENERAL_RW_R,
+ /* MINPD => */ GENERAL_RW_R,
+ /* MOVAPS => */ GENERAL_W_R,
+ /* MOVAPD => */ GENERAL_W_R,
+ /* MOVD => */ GENERAL_W_R,
+ /* MOVLPS => */ GENERAL_RW_R,
+ /* MOVLPD => */ GENERAL_RW_R,
+ /* MOVHPS => */ GENERAL_RW_R,
+ /* MOVHPD => */ GENERAL_RW_R,
+ /* MOVLHPS => */ GENERAL_RW_R,
+ /* MOVHLPS => */ GENERAL_RW_R,
+ /* MOVUPD => */ GENERAL_W_R,
+ /* MOVMSKPS => */ GENERAL_RW_R,
+ /* MOVMSKPD => */ GENERAL_RW_R,
+ /* MOVNTI => */ GENERAL_W_R,
+ /* MOVNTPS => */ GENERAL_W_R,
+ /* MOVNTPD => */ GENERAL_W_R,
+ /* EXTRQ => */ GENERAL_RW_R
+ .set_nontrivial(true),
+ /* INSERTQ => */ GENERAL_RW_R
+ .set_nontrivial(true),
+ /* MOVNTSS => */ GENERAL_W_R,
+ /* MOVNTSD => */ GENERAL_W_R,
+ /* MOVNTQ => */ GENERAL_W_R,
+ /* MOVNTDQ => */ GENERAL_W_R,
+ /* MULPS => */ GENERAL_RW_R,
+ /* MULPD => */ GENERAL_RW_R,
+ /* ORPS => */ GENERAL_RW_R,
+ /* ORPD => */ GENERAL_RW_R,
+ /* PACKSSDW => */ GENERAL_RW_R,
+ /* PACKSSWB => */ GENERAL_RW_R,
+ /* PACKUSWB => */ GENERAL_RW_R,
+ /* PADDB => */ GENERAL_RW_R,
+ /* PADDD => */ GENERAL_RW_R,
+ /* PADDQ => */ GENERAL_RW_R,
+ /* PADDSB => */ GENERAL_RW_R,
+ /* PADDSW => */ GENERAL_RW_R,
+ /* PADDUSB => */ GENERAL_RW_R,
+ /* PADDUSW => */ GENERAL_RW_R,
+ /* PADDW => */ GENERAL_RW_R,
+ /* PAND => */ GENERAL_RW_R,
+ /* PANDN => */ GENERAL_RW_R,
+ /* PAVGB => */ GENERAL_RW_R,
+ /* PAVGW => */ GENERAL_RW_R,
+ /* PCMPEQB => */ GENERAL_RW_R,
+ /* PCMPEQD => */ GENERAL_RW_R,
+ /* PCMPEQW => */ GENERAL_RW_R,
+ /* PCMPGTB => */ GENERAL_RW_R,
+ /* PCMPGTD => */ GENERAL_RW_R,
+ /* PCMPGTW => */ GENERAL_RW_R,
+ /* PINSRW => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PMADDWD => */ GENERAL_RW_R,
+ /* PMAXSW => */ GENERAL_RW_R,
+ /* PMAXUB => */ GENERAL_RW_R,
+ /* PMINSW => */ GENERAL_RW_R,
+ /* PMINUB => */ GENERAL_RW_R,
+ /* PMOVMSKB => */ GENERAL_RW_R,
+ /* PMULHUW => */ GENERAL_RW_R,
+ /* PMULHW => */ GENERAL_RW_R,
+ /* PMULLW => */ GENERAL_RW_R,
+ /* PMULUDQ => */ GENERAL_RW_R,
+ /* POR => */ GENERAL_RW_R,
+ /* PSADBW => */ GENERAL_RW_R,
+ /* PSHUFW => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PSHUFD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PSLLD => */ GENERAL_RW_R,
+ /* PSLLDQ => */ GENERAL_RW_R,
+ /* PSLLQ => */ GENERAL_RW_R,
+ /* PSLLW => */ GENERAL_RW_R,
+ /* PSRAD => */ GENERAL_RW_R,
+ /* PSRAW => */ GENERAL_RW_R,
+ /* PSRLD => */ GENERAL_RW_R,
+ /* PSRLDQ => */ GENERAL_RW_R,
+ /* PSRLQ => */ GENERAL_RW_R,
+ /* PSRLW => */ GENERAL_RW_R,
+ /* PSUBB => */ GENERAL_RW_R,
+ /* PSUBD => */ GENERAL_RW_R,
+ /* PSUBQ => */ GENERAL_RW_R,
+ /* PSUBSB => */ GENERAL_RW_R,
+ /* PSUBSW => */ GENERAL_RW_R,
+ /* PSUBUSB => */ GENERAL_RW_R,
+ /* PSUBUSW => */ GENERAL_RW_R,
+ /* PSUBW => */ GENERAL_RW_R,
+ /* PUNPCKHBW => */ GENERAL_RW_R,
+ /* PUNPCKHDQ => */ GENERAL_RW_R,
+ /* PUNPCKHWD => */ GENERAL_RW_R,
+ /* PUNPCKLBW => */ GENERAL_RW_R,
+ /* PUNPCKLDQ => */ GENERAL_RW_R,
+ /* PUNPCKLWD => */ GENERAL_RW_R,
+ /* PUNPCKLQDQ => */ GENERAL_RW_R,
+ /* PUNPCKHQDQ => */ GENERAL_RW_R,
+ /* PXOR => */ GENERAL_RW_R,
+ /* RCPPS => */ GENERAL_W_R,
+ /* RSM => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* RSQRTPS => */ GENERAL_W_R,
+ /* SHLD => */ GENERAL_RW_R_R
+ .set_flags_access(Access::Write),
+ /* SHUFPD => */ GENERAL_RW_R_R,
+ /* SHUFPS => */ GENERAL_RW_R_R,
+ // TODO: slhd is not real, typo of shld
+ /* SLHD => */ BehaviorDigest::empty(),
+ /* SQRTPS => */ GENERAL_W_R,
+ /* SQRTPD => */ GENERAL_W_R,
+ /* SUBPS => */ GENERAL_RW_R,
+ /* SUBPD => */ GENERAL_RW_R,
+ /* SYSENTER => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* SYSEXIT => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* UCOMISD => */ GENERAL_R_R_FLAGWRITE,
+ /* UCOMISS => */ GENERAL_R_R_FLAGWRITE,
+ /* VMREAD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Read)
+ .set_pl0()
+ .set_complex(true),
+ /* VMWRITE => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read)
+ .set_pl0()
+ .set_complex(true),
+ /* XORPS => */ GENERAL_RW_R,
+ /* XORPD => */ GENERAL_RW_R,
+
+ /* VMOVDDUP => */ GENERAL_W_R,
+ /* VPSHUFLW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VPSHUFHW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VHADDPS => */ GENERAL_W_R_R,
+ /* VHSUBPS => */ GENERAL_W_R_R,
+ /* VADDSUBPS => */ GENERAL_W_R_R,
+ /* VCVTPD2DQ => */ GENERAL_W_R,
+ /* VLDDQU => */ GENERAL_W_R,
+
+ /* VCOMISD => */ GENERAL_R_R_FLAGWRITE,
+ /* VCOMISS => */ GENERAL_R_R_FLAGWRITE,
+ /* VUCOMISD => */ GENERAL_R_R_FLAGWRITE,
+ /* VUCOMISS => */ GENERAL_R_R_FLAGWRITE,
+ /* VADDPD => */ GENERAL_W_R_R,
+ /* VADDPS => */ GENERAL_W_R_R,
+ /* VADDSD => */ GENERAL_W_R_R,
+ /* VADDSS => */ GENERAL_W_R_R,
+ /* VADDSUBPD => */ GENERAL_W_R_R,
+ /* VAESDEC => */ GENERAL_W_R_R,
+ /* VAESDECLAST => */ GENERAL_W_R_R,
+ /* VAESENC => */ GENERAL_W_R_R,
+ /* VAESENCLAST => */ GENERAL_W_R_R,
+ /* VAESIMC => */ GENERAL_W_R,
+ /* VAESKEYGENASSIST => */ GENERAL_W_R_R,
+ /* VBLENDPD => */ GENERAL_W_R_R_IMM8,
+ /* VBLENDPS => */ GENERAL_W_R_R_IMM8,
+ /* VBLENDVPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VBLENDVPS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VBROADCASTF128 => */ GENERAL_W_R,
+ /* VBROADCASTI128 => */ GENERAL_W_R,
+ /* VBROADCASTSD => */ GENERAL_W_R,
+ /* VBROADCASTSS => */ GENERAL_W_R,
+ /* VCMPSD => */ GENERAL_W_R_R_IMM8,
+ /* VCMPSS => */ GENERAL_W_R_R_IMM8,
+ /* VCMPPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VCMPPS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ // TODO: SUPER suspicious about the RW_R/W_R confusion here.
+ // vcvtss2si says that dest[0:63] are written but omits bits 64 and up.
+ // vcvtsd2si says taht dest[0:63] are written and upper bits are taken from src1.
+ // is src1 used generally?
+ /* VCVTDQ2PD => */ GENERAL_W_R,
+ /* VCVTDQ2PS => */ GENERAL_W_R,
+ /* VCVTPD2PS => */ GENERAL_W_R,
+ /* VCVTPH2PS => */ GENERAL_W_R,
+ /* VCVTPS2DQ => */ GENERAL_W_R,
+ /* VCVTPS2PD => */ GENERAL_W_R,
+ /* VCVTSS2SD => */ GENERAL_W_R_R,
+ /* VCVTSI2SS => */ GENERAL_W_R_R,
+ /* VCVTSI2SD => */ GENERAL_W_R_R,
+ /* VCVTSD2SI => */ GENERAL_RW_R,
+ /* VCVTSD2SS => */ GENERAL_W_R_R,
+ /* VCVTPS2PH => */ GENERAL_W_R_R,
+ /* VCVTSS2SI => */ GENERAL_RW_R,
+ /* VCVTTPD2DQ => */ GENERAL_W_R,
+ /* VCVTTPS2DQ => */ GENERAL_W_R,
+ /* VCVTTSS2SI => */ GENERAL_RW_R,
+ /* VCVTTSD2SI => */ GENERAL_RW_R,
+ /* VDIVPD => */ GENERAL_W_R_R,
+ /* VDIVPS => */ GENERAL_W_R_R,
+ /* VDIVSD => */ GENERAL_W_R_R,
+ /* VDIVSS => */ GENERAL_W_R_R,
+ /* VDPPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VDPPS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VEXTRACTF128 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTI128 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFMADD132PD => */ GENERAL_RW_R_R,
+ /* VFMADD132PS => */ GENERAL_RW_R_R,
+ /* VFMADD132SD => */ GENERAL_RW_R_R,
+ /* VFMADD132SS => */ GENERAL_RW_R_R,
+ /* VFMADD213PD => */ GENERAL_RW_R_R,
+ /* VFMADD213PS => */ GENERAL_RW_R_R,
+ /* VFMADD213SD => */ GENERAL_RW_R_R,
+ /* VFMADD213SS => */ GENERAL_RW_R_R,
+ /* VFMADD231PD => */ GENERAL_RW_R_R,
+ /* VFMADD231PS => */ GENERAL_RW_R_R,
+ /* VFMADD231SD => */ GENERAL_RW_R_R,
+ /* VFMADD231SS => */ GENERAL_RW_R_R,
+ /* VFMADDSUB132PD => */ GENERAL_RW_R_R,
+ /* VFMADDSUB132PS => */ GENERAL_RW_R_R,
+ /* VFMADDSUB213PD => */ GENERAL_RW_R_R,
+ /* VFMADDSUB213PS => */ GENERAL_RW_R_R,
+ /* VFMADDSUB231PD => */ GENERAL_RW_R_R,
+ /* VFMADDSUB231PS => */ GENERAL_RW_R_R,
+ /* VFMSUB132PD => */ GENERAL_RW_R_R,
+ /* VFMSUB132PS => */ GENERAL_RW_R_R,
+ /* VFMSUB132SD => */ GENERAL_RW_R_R,
+ /* VFMSUB132SS => */ GENERAL_RW_R_R,
+ /* VFMSUB213PD => */ GENERAL_RW_R_R,
+ /* VFMSUB213PS => */ GENERAL_RW_R_R,
+ /* VFMSUB213SD => */ GENERAL_RW_R_R,
+ /* VFMSUB213SS => */ GENERAL_RW_R_R,
+ /* VFMSUB231PD => */ GENERAL_RW_R_R,
+ /* VFMSUB231PS => */ GENERAL_RW_R_R,
+ /* VFMSUB231SD => */ GENERAL_RW_R_R,
+ /* VFMSUB231SS => */ GENERAL_RW_R_R,
+ /* VFMSUBADD132PD => */ GENERAL_RW_R_R,
+ /* VFMSUBADD132PS => */ GENERAL_RW_R_R,
+ /* VFMSUBADD213PD => */ GENERAL_RW_R_R,
+ /* VFMSUBADD213PS => */ GENERAL_RW_R_R,
+ /* VFMSUBADD231PD => */ GENERAL_RW_R_R,
+ /* VFMSUBADD231PS => */ GENERAL_RW_R_R,
+ /* VFNMADD132PD => */ GENERAL_RW_R_R,
+ /* VFNMADD132PS => */ GENERAL_RW_R_R,
+ /* VFNMADD132SD => */ GENERAL_RW_R_R,
+ /* VFNMADD132SS => */ GENERAL_RW_R_R,
+ /* VFNMADD213PD => */ GENERAL_RW_R_R,
+ /* VFNMADD213PS => */ GENERAL_RW_R_R,
+ /* VFNMADD213SD => */ GENERAL_RW_R_R,
+ /* VFNMADD213SS => */ GENERAL_RW_R_R,
+ /* VFNMADD231PD => */ GENERAL_RW_R_R,
+ /* VFNMADD231PS => */ GENERAL_RW_R_R,
+ /* VFNMADD231SD => */ GENERAL_RW_R_R,
+ /* VFNMADD231SS => */ GENERAL_RW_R_R,
+ /* VFNMSUB132PD => */ GENERAL_RW_R_R,
+ /* VFNMSUB132PS => */ GENERAL_RW_R_R,
+ /* VFNMSUB132SD => */ GENERAL_RW_R_R,
+ /* VFNMSUB132SS => */ GENERAL_RW_R_R,
+ /* VFNMSUB213PD => */ GENERAL_RW_R_R,
+ /* VFNMSUB213PS => */ GENERAL_RW_R_R,
+ /* VFNMSUB213SD => */ GENERAL_RW_R_R,
+ /* VFNMSUB213SS => */ GENERAL_RW_R_R,
+ /* VFNMSUB231PD => */ GENERAL_RW_R_R,
+ /* VFNMSUB231PS => */ GENERAL_RW_R_R,
+ /* VFNMSUB231SD => */ GENERAL_RW_R_R,
+ /* VFNMSUB231SS => */ GENERAL_RW_R_R,
+ /* VGATHERDPD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VGATHERDPS => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VGATHERQPD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VGATHERQPS => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VHADDPD => */ GENERAL_W_R_R,
+ /* VHSUBPD => */ GENERAL_W_R_R,
+ /* VINSERTF128 => */ GENERAL_W_R_R_IMM8,
+ /* VINSERTI128 => */ GENERAL_W_R_R_IMM8,
+ /* VINSERTPS => */ GENERAL_W_R_R_IMM8,
+ /* VMASKMOVDQU => */ GENERAL_R_R
+ .set_nontrivial(true),
+ /* VMASKMOVPD => */ GENERAL_W_R_R,
+ /* VMASKMOVPS => */ GENERAL_W_R_R,
+ /* VMAXPD => */ GENERAL_W_R_R,
+ /* VMAXPS => */ GENERAL_W_R_R,
+ /* VMAXSD => */ GENERAL_W_R_R,
+ /* VMAXSS => */ GENERAL_W_R_R,
+ /* VMINPD => */ GENERAL_W_R_R,
+ /* VMINPS => */ GENERAL_W_R_R,
+ /* VMINSD => */ GENERAL_W_R_R,
+ /* VMINSS => */ GENERAL_W_R_R,
+ /* VMOVAPD => */ GENERAL_W_R,
+ /* VMOVAPS => */ GENERAL_W_R,
+ /* VMOVD => */ GENERAL_W_R,
+ /* VMOVDQA => */ GENERAL_W_R,
+ /* VMOVDQU => */ GENERAL_W_R,
+ /* VMOVHLPS => */ GENERAL_W_R_R,
+ // these four are not actually reached due to check above
+ /* VMOVHPD => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(1, Access::Read)
+ .set_nontrivial(true),
+ /* VMOVHPS => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(1, Access::Read)
+ .set_nontrivial(true),
+ /* VMOVLHPS => */ GENERAL_W_R_R,
+ /* VMOVLPD => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(1, Access::Read)
+ .set_nontrivial(true),
+ /* VMOVLPS => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(1, Access::Read)
+ .set_nontrivial(true),
+ /* VMOVMSKPD => */ GENERAL_W_R,
+ /* VMOVMSKPS => */ GENERAL_W_R,
+ /* VMOVNTDQ => */ GENERAL_W_R,
+ /* VMOVNTDQA => */ GENERAL_W_R,
+ /* VMOVNTPD => */ GENERAL_W_R,
+ /* VMOVNTPS => */ GENERAL_W_R,
+ /* VMOVQ => */ GENERAL_W_R,
+ /* VMOVSS => */ GENERAL_W_R_R,
+ /* VMOVSD => */ GENERAL_W_R_R,
+ /* VMOVSHDUP => */ GENERAL_W_R,
+ /* VMOVSLDUP => */ GENERAL_W_R,
+ /* VMOVUPD => */ GENERAL_W_R,
+ /* VMOVUPS => */ GENERAL_W_R,
+ /* VMPSADBW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VMULPD => */ GENERAL_W_R_R,
+ /* VMULPS => */ GENERAL_W_R_R,
+ /* VMULSD => */ GENERAL_W_R_R,
+ /* VMULSS => */ GENERAL_W_R_R,
+ /* VPABSB => */ GENERAL_W_R,
+ /* VPABSD => */ GENERAL_W_R,
+ /* VPABSW => */ GENERAL_W_R,
+ /* VPACKSSDW => */ GENERAL_W_R_R,
+ /* VPACKUSDW => */ GENERAL_W_R_R,
+ /* VPACKSSWB => */ GENERAL_W_R_R,
+ /* VPACKUSWB => */ GENERAL_W_R_R,
+ /* VPADDB => */ GENERAL_W_R_R,
+ /* VPADDD => */ GENERAL_W_R_R,
+ /* VPADDQ => */ GENERAL_W_R_R,
+ /* VPADDSB => */ GENERAL_W_R_R,
+ /* VPADDSW => */ GENERAL_W_R_R,
+ /* VPADDUSB => */ GENERAL_W_R_R,
+ /* VPADDUSW => */ GENERAL_W_R_R,
+ /* VPADDW => */ GENERAL_W_R_R,
+ /* VPALIGNR => */ GENERAL_W_R_R_IMM8,
+ /* VANDPD => */ GENERAL_W_R_R,
+ /* VANDPS => */ GENERAL_W_R_R,
+ /* VORPD => */ GENERAL_W_R_R,
+ /* VORPS => */ GENERAL_W_R_R,
+ /* VANDNPD => */ GENERAL_W_R_R,
+ /* VANDNPS => */ GENERAL_W_R_R,
+ /* VPAND => */ GENERAL_W_R_R,
+ /* VPANDN => */ GENERAL_W_R_R,
+ /* VPAVGB => */ GENERAL_W_R_R,
+ /* VPAVGW => */ GENERAL_W_R_R,
+ /* VPBLENDD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPBLENDVB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPBLENDW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPBROADCASTB => */ GENERAL_W_R,
+ /* VPBROADCASTD => */ GENERAL_W_R,
+ /* VPBROADCASTQ => */ GENERAL_W_R,
+ /* VPBROADCASTW => */ GENERAL_W_R,
+ /* VPCLMULQDQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPEQB => */ GENERAL_W_R_R,
+ /* VPCMPEQD => */ GENERAL_W_R_R,
+ /* VPCMPEQQ => */ GENERAL_W_R_R,
+ /* VPCMPEQW => */ GENERAL_W_R_R,
+ /* VPCMPGTB => */ GENERAL_W_R_R,
+ /* VPCMPGTD => */ GENERAL_W_R_R,
+ /* VPCMPGTQ => */ GENERAL_W_R_R,
+ /* VPCMPGTW => */ GENERAL_W_R_R,
+ /* VPCMPESTRI => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_nontrivial(true),
+ /* VPCMPESTRM => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_nontrivial(true),
+ /* VPCMPISTRI => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(PCMPISTRI_IDX),
+ /* VPCMPISTRM => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(PCMPISTRM_IDX),
+ /* VPERM2F128 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPERM2I128 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPERMD => */ GENERAL_W_R_R,
+ /* VPERMILPD => */ GENERAL_W_R_R,
+ /* VPERMILPS => */ GENERAL_W_R_R,
+ /* VPERMPD => */ GENERAL_W_R_R,
+ /* VPERMPS => */ GENERAL_W_R_R,
+ /* VPERMQ => */ GENERAL_W_R_R,
+ /* VPEXTRB => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VPEXTRD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VPEXTRQ => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VPEXTRW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ // TODO: complex
+ /* VPGATHERDD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPGATHERDQ => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPGATHERQD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPGATHERQQ => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPHADDD => */ GENERAL_W_R_R,
+ /* VPHADDSW => */ GENERAL_W_R_R,
+ /* VPHADDW => */ GENERAL_W_R_R,
+ /* VPMADDUBSW => */ GENERAL_W_R_R,
+ /* VPHMINPOSUW => */ GENERAL_W_R,
+ /* VPHSUBD => */ GENERAL_W_R_R,
+ /* VPHSUBSW => */ GENERAL_W_R_R,
+ /* VPHSUBW => */ GENERAL_W_R_R,
+ /* VPINSRB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPINSRD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPINSRQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPINSRW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPMADDWD => */ GENERAL_W_R_R,
+ /* VPMASKMOVD => */ GENERAL_W_R_R,
+ /* VPMASKMOVQ => */ GENERAL_W_R_R,
+ /* VPMAXSB => */ GENERAL_W_R_R,
+ /* VPMAXSD => */ GENERAL_W_R_R,
+ /* VPMAXSW => */ GENERAL_W_R_R,
+ /* VPMAXUB => */ GENERAL_W_R_R,
+ /* VPMAXUW => */ GENERAL_W_R_R,
+ /* VPMAXUD => */ GENERAL_W_R_R,
+ /* VPMINSB => */ GENERAL_W_R_R,
+ /* VPMINSW => */ GENERAL_W_R_R,
+ /* VPMINSD => */ GENERAL_W_R_R,
+ /* VPMINUB => */ GENERAL_W_R_R,
+ /* VPMINUW => */ GENERAL_W_R_R,
+ /* VPMINUD => */ GENERAL_W_R_R,
+ /* VPMOVMSKB => */ GENERAL_W_R,
+ /* VPMOVSXBD => */ GENERAL_W_R,
+ /* VPMOVSXBQ => */ GENERAL_W_R,
+ /* VPMOVSXBW => */ GENERAL_W_R,
+ /* VPMOVSXDQ => */ GENERAL_W_R,
+ /* VPMOVSXWD => */ GENERAL_W_R,
+ /* VPMOVSXWQ => */ GENERAL_W_R,
+ /* VPMOVZXBD => */ GENERAL_W_R,
+ /* VPMOVZXBQ => */ GENERAL_W_R,
+ /* VPMOVZXBW => */ GENERAL_W_R,
+ /* VPMOVZXDQ => */ GENERAL_W_R,
+ /* VPMOVZXWD => */ GENERAL_W_R,
+ /* VPMOVZXWQ => */ GENERAL_W_R,
+ /* VPMULDQ => */ GENERAL_W_R_R,
+ /* VPMULHRSW => */ GENERAL_W_R_R,
+ /* VPMULHUW => */ GENERAL_W_R_R,
+ /* VPMULHW => */ GENERAL_W_R_R,
+ /* VPMULLQ => */ GENERAL_W_R_R,
+ /* VPMULLD => */ GENERAL_W_R_R,
+ /* VPMULLW => */ GENERAL_W_R_R,
+ /* VPMULUDQ => */ GENERAL_W_R_R,
+ /* VPOR => */ GENERAL_W_R_R,
+ /* VPSADBW => */ GENERAL_W_R_R,
+ /* VPSHUFB => */ GENERAL_W_R_R,
+ /* VPSHUFD => */ GENERAL_W_R_R,
+ /* VPSIGNB => */ GENERAL_W_R_R,
+ /* VPSIGND => */ GENERAL_W_R_R,
+ /* VPSIGNW => */ GENERAL_W_R_R,
+ /* VPSLLD => */ GENERAL_W_R_R,
+ /* VPSLLDQ => */ GENERAL_W_R_R,
+ /* VPSLLQ => */ GENERAL_W_R_R,
+ /* VPSLLVD => */ GENERAL_W_R_R,
+ /* VPSLLVQ => */ GENERAL_W_R_R,
+ /* VPSLLW => */ GENERAL_W_R_R,
+ /* VPSRAD => */ GENERAL_W_R_R,
+ /* VPSRAVD => */ GENERAL_W_R_R,
+ /* VPSRAW => */ GENERAL_W_R_R,
+ /* VPSRLD => */ GENERAL_W_R_R,
+ /* VPSRLDQ => */ GENERAL_W_R_R,
+ /* VPSRLQ => */ GENERAL_W_R_R,
+ /* VPSRLVD => */ GENERAL_W_R_R,
+ /* VPSRLVQ => */ GENERAL_W_R_R,
+ /* VPSRLW => */ GENERAL_W_R_R,
+ /* VPSUBB => */ GENERAL_W_R_R,
+ /* VPSUBD => */ GENERAL_W_R_R,
+ /* VPSUBQ => */ GENERAL_W_R_R,
+ /* VPSUBSB => */ GENERAL_W_R_R,
+ /* VPSUBSW => */ GENERAL_W_R_R,
+ /* VPSUBUSB => */ GENERAL_W_R_R,
+ /* VPSUBUSW => */ GENERAL_W_R_R,
+ /* VPSUBW => */ GENERAL_W_R_R,
+ /* VPTEST => */ GENERAL_R_R_FLAGWRITE,
+ /* VPUNPCKHBW => */ GENERAL_W_R_R,
+ /* VPUNPCKHDQ => */ GENERAL_W_R_R,
+ /* VPUNPCKHQDQ => */ GENERAL_W_R_R,
+ /* VPUNPCKHWD => */ GENERAL_W_R_R,
+ /* VPUNPCKLBW => */ GENERAL_W_R_R,
+ /* VPUNPCKLDQ => */ GENERAL_W_R_R,
+ /* VPUNPCKLQDQ => */ GENERAL_W_R_R,
+ /* VPUNPCKLWD => */ GENERAL_W_R_R,
+ /* VPXOR => */ GENERAL_W_R_R,
+ /* VRCPPS => */ GENERAL_W_R,
+ /* VROUNDPD => */ GENERAL_W_R_R,
+ /* VROUNDPS => */ GENERAL_W_R_R,
+ /* VROUNDSD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VROUNDSS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRSQRTPS => */ GENERAL_W_R,
+ /* VRSQRTSS => */ GENERAL_W_R_R,
+ /* VRCPSS => */ GENERAL_W_R_R,
+ /* VSHUFPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VSHUFPS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VSQRTPD => */ GENERAL_W_R,
+ /* VSQRTPS => */ GENERAL_W_R,
+ /* VSQRTSS => */ GENERAL_W_R_R,
+ /* VSQRTSD => */ GENERAL_W_R_R,
+ /* VSUBPD => */ GENERAL_W_R_R,
+ /* VSUBPS => */ GENERAL_W_R_R,
+ /* VSUBSD => */ GENERAL_W_R_R,
+ /* VSUBSS => */ GENERAL_W_R_R,
+ /* VTESTPD => */ GENERAL_R_R
+ .set_flags_access(Access::Write),
+ /* VTESTPS => */ GENERAL_R_R
+ .set_flags_access(Access::Write),
+ /* VUNPCKHPD => */ GENERAL_W_R_R,
+ /* VUNPCKHPS => */ GENERAL_W_R_R,
+ /* VUNPCKLPD => */ GENERAL_W_R_R,
+ /* VUNPCKLPS => */ GENERAL_W_R_R,
+ /* VXORPD => */ GENERAL_W_R_R,
+ /* VXORPS => */ GENERAL_W_R_R,
+ /* VZEROUPPER => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VZEROALL => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VLDMXCSR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true)
+ .set_pl_any(),
+ /* VSTMXCSR => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_complex(true)
+ .set_pl_any(),
+
+ /* PCLMULQDQ => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* AESKEYGENASSIST => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* AESIMC => */ GENERAL_W_R,
+ /* AESENC => */ GENERAL_RW_R,
+ /* AESENCLAST => */ GENERAL_RW_R,
+ /* AESDEC => */ GENERAL_RW_R,
+ /* AESDECLAST => */ GENERAL_RW_R,
+ /* PCMPGTQ => */ GENERAL_RW_R,
+ /* PCMPISTRM => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(PCMPISTRM_IDX),
+ /* PCMPISTRI => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(PCMPISTRI_IDX),
+ /* PCMPESTRI => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_nontrivial(true),
+ /* PACKUSDW => */ GENERAL_RW_R,
+ /* PCMPESTRM => */ GENERAL_R_R
+ .set_operand(2, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_nontrivial(true),
+ /* PCMPEQQ => */ GENERAL_RW_R,
+ /* PTEST => */ GENERAL_R_R
+ .set_flags_access(Access::Write),
+ /* PHMINPOSUW => */ GENERAL_W_R,
+ /* DPPS => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* DPPD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* MPSADBW => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PMOVZXDQ => */ GENERAL_RW_R,
+ /* PMOVSXDQ => */ GENERAL_RW_R,
+ /* PMOVZXBD => */ GENERAL_RW_R,
+ /* PMOVSXBD => */ GENERAL_RW_R,
+ /* PMOVZXWQ => */ GENERAL_RW_R,
+ /* PMOVSXWQ => */ GENERAL_RW_R,
+ /* PMOVZXBQ => */ GENERAL_RW_R,
+ /* PMOVSXBQ => */ GENERAL_RW_R,
+ /* PMOVSXWD => */ GENERAL_RW_R,
+ /* PMOVZXWD => */ GENERAL_RW_R,
+ /* PEXTRQ => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PEXTRD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PEXTRW => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PEXTRB => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PMOVSXBW => */ GENERAL_RW_R,
+ /* PMOVZXBW => */ GENERAL_RW_R,
+ /* PINSRQ => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PINSRD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PINSRB => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* EXTRACTPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* INSERTPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* ROUNDSS => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* ROUNDSD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* ROUNDPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* ROUNDPD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* PMAXSB => */ GENERAL_RW_R,
+ /* PMAXSD => */ GENERAL_RW_R,
+ /* PMAXUW => */ GENERAL_RW_R,
+ /* PMAXUD => */ GENERAL_RW_R,
+ /* PMINSD => */ GENERAL_RW_R,
+ /* PMINSB => */ GENERAL_RW_R,
+ /* PMINUD => */ GENERAL_RW_R,
+ /* PMINUW => */ GENERAL_RW_R,
+ // TODO: need to remove; doesn't exist
+ /* BLENDW => */ BehaviorDigest::empty(),
+ /* PBLENDVB => */ GENERAL_RW_R
+ .set_implicit_ops(XMM0_READ_IDX),
+ /* PBLENDW => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* BLENDVPS => */ GENERAL_RW_R
+ .set_implicit_ops(XMM0_READ_IDX),
+ /* BLENDVPD => */ GENERAL_RW_R
+ .set_implicit_ops(XMM0_READ_IDX),
+ /* BLENDPS => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* BLENDPD => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PMULDQ => */ GENERAL_RW_R,
+ /* MOVNTDQA => */ GENERAL_W_R,
+ /* PMULLD => */ GENERAL_RW_R,
+ /* PALIGNR => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* PSIGNW => */ GENERAL_RW_R,
+ /* PSIGND => */ GENERAL_RW_R,
+ /* PSIGNB => */ GENERAL_RW_R,
+ /* PSHUFB => */ GENERAL_RW_R,
+ /* PMULHRSW => */ GENERAL_RW_R,
+ /* PMADDUBSW => */ GENERAL_RW_R,
+ /* PABSD => */ GENERAL_W_R,
+ /* PABSW => */ GENERAL_W_R,
+ /* PABSB => */ GENERAL_W_R,
+ /* PHSUBSW => */ GENERAL_RW_R,
+ /* PHSUBW => */ GENERAL_RW_R,
+ /* PHSUBD => */ GENERAL_RW_R,
+ /* PHADDD => */ GENERAL_RW_R,
+ /* PHADDSW => */ GENERAL_RW_R,
+ /* PHADDW => */ GENERAL_RW_R,
+ /* HSUBPD => */ GENERAL_RW_R,
+ /* HADDPD => */ GENERAL_RW_R,
+
+ /* SHA1RNDS4 => */ GENERAL_RW_R_R,
+ /* SHA1NEXTE => */ GENERAL_RW_R,
+ /* SHA1MSG1 => */ GENERAL_RW_R,
+ /* SHA1MSG2 => */ GENERAL_RW_R,
+ /* SHA256RNDS2 => */ GENERAL_RW_R
+ .set_implicit_ops(XMM0_READ_IDX),
+ /* SHA256MSG1 => */ GENERAL_RW_R,
+ /* SHA256MSG2 => */ GENERAL_RW_R,
+
+ /* LZCNT => */ GENERAL_W_R
+ .set_flags_access(Access::Write),
+ /* CLGI => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* STGI => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* SKINIT => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read),
+ /* VMLOAD => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read),
+ /* VMMCALL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* VMSAVE => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read),
+ /* VMRUN => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read),
+ /* INVLPGA => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read),
+ /* INVLPGB => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read)
+ .set_operand(2, Access::Read),
+ /* TLBSYNC => */ BehaviorDigest::empty()
+ .set_pl0(),
+
+ /* MOVBE => */ GENERAL_W_R,
+
+ /* ADCX => */ GENERAL_RW_R
+ .set_flags_access(Access::ReadWrite),
+ /* ADOX => */ GENERAL_RW_R
+ .set_flags_access(Access::ReadWrite),
+
+ /* PREFETCHW => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+
+ /* RDPID => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write),
+ /* VMPTRLD => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VMPTRST => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+
+ /* BZHI => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* MULX => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Write)
+ .set_operand(1, Access::Write)
+ .set_operand(2, Access::Read)
+ .set_nontrivial(true),
+ /* SHLX => */ GENERAL_W_R_R,
+ /* SHRX => */ GENERAL_W_R_R,
+ /* SARX => */ GENERAL_W_R_R,
+ /* PDEP => */ GENERAL_W_R_R,
+ /* PEXT => */ GENERAL_W_R_R,
+ /* RORX => */ GENERAL_W_R_R,
+ /* XRSTORS => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+ /* XRSTORS64 => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Read)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+ /* XSAVEC => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true)
+ .set_operand(0, Access::Write)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+ /* XSAVEC64 => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true)
+ .set_operand(0, Access::Write)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+ /* XSAVES => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Write)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+ /* XSAVES64 => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true)
+ .set_operand(0, Access::Write)
+ .set_implicit_ops(READ_EDX_EAX_IDX),
+
+ /* RDFSBASE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write),
+ /* RDGSBASE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write),
+ /* WRFSBASE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+ /* WRGSBASE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read),
+
+ /* CRC32 => */ GENERAL_RW_R,
+ /* SALC => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Read)
+ .set_implicit_ops(WRITE_AL_IDX),
+ /* XLAT => */ BehaviorDigest::empty()
+ .set_implicit_ops(XLAT_IDX)
+ .set_pl_any(),
+
+ // TODO: none of x87 is verified well.. and what about the bits in the FPU status word..
+ // and what about pushes/pops from the x87 operand stack..
+ // TODO: read st(0), write st(0)
+ /* F2XM1 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FABS => */ GENERAL, // TODO: this is really an implicit write to st(0)
+ /* FADD => */ GENERAL_RW_R,
+ /* FADDP => */ GENERAL_RW_R,
+ /* FBLD => */ GENERAL_W_R,
+ /* FBSTP => */ GENERAL_W_R,
+ /* FCHS => */ GENERAL_W_R,
+ /* FCMOVB => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVBE => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVE => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVNB => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVNBE => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVNE => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVNU => */ GENERAL_W_R_FLAGREAD,
+ /* FCMOVU => */ GENERAL_W_R_FLAGREAD,
+ /* FCOM => */ GENERAL_R_R,
+ /* FCOMI => */ GENERAL_R_R_FLAGWRITE,
+ /* FCOMIP => */ GENERAL_R_R_FLAGWRITE,
+ /* FCOMP => */ GENERAL_R_R,
+ /* FCOMPP => */ GENERAL_R_R,
+ // TODO: st(0) -> st(0)
+ /* FCOS => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: x87 stack pointer dec
+ /* FDECSTP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FDISI8087_NOP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FDIV => */ GENERAL_RW_R,
+ // TODO: x87 stack pop
+ /* FDIVP => */ GENERAL_RW_R,
+ /* FDIVR => */ GENERAL_RW_R,
+ // TODO: x87 stack pop
+ /* FDIVRP => */ GENERAL_RW_R,
+ /* FENI8087_NOP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: the behavior here is ... inaccurate. st(i) is not read, but state associated with
+ // that register is modified. so it's kind of read?
+ /* FFREE => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_pl_any(),
+ // same as `ffree` above.
+ /* FFREEP => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_pl_any(),
+ /* FIADD => */ GENERAL_RW_R,
+ /* FICOM => */ GENERAL_R_R,
+ /* FICOMP => */ GENERAL_R_R,
+ /* FIDIV => */ GENERAL_RW_R,
+ /* FIDIVR => */ GENERAL_RW_R,
+ // TODO: writing to st(0) is only kind of accurate, this *pushes* to the operand stack..
+ /* FILD => */ GENERAL_W_R,
+ /* FIMUL => */ GENERAL_RW_R,
+ // TODO: x87 stack pointer inc
+ /* FINCSTP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FIST => */ GENERAL_W_R,
+ /* FISTP => */ GENERAL_W_R,
+ /* FISTTP => */ GENERAL_W_R,
+ /* FISUB => */ GENERAL_RW_R,
+ /* FISUBR => */ GENERAL_RW_R,
+ // TODO: writing to st(0) is only kind of accurate, this *pushes* to the operand stack..
+ /* FLD => */ GENERAL_W_R,
+ // TODO: fpu stack write
+ /* FLD1 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FLDCW => */ GENERAL_R,
+ /* FLDENV => */ GENERAL_R
+ .set_pl_any()
+ .set_complex(true),
+ // TODO: fpu stack write
+ /* FLDL2E => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: fpu stack write
+ /* FLDL2T => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: fpu stack write
+ /* FLDLG2 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: fpu stack write
+ /* FLDLN2 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: fpu stack write
+ /* FLDPI => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: fpu stack write
+ /* FLDZ => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FMUL => */ GENERAL_RW_R,
+ /* FMULP => */ GENERAL_RW_R,
+ // TODO: report change to x87 flags?
+ /* FNCLEX => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: report change to x87 flags?
+ /* FNINIT => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FNOP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FNSAVE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ /* FNSTCW => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ /* FNSTENV => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ // TODO: never produced..
+ /* FNSTOR => */ BehaviorDigest::empty(),
+ /* FNSTSW => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ // TODO: read st(1) with atan(st(1)/st(0)) and pop
+ /* FPATAN => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), st(1), write st(0)
+ /* FPREM => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), st(1), write st(0)
+ /* FPREM1 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), write, push?
+ /* FPTAN => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), write, push?
+ /* FRNDINT => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FRSTOR => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Write)
+ .set_complex(true),
+ // TODO: read st(0), st(1)
+ /* FSCALE => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: report this as a complex instruction?
+ /* FSETPM287_NOP => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: st(0) -> st(0)
+ /* FSIN => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: st(0) -> st(0)
+ /* FSINCOS => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: st(0) -> st(0)
+ /* FSQRT => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FST => */ GENERAL_W_R,
+ /* FSTP => */ GENERAL_W_R,
+ /* FSTPNCE => */ GENERAL_W_R,
+ /* FSUB => */ GENERAL_RW_R,
+ /* FSUBP => */ GENERAL_RW_R,
+ /* FSUBR => */ GENERAL_RW_R,
+ /* FSUBRP => */ GENERAL_RW_R,
+ // TODO: report change to x87 flags, read of st(0)?
+ /* FTST => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FUCOM => */ GENERAL_R_R,
+ /* FUCOMI => */ GENERAL_R_R_FLAGWRITE,
+ /* FUCOMIP => */ GENERAL_R_R_FLAGWRITE,
+ /* FUCOMP => */ GENERAL_R_R,
+ /* FUCOMPP => */ GENERAL_R_R,
+ // TODO: report change to x87 flags?
+ /* FXAM => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* FXCH => */ GENERAL_RW_RW,
+ // TODO: read st(0), write st(0), x87 push
+ /* FXTRACT => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), write st(0)
+ /* FYL2X => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ // TODO: read st(0), write st(0)
+ /* FYL2XP1 => */ BehaviorDigest::empty()
+ .set_pl_any(),
+
+ /* LOOPNZ => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::ReadWrite)
+ .set_nontrivial(true),
+ /* LOOPZ => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::ReadWrite)
+ .set_nontrivial(true),
+ /* LOOP => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::ReadWrite)
+ .set_nontrivial(true),
+ /* JECXZ => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_nontrivial(true),
+
+ /* PUSHA => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_nontrivial(true), // 66 prefix adjusts size of pushed registers
+ /* POPA => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_nontrivial(true), // 66 prefix adjusts size of popped registers
+ /* BOUND => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_operand(1, Access::Read),
+ /* ARPL => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::ReadWrite)
+ .set_operand(1, Access::Read)
+ .set_flags_access(Access::Write),
+ /* AAS => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(RW_AX_IDX),
+ /* AAA => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(RW_AX_IDX),
+ /* DAS => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::ReadWrite)
+ .set_implicit_ops(RW_AL_IDX),
+ /* DAA => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_flags_access(Access::ReadWrite)
+ .set_implicit_ops(RW_AL_IDX),
+ /* AAM => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(RW_AX_IDX),
+ /* AAD => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_operand(0, Access::Read)
+ .set_flags_access(Access::Write)
+ .set_implicit_ops(RW_AX_IDX),
+
+ // started shipping in Tremont, 2020 sept 23
+ // while this instruction is marked "write, read", the written first operand is a register
+ // interpreteed as an address for a memory destination through the `es` selector.
+ /* MOVDIR64B => */ GENERAL_W_R
+ .set_complex(true),
+ /* MOVDIRI => */ GENERAL_W_R,
+
+ // started shipping in Tiger Lake, 2020 sept 2
+ /* AESDEC128KL => */ GENERAL_RW_R
+ .set_flags_access(Access::Write),
+ /* AESDEC256KL => */ GENERAL_RW_R
+ .set_flags_access(Access::Write),
+ /* AESDECWIDE128KL => */ GENERAL_R
+ .set_implicit_ops(RW_XMM0TO7_IDX)
+ .set_flags_access(Access::Write),
+ /* AESDECWIDE256KL => */ GENERAL_R
+ .set_implicit_ops(RW_XMM0TO7_IDX)
+ .set_flags_access(Access::Write),
+ /* AESENC128KL => */ GENERAL_RW_R
+ .set_flags_access(Access::Write),
+ /* AESENC256KL => */ GENERAL_RW_R
+ .set_flags_access(Access::Write),
+ /* AESENCWIDE128KL => */ GENERAL_R
+ .set_implicit_ops(RW_XMM0TO7_IDX)
+ .set_flags_access(Access::Write),
+ /* AESENCWIDE256KL => */ GENERAL_R
+ .set_implicit_ops(RW_XMM0TO7_IDX)
+ .set_flags_access(Access::Write),
+ /* ENCODEKEY128 => */ GENERAL_W_R
+ .set_implicit_ops(ENCODEKEY_IDX)
+ .set_flags_access(Access::Write),
+ /* ENCODEKEY256 => */ GENERAL_W_R
+ .set_implicit_ops(ENCODEKEY_IDX)
+ .set_flags_access(Access::Write),
+ /* LOADIWKEY => */ GENERAL_R_R
+ .set_implicit_ops(LOADIWKEY_IDX)
+ .set_flags_access(Access::Write),
+
+ // unsure
+ /* HRESET => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read) // but really, "explicit imm8 operand is ignored"
+ .set_implicit_ops(READ_EAX_IDX)
+ .set_complex(true),
+
+ // 3dnow. note these are yet untested!
+ // the 3dnow DSP instructions (pi2fw, pf2iw, pfnacc, pfpnacc, pswapd)
+ // are even more untested.
+ /* FEMMS => */ GENERAL,
+ /* PI2FW => */ GENERAL_RW_R,
+ /* PI2FD => */ GENERAL_W_R,
+ /* PF2IW => */ GENERAL_RW_R,
+ /* PF2ID => */ GENERAL_W_R,
+ /* PMULHRW => */ GENERAL_RW_R,
+ /* PFCMPGE => */ GENERAL_RW_R,
+ /* PFMIN => */ GENERAL_RW_R,
+ /* PFRCP => */ GENERAL_W_R,
+ /* PFRSQRT => */ GENERAL_W_R,
+ /* PFSUB => */ GENERAL_RW_R,
+ /* PFADD => */ GENERAL_RW_R,
+ /* PFCMPGT => */ GENERAL_RW_R,
+ /* PFMAX => */ GENERAL_RW_R,
+ /* PFRCPIT1 => */ GENERAL_RW_R,
+ /* PFRSQIT1 => */ GENERAL_RW_R,
+ /* PFSUBR => */ GENERAL_RW_R,
+ /* PFACC => */ GENERAL_RW_R,
+ /* PFCMPEQ => */ GENERAL_RW_R,
+ /* PFMUL => */ GENERAL_RW_R,
+ /* PFMULHRW => */ GENERAL_RW_R,
+ /* PFRCPIT2 => */ GENERAL_RW_R,
+ /* PFNACC => */ GENERAL_RW_R,
+ /* PFPNACC => */ GENERAL_RW_R,
+ /* PSWAPD => */ GENERAL_RW_RW,
+ /* PAVGUSB => */ GENERAL_RW_R,
+
+ // ENQCMD
+ // similar to movdir64b, but more complex; the first operand is also an address for a
+ // memory destination.
+ /* ENQCMD => */ GENERAL_W_R
+ .set_flags_access(Access::Write)
+ .set_pl0()
+ .set_complex(true),
+ /* ENQCMDS => */ GENERAL_W_R
+ .set_flags_access(Access::Write)
+ .set_pl0()
+ .set_complex(true),
+
+ // INVPCID
+ // this almost meets the bar to be "complex", given that it manages non-architectural
+ // state not described by the operand iterator. but.. not quite, for now?
+ /* INVEPT => */ GENERAL_R_R,
+ // similar to above.
+ /* INVVPID => */ GENERAL_R_R,
+ // again, similar to `invept` above.
+ /* INVPCID => */ GENERAL_R_R,
+
+ // PTWRITE
+ // TODO: untested
+ /* PTWRITE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ // GFNI
+ /* GF2P8AFFINEQB => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* GF2P8AFFINEINVQB => */ GENERAL_RW_R
+ .set_operand(2, Access::Read),
+ /* GF2P8MULB => */ GENERAL_RW_R,
+
+ // CET
+ /* WRUSS => */ GENERAL_W_R
+ .set_pl0()
+ .set_complex(true),
+ /* WRSS => */ GENERAL_W_R
+ .set_pl_special()
+ .set_complex(true),
+ /* INCSSP => */ GENERAL_R
+ .set_pl_special()
+ .set_complex(true),
+ /* SAVEPREVSSP => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_complex(true),
+ /* SETSSBSY => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* CLRSSBSY => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_operand(0, Access::ReadWrite)
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+ /* RSTORSSP => */ BehaviorDigest::empty()
+ .set_pl_special()
+ .set_operand(0, Access::ReadWrite)
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+ /* ENDBR64 => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* ENDBR32 => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ // TDX
+ /* TDCALL => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* SEAMRET => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* SEAMOPS => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+ /* SEAMCALL => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_complex(true),
+
+ // WAITPKG
+ /* TPAUSE => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* UMONITOR => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* UMWAIT => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ // UINTR
+ /* UIRET => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* TESTUI => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* CLUI => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* STUI => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+ /* SENDUIPI => */ BehaviorDigest::empty()
+ .set_pl_any()
+ .set_complex(true),
+
+ // TSXLDTRK
+ // arguably these should be considered "complex" similar to `uintr` instructions above, but
+ // they are not (at this time). the arbitrary distinction here is that x{sus,res}ldtrk
+ // operate on nothing *but* the trackedness of loads, so interactions with this processor
+ // state can be easily determined by looking for these instructions. additionally, a user
+ // interested in this state is probably already looking for these instructions, so
+ // declaring them complex adds burden to all other use for no benefit.
+ /* XSUSLDTRK => */ BehaviorDigest::empty()
+ .set_pl_any(),
+ /* XRESLDTRK => */ BehaviorDigest::empty()
+ .set_pl_any(),
+
+ // AVX512F
+ /* VALIGND => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VALIGNQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VBLENDMPD => */ GENERAL_W_R_R,
+ /* VBLENDMPS => */ GENERAL_W_R_R,
+ /* VCOMPRESSPD => */ GENERAL_W_R,
+ /* VCOMPRESSPS => */ GENERAL_W_R,
+ /* VCVTPD2UDQ => */ GENERAL_W_R,
+ /* VCVTTPD2UDQ => */ GENERAL_W_R,
+ /* VCVTPS2UDQ => */ GENERAL_W_R,
+ /* VCVTTPS2UDQ => */ GENERAL_W_R,
+ /* VCVTQQ2PD => */ GENERAL_W_R,
+ /* VCVTQQ2PS => */ GENERAL_W_R,
+ /* VCVTSD2USI => */ GENERAL_W_R,
+ /* VCVTTSD2USI => */ GENERAL_W_R,
+ /* VCVTSS2USI => */ GENERAL_W_R,
+ /* VCVTTSS2USI => */ GENERAL_W_R,
+ /* VCVTUDQ2PD => */ GENERAL_W_R,
+ /* VCVTUDQ2PS => */ GENERAL_W_R,
+ /* VCVTUSI2USD => */ GENERAL_W_R,
+ /* VCVTUSI2USS => */ GENERAL_W_R,
+ /* VEXPANDPD => */ GENERAL_W_R,
+ /* VEXPANDPS => */ GENERAL_W_R,
+ /* VEXTRACTF32X4 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTF64X4 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTI32X4 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTI64X4 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFIXUPIMMPD => */ GENERAL_RW_R_R
+ .set_operand(3, Access::Read),
+ /* VFIXUPIMMPS => */ GENERAL_RW_R_R
+ .set_operand(3, Access::Read),
+ /* VFIXUPIMMSD => */ GENERAL_RW_R_R
+ .set_operand(3, Access::Read),
+ /* VFIXUPIMMSS => */ GENERAL_RW_R_R
+ .set_operand(3, Access::Read),
+ /* VGETEXPPD => */ GENERAL_W_R,
+ /* VGETEXPPS => */ GENERAL_W_R,
+ /* VGETEXPSD => */ GENERAL_W_R_R,
+ /* VGETEXPSS => */ GENERAL_W_R_R,
+ /* VGETMANTPD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VGETMANTPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VGETMANTSD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VGETMANTSS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTF32X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTF64X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTI64X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VMOVDQA32 => */ GENERAL_W_R,
+ /* VMOVDQA64 => */ GENERAL_W_R,
+ /* VMOVDQU32 => */ GENERAL_W_R,
+ /* VMOVDQU64 => */ GENERAL_W_R,
+ /* VPBLENDMD => */ GENERAL_W_R_R,
+ /* VPBLENDMQ => */ GENERAL_W_R_R,
+ /* VPCMPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPUD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPUQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCOMPRESSQ => */ GENERAL_W_R,
+ /* VPCOMPRESSD => */ GENERAL_W_R,
+ /* VPERMI2D => */ GENERAL_W_R_R,
+ /* VPERMI2Q => */ GENERAL_W_R_R,
+ /* VPERMI2PD => */ GENERAL_W_R_R,
+ /* VPERMI2PS => */ GENERAL_W_R_R,
+ /* VPERMT2D => */ GENERAL_W_R_R,
+ /* VPERMT2Q => */ GENERAL_W_R_R,
+ /* VPERMT2PD => */ GENERAL_W_R_R,
+ /* VPERMT2PS => */ GENERAL_W_R_R,
+ /* VPMAXSQ => */ GENERAL_W_R_R,
+ /* VPMAXUQ => */ GENERAL_W_R_R,
+ /* VPMINSQ => */ GENERAL_W_R_R,
+ /* VPMINUQ => */ GENERAL_W_R_R,
+ /* VPMOVSQB => */ GENERAL_W_R,
+ /* VPMOVUSQB => */ GENERAL_W_R,
+ /* VPMOVSQW => */ GENERAL_W_R,
+ /* VPMOVUSQW => */ GENERAL_W_R,
+ /* VPMOVSQD => */ GENERAL_W_R,
+ /* VPMOVUSQD => */ GENERAL_W_R,
+ /* VPMOVSDB => */ GENERAL_W_R,
+ /* VPMOVUSDB => */ GENERAL_W_R,
+ /* VPMOVSDW => */ GENERAL_W_R,
+ /* VPMOVUSDW => */ GENERAL_W_R,
+ /* VPROLD => */ GENERAL_W_R_R,
+ /* VPROLQ => */ GENERAL_W_R_R,
+ /* VPROLVD => */ GENERAL_W_R_R,
+ /* VPROLVQ => */ GENERAL_W_R_R,
+ /* VPRORD => */ GENERAL_W_R_R,
+ /* VPRORQ => */ GENERAL_W_R_R,
+ /* VPRORRD => */ GENERAL_W_R_R,
+ /* VPRORRQ => */ GENERAL_W_R_R,
+ // TODO: complex
+ /* VPSCATTERDD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPSCATTERDQ => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPSCATTERQD => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPSCATTERQQ => */ BehaviorDigest::empty()
+ .set_complex(true),
+ /* VPSRAQ => */ GENERAL_W_R_R,
+ /* VPSRAVQ => */ GENERAL_W_R_R,
+ /* VPTESTNMD => */ GENERAL_W_R_R,
+ /* VPTESTNMQ => */ GENERAL_W_R_R,
+ /* VPTERNLOGD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPTERNLOGQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPTESTMD => */ GENERAL_W_R_R,
+ /* VPTESTMQ => */ GENERAL_W_R_R,
+ /* VRCP14PD => */ GENERAL_W_R,
+ /* VRCP14PS => */ GENERAL_W_R,
+ /* VRCP14SD => */ GENERAL_W_R_R,
+ /* VRCP14SS => */ GENERAL_W_R_R,
+ /* VRNDSCALEPD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VRNDSCALEPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VRNDSCALESD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRNDSCALESS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRSQRT14PD => */ GENERAL_W_R,
+ /* VRSQRT14PS => */ GENERAL_W_R,
+ /* VRSQRT14SD => */ GENERAL_W_R_R,
+ /* VRSQRT14SS => */ GENERAL_W_R_R,
+ // vvv --- these don't exist..
+ /* VSCALEDPD => */ BehaviorDigest::empty(),
+ /* VSCALEDPS => */ BehaviorDigest::empty(),
+ /* VSCALEDSD => */ BehaviorDigest::empty(),
+ /* VSCALEDSS => */ BehaviorDigest::empty(),
+ /* VSCATTERDD => */ BehaviorDigest::empty(),
+ /* VSCATTERDQ => */ BehaviorDigest::empty(),
+ /* VSCATTERQD => */ BehaviorDigest::empty(),
+ /* VSCATTERQQ => */ BehaviorDigest::empty(),
+ // ^^^ --- these don't exist..
+ /* VSHUFF32X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VSHUFF64X2 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VSHUFI32X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VSHUFI64X2 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+
+ // AVX512DQ
+ /* VCVTTPD2QQ => */ GENERAL_W_R,
+ /* VCVTPD2QQ => */ GENERAL_W_R,
+ /* VCVTTPD2UQQ => */ GENERAL_W_R,
+ /* VCVTPD2UQQ => */ GENERAL_W_R,
+ /* VCVTTPS2QQ => */ GENERAL_W_R,
+ /* VCVTPS2QQ => */ GENERAL_W_R,
+ /* VCVTTPS2UQQ => */ GENERAL_W_R,
+ /* VCVTPS2UQQ => */ GENERAL_W_R,
+ /* VCVTUQQ2PD => */ GENERAL_W_R,
+ /* VCVTUQQ2PS => */ GENERAL_W_R,
+ /* VEXTRACTF64X2 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTI64X2 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFPCLASSPD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFPCLASSPS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFPCLASSSD => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VFPCLASSSS => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VINSERTF64X2 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTI64X2 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPMOVM2D => */ GENERAL_W_R,
+ /* VPMOVM2Q => */ GENERAL_W_R,
+ /* VPMOVB2D => */ GENERAL_W_R,
+ /* VPMOVQ2M => */ GENERAL_W_R,
+ /* VRANGEPD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRANGEPS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRANGESD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VRANGESS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VREDUCEPD => */ GENERAL_W_R_R
+ .set_operand(2, Access::Read),
+ /* VREDUCEPS => */ GENERAL_W_R_R
+ .set_operand(2, Access::Read),
+ /* VREDUCESD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VREDUCESS => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+
+ // AVX512BW
+ /* VDBPSADBW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VMOVDQU8 => */ GENERAL_W_R,
+ /* VMOVDQU16 => */ GENERAL_W_R,
+ /* VPBLENDMB => */ GENERAL_W_R_R,
+ /* VPBLENDMW => */ GENERAL_W_R_R,
+ /* VPCMPB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPUB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPCMPUW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPERMW => */ GENERAL_W_R_R,
+ /* VPERMI2B => */ GENERAL_W_R_R,
+ /* VPERMI2W => */ GENERAL_W_R_R,
+ /* VPMOVM2B => */ GENERAL_W_R,
+ /* VPMOVM2W => */ GENERAL_W_R,
+ /* VPMOVB2M => */ GENERAL_W_R,
+ /* VPMOVW2M => */ GENERAL_W_R,
+ /* VPMOVSWB => */ GENERAL_W_R,
+ /* VPMOVUSWB => */ GENERAL_W_R,
+ /* VPSLLVW => */ GENERAL_W_R_R,
+ /* VPSRAVW => */ GENERAL_W_R_R,
+ /* VPSRLVW => */ GENERAL_W_R_R,
+ /* VPTESTNMB => */ GENERAL_W_R_R,
+ /* VPTESTNMW => */ GENERAL_W_R_R,
+ /* VPTESTMB => */ GENERAL_W_R_R,
+ /* VPTESTMW => */ GENERAL_W_R_R,
+
+ // AVX512CD
+ // TODO: this one does not exist
+ /* VPBROADCASTM => */ BehaviorDigest::empty(),
+ /* VPCONFLICTD => */ GENERAL_W_R,
+ /* VPCONFLICTQ => */ GENERAL_W_R,
+ /* VPLZCNTD => */ GENERAL_W_R,
+ /* VPLZCNTQ => */ GENERAL_W_R,
+
+ /* KUNPCKBW => */ GENERAL_W_R_R,
+ /* KUNPCKWD => */ GENERAL_W_R_R,
+ /* KUNPCKDQ => */ GENERAL_W_R_R,
+
+ /* KADDB => */ GENERAL_W_R_R,
+ /* KANDB => */ GENERAL_W_R_R,
+ /* KANDNB => */ GENERAL_W_R_R,
+ /* KMOVB => */ GENERAL_W_R,
+ /* KNOTB => */ GENERAL_W_R,
+ /* KORB => */ GENERAL_W_R_R,
+ /* KORTESTB => */ GENERAL_R_R
+ .set_flags_access(Access::Write),
+ /* KSHIFTLB => */ GENERAL_W_R_R,
+ /* KSHIFTRB => */ GENERAL_W_R_R,
+ /* KTESTB => */ GENERAL_R_R
+ .set_flags_access(Access::Write),
+ /* KXNORB => */ GENERAL_W_R_R,
+ /* KXORB => */ GENERAL_W_R_R,
+ /* KADDW => */ GENERAL_W_R_R,
+ /* KANDW => */ GENERAL_W_R_R,
+ /* KANDNW => */ GENERAL_W_R_R,
+ /* KMOVW => */ GENERAL_W_R,
+ /* KNOTW => */ GENERAL_W_R,
+ /* KORW => */ GENERAL_W_R_R,
+ /* KORTESTW => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KSHIFTLW => */ GENERAL_W_R_R,
+ /* KSHIFTRW => */ GENERAL_W_R_R,
+ /* KTESTW => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KXNORW => */ GENERAL_W_R_R,
+ /* KXORW => */ GENERAL_W_R_R,
+ /* KADDD => */ GENERAL_W_R_R,
+ /* KANDD => */ GENERAL_W_R_R,
+ /* KANDND => */ GENERAL_W_R_R,
+ /* KMOVD => */ GENERAL_W_R,
+ /* KNOTD => */ GENERAL_W_R,
+ /* KORD => */ GENERAL_W_R_R,
+ /* KORTESTD => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KSHIFTLD => */ GENERAL_W_R_R,
+ /* KSHIFTRD => */ GENERAL_W_R_R,
+ /* KTESTD => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KXNORD => */ GENERAL_W_R_R,
+ /* KXORD => */ GENERAL_W_R_R,
+ /* KADDQ => */ GENERAL_W_R_R,
+ /* KANDQ => */ GENERAL_W_R_R,
+ /* KANDNQ => */ GENERAL_W_R_R,
+ /* KMOVQ => */ GENERAL_W_R,
+ /* KNOTQ => */ GENERAL_W_R,
+ /* KORQ => */ GENERAL_W_R_R,
+ /* KORTESTQ => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KSHIFTLQ => */ GENERAL_W_R_R,
+ /* KSHIFTRQ => */ GENERAL_W_R_R,
+ /* KTESTQ => */ GENERAL_W_R_R
+ .set_flags_access(Access::Write),
+ /* KXNORQ => */ GENERAL_W_R_R,
+ /* KXORQ => */ GENERAL_W_R_R,
+
+ // AVX512ER
+ /* VEXP2PD => */ GENERAL_W_R,
+ /* VEXP2PS => */ GENERAL_W_R,
+ // TODO: well, this one isn't real.
+ /* VEXP2SD => */ BehaviorDigest::empty(),
+ // TODO: or this one.
+ /* VEXP2SS => */ BehaviorDigest::empty(),
+ /* VRCP28PD => */ GENERAL_W_R,
+ /* VRCP28PS => */ GENERAL_W_R,
+ /* VRCP28SD => */ GENERAL_W_R_R,
+ /* VRCP28SS => */ GENERAL_W_R_R,
+ /* VRSQRT28PD => */ GENERAL_W_R,
+ /* VRSQRT28PS => */ GENERAL_W_R,
+ /* VRSQRT28SD => */ GENERAL_W_R_R,
+ /* VRSQRT28SS => */ GENERAL_W_R_R,
+
+ // AVX512PF
+ /* VGATHERPF0DPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF0DPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF0QPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF0QPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF1DPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF1DPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF1QPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VGATHERPF1QPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF0DPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF0DPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF0QPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF0QPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF1DPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF1DPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF1QPD => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+ /* VSCATTERPF1QPS => */ BehaviorDigest::empty()
+ .set_operand(0, Access::Read)
+ .set_complex(true),
+
+ // MPX
+ /* BNDMK => */ GENERAL_W_R,
+ /* BNDCL => */ GENERAL_R,
+ /* BNDCU => */ GENERAL_R,
+ /* BNDCN => */ GENERAL_R,
+ /* BNDMOV => */ GENERAL_W_R,
+ /* BNDLDX => */ GENERAL_W_R
+ .set_complex(true),
+ /* BNDSTX => */ GENERAL_W_R
+ .set_complex(true),
+
+ /* VGF2P8AFFINEQB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VGF2P8AFFINEINVQB => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHRDQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHRDD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHRDW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHLDQ => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHLDD => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VPSHLDW => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VBROADCASTF32X8 => */ GENERAL_W_R,
+ /* VBROADCASTF64X4 => */ GENERAL_W_R,
+ /* VBROADCASTF32X4 => */ GENERAL_W_R,
+ /* VBROADCASTF64X2 => */ GENERAL_W_R,
+ /* VBROADCASTF32X2 => */ GENERAL_W_R,
+ /* VBROADCASTI32X8 => */ GENERAL_W_R,
+ /* VBROADCASTI64X4 => */ GENERAL_W_R,
+ /* VBROADCASTI32X4 => */ GENERAL_W_R,
+ /* VBROADCASTI64X2 => */ GENERAL_W_R,
+ /* VBROADCASTI32X2 => */ GENERAL_W_R,
+ /* VEXTRACTI32X8 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VEXTRACTF32X8 => */ GENERAL_W_R
+ .set_operand(2, Access::Read),
+ /* VINSERTI32X8 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTF32X8 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* VINSERTI32X4 => */ GENERAL_W_R_R
+ .set_operand(3, Access::Read),
+ /* V4FNMADDSS => */ GENERAL_RW_R_R
+ .set_complex(true),
+ /* V4FNMADDPS => */ GENERAL_RW_R_R
+ .set_complex(true),
+ /* VCVTNEPS2BF16 => */ GENERAL_W_R,
+ /* V4FMADDSS => */ GENERAL_RW_R_R
+ .set_complex(true),
+ /* V4FMADDPS => */ GENERAL_RW_R_R
+ .set_complex(true),
+ /* VCVTNE2PS2BF16 => */ GENERAL_W_R_R,
+ /* VP2INTERSECTD => */ GENERAL_W_R_R,
+ /* VP2INTERSECTQ => */ GENERAL_W_R_R,
+ /* VP4DPWSSDS => */ GENERAL_RW_R_R,
+ /* VP4DPWSSD => */ GENERAL_RW_R_R,
+ /* VPDPWSSDS => */ GENERAL_RW_R_R,
+ /* VPDPWSSD => */ GENERAL_RW_R_R,
+ /* VPDPBUSDS => */ GENERAL_RW_R_R,
+ /* VDPBF16PS => */ GENERAL_RW_R_R,
+ /* VPBROADCASTMW2D => */ GENERAL_W_R,
+ /* VPBROADCASTMB2Q => */ GENERAL_W_R,
+ /* VPMOVD2M => */ GENERAL_W_R,
+ /* VPMOVQD => */ GENERAL_W_R,
+ /* VPMOVWB => */ GENERAL_W_R,
+ /* VPMOVDB => */ GENERAL_W_R,
+ /* VPMOVDW => */ GENERAL_W_R,
+ /* VPMOVQB => */ GENERAL_W_R,
+ /* VPMOVQW => */ GENERAL_W_R,
+ /* VGF2P8MULB => */ GENERAL_RW_R_R,
+ /* VPMADD52HUQ => */ GENERAL_RW_R_R,
+ /* VPMADD52LUQ => */ GENERAL_RW_R_R,
+ /* VPSHUFBITQMB => */ GENERAL_W_R_R,
+ /* VPERMB => */ GENERAL_W_R_R,
+ /* VPEXPANDD => */ GENERAL_W_R,
+ /* VPEXPANDQ => */ GENERAL_W_R,
+ /* VPABSQ => */ GENERAL_W_R,
+ /* VPRORVD => */ GENERAL_W_R_R,
+ /* VPRORVQ => */ GENERAL_W_R_R,
+ /* VPMULTISHIFTQB => */ GENERAL_W_R_R,
+ /* VPERMT2B => */ GENERAL_RW_R_R,
+ /* VPERMT2W => */ GENERAL_RW_R_R,
+ /* VPSHRDVQ => */ GENERAL_RW_R_R,
+ /* VPSHRDVD => */ GENERAL_RW_R_R,
+ /* VPSHRDVW => */ GENERAL_RW_R_R,
+ /* VPSHLDVQ => */ GENERAL_RW_R_R,
+ /* VPSHLDVD => */ GENERAL_RW_R_R,
+ /* VPSHLDVW => */ GENERAL_RW_R_R,
+ /* VPCOMPRESSB => */ GENERAL_W_R,
+ /* VPCOMPRESSW => */ GENERAL_W_R,
+ /* VPEXPANDB => */ GENERAL_W_R,
+ /* VPEXPANDW => */ GENERAL_W_R,
+ /* VPOPCNTD => */ GENERAL_W_R,
+ /* VPOPCNTQ => */ GENERAL_W_R,
+ /* VPOPCNTB => */ GENERAL_W_R,
+ /* VPOPCNTW => */ GENERAL_W_R,
+ /* VSCALEFSS => */ GENERAL_W_R_R,
+ /* VSCALEFSD => */ GENERAL_W_R_R,
+ /* VSCALEFPS => */ GENERAL_W_R_R,
+ /* VSCALEFPD => */ GENERAL_W_R_R,
+ /* VPDPBUSD => */ GENERAL_W_R_R,
+ /* VCVTUSI2SD => */ GENERAL_W_R_R,
+ /* VCVTUSI2SS => */ GENERAL_W_R_R,
+ /* VPXORD => */ GENERAL_W_R_R,
+ /* VPXORQ => */ GENERAL_W_R_R,
+ /* VPORD => */ GENERAL_W_R_R,
+ /* VPORQ => */ GENERAL_W_R_R,
+ /* VPANDND => */ GENERAL_W_R_R,
+ /* VPANDNQ => */ GENERAL_W_R_R,
+ /* VPANDD => */ GENERAL_W_R_R,
+ /* VPANDQ => */ GENERAL_W_R_R,
+
+ /* PSMASH => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+ /* PVALIDATE => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+ /* RMPADJUST => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+ /* RMPUPDATE => */ BehaviorDigest::empty()
+ .set_pl0()
+ .set_flags_access(Access::Write)
+ .set_complex(true),
+];
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index 4736352..5e7ddc6 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -3,6 +3,8 @@ mod evex;
#[cfg(feature = "fmt")]
mod display;
pub mod uarch;
+#[cfg(feature = "behavior")]
+pub mod behavior;
pub use crate::MemoryAccessSize;
use crate::{Address, Word};
@@ -1000,6 +1002,7 @@ const XSAVE: [Opcode; 10] = [
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "_debug_internal_asserts", derive(strum::EnumCount))]
#[non_exhaustive]
#[repr(u32)]
pub enum Opcode {