use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DeepX86Opcode {
NOP,
INT3,
UD2,
MOV,
MOVSX,
MOVZX,
MOVABS,
CMOVO,
CMOVNO,
CMOVB,
CMOVAE,
CMOVE,
CMOVNE,
CMOVBE,
CMOVA,
CMOVS,
CMOVNS,
CMOVP,
CMOVNP,
CMOVL,
CMOVGE,
CMOVLE,
CMOVG,
XCHG,
PUSH,
POP,
PUSHF,
POPF,
LEA,
ADD,
ADC,
SUB,
SBB,
MUL,
IMUL,
DIV,
IDIV,
INC,
DEC,
NEG,
AND,
OR,
XOR,
NOT,
TEST,
BSWAP,
SHL,
SHR,
SAR,
ROL,
ROR,
RCL,
RCR,
SHLD,
SHRD,
JMP,
JO,
JNO,
JB,
JAE,
JE,
JNE,
JBE,
JA,
JS,
JNS,
JP,
JNP,
JL,
JGE,
JLE,
JG,
CALL,
RET,
LOOP,
LOOPE,
LOOPNE,
SETO,
SETNO,
SETB,
SETAE,
SETE,
SETNE,
SETBE,
SETA,
SETS,
SETNS,
SETP,
SETNP,
SETL,
SETGE,
SETLE,
SETG,
CMP,
MOVSS,
MOVSD,
ADDSS,
ADDSD,
SUBSS,
SUBSD,
MULSS,
MULSD,
DIVSS,
DIVSD,
SQRTSS,
SQRTSD,
MINSS,
MINSD,
MAXSS,
MAXSD,
CMPSS,
CMPSD,
RCPSS,
RSQRTSS,
ROUNDSS,
ROUNDSD,
CVTSI2SS,
CVTSI2SD,
CVTSS2SI,
CVTSD2SI,
CVTTSS2SI,
CVTTSD2SI,
CVTSS2SD,
CVTSD2SS,
CVTDQ2PS,
CVTPS2DQ,
CVTTPS2DQ,
CVTDQ2PD,
CVTPD2DQ,
CVTTPD2DQ,
ANDPS,
ANDNPS,
ORPS,
XORPS,
ANDPD,
ANDNPD,
ORPD,
XORPD,
ADDPS,
ADDPD,
SUBPS,
SUBPD,
MULPS,
MULPD,
DIVPS,
DIVPD,
RCPPS,
SQRTPS,
RSQRTPS,
MAXPS,
MAXPD,
MINPS,
MINPD,
SHUFPS,
SHUFPD,
PSHUFD,
PSHUFHW,
PSHUFLW,
UNPCKLPS,
UNPCKHPS,
UNPCKLPD,
UNPCKHPD,
MOVHLPS,
MOVLHPS,
MOVAPS,
MOVAPD,
MOVUPS,
MOVUPD,
MOVDQA,
MOVDQU,
PEXTRB,
PEXTRW,
PEXTRD,
PEXTRQ,
PINSRB,
PINSRW,
PINSRD,
PINSRQ,
PADDB,
PADDW,
PADDD,
PADDQ,
PSUBB,
PSUBW,
PSUBD,
PSUBQ,
PMULLW,
PMULHW,
PMULHUW,
PMULUDQ,
PAND,
PANDN,
POR,
PXOR,
PCMPEQB,
PCMPEQW,
PCMPEQD,
PCMPGTB,
PCMPGTW,
PCMPGTD,
PACKSSWB,
PACKSSDW,
PACKUSWB,
PUNPCKLBW,
PUNPCKLWD,
PUNPCKLDQ,
PUNPCKLQDQ,
PUNPCKHBW,
PUNPCKHWD,
PUNPCKHDQ,
PUNPCKHQDQ,
PSLLW,
PSLLD,
PSLLQ,
PSRLW,
PSRLD,
PSRLQ,
PSRAW,
PSRAD,
UCOMISS,
UCOMISD,
COMISS,
COMISD,
MOVD,
MOVQ,
ADDSUBPS,
ADDSUBPD,
HADDPS,
HADDPD,
HSUBPS,
HSUBPD,
MOVSLDUP,
MOVSHDUP,
MOVDDUP,
PHADDW,
PHADDD,
PHADDSW,
PMADDUBSW,
PHSUBW,
PHSUBD,
PHSUBSW,
PSIGNB,
PSIGNW,
PSIGND,
PABSB,
PABSW,
PABSD,
PALIGNR,
BLENDPS,
BLENDPD,
BLENDVPS,
BLENDVPD,
PBLENDW,
DPPS,
DPPD,
EXTRACTPS,
INSERTPS,
PMULLD,
PMULDQ,
PMINSB,
PMINSD,
PMINUW,
PMINUD,
PMAXSB,
PMAXSD,
PMAXUW,
PMAXUD,
PACKUSDW,
PCMPEQQ,
PCMPGTQ,
CRC32,
PTEST,
ROUNDPS,
ROUNDPD,
PMOVSXBW,
PMOVSXWD,
PMOVSXDQ,
PMOVZXBW,
PMOVZXWD,
PMOVZXDQ,
PMOVSXBD,
PMOVSXWQ,
PMOVSXBQ,
PMOVZXBD,
PMOVZXWQ,
PMOVZXBQ,
PHMINPOSUW,
MPSADBW,
POPCNT,
LZCNT,
TZCNT,
ANDN,
BEXTR,
BLSI,
BLSMSK,
BLSR,
BZHI,
MULX,
PDEP,
PEXT,
RORX,
SARX,
SHLX,
SHRX,
VADDPS,
VADDPD,
VADDSS,
VADDSD,
VSUBPS,
VSUBPD,
VSUBSS,
VSUBSD,
VMULPS,
VMULPD,
VMULSS,
VMULSD,
VDIVPS,
VDIVPD,
VDIVSS,
VDIVSD,
VANDPS,
VANDNPS,
VORPS,
VXORPS,
VANDPD,
VANDNPD,
VORPD,
VXORPD,
VBROADCASTSS,
VBROADCASTSD,
VPERMILPS,
VPERMILPD,
VPERM2F128,
VPERM2I128,
VINSERTF128,
VINSERTI128,
VEXTRACTF128,
VEXTRACTI128,
VZEROALL,
VZEROUPPER,
VMOVAPS,
VMOVAPD,
VMOVUPS,
VMOVUPD,
VMOVDQA,
VMOVDQU,
VMOVSS,
VMOVSD,
VCVTSD2SS,
VCVTSS2SD,
VCVTSI2SS,
VCVTSI2SD,
VCVTSS2SI,
VCVTSD2SI,
VCVTTSS2SI,
VCVTTSD2SI,
VSQRTPS,
VSQRTPD,
VSQRTSS,
VSQRTSD,
VRSQRTPS,
VRSQRTSS,
VRCPPS,
VRCPSS,
VMINPS,
VMINPD,
VMAXPS,
VMAXPD,
VMINSS,
VMINSD,
VMAXSS,
VMAXSD,
VSHUFPS,
VSHUFPD,
VUNPCKLPS,
VUNPCKHPS,
VUNPCKLPD,
VUNPCKHPD,
VCMPPS,
VCMPPD,
VCMPSS,
VCMPSD,
VUCOMISS,
VUCOMISD,
VCOMISS,
VCOMISD,
VPTEST,
VMOVMSKPS,
VMOVMSKPD,
VPBROADCASTB,
VPBROADCASTW,
VPBROADCASTD,
VPBROADCASTQ,
VPERMQ,
VPERMPD,
VPERMPS,
VPERMD,
VPGATHERDD,
VPGATHERDQ,
VPGATHERQD,
VPGATHERQQ,
VGATHERDPS,
VGATHERDPD,
VGATHERQPS,
VGATHERQPD,
VPADDUSB,
VPADDUSW,
VPADDSB,
VPADDSW,
VPSUBUSB,
VPSUBUSW,
VPSUBSB,
VPSUBSW,
VPMULLW,
VPMULLD,
VPMULHW,
VPMULHUW,
VPMULHRSW,
VPMULUDQ,
VPMULDQ,
VPSLLVD,
VPSLLVQ,
VPSRLVD,
VPSRLVQ,
VPSRAVD,
VPSRAVQ,
VPABS,
VPSIGN,
VPHADD,
VPHSUB,
VPMADD,
VPSADBW,
VPALIGNR,
VFMADD132PD,
VFMADD213PD,
VFMADD231PD,
VFMSUB132PD,
VFMSUB213PD,
VFMSUB231PD,
VFNMADD132PD,
VFNMADD213PD,
VFNMADD231PD,
VFNMSUB132PD,
VFNMSUB213PD,
VFNMSUB231PD,
VFMADD132PS,
VFMADD213PS,
VFMADD231PS,
VFMSUB132PS,
VFMSUB213PS,
VFMSUB231PS,
VFNMADD132PS,
VFNMADD213PS,
VFNMADD231PS,
VFNMSUB132PS,
VFNMSUB213PS,
VFNMSUB231PS,
VFMADD132SS,
VFMADD213SS,
VFMADD231SS,
VFMSUB132SS,
VFMSUB213SS,
VFMSUB231SS,
VFNMADD132SS,
VFNMADD213SS,
VFNMADD231SS,
VFNMSUB132SS,
VFNMSUB213SS,
VFNMSUB231SS,
VFMADD132SD,
VFMADD213SD,
VFMADD231SD,
VFMSUB132SD,
VFMSUB213SD,
VFMSUB231SD,
VFNMADD132SD,
VFNMADD213SD,
VFNMADD231SD,
VFNMSUB132SD,
VFNMSUB213SD,
VFNMSUB231SD,
VADDPD_Z,
VADDPS_Z,
VADDSD_Z,
VADDSS_Z,
VSUBPD_Z,
VSUBPS_Z,
VSUBSD_Z,
VSUBSS_Z,
VMULPD_Z,
VMULPS_Z,
VMULSD_Z,
VMULSS_Z,
VDIVPD_Z,
VDIVPS_Z,
VDIVSD_Z,
VDIVSS_Z,
VFMADD132PD_Z,
VFMADD213PD_Z,
VFMADD231PD_Z,
VFMADD132PS_Z,
VFMADD213PS_Z,
VFMADD231PS_Z,
VBROADCASTSS_Z,
VBROADCASTSD_Z,
VPERMQ_Z,
VPERMPD_Z,
VSHUFPD_Z,
VSHUFPS_Z,
VSHUFI32X4,
VSHUFI64X2,
VSHUFF32X4,
VSHUFF64X2,
VINSERTI32X4,
VINSERTI64X2,
VINSERTF32X4,
VINSERTF64X2,
VEXTRACTI32X4,
VEXTRACTI64X2,
VEXTRACTF32X4,
VEXTRACTF64X2,
VBLENDMPD,
VBLENDMPS,
VPBLENDMD,
VPBLENDMQ,
VPBLENDMB,
VPBLENDMW,
VPCMPEQD_Z,
VPCMPEQQ_Z,
VPCMPGTD_Z,
VPCMPGTQ_Z,
VPCMPEQD_K,
VPCMPEQQ_K,
VPCMPGTD_K,
VPCMPGTQ_K,
KANDW,
KANDNW,
KORW,
KXORW,
KNOTW,
KORTESTW,
KTESTW,
KSHIFTLW,
KSHIFTRW,
KMOVW,
KUNPCKBW,
VPCOMPRESSD,
VPCOMPRESSQ,
VPEXPANDD,
VPEXPANDQ,
LOCK_ADD,
LOCK_SUB,
LOCK_AND,
LOCK_OR,
LOCK_XOR,
LOCK_XCHG,
LOCK_CMPXCHG,
LOCK_INC,
LOCK_DEC,
XADD,
CMPXCHG,
CMPXCHG8B,
CMPXCHG16B,
MFENCE,
LFENCE,
SFENCE,
MOVSB,
MOVSW,
MOVSD_STR,
MOVSQ,
STOSB,
STOSW,
STOSD,
STOSQ,
LODSB,
LODSW,
LODSD,
LODSQ,
CMPSB,
CMPSW,
CMPSD_STR,
CMPSQ,
SCASB,
SCASW,
SCASD,
SCASQ,
REP_MOVSB,
REP_STOSB,
CBW,
CWDE,
CDQE,
CWD,
CDQ,
CQO,
MOVSXD,
LAHF,
SAHF,
CLC,
STC,
CMC,
CLD,
STD,
CLTS,
HLT,
PAUSE,
CPUID,
RDTSC,
RDTSCP,
RDPMC,
XGETBV,
XSETBV,
CLFLUSH,
CLFLUSHOPT,
CLWB,
PREFETCH,
PREFETCHW,
PREFETCHWT1,
ENDBR32,
ENDBR64,
HRESET,
}
impl DeepX86Opcode {
pub fn mnemonic(&self) -> &'static str {
match self {
Self::NOP => "nop",
Self::INT3 => "int3",
Self::UD2 => "ud2",
Self::MOV => "mov",
Self::MOVSX => "movsx",
Self::MOVZX => "movzx",
Self::MOVABS => "movabs",
Self::CMOVO => "cmovo",
Self::CMOVNO => "cmovno",
Self::CMOVB => "cmovb",
Self::CMOVAE => "cmovae",
Self::CMOVE => "cmove",
Self::CMOVNE => "cmovne",
Self::CMOVBE => "cmovbe",
Self::CMOVA => "cmova",
Self::CMOVS => "cmovs",
Self::CMOVNS => "cmovns",
Self::CMOVP => "cmovp",
Self::CMOVNP => "cmovnp",
Self::CMOVL => "cmovl",
Self::CMOVGE => "cmovge",
Self::CMOVLE => "cmovle",
Self::CMOVG => "cmovg",
Self::XCHG => "xchg",
Self::PUSH => "push",
Self::POP => "pop",
Self::PUSHF => "pushf",
Self::POPF => "popf",
Self::LEA => "lea",
Self::ADD => "add",
Self::ADC => "adc",
Self::SUB => "sub",
Self::SBB => "sbb",
Self::MUL => "mul",
Self::IMUL => "imul",
Self::DIV => "div",
Self::IDIV => "idiv",
Self::INC => "inc",
Self::DEC => "dec",
Self::NEG => "neg",
Self::AND => "and",
Self::OR => "or",
Self::XOR => "xor",
Self::NOT => "not",
Self::TEST => "test",
Self::BSWAP => "bswap",
Self::SHL => "shl",
Self::SHR => "shr",
Self::SAR => "sar",
Self::ROL => "rol",
Self::ROR => "ror",
Self::RCL => "rcl",
Self::RCR => "rcr",
Self::SHLD => "shld",
Self::SHRD => "shrd",
Self::JMP => "jmp",
Self::JO => "jo",
Self::JNO => "jno",
Self::JB => "jb",
Self::JAE => "jae",
Self::JE => "je",
Self::JNE => "jne",
Self::JBE => "jbe",
Self::JA => "ja",
Self::JS => "js",
Self::JNS => "jns",
Self::JP => "jp",
Self::JNP => "jnp",
Self::JL => "jl",
Self::JGE => "jge",
Self::JLE => "jle",
Self::JG => "jg",
Self::CALL => "call",
Self::RET => "ret",
Self::LOOP => "loop",
Self::LOOPE => "loope",
Self::LOOPNE => "loopne",
Self::SETO => "seto",
Self::SETNO => "setno",
Self::SETB => "setb",
Self::SETAE => "setae",
Self::SETE => "sete",
Self::SETNE => "setne",
Self::SETBE => "setbe",
Self::SETA => "seta",
Self::SETS => "sets",
Self::SETNS => "setns",
Self::SETP => "setp",
Self::SETNP => "setnp",
Self::SETL => "setl",
Self::SETGE => "setge",
Self::SETLE => "setle",
Self::SETG => "setg",
Self::CMP => "cmp",
Self::MOVSS => "movss",
Self::MOVSD => "movsd",
Self::ADDSS => "addss",
Self::ADDSD => "addsd",
Self::SUBSS => "subss",
Self::SUBSD => "subsd",
Self::MULSS => "mulss",
Self::MULSD => "mulsd",
Self::DIVSS => "divss",
Self::DIVSD => "divsd",
Self::SQRTSS => "sqrtss",
Self::SQRTSD => "sqrtsd",
Self::MINSS => "minss",
Self::MINSD => "minsd",
Self::MAXSS => "maxss",
Self::MAXSD => "maxsd",
Self::CMPSS => "cmpss",
Self::CMPSD => "cmpsd",
Self::RCPSS => "rcpss",
Self::RSQRTSS => "rsqrtss",
Self::ROUNDSS => "roundss",
Self::ROUNDSD => "roundsd",
Self::CVTSI2SS => "cvtsi2ss",
Self::CVTSI2SD => "cvtsi2sd",
Self::CVTSS2SI => "cvtss2si",
Self::CVTSD2SI => "cvtsd2si",
Self::CVTTSS2SI => "cvttss2si",
Self::CVTTSD2SI => "cvttsd2si",
Self::CVTSS2SD => "cvtss2sd",
Self::CVTSD2SS => "cvtsd2ss",
Self::CVTDQ2PS => "cvtdq2ps",
Self::CVTPS2DQ => "cvtps2dq",
Self::CVTTPS2DQ => "cvttps2dq",
Self::CVTDQ2PD => "cvtdq2pd",
Self::CVTPD2DQ => "cvtpd2dq",
Self::CVTTPD2DQ => "cvttpd2dq",
Self::ANDPS => "andps",
Self::ANDNPS => "andnps",
Self::ORPS => "orps",
Self::XORPS => "xorps",
Self::ANDPD => "andpd",
Self::ANDNPD => "andnpd",
Self::ORPD => "orpd",
Self::XORPD => "xorpd",
Self::ADDPS => "addps",
Self::ADDPD => "addpd",
Self::SUBPS => "subps",
Self::SUBPD => "subpd",
Self::MULPS => "mulps",
Self::MULPD => "mulpd",
Self::DIVPS => "divps",
Self::DIVPD => "divpd",
Self::RCPPS => "rcpps",
Self::SQRTPS => "sqrtps",
Self::RSQRTPS => "rsqrtps",
Self::MAXPS => "maxps",
Self::MAXPD => "maxpd",
Self::MINPS => "minps",
Self::MINPD => "minpd",
Self::SHUFPS => "shufps",
Self::SHUFPD => "shufpd",
Self::PSHUFD => "pshufd",
Self::PSHUFHW => "pshufhw",
Self::PSHUFLW => "pshuflw",
Self::UNPCKLPS => "unpcklps",
Self::UNPCKHPS => "unpckhps",
Self::UNPCKLPD => "unpcklpd",
Self::UNPCKHPD => "unpckhpd",
Self::MOVHLPS => "movhlps",
Self::MOVLHPS => "movlhps",
Self::MOVAPS => "movaps",
Self::MOVAPD => "movapd",
Self::MOVUPS => "movups",
Self::MOVUPD => "movupd",
Self::MOVDQA => "movdqa",
Self::MOVDQU => "movdqu",
Self::PEXTRB => "pextrb",
Self::PEXTRW => "pextrw",
Self::PEXTRD => "pextrd",
Self::PEXTRQ => "pextrq",
Self::PINSRB => "pinsrb",
Self::PINSRW => "pinsrw",
Self::PINSRD => "pinsrd",
Self::PINSRQ => "pinsrq",
Self::PADDB => "paddb",
Self::PADDW => "paddw",
Self::PADDD => "paddd",
Self::PADDQ => "paddq",
Self::PSUBB => "psubb",
Self::PSUBW => "psubw",
Self::PSUBD => "psubd",
Self::PSUBQ => "psubq",
Self::PMULLW => "pmullw",
Self::PMULHW => "pmulhw",
Self::PMULHUW => "pmulhuw",
Self::PMULUDQ => "pmuludq",
Self::PAND => "pand",
Self::PANDN => "pandn",
Self::POR => "por",
Self::PXOR => "pxor",
Self::PCMPEQB => "pcmpeqb",
Self::PCMPEQW => "pcmpeqw",
Self::PCMPEQD => "pcmpeqd",
Self::PCMPGTB => "pcmpgtb",
Self::PCMPGTW => "pcmpgtw",
Self::PCMPGTD => "pcmpgtd",
Self::PACKSSWB => "packsswb",
Self::PACKSSDW => "packssdw",
Self::PACKUSWB => "packuswb",
Self::PUNPCKLBW => "punpcklbw",
Self::PUNPCKLWD => "punpcklwd",
Self::PUNPCKLDQ => "punpckldq",
Self::PUNPCKLQDQ => "punpcklqdq",
Self::PUNPCKHBW => "punpckhbw",
Self::PUNPCKHWD => "punpckhwd",
Self::PUNPCKHDQ => "punpckhdq",
Self::PUNPCKHQDQ => "punpckhqdq",
Self::PSLLW => "psllw",
Self::PSLLD => "pslld",
Self::PSLLQ => "psllq",
Self::PSRLW => "psrlw",
Self::PSRLD => "psrld",
Self::PSRLQ => "psrlq",
Self::PSRAW => "psraw",
Self::PSRAD => "psrad",
Self::UCOMISS => "ucomiss",
Self::UCOMISD => "ucomisd",
Self::COMISS => "comiss",
Self::COMISD => "comisd",
Self::MOVD => "movd",
Self::MOVQ => "movq",
Self::ADDSUBPS => "addsubps",
Self::ADDSUBPD => "addsubpd",
Self::HADDPS => "haddps",
Self::HADDPD => "haddpd",
Self::HSUBPS => "hsubps",
Self::HSUBPD => "hsubpd",
Self::MOVSLDUP => "movsldup",
Self::MOVSHDUP => "movshdup",
Self::MOVDDUP => "movddup",
Self::PHADDW => "phaddw",
Self::PHADDD => "phaddd",
Self::PHADDSW => "phaddsw",
Self::PMADDUBSW => "pmaddubsw",
Self::PHSUBW => "phsubw",
Self::PHSUBD => "phsubd",
Self::PHSUBSW => "phsubsw",
Self::PSIGNB => "psignb",
Self::PSIGNW => "psignw",
Self::PSIGND => "psignd",
Self::PABSB => "pabsb",
Self::PABSW => "pabsw",
Self::PABSD => "pabsd",
Self::PALIGNR => "palignr",
Self::BLENDPS => "blendps",
Self::BLENDPD => "blendpd",
Self::BLENDVPS => "blendvps",
Self::BLENDVPD => "blendvpd",
Self::PBLENDW => "pblendw",
Self::DPPS => "dpps",
Self::DPPD => "dppd",
Self::EXTRACTPS => "extractps",
Self::INSERTPS => "insertps",
Self::PMULLD => "pmulld",
Self::PMULDQ => "pmuldq",
Self::PMINSB => "pminsb",
Self::PMINSD => "pminsd",
Self::PMINUW => "pminuw",
Self::PMINUD => "pminud",
Self::PMAXSB => "pmaxsb",
Self::PMAXSD => "pmaxsd",
Self::PMAXUW => "pmaxuw",
Self::PMAXUD => "pmaxud",
Self::PACKUSDW => "packusdw",
Self::PCMPEQQ => "pcmpeqq",
Self::PCMPGTQ => "pcmpgtq",
Self::CRC32 => "crc32",
Self::PTEST => "ptest",
Self::ROUNDPS => "roundps",
Self::ROUNDPD => "roundpd",
Self::PMOVSXBW => "pmovsxbw",
Self::PMOVSXWD => "pmovsxwd",
Self::PMOVSXDQ => "pmovsxdq",
Self::PMOVZXBW => "pmovzxbw",
Self::PMOVZXWD => "pmovzxwd",
Self::PMOVZXDQ => "pmovzxdq",
Self::PMOVSXBD => "pmovsxbd",
Self::PMOVSXWQ => "pmovsxwq",
Self::PMOVSXBQ => "pmovsxbq",
Self::PMOVZXBD => "pmovzxbd",
Self::PMOVZXWQ => "pmovzxwq",
Self::PMOVZXBQ => "pmovzxbq",
Self::PHMINPOSUW => "phminposuw",
Self::MPSADBW => "mpsadbw",
Self::POPCNT => "popcnt",
Self::LZCNT => "lzcnt",
Self::TZCNT => "tzcnt",
Self::ANDN => "andn",
Self::BEXTR => "bextr",
Self::BLSI => "blsi",
Self::BLSMSK => "blsmsk",
Self::BLSR => "blsr",
Self::BZHI => "bzhi",
Self::MULX => "mulx",
Self::PDEP => "pdep",
Self::PEXT => "pext",
Self::RORX => "rorx",
Self::SARX => "sarx",
Self::SHLX => "shlx",
Self::SHRX => "shrx",
Self::VADDPS => "vaddps",
Self::VADDPD => "vaddpd",
Self::VADDSS => "vaddss",
Self::VADDSD => "vaddsd",
Self::VSUBPS => "vsubps",
Self::VSUBPD => "vsubpd",
Self::VSUBSS => "vsubss",
Self::VSUBSD => "vsubsd",
Self::VMULPS => "vmulps",
Self::VMULPD => "vmulpd",
Self::VMULSS => "vmulss",
Self::VMULSD => "vmulsd",
Self::VDIVPS => "vdivps",
Self::VDIVPD => "vdivpd",
Self::VDIVSS => "vdivss",
Self::VDIVSD => "vdivsd",
Self::VANDPS => "vandps",
Self::VANDNPS => "vandnps",
Self::VORPS => "vorps",
Self::VXORPS => "vxorps",
Self::VANDPD => "vandpd",
Self::VANDNPD => "vandnpd",
Self::VORPD => "vorpd",
Self::VXORPD => "vorxpd", Self::VBROADCASTSS => "vbroadcastss",
Self::VBROADCASTSD => "vbroadcastsd",
Self::VPERMILPS => "vpermilps",
Self::VPERMILPD => "vpermilpd",
Self::VPERM2F128 => "vperm2f128",
Self::VPERM2I128 => "vperm2i128",
Self::VINSERTF128 => "vinsertf128",
Self::VINSERTI128 => "vinserti128",
Self::VEXTRACTF128 => "vextractf128",
Self::VEXTRACTI128 => "vextracti128",
Self::VZEROALL => "vzeroall",
Self::VZEROUPPER => "vzeroupper",
Self::VMOVAPS => "vmovaps",
Self::VMOVAPD => "vmovapd",
Self::VMOVUPS => "vmovups",
Self::VMOVUPD => "vmovupd",
Self::VMOVDQA => "vmovdqa",
Self::VMOVDQU => "vmovdqu",
Self::VMOVSS => "vmovss",
Self::VMOVSD => "vmovsd",
Self::VCVTSD2SS => "vcvtsd2ss",
Self::VCVTSS2SD => "vcvtss2sd",
Self::VCVTSI2SS => "vcvtsi2ss",
Self::VCVTSI2SD => "vcvtsi2sd",
Self::VCVTSS2SI => "vcvtss2si",
Self::VCVTSD2SI => "vcvtsd2si",
Self::VCVTTSS2SI => "vcvttss2si",
Self::VCVTTSD2SI => "vcvttsd2si",
Self::VSQRTPS => "vsqrtps",
Self::VSQRTPD => "vsqrtpd",
Self::VSQRTSS => "vsqrtss",
Self::VSQRTSD => "vsqrtsd",
Self::VRSQRTPS => "vrsqrtps",
Self::VRSQRTSS => "vrsqrtss",
Self::VRCPPS => "vrcpps",
Self::VRCPSS => "vrcpss",
Self::VMINPS => "vminps",
Self::VMINPD => "vminpd",
Self::VMAXPS => "vmaxps",
Self::VMAXPD => "vmaxpd",
Self::VMINSS => "vminss",
Self::VMINSD => "vminsd",
Self::VMAXSS => "vmaxss",
Self::VMAXSD => "vmaxsd",
Self::VSHUFPS => "vshufps",
Self::VSHUFPD => "vshufpd",
Self::VUNPCKLPS => "vunpcklps",
Self::VUNPCKHPS => "vunpckhps",
Self::VUNPCKLPD => "vunpcklpd",
Self::VUNPCKHPD => "vunpckhpd",
Self::VCMPPS => "vcmpps",
Self::VCMPPD => "vcmppd",
Self::VCMPSS => "vcmpss",
Self::VCMPSD => "vcmpsd",
Self::VUCOMISS => "vucomiss",
Self::VUCOMISD => "vucomisd",
Self::VCOMISS => "vcomiss",
Self::VCOMISD => "vcomisd",
Self::VPTEST => "vptest",
Self::VMOVMSKPS => "vmovmskps",
Self::VMOVMSKPD => "vmovmskpd",
Self::VPBROADCASTB => "vpbroadcastb",
Self::VPBROADCASTW => "vpbroadcastw",
Self::VPBROADCASTD => "vpbroadcastd",
Self::VPBROADCASTQ => "vpbroadcastq",
Self::VPERMQ => "vpermq",
Self::VPERMPD => "vpermpd",
Self::VPERMPS => "vpermps",
Self::VPERMD => "vpermd",
Self::VPGATHERDD => "vpgatherdd",
Self::VPGATHERDQ => "vpgatherdq",
Self::VPGATHERQD => "vpgatherqd",
Self::VPGATHERQQ => "vpgatherqq",
Self::VGATHERDPS => "vgatherdps",
Self::VGATHERDPD => "vgatherdpd",
Self::VGATHERQPS => "vgatherqps",
Self::VGATHERQPD => "vgatherqpd",
Self::VPADDUSB => "vpaddusb",
Self::VPADDUSW => "vpaddusw",
Self::VPADDSB => "vpaddsb",
Self::VPADDSW => "vpaddsw",
Self::VPSUBUSB => "vpsubusb",
Self::VPSUBUSW => "vpsubusw",
Self::VPSUBSB => "vpsubsb",
Self::VPSUBSW => "vpsubsw",
Self::VPMULLW => "vpmullw",
Self::VPMULLD => "vpmulld",
Self::VPMULHW => "vpmulhw",
Self::VPMULHUW => "vpmulhuw",
Self::VPMULHRSW => "vpmulhrsw",
Self::VPMULUDQ => "vpmuludq",
Self::VPMULDQ => "vpmuldq",
Self::VPSLLVD => "vpsllvd",
Self::VPSLLVQ => "vpsllvq",
Self::VPSRLVD => "vpsrlvd",
Self::VPSRLVQ => "vpsrlvq",
Self::VPSRAVD => "vpsravd",
Self::VPSRAVQ => "vpsravq",
Self::VPABS => "vpabs",
Self::VPSIGN => "vpsign",
Self::VPHADD => "vphadd",
Self::VPHSUB => "vphsub",
Self::VPMADD => "vpmadd",
Self::VPSADBW => "vpsadbw",
Self::VPALIGNR => "vpalignr",
Self::VFMADD132PD => "vfmadd132pd",
Self::VFMADD213PD => "vfmadd213pd",
Self::VFMADD231PD => "vfmadd231pd",
Self::VFMSUB132PD => "vfmsub132pd",
Self::VFMSUB213PD => "vfmsub213pd",
Self::VFMSUB231PD => "vfmsub231pd",
Self::VFNMADD132PD => "vfnmadd132pd",
Self::VFNMADD213PD => "vfnmadd213pd",
Self::VFNMADD231PD => "vfnmadd231pd",
Self::VFNMSUB132PD => "vfnmsub132pd",
Self::VFNMSUB213PD => "vfnmsub213pd",
Self::VFNMSUB231PD => "vfnmsub231pd",
Self::VFMADD132PS => "vfmadd132ps",
Self::VFMADD213PS => "vfmadd213ps",
Self::VFMADD231PS => "vfmadd231ps",
Self::VFMSUB132PS => "vfmsub132ps",
Self::VFMSUB213PS => "vfmsub213ps",
Self::VFMSUB231PS => "vfmsub231ps",
Self::VFNMADD132PS => "vfnmadd132ps",
Self::VFNMADD213PS => "vfnmadd213ps",
Self::VFNMADD231PS => "vfnmadd231ps",
Self::VFNMSUB132PS => "vfnmsub132ps",
Self::VFNMSUB213PS => "vfnmsub213ps",
Self::VFNMSUB231PS => "vfnmsub231ps",
Self::VFMADD132SS => "vfmadd132ss",
Self::VFMADD213SS => "vfmadd213ss",
Self::VFMADD231SS => "vfmadd231ss",
Self::VFMSUB132SS => "vfmsub132ss",
Self::VFMSUB213SS => "vfmsub213ss",
Self::VFMSUB231SS => "vfmsub231ss",
Self::VFNMADD132SS => "vfnmadd132ss",
Self::VFNMADD213SS => "vfnmadd213ss",
Self::VFNMADD231SS => "vfnmadd231ss",
Self::VFNMSUB132SS => "vfnmsub132ss",
Self::VFNMSUB213SS => "vfnmsub213ss",
Self::VFNMSUB231SS => "vfnmsub231ss",
Self::VFMADD132SD => "vfmadd132sd",
Self::VFMADD213SD => "vfmadd213sd",
Self::VFMADD231SD => "vfmadd231sd",
Self::VFMSUB132SD => "vfmsub132sd",
Self::VFMSUB213SD => "vfmsub213sd",
Self::VFMSUB231SD => "vfmsub231sd",
Self::VFNMADD132SD => "vfnmadd132sd",
Self::VFNMADD213SD => "vfnmadd213sd",
Self::VFNMADD231SD => "vfnmadd231sd",
Self::VFNMSUB132SD => "vfnmsub132sd",
Self::VFNMSUB213SD => "vfnmsub213sd",
Self::VFNMSUB231SD => "vfnmsub231sd",
Self::VADDPD_Z => "vaddpd",
Self::VADDPS_Z => "vaddps",
Self::VADDSD_Z => "vaddsd",
Self::VADDSS_Z => "vaddss",
Self::VSUBPD_Z => "vsubpd",
Self::VSUBPS_Z => "vsubps",
Self::VSUBSD_Z => "vsubsd",
Self::VSUBSS_Z => "vsubss",
Self::VMULPD_Z => "vmulpd",
Self::VMULPS_Z => "vmulps",
Self::VMULSD_Z => "vmulsd",
Self::VMULSS_Z => "vmulss",
Self::VDIVPD_Z => "vdivpd",
Self::VDIVPS_Z => "vdivps",
Self::VDIVSD_Z => "vdivsd",
Self::VDIVSS_Z => "vdivss",
Self::VFMADD132PD_Z => "vfmadd132pd",
Self::VFMADD213PD_Z => "vfmadd213pd",
Self::VFMADD231PD_Z => "vfmadd231pd",
Self::VFMADD132PS_Z => "vfmadd132ps",
Self::VFMADD213PS_Z => "vfmadd213ps",
Self::VFMADD231PS_Z => "vfmadd231ps",
Self::VBROADCASTSS_Z => "vbroadcastss",
Self::VBROADCASTSD_Z => "vbroadcastsd",
Self::VPERMQ_Z => "vpermq",
Self::VPERMPD_Z => "vpermpd",
Self::VSHUFPD_Z => "vshufpd",
Self::VSHUFPS_Z => "vshufps",
Self::VSHUFI32X4 => "vshufi32x4",
Self::VSHUFI64X2 => "vshufi64x2",
Self::VSHUFF32X4 => "vshuff32x4",
Self::VSHUFF64X2 => "vshuff64x2",
Self::VINSERTI32X4 => "vinserti32x4",
Self::VINSERTI64X2 => "vinserti64x2",
Self::VINSERTF32X4 => "vinsertf32x4",
Self::VINSERTF64X2 => "vinsertf64x2",
Self::VEXTRACTI32X4 => "vextracti32x4",
Self::VEXTRACTI64X2 => "vextracti64x2",
Self::VEXTRACTF32X4 => "vextractf32x4",
Self::VEXTRACTF64X2 => "vextractf64x2",
Self::VBLENDMPD => "vblendmpd",
Self::VBLENDMPS => "vblendmps",
Self::VPBLENDMD => "vpblendmd",
Self::VPBLENDMQ => "vpblendmq",
Self::VPBLENDMB => "vpblendmb",
Self::VPBLENDMW => "vpblendmw",
Self::VPCMPEQD_Z => "vpcmpeqd",
Self::VPCMPEQQ_Z => "vpcmpeqq",
Self::VPCMPGTD_Z => "vpcmpgtd",
Self::VPCMPGTQ_Z => "vpcmpgtq",
Self::VPCMPEQD_K => "vpcmpeqd",
Self::VPCMPEQQ_K => "vpcmpeqq",
Self::VPCMPGTD_K => "vpcmpgtd",
Self::VPCMPGTQ_K => "vpcmpgtq",
Self::KANDW => "kandw",
Self::KANDNW => "kandnw",
Self::KORW => "korw",
Self::KXORW => "kxorw",
Self::KNOTW => "knotw",
Self::KORTESTW => "kortestw",
Self::KTESTW => "ktestw",
Self::KSHIFTLW => "kshiftlw",
Self::KSHIFTRW => "kshiftrw",
Self::KMOVW => "kmovw",
Self::KUNPCKBW => "kunpckbw",
Self::VPCOMPRESSD => "vpcompressd",
Self::VPCOMPRESSQ => "vpcompressq",
Self::VPEXPANDD => "vpexpandd",
Self::VPEXPANDQ => "vpexpandq",
Self::LOCK_ADD => "lock add",
Self::LOCK_SUB => "lock sub",
Self::LOCK_AND => "lock and",
Self::LOCK_OR => "lock or",
Self::LOCK_XOR => "lock xor",
Self::LOCK_XCHG => "lock xchg",
Self::LOCK_CMPXCHG => "lock cmpxchg",
Self::LOCK_INC => "lock inc",
Self::LOCK_DEC => "lock dec",
Self::XADD => "xadd",
Self::CMPXCHG => "cmpxchg",
Self::CMPXCHG8B => "cmpxchg8b",
Self::CMPXCHG16B => "cmpxchg16b",
Self::MFENCE => "mfence",
Self::LFENCE => "lfence",
Self::SFENCE => "sfence",
Self::MOVSB => "movsb",
Self::MOVSW => "movsw",
Self::MOVSD_STR => "movsd",
Self::MOVSQ => "movsq",
Self::STOSB => "stosb",
Self::STOSW => "stosw",
Self::STOSD => "stosd",
Self::STOSQ => "stosq",
Self::LODSB => "lodsb",
Self::LODSW => "lodsw",
Self::LODSD => "lodsd",
Self::LODSQ => "lodsq",
Self::CMPSB => "cmpsb",
Self::CMPSW => "cmpsw",
Self::CMPSD_STR => "cmpsd",
Self::CMPSQ => "cmpsq",
Self::SCASB => "scasb",
Self::SCASW => "scasw",
Self::SCASD => "scasd",
Self::SCASQ => "scasq",
Self::REP_MOVSB => "rep movsb",
Self::REP_STOSB => "rep stosb",
Self::CBW => "cbw",
Self::CWDE => "cwde",
Self::CDQE => "cdqe",
Self::CWD => "cwd",
Self::CDQ => "cdq",
Self::CQO => "cqo",
Self::MOVSXD => "movsxd",
Self::LAHF => "lahf",
Self::SAHF => "sahf",
Self::CLC => "clc",
Self::STC => "stc",
Self::CMC => "cmc",
Self::CLD => "cld",
Self::STD => "std",
Self::CLTS => "clts",
Self::HLT => "hlt",
Self::PAUSE => "pause",
Self::CPUID => "cpuid",
Self::RDTSC => "rdtsc",
Self::RDTSCP => "rdtscp",
Self::RDPMC => "rdpmc",
Self::XGETBV => "xgetbv",
Self::XSETBV => "xsetbv",
Self::CLFLUSH => "clflush",
Self::CLFLUSHOPT => "clflushopt",
Self::CLWB => "clwb",
Self::PREFETCH => "prefetch",
Self::PREFETCHW => "prefetchw",
Self::PREFETCHWT1 => "prefetchwt1",
Self::ENDBR32 => "endbr32",
Self::ENDBR64 => "endbr64",
Self::HRESET => "hreset",
}
}
pub fn is_floating_point(&self) -> bool {
matches!(
self,
Self::ADDSS
| Self::ADDSD
| Self::SUBSS
| Self::SUBSD
| Self::MULSS
| Self::MULSD
| Self::DIVSS
| Self::DIVSD
| Self::SQRTSS
| Self::SQRTSD
| Self::MINSS
| Self::MINSD
| Self::MAXSS
| Self::MAXSD
| Self::CMPSS
| Self::CMPSD
| Self::ADDPS
| Self::ADDPD
| Self::SUBPS
| Self::SUBPD
| Self::MULPS
| Self::MULPD
| Self::DIVPS
| Self::DIVPD
| Self::SQRTPS
| Self::RCPPS
| Self::RSQRTPS
| Self::MAXPS
| Self::MAXPD
| Self::MINPS
| Self::MINPD
| Self::UCOMISS
| Self::UCOMISD
| Self::COMISS
| Self::COMISD
)
}
pub fn is_cmov(&self) -> bool {
matches!(
self,
Self::CMOVO
| Self::CMOVNO
| Self::CMOVB
| Self::CMOVAE
| Self::CMOVE
| Self::CMOVNE
| Self::CMOVBE
| Self::CMOVA
| Self::CMOVS
| Self::CMOVNS
| Self::CMOVP
| Self::CMOVNP
| Self::CMOVL
| Self::CMOVGE
| Self::CMOVLE
| Self::CMOVG
)
}
pub fn is_setcc(&self) -> bool {
matches!(
self,
Self::SETO
| Self::SETNO
| Self::SETB
| Self::SETAE
| Self::SETE
| Self::SETNE
| Self::SETBE
| Self::SETA
| Self::SETS
| Self::SETNS
| Self::SETP
| Self::SETNP
| Self::SETL
| Self::SETGE
| Self::SETLE
| Self::SETG
)
}
pub fn is_jcc(&self) -> bool {
matches!(
self,
Self::JO
| Self::JNO
| Self::JB
| Self::JAE
| Self::JE
| Self::JNE
| Self::JBE
| Self::JA
| Self::JS
| Self::JNS
| Self::JP
| Self::JNP
| Self::JL
| Self::JGE
| Self::JLE
| Self::JG
)
}
pub fn is_avx(&self) -> bool {
let name = self.mnemonic();
name.starts_with('v') && name.len() > 1 && name.as_bytes()[1].is_ascii_alphabetic()
}
pub fn is_avx512(&self) -> bool {
matches!(
self,
Self::VADDPD_Z
| Self::VADDPS_Z
| Self::VADDSD_Z
| Self::VADDSS_Z
| Self::VSUBPD_Z
| Self::VSUBPS_Z
| Self::VSUBSD_Z
| Self::VSUBSS_Z
| Self::VMULPD_Z
| Self::VMULPS_Z
| Self::VMULSD_Z
| Self::VMULSS_Z
| Self::VDIVPD_Z
| Self::VDIVPS_Z
| Self::VDIVSD_Z
| Self::VDIVSS_Z
| Self::VSHUFI32X4
| Self::VSHUFI64X2
| Self::VSHUFF32X4
| Self::VSHUFF64X2
| Self::VINSERTI32X4
| Self::VINSERTI64X2
| Self::VINSERTF32X4
| Self::VINSERTF64X2
| Self::VEXTRACTI32X4
| Self::VEXTRACTI64X2
| Self::VEXTRACTF32X4
| Self::VEXTRACTF64X2
| Self::VBLENDMPD
| Self::VBLENDMPS
| Self::VPBLENDMD
| Self::VPBLENDMQ
| Self::VPBLENDMB
| Self::VPBLENDMW
| Self::KANDW
| Self::KANDNW
| Self::KORW
| Self::KXORW
| Self::KNOTW
| Self::KORTESTW
| Self::KTESTW
| Self::KSHIFTLW
| Self::KSHIFTRW
| Self::KMOVW
| Self::KUNPCKBW
| Self::VPCOMPRESSD
| Self::VPCOMPRESSQ
| Self::VPEXPANDD
| Self::VPEXPANDQ
)
}
pub fn is_atomic(&self) -> bool {
matches!(
self,
Self::LOCK_ADD
| Self::LOCK_SUB
| Self::LOCK_AND
| Self::LOCK_OR
| Self::LOCK_XOR
| Self::LOCK_XCHG
| Self::LOCK_CMPXCHG
| Self::LOCK_INC
| Self::LOCK_DEC
| Self::XADD
| Self::CMPXCHG
| Self::CMPXCHG8B
| Self::CMPXCHG16B
)
}
pub fn num_operands(&self) -> u8 {
match self {
Self::NOP | Self::INT3 | Self::UD2 | Self::RET | Self::VZEROALL | Self::VZEROUPPER
| Self::MFENCE | Self::LFENCE | Self::SFENCE | Self::PAUSE | Self::HLT
| Self::CLTS | Self::CLD | Self::STD | Self::CLC | Self::STC | Self::CMC
| Self::ENDBR32 | Self::ENDBR64 | Self::CPUID | Self::RDTSC | Self::RDTSCP
| Self::CWDE | Self::CDQE | Self::CWD | Self::CDQ | Self::CQO | Self::CBW
| Self::SAHF | Self::LAHF => 0,
Self::PUSH | Self::POP | Self::INC | Self::DEC | Self::NEG | Self::NOT
| Self::MUL | Self::IMUL | Self::DIV | Self::IDIV | Self::JMP | Self::CALL
| Self::SETO | Self::SETNO | Self::SETB | Self::SETAE | Self::SETE | Self::SETNE
| Self::SETBE | Self::SETA | Self::SETS | Self::SETNS | Self::SETP | Self::SETNP
| Self::SETL | Self::SETGE | Self::SETLE | Self::SETG | Self::BSWAP
| Self::POPCNT | Self::LZCNT | Self::TZCNT | Self::SQRTSS | Self::SQRTSD
| Self::SQRTPS | Self::RCPSS | Self::RCPPS | Self::RSQRTSS | Self::RSQRTPS => 1,
Self::MOV | Self::MOVSX | Self::MOVZX | Self::MOVABS | Self::MOVSXD
| Self::XCHG | Self::ADD | Self::ADC | Self::SUB | Self::SBB
| Self::AND | Self::OR | Self::XOR | Self::TEST | Self::CMP
| Self::SHL | Self::SHR | Self::SAR | Self::ROL | Self::ROR
| Self::RCL | Self::RCR | Self::SHLD | Self::SHRD
| Self::MOVSS | Self::MOVSD | Self::ADDSS | Self::ADDSD
| Self::SUBSS | Self::SUBSD | Self::MULSS | Self::MULSD
| Self::DIVSS | Self::DIVSD | Self::MINSS | Self::MINSD
| Self::MAXSS | Self::MAXSD | Self::CMPSS | Self::CMPSD
| Self::UCOMISS | Self::UCOMISD | Self::COMISS | Self::COMISD
| Self::CVTSI2SS | Self::CVTSI2SD | Self::CVTSS2SI | Self::CVTSD2SI
| Self::CVTTSS2SI | Self::CVTTSD2SI | Self::CVTSS2SD | Self::CVTSD2SS
| Self::MOVD | Self::MOVQ | Self::LEA | Self::BEXTR | Self::BZHI
| Self::RORX | Self::SHLX | Self::SHRX | Self::SARX
| Self::XADD | Self::CMPXCHG => 2,
Self::CMOVO | Self::CMOVNO | Self::CMOVB | Self::CMOVAE | Self::CMOVE
| Self::CMOVNE | Self::CMOVBE | Self::CMOVA | Self::CMOVS | Self::CMOVNS
| Self::CMOVP | Self::CMOVNP | Self::CMOVL | Self::CMOVGE | Self::CMOVLE
| Self::CMOVG | Self::BLSI | Self::BLSMSK | Self::BLSR | Self::ANDN
| Self::MULX | Self::PDEP | Self::PEXT | Self::PINSRB | Self::PINSRW
| Self::PINSRD | Self::PINSRQ => 2,
Self::ADDPS | Self::ADDPD | Self::SUBPS | Self::SUBPD | Self::MULPS
| Self::MULPD | Self::DIVPS | Self::DIVPD | Self::ANDPS | Self::ANDNPS
| Self::ORPS | Self::XORPS | Self::ANDPD | Self::ANDNPD | Self::ORPD
| Self::XORPD | Self::MAXPS | Self::MAXPD | Self::MINPS | Self::MINPD
| Self::PADDB | Self::PADDW | Self::PADDD | Self::PADDQ
| Self::PSUBB | Self::PSUBW | Self::PSUBD | Self::PSUBQ
| Self::PCMPEQB | Self::PCMPEQW | Self::PCMPEQD | Self::PCMPGTB
| Self::PCMPGTW | Self::PCMPGTD | Self::PAND | Self::PANDN
| Self::POR | Self::PXOR | Self::PSLLW | Self::PSLLD | Self::PSLLQ
| Self::PSRLW | Self::PSRLD | Self::PSRLQ | Self::PSRAW | Self::PSRAD
| Self::PACKSSWB | Self::PACKSSDW | Self::PACKUSWB
| Self::PUNPCKLBW | Self::PUNPCKLWD | Self::PUNPCKLDQ | Self::PUNPCKLQDQ
| Self::PUNPCKHBW | Self::PUNPCKHWD | Self::PUNPCKHDQ | Self::PUNPCKHQDQ
| Self::PMULLW | Self::PMULHW | Self::PMULHUW | Self::PMULUDQ
| Self::CMPXCHG8B | Self::CMPXCHG16B => 2,
Self::SHUFPS | Self::SHUFPD | Self::BLENDPS | Self::BLENDPD
| Self::PBLENDW | Self::PALIGNR | Self::PSHUFD | Self::PSHUFHW
| Self::PSHUFLW => 3,
_ => {
if self.is_avx() {
3 } else {
2
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DeepX86CondCode {
O, NO, B, AE, E, NE, BE, A, S, NS, P, NP, L, GE, LE, G, }
impl DeepX86CondCode {
pub fn suffix(&self) -> &'static str {
match self {
Self::O => "o",
Self::NO => "no",
Self::B => "b",
Self::AE => "ae",
Self::E => "e",
Self::NE => "ne",
Self::BE => "be",
Self::A => "a",
Self::S => "s",
Self::NS => "ns",
Self::P => "p",
Self::NP => "np",
Self::L => "l",
Self::GE => "ge",
Self::LE => "le",
Self::G => "g",
}
}
pub fn invert(&self) -> Self {
match self {
Self::O => Self::NO,
Self::NO => Self::O,
Self::B => Self::AE,
Self::AE => Self::B,
Self::E => Self::NE,
Self::NE => Self::E,
Self::BE => Self::A,
Self::A => Self::BE,
Self::S => Self::NS,
Self::NS => Self::S,
Self::P => Self::NP,
Self::NP => Self::P,
Self::L => Self::GE,
Self::GE => Self::L,
Self::LE => Self::G,
Self::G => Self::LE,
}
}
pub fn swap_unsigned(&self) -> Self {
match self {
Self::B => Self::A,
Self::A => Self::B,
Self::BE => Self::AE,
Self::AE => Self::BE,
other => *other,
}
}
pub fn swap_signed(&self) -> Self {
match self {
Self::L => Self::G,
Self::G => Self::L,
Self::LE => Self::GE,
Self::GE => Self::LE,
other => *other,
}
}
}
impl fmt::Display for DeepX86CondCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.suffix())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum DeepX86Reg {
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
EAX,
ECX,
EDX,
EBX,
ESP,
EBP,
ESI,
EDI,
R8D,
R9D,
R10D,
R11D,
R12D,
R13D,
R14D,
R15D,
AX,
CX,
DX,
BX,
SP,
BP,
SI,
DI,
R8W,
R9W,
R10W,
R11W,
R12W,
R13W,
R14W,
R15W,
AL,
CL,
DL,
BL,
AH,
CH,
DH,
BH,
SPL,
BPL,
SIL,
DIL,
R8B,
R9B,
R10B,
R11B,
R12B,
R13B,
R14B,
R15B,
XMM0,
XMM1,
XMM2,
XMM3,
XMM4,
XMM5,
XMM6,
XMM7,
XMM8,
XMM9,
XMM10,
XMM11,
XMM12,
XMM13,
XMM14,
XMM15,
YMM0,
YMM1,
YMM2,
YMM3,
YMM4,
YMM5,
YMM6,
YMM7,
YMM8,
YMM9,
YMM10,
YMM11,
YMM12,
YMM13,
YMM14,
YMM15,
ZMM0,
ZMM1,
ZMM2,
ZMM3,
ZMM4,
ZMM5,
ZMM6,
ZMM7,
ZMM8,
ZMM9,
ZMM10,
ZMM11,
ZMM12,
ZMM13,
ZMM14,
ZMM15,
ZMM16,
ZMM17,
ZMM18,
ZMM19,
ZMM20,
ZMM21,
ZMM22,
ZMM23,
ZMM24,
ZMM25,
ZMM26,
ZMM27,
ZMM28,
ZMM29,
ZMM30,
ZMM31,
K0,
K1,
K2,
K3,
K4,
K5,
K6,
K7,
CS,
DS,
ES,
FS,
GS,
SS,
RIP,
}
impl DeepX86Reg {
pub fn dwarf_num(&self) -> u16 {
match self {
Self::RAX | Self::EAX | Self::AX | Self::AL => 0,
Self::RDX | Self::EDX | Self::DX | Self::DL => 1,
Self::RCX | Self::ECX | Self::CX | Self::CL => 2,
Self::RBX | Self::EBX | Self::BX | Self::BL => 3,
Self::RSI | Self::ESI | Self::SI | Self::SIL => 4,
Self::RDI | Self::EDI | Self::DI | Self::DIL => 5,
Self::RBP | Self::EBP | Self::BP | Self::BPL => 6,
Self::RSP | Self::ESP | Self::SP | Self::SPL => 7,
Self::R8 | Self::R8D | Self::R8W | Self::R8B => 8,
Self::R9 | Self::R9D | Self::R9W | Self::R9B => 9,
Self::R10 | Self::R10D | Self::R10W | Self::R10B => 10,
Self::R11 | Self::R11D | Self::R11W | Self::R11B => 11,
Self::R12 | Self::R12D | Self::R12W | Self::R12B => 12,
Self::R13 | Self::R13D | Self::R13W | Self::R13B => 13,
Self::R14 | Self::R14D | Self::R14W | Self::R14B => 14,
Self::R15 | Self::R15D | Self::R15W | Self::R15B => 15,
Self::AH => 0, Self::CH => 2,
Self::DH => 1,
Self::BH => 3,
Self::XMM0 | Self::YMM0 | Self::ZMM0 => 17,
Self::XMM1 | Self::YMM1 | Self::ZMM1 => 18,
Self::XMM2 | Self::YMM2 | Self::ZMM2 => 19,
Self::XMM3 | Self::YMM3 | Self::ZMM3 => 20,
Self::XMM4 | Self::YMM4 | Self::ZMM4 => 21,
Self::XMM5 | Self::YMM5 | Self::ZMM5 => 22,
Self::XMM6 | Self::YMM6 | Self::ZMM6 => 23,
Self::XMM7 | Self::YMM7 | Self::ZMM7 => 24,
Self::XMM8 | Self::YMM8 | Self::ZMM8 => 25,
Self::XMM9 | Self::YMM9 | Self::ZMM9 => 26,
Self::XMM10 | Self::YMM10 | Self::ZMM10 => 27,
Self::XMM11 | Self::YMM11 | Self::ZMM11 => 28,
Self::XMM12 | Self::YMM12 | Self::ZMM12 => 29,
Self::XMM13 | Self::YMM13 | Self::ZMM13 => 30,
Self::XMM14 | Self::YMM14 | Self::ZMM14 => 31,
Self::XMM15 | Self::YMM15 | Self::ZMM15 => 32,
Self::ZMM16 => 33,
Self::ZMM17 => 34,
Self::ZMM18 => 35,
Self::ZMM19 => 36,
Self::ZMM20 => 37,
Self::ZMM21 => 38,
Self::ZMM22 => 39,
Self::ZMM23 => 40,
Self::ZMM24 => 41,
Self::ZMM25 => 42,
Self::ZMM26 => 43,
Self::ZMM27 => 44,
Self::ZMM28 => 45,
Self::ZMM29 => 46,
Self::ZMM30 => 47,
Self::ZMM31 => 48,
Self::K0 => 118,
Self::K1 => 119,
Self::K2 => 120,
Self::K3 => 121,
Self::K4 => 122,
Self::K5 => 123,
Self::K6 => 124,
Self::K7 => 125,
Self::RIP => 16,
Self::CS => 51,
Self::DS => 52,
Self::ES => 53,
Self::FS => 54,
Self::GS => 55,
Self::SS => 56,
}
}
pub fn is_gpr(&self) -> bool {
matches!(
self,
Self::RAX
| Self::RCX
| Self::RDX
| Self::RBX
| Self::RSP
| Self::RBP
| Self::RSI
| Self::RDI
| Self::R8
| Self::R9
| Self::R10
| Self::R11
| Self::R12
| Self::R13
| Self::R14
| Self::R15
| Self::EAX
| Self::ECX
| Self::EDX
| Self::EBX
| Self::ESP
| Self::EBP
| Self::ESI
| Self::EDI
| Self::R8D
| Self::R9D
| Self::R10D
| Self::R11D
| Self::R12D
| Self::R13D
| Self::R14D
| Self::R15D
| Self::AX
| Self::CX
| Self::DX
| Self::BX
| Self::SP
| Self::BP
| Self::SI
| Self::DI
| Self::R8W
| Self::R9W
| Self::R10W
| Self::R11W
| Self::R12W
| Self::R13W
| Self::R14W
| Self::R15W
| Self::AL
| Self::CL
| Self::DL
| Self::BL
| Self::AH
| Self::CH
| Self::DH
| Self::BH
| Self::SPL
| Self::BPL
| Self::SIL
| Self::DIL
| Self::R8B
| Self::R9B
| Self::R10B
| Self::R11B
| Self::R12B
| Self::R13B
| Self::R14B
| Self::R15B
)
}
pub fn is_simd(&self) -> bool {
matches!(
self,
Self::XMM0
| Self::XMM1
| Self::XMM2
| Self::XMM3
| Self::XMM4
| Self::XMM5
| Self::XMM6
| Self::XMM7
| Self::XMM8
| Self::XMM9
| Self::XMM10
| Self::XMM11
| Self::XMM12
| Self::XMM13
| Self::XMM14
| Self::XMM15
| Self::YMM0
| Self::YMM1
| Self::YMM2
| Self::YMM3
| Self::YMM4
| Self::YMM5
| Self::YMM6
| Self::YMM7
| Self::YMM8
| Self::YMM9
| Self::YMM10
| Self::YMM11
| Self::YMM12
| Self::YMM13
| Self::YMM14
| Self::YMM15
| Self::ZMM0
| Self::ZMM1
| Self::ZMM2
| Self::ZMM3
| Self::ZMM4
| Self::ZMM5
| Self::ZMM6
| Self::ZMM7
| Self::ZMM8
| Self::ZMM9
| Self::ZMM10
| Self::ZMM11
| Self::ZMM12
| Self::ZMM13
| Self::ZMM14
| Self::ZMM15
| Self::ZMM16
| Self::ZMM17
| Self::ZMM18
| Self::ZMM19
| Self::ZMM20
| Self::ZMM21
| Self::ZMM22
| Self::ZMM23
| Self::ZMM24
| Self::ZMM25
| Self::ZMM26
| Self::ZMM27
| Self::ZMM28
| Self::ZMM29
| Self::ZMM30
| Self::ZMM31
)
}
pub fn is_mask(&self) -> bool {
matches!(
self,
Self::K0 | Self::K1 | Self::K2 | Self::K3 | Self::K4 | Self::K5 | Self::K6 | Self::K7
)
}
pub fn is_callee_saved(&self) -> bool {
matches!(
self,
Self::RBX
| Self::RBP
| Self::RSP
| Self::R12
| Self::R13
| Self::R14
| Self::R15
| Self::EBX
| Self::EBP
| Self::ESP
| Self::R12D
| Self::R13D
| Self::R14D
| Self::R15D
)
}
pub fn is_caller_saved(&self) -> bool {
!self.is_callee_saved()
|| matches!(
self,
Self::RAX
| Self::RCX
| Self::RDX
| Self::RSI
| Self::RDI
| Self::R8
| Self::R9
| Self::R10
| Self::R11
)
}
}
impl fmt::Display for DeepX86Reg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::RAX => "rax",
Self::RCX => "rcx",
Self::RDX => "rdx",
Self::RBX => "rbx",
Self::RSP => "rsp",
Self::RBP => "rbp",
Self::RSI => "rsi",
Self::RDI => "rdi",
Self::R8 => "r8",
Self::R9 => "r9",
Self::R10 => "r10",
Self::R11 => "r11",
Self::R12 => "r12",
Self::R13 => "r13",
Self::R14 => "r14",
Self::R15 => "r15",
Self::EAX => "eax",
Self::ECX => "ecx",
Self::EDX => "edx",
Self::EBX => "ebx",
Self::ESP => "esp",
Self::EBP => "ebp",
Self::ESI => "esi",
Self::EDI => "edi",
Self::R8D => "r8d",
Self::R9D => "r9d",
Self::R10D => "r10d",
Self::R11D => "r11d",
Self::R12D => "r12d",
Self::R13D => "r13d",
Self::R14D => "r14d",
Self::R15D => "r15d",
Self::AX => "ax",
Self::CX => "cx",
Self::DX => "dx",
Self::BX => "bx",
Self::SP => "sp",
Self::BP => "bp",
Self::SI => "si",
Self::DI => "di",
Self::R8W => "r8w",
Self::R9W => "r9w",
Self::R10W => "r10w",
Self::R11W => "r11w",
Self::R12W => "r12w",
Self::R13W => "r13w",
Self::R14W => "r14w",
Self::R15W => "r15w",
Self::AL => "al",
Self::CL => "cl",
Self::DL => "dl",
Self::BL => "bl",
Self::AH => "ah",
Self::CH => "ch",
Self::DH => "dh",
Self::BH => "bh",
Self::SPL => "spl",
Self::BPL => "bpl",
Self::SIL => "sil",
Self::DIL => "dil",
Self::R8B => "r8b",
Self::R9B => "r9b",
Self::R10B => "r10b",
Self::R11B => "r11b",
Self::R12B => "r12b",
Self::R13B => "r13b",
Self::R14B => "r14b",
Self::R15B => "r15b",
Self::XMM0 => "xmm0",
Self::XMM1 => "xmm1",
Self::XMM2 => "xmm2",
Self::XMM3 => "xmm3",
Self::XMM4 => "xmm4",
Self::XMM5 => "xmm5",
Self::XMM6 => "xmm6",
Self::XMM7 => "xmm7",
Self::XMM8 => "xmm8",
Self::XMM9 => "xmm9",
Self::XMM10 => "xmm10",
Self::XMM11 => "xmm11",
Self::XMM12 => "xmm12",
Self::XMM13 => "xmm13",
Self::XMM14 => "xmm14",
Self::XMM15 => "xmm15",
Self::YMM0 => "ymm0",
Self::YMM1 => "ymm1",
Self::YMM2 => "ymm2",
Self::YMM3 => "ymm3",
Self::YMM4 => "ymm4",
Self::YMM5 => "ymm5",
Self::YMM6 => "ymm6",
Self::YMM7 => "ymm7",
Self::YMM8 => "ymm8",
Self::YMM9 => "ymm9",
Self::YMM10 => "ymm10",
Self::YMM11 => "ymm11",
Self::YMM12 => "ymm12",
Self::YMM13 => "ymm13",
Self::YMM14 => "ymm14",
Self::YMM15 => "ymm15",
Self::ZMM0 => "zmm0",
Self::ZMM1 => "zmm1",
Self::ZMM2 => "zmm2",
Self::ZMM3 => "zmm3",
Self::ZMM4 => "zmm4",
Self::ZMM5 => "zmm5",
Self::ZMM6 => "zmm6",
Self::ZMM7 => "zmm7",
Self::ZMM8 => "zmm8",
Self::ZMM9 => "zmm9",
Self::ZMM10 => "zmm10",
Self::ZMM11 => "zmm11",
Self::ZMM12 => "zmm12",
Self::ZMM13 => "zmm13",
Self::ZMM14 => "zmm14",
Self::ZMM15 => "zmm15",
Self::ZMM16 => "zmm16",
Self::ZMM17 => "zmm17",
Self::ZMM18 => "zmm18",
Self::ZMM19 => "zmm19",
Self::ZMM20 => "zmm20",
Self::ZMM21 => "zmm21",
Self::ZMM22 => "zmm22",
Self::ZMM23 => "zmm23",
Self::ZMM24 => "zmm24",
Self::ZMM25 => "zmm25",
Self::ZMM26 => "zmm26",
Self::ZMM27 => "zmm27",
Self::ZMM28 => "zmm28",
Self::ZMM29 => "zmm29",
Self::ZMM30 => "zmm30",
Self::ZMM31 => "zmm31",
Self::K0 => "k0",
Self::K1 => "k1",
Self::K2 => "k2",
Self::K3 => "k3",
Self::K4 => "k4",
Self::K5 => "k5",
Self::K6 => "k6",
Self::K7 => "k7",
Self::CS => "cs",
Self::DS => "ds",
Self::ES => "es",
Self::FS => "fs",
Self::GS => "gs",
Self::SS => "ss",
Self::RIP => "rip",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum DeepX86Operand {
Reg(DeepX86Reg),
Imm(i64),
Mem {
base: Option<DeepX86Reg>,
index: Option<DeepX86Reg>,
scale: u8, disp: i32,
size: u8,
},
RIPMem(i32, u8), Symbol(String),
CondCode(DeepX86CondCode),
SSECmpPred(u8),
RoundingControl(u8),
MaskReg(DeepX86Reg),
ZeroMask,
Broadcast { element_size: u8 },
}
impl DeepX86Operand {
pub fn reg(r: DeepX86Reg) -> Self {
Self::Reg(r)
}
pub fn imm(v: i64) -> Self {
Self::Imm(v)
}
pub fn rip_mem(disp: i32, size: u8) -> Self {
Self::RIPMem(disp, size)
}
pub fn base_disp(base: DeepX86Reg, disp: i32, size: u8) -> Self {
Self::Mem {
base: Some(base),
index: None,
scale: 1,
disp,
size,
}
}
pub fn base_index_scale_disp(
base: DeepX86Reg,
index: DeepX86Reg,
scale: u8,
disp: i32,
size: u8,
) -> Self {
Self::Mem {
base: Some(base),
index: Some(index),
scale,
disp,
size,
}
}
pub fn is_reg(&self) -> bool {
matches!(self, Self::Reg(_))
}
pub fn is_imm(&self) -> bool {
matches!(self, Self::Imm(_))
}
pub fn is_mem(&self) -> bool {
matches!(self, Self::Mem { .. } | Self::RIPMem(..))
}
}
impl fmt::Display for DeepX86Operand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reg(r) => write!(f, "%{}", r),
Self::Imm(v) => write!(f, "${}", v),
Self::Mem {
base,
index,
scale,
disp,
size: _,
} => {
write!(f, "[")?;
let mut first = true;
if let Some(b) = base {
write!(f, "%{}", b)?;
first = false;
}
if let Some(idx) = index {
if !first {
write!(f, " + ")?;
}
if *scale > 1 {
write!(f, "%{}*{}", idx, scale)?;
} else {
write!(f, "%{}", idx)?;
}
first = false;
}
if *disp != 0 || first {
if !first && *disp >= 0 {
write!(f, " + ")?;
}
write!(f, "{}", disp)?;
}
write!(f, "]")
}
Self::RIPMem(disp, _) => write!(f, "[rip + {}]", disp),
Self::Symbol(s) => write!(f, "{}", s),
Self::CondCode(cc) => write!(f, "{}", cc),
Self::SSECmpPred(p) => write!(f, "${}", p),
Self::RoundingControl(_rc) => write!(f, "{{rn-sae}}"),
Self::MaskReg(k) => write!(f, "%{{{}}}", k),
Self::ZeroMask => write!(f, "{{z}}"),
Self::Broadcast { element_size } => write!(f, "{{1to{}}}", element_size),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum IROpcodeKind {
Ret,
Br,
Switch,
IndirectBr,
Invoke,
CallBr,
Unreachable,
Add,
Sub,
Mul,
UDiv,
SDiv,
URem,
SRem,
FAdd,
FSub,
FMul,
FDiv,
FRem,
Shl,
LShr,
AShr,
And,
Or,
Xor,
Alloca,
Load,
Store,
GetElementPtr,
Fence,
CmpXchg,
AtomicRMW,
Trunc,
ZExt,
SExt,
FPToUI,
FPToSI,
UIToFP,
SIToFP,
FPTrunc,
FPExt,
PtrToInt,
IntToPtr,
BitCast,
ICmp,
FCmp,
Phi,
Call,
Select,
ExtractElement,
InsertElement,
ShuffleVector,
ExtractValue,
InsertValue,
Freeze,
}
impl IROpcodeKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::Ret => "ret",
Self::Br => "br",
Self::Switch => "switch",
Self::IndirectBr => "indirectbr",
Self::Invoke => "invoke",
Self::CallBr => "callbr",
Self::Unreachable => "unreachable",
Self::Add => "add",
Self::Sub => "sub",
Self::Mul => "mul",
Self::UDiv => "udiv",
Self::SDiv => "sdiv",
Self::URem => "urem",
Self::SRem => "srem",
Self::FAdd => "fadd",
Self::FSub => "fsub",
Self::FMul => "fmul",
Self::FDiv => "fdiv",
Self::FRem => "frem",
Self::Shl => "shl",
Self::LShr => "lshr",
Self::AShr => "ashr",
Self::And => "and",
Self::Or => "or",
Self::Xor => "xor",
Self::Alloca => "alloca",
Self::Load => "load",
Self::Store => "store",
Self::GetElementPtr => "getelementptr",
Self::Fence => "fence",
Self::CmpXchg => "cmpxchg",
Self::AtomicRMW => "atomicrmw",
Self::Trunc => "trunc",
Self::ZExt => "zext",
Self::SExt => "sext",
Self::FPToUI => "fptoui",
Self::FPToSI => "fptosi",
Self::UIToFP => "uitofp",
Self::SIToFP => "sitofp",
Self::FPTrunc => "fptrunc",
Self::FPExt => "fpext",
Self::PtrToInt => "ptrtoint",
Self::IntToPtr => "inttoptr",
Self::BitCast => "bitcast",
Self::ICmp => "icmp",
Self::FCmp => "fcmp",
Self::Phi => "phi",
Self::Call => "call",
Self::Select => "select",
Self::ExtractElement => "extractelement",
Self::InsertElement => "insertelement",
Self::ShuffleVector => "shufflevector",
Self::ExtractValue => "extractvalue",
Self::InsertValue => "insertvalue",
Self::Freeze => "freeze",
}
}
pub fn is_binary(&self) -> bool {
matches!(
self,
Self::Add
| Self::Sub
| Self::Mul
| Self::UDiv
| Self::SDiv
| Self::URem
| Self::SRem
| Self::FAdd
| Self::FSub
| Self::FMul
| Self::FDiv
| Self::FRem
| Self::Shl
| Self::LShr
| Self::AShr
| Self::And
| Self::Or
| Self::Xor
)
}
pub fn is_bitwise(&self) -> bool {
matches!(
self,
Self::Shl | Self::LShr | Self::AShr | Self::And | Self::Or | Self::Xor
)
}
pub fn is_cast(&self) -> bool {
matches!(
self,
Self::Trunc
| Self::ZExt
| Self::SExt
| Self::FPToUI
| Self::FPToSI
| Self::UIToFP
| Self::SIToFP
| Self::FPTrunc
| Self::FPExt
| Self::PtrToInt
| Self::IntToPtr
| Self::BitCast
)
}
pub fn is_integer_arith(&self) -> bool {
matches!(
self,
Self::Add | Self::Sub | Self::Mul | Self::UDiv | Self::SDiv | Self::URem | Self::SRem
)
}
pub fn is_floating_arith(&self) -> bool {
matches!(
self,
Self::FAdd | Self::FSub | Self::FMul | Self::FDiv | Self::FRem
)
}
pub fn is_memory(&self) -> bool {
matches!(
self,
Self::Alloca | Self::Load | Self::Store | Self::GetElementPtr
)
}
pub fn is_control_flow(&self) -> bool {
matches!(
self,
Self::Ret
| Self::Br
| Self::Switch
| Self::IndirectBr
| Self::Invoke
| Self::CallBr
| Self::Unreachable
| Self::Call
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CmpPredicate {
EQ,
NE,
UGT,
UGE,
ULT,
ULE,
SGT,
SGE,
SLT,
SLE,
FALSE,
OEQ,
OGT,
OGE,
OLT,
OLE,
ONE,
ORD,
UNO,
UEQ,
UNE,
UGT_F,
UGE_F,
ULT_F,
ULE_F,
TRUE,
}
impl CmpPredicate {
pub fn to_icmp_cc(&self) -> Option<DeepX86CondCode> {
match self {
Self::EQ => Some(DeepX86CondCode::E),
Self::NE => Some(DeepX86CondCode::NE),
Self::UGT => Some(DeepX86CondCode::A),
Self::UGE => Some(DeepX86CondCode::AE),
Self::ULT => Some(DeepX86CondCode::B),
Self::ULE => Some(DeepX86CondCode::BE),
Self::SGT => Some(DeepX86CondCode::G),
Self::SGE => Some(DeepX86CondCode::GE),
Self::SLT => Some(DeepX86CondCode::L),
Self::SLE => Some(DeepX86CondCode::LE),
_ => None,
}
}
pub fn to_fcmp_cc(&self) -> Option<(DeepX86CondCode, DeepX86CondCode)> {
match self {
Self::OEQ => Some((DeepX86CondCode::E, DeepX86CondCode::NP)),
Self::OGT => Some((DeepX86CondCode::A, DeepX86CondCode::NP)),
Self::OGE => Some((DeepX86CondCode::AE, DeepX86CondCode::NP)),
Self::OLT => Some((DeepX86CondCode::B, DeepX86CondCode::NP)),
Self::OLE => Some((DeepX86CondCode::BE, DeepX86CondCode::NP)),
Self::ONE => Some((DeepX86CondCode::NE, DeepX86CondCode::NP)),
Self::ORD => Some((DeepX86CondCode::NP, DeepX86CondCode::NP)),
Self::UNO => Some((DeepX86CondCode::P, DeepX86CondCode::P)),
Self::UEQ => Some((DeepX86CondCode::E, DeepX86CondCode::E)),
Self::UGT_F => Some((DeepX86CondCode::A, DeepX86CondCode::A)),
Self::UGE_F => Some((DeepX86CondCode::AE, DeepX86CondCode::AE)),
Self::ULT_F => Some((DeepX86CondCode::B, DeepX86CondCode::B)),
Self::ULE_F => Some((DeepX86CondCode::BE, DeepX86CondCode::BE)),
Self::UNE => Some((DeepX86CondCode::NE, DeepX86CondCode::NE)),
_ => None,
}
}
pub fn swap_operands(&self) -> Self {
match self {
Self::UGT => Self::ULT,
Self::UGE => Self::ULE,
Self::ULT => Self::UGT,
Self::ULE => Self::UGE,
Self::SGT => Self::SLT,
Self::SGE => Self::SLE,
Self::SLT => Self::SGT,
Self::SLE => Self::SGE,
Self::OGT => Self::OLT,
Self::OGE => Self::OLE,
Self::OLT => Self::OGT,
Self::OLE => Self::OGE,
Self::UGT_F => Self::ULT_F,
Self::UGE_F => Self::ULE_F,
Self::ULT_F => Self::UGT_F,
Self::ULE_F => Self::UGE_F,
other => *other,
}
}
pub fn is_signed_int(&self) -> bool {
matches!(self, Self::SGT | Self::SGE | Self::SLT | Self::SLE)
}
pub fn is_unsigned_int(&self) -> bool {
matches!(self, Self::UGT | Self::UGE | Self::ULT | Self::ULE | Self::EQ | Self::NE)
}
pub fn is_ordered_float(&self) -> bool {
matches!(
self,
Self::OEQ | Self::OGT | Self::OGE | Self::OLT | Self::OLE | Self::ONE | Self::ORD
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ISelPatternNode {
Op(IROpcodeKind),
ConstInt(i64),
ConstIntRange(i64, i64),
ConstAny,
Type(String),
Reg,
Imm,
Predicate(CmpPredicate),
AnyPredicate(Vec<CmpPredicate>),
MemRef,
Any,
Tree(Box<ISelPatternNode>, Vec<Box<ISelPatternNode>>),
Not(Box<ISelPatternNode>),
IntType(u8), FloatType(u8), VecType(u8, u16),
SameReg(usize, usize), }
impl ISelPatternNode {
pub fn tree(op: IROpcodeKind, children: Vec<ISelPatternNode>) -> Self {
Self::Tree(
Box::new(Self::Op(op)),
children.into_iter().map(|c| Box::new(c)).collect(),
)
}
pub fn not(child: ISelPatternNode) -> Self {
Self::Not(Box::new(child))
}
pub fn is_any(&self) -> bool {
matches!(self, Self::Any)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ISelPatternCondition {
Always,
Is64Bit,
Is32Bit,
ImmFits(u32),
IsPowerOfTwo,
TypeMatches(String),
HasFeature(String),
OperandSize(u8),
IsSignExtended,
IsZeroExtended,
All(Vec<ISelPatternCondition>),
Any(Vec<ISelPatternCondition>),
Not(Box<ISelPatternCondition>),
ValidShiftAmt(u8), PreferAVX,
NoAVX,
}
impl ISelPatternCondition {
pub fn evaluate(
&self,
is_64bit: bool,
features: &HashSet<String>,
operand_size: Option<u8>,
imm_value: Option<i64>,
type_name: Option<&str>,
) -> bool {
match self {
Self::Always => true,
Self::Is64Bit => is_64bit,
Self::Is32Bit => !is_64bit,
Self::ImmFits(bits) => {
if let Some(v) = imm_value {
let max = (1i64 << (bits - 1)) - 1;
let min = -(1i64 << (bits - 1));
v >= min && v <= max
} else {
false
}
}
Self::IsPowerOfTwo => {
if let Some(v) = imm_value {
v > 0 && (v & (v - 1)) == 0
} else {
false
}
}
Self::TypeMatches(t) => type_name.map_or(false, |tn| tn == t.as_str()),
Self::HasFeature(f) => features.contains(f.as_str()),
Self::OperandSize(sz) => operand_size.map_or(false, |os| os == *sz),
Self::IsSignExtended => false, Self::IsZeroExtended => false, Self::All(conds) => conds
.iter()
.all(|c| c.evaluate(is_64bit, features, operand_size, imm_value, type_name)),
Self::Any(conds) => conds
.iter()
.any(|c| c.evaluate(is_64bit, features, operand_size, imm_value, type_name)),
Self::Not(c) => {
!c.evaluate(is_64bit, features, operand_size, imm_value, type_name)
}
Self::ValidShiftAmt(w) => {
if let Some(v) = imm_value {
v >= 0 && v < (*w as i64)
} else {
false
}
}
Self::PreferAVX => features.contains("avx"),
Self::NoAVX => !features.contains("avx"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ISelPatternResult {
pub opcodes: Vec<DeepX86Opcode>,
pub operand_mapping: Vec<OperandMapEntry>,
pub cost: u32,
pub sets_flags: bool,
pub is_conditional: bool,
pub requires_feature: Option<&'static str>,
pub latency: u32,
pub throughput: f64,
}
impl ISelPatternResult {
pub fn new(opcode: DeepX86Opcode) -> Self {
Self {
opcodes: vec![opcode],
operand_mapping: Vec::new(),
cost: 1,
sets_flags: false,
is_conditional: false,
requires_feature: None,
latency: 1,
throughput: 0.25,
}
}
pub fn with_mapping(opcode: DeepX86Opcode, mapping: Vec<OperandMapEntry>) -> Self {
Self {
opcodes: vec![opcode],
operand_mapping: mapping,
cost: 1,
sets_flags: false,
is_conditional: false,
requires_feature: None,
latency: 1,
throughput: 0.25,
}
}
pub fn with_cost(mut self, cost: u32) -> Self {
self.cost = cost;
self
}
pub fn with_flags(mut self) -> Self {
self.sets_flags = true;
self
}
pub fn with_conditional(mut self) -> Self {
self.is_conditional = true;
self
}
pub fn with_feature(mut self, feature: &'static str) -> Self {
self.requires_feature = Some(feature);
self
}
pub fn with_latency(mut self, latency: u32) -> Self {
self.latency = latency;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OperandMapEntry {
ResultToDest,
SrcToOperand(usize, usize), FixedReg(DeepX86Reg, usize), FixedImm(i64, usize), SrcToTwoOperands(usize, usize, usize), }
#[derive(Debug, Clone)]
pub struct ISelPattern {
pub name: String,
pub pattern: ISelPatternNode,
pub condition: ISelPatternCondition,
pub result: ISelPatternResult,
pub priority: u16,
pub desc: String,
}
impl ISelPattern {
pub fn new(
name: &str,
pattern: ISelPatternNode,
condition: ISelPatternCondition,
result: ISelPatternResult,
priority: u16,
desc: &str,
) -> Self {
Self {
name: name.to_string(),
pattern,
condition,
result,
priority,
desc: desc.to_string(),
}
}
}
pub struct PatternDatabase {
pub patterns: Vec<ISelPattern>,
pub by_opcode: HashMap<String, Vec<usize>>,
pub features: HashSet<String>,
pub is_64bit: bool,
}
impl PatternDatabase {
pub fn new(is_64bit: bool, features: HashSet<String>) -> Self {
Self {
patterns: Vec::new(),
by_opcode: HashMap::new(),
features,
is_64bit,
}
}
fn register(&mut self, pat: ISelPattern) {
let idx = self.patterns.len();
let opcode_name = match &pat.pattern {
ISelPatternNode::Op(kind) => kind.as_str().to_string(),
ISelPatternNode::Tree(root, _) => {
if let ISelPatternNode::Op(kind) = root.as_ref() {
kind.as_str().to_string()
} else {
"any".to_string()
}
}
_ => "any".to_string(),
};
self.by_opcode
.entry(opcode_name)
.or_insert_with(Vec::new)
.push(idx);
self.patterns.push(pat);
}
pub fn build(&mut self) {
self.build_integer_arithmetic_patterns();
self.build_bitwise_patterns();
self.build_shift_rotate_patterns();
self.build_floating_point_patterns();
self.build_conversion_patterns();
self.build_memory_patterns();
self.build_control_flow_patterns();
self.build_vector_patterns();
self.build_atomic_patterns();
self.build_comparison_patterns();
self.build_call_patterns();
self.build_misc_patterns();
self.build_extended_integer_patterns();
self.build_extended_vector_patterns();
self.build_system_patterns();
}
fn build_integer_arithmetic_patterns(&mut self) {
let sizes = [(8u8, "i8"), (16u8, "i16"), (32u8, "i32"), (64u8, "i64")];
for &(sz, tname) in &sizes {
let (opcode, name_suffix) = match sz {
8 => (DeepX86Opcode::ADD, "8"),
16 => (DeepX86Opcode::ADD, "16"),
32 => (DeepX86Opcode::ADD, "32"),
64 => (DeepX86Opcode::ADD, "64"),
_ => continue,
};
self.register(ISelPattern::new(
&format!("add_rr_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
]),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_flags()
.with_latency(1),
100,
&format!("ADD reg, reg — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("add_ri_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternCondition::ImmFits(sz as u32),
]),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_flags()
.with_latency(1),
99,
&format!("ADD reg, imm — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("add_rm_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::MemRef,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
]),
ISelPatternResult::new(opcode)
.with_cost(2)
.with_flags()
.with_latency(4),
96,
&format!("ADD reg, mem — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("lea_add_mul_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
]),
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
]),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
120,
&format!("LEA — fold multiply into address (x + y*1) {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let opcode = DeepX86Opcode::SUB;
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("sub_rr_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(opcode).with_cost(1).with_flags(),
100,
&format!("SUB reg, reg — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("sub_ri_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(opcode).with_cost(1).with_flags(),
99,
&format!("SUB reg, imm — {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("imul_rr_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::IMUL)
.with_cost(3)
.with_flags()
.with_latency(3),
100,
&format!("IMUL reg, reg — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("imul_ri_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::IMUL)
.with_cost(3)
.with_flags()
.with_latency(3),
99,
&format!("IMUL reg, imm — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("mul_pow2_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(2),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::ADD)
.with_cost(1)
.with_flags(),
150,
&format!("MUL by 2 -> ADD reg, reg — {} bit", tname),
));
}
for &(sz, tname) in &sizes[2..] {
self.register(ISelPattern::new(
&format!("mul3_lea_{}", tname),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(3),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
145,
&format!("MUL by 3 -> LEA (x, x*2) — {} bit", tname),
));
}
for &(sz, tname) in &sizes[2..] {
self.register(ISelPattern::new(
&format!("mul5_lea_{}", tname),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(5),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
145,
&format!("MUL by 5 -> LEA (x, x*4) — {} bit", tname),
));
}
for &(sz, tname) in &sizes[2..] {
self.register(ISelPattern::new(
&format!("mul9_lea_{}", tname),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(9),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
145,
&format!("MUL by 9 -> LEA (x, x*8) — {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let (unsigned_op, signed_op) = (DeepX86Opcode::DIV, DeepX86Opcode::IDIV);
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("udiv_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::UDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(unsigned_op)
.with_cost(14)
.with_latency(14),
90,
&format!("DIV reg — unsigned {} bit", tname),
));
self.register(ISelPattern::new(
&format!("sdiv_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::SDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(signed_op)
.with_cost(18)
.with_latency(18),
90,
&format!("IDIV reg — signed {} bit", tname),
));
self.register(ISelPattern::new(
&format!("udiv_pow2_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::UDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternCondition::IsPowerOfTwo,
]),
ISelPatternResult::new(DeepX86Opcode::SHR)
.with_cost(1)
.with_latency(1),
130,
&format!("UDiv by pow2 -> SHR — {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("urem_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::URem, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::DIV)
.with_cost(14)
.with_latency(14),
85,
&format!("DIV (remainder in EDX) — unsigned {} bit", tname),
));
self.register(ISelPattern::new(
&format!("srem_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::SRem, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::IDIV)
.with_cost(18)
.with_latency(18),
85,
&format!("IDIV (remainder in EDX) — signed {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("neg_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::ConstInt(0),
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::NEG)
.with_cost(1)
.with_flags()
.with_latency(1),
110,
&format!("SUB 0, x -> NEG — {} bit", tname),
));
}
for &(sz, tname) in &sizes {
let name_suffix = match sz {
8 => "8",
16 => "16",
32 => "32",
64 => "64",
_ => continue,
};
self.register(ISelPattern::new(
&format!("inc_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::INC)
.with_cost(1)
.with_flags(),
105,
&format!("ADD x, 1 -> INC — {} bit", tname),
));
self.register(ISelPattern::new(
&format!("dec_{}", name_suffix),
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::DEC)
.with_cost(1)
.with_flags(),
105,
&format!("SUB x, 1 -> DEC — {} bit", tname),
));
}
self.register(ISelPattern::new(
"adc_rr",
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::ADC)
.with_cost(2)
.with_flags(),
80,
"ADC reg, reg",
));
self.register(ISelPattern::new(
"sbb_rr",
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SBB)
.with_cost(2)
.with_flags(),
80,
"SBB reg, reg",
));
self.register(ISelPattern::new(
"mulx_i64",
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("bmi2".into()),
ISelPatternCondition::TypeMatches("i64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::MULX)
.with_cost(3)
.with_feature("bmi2")
.with_latency(3),
200,
"MULX reg, reg — BMI2",
));
for &(sz, tname) in &sizes[2..] {
self.register(ISelPattern::new(
&format!("add_lea_{}", tname),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
115,
&format!("ADD x, imm -> LEA [x + imm] {} bit", tname),
));
}
}
fn build_bitwise_patterns(&mut self) {
let sizes = [("i8", "8"), ("i16", "16"), ("i32", "32"), ("i64", "64")];
for &(tname, suffix) in &sizes {
self.register(ISelPattern::new(
&format!("and_rr_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::AND)
.with_cost(1)
.with_flags()
.with_latency(1),
100,
&format!("AND reg, reg — {}", tname),
));
self.register(ISelPattern::new(
&format!("and_ri_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::AND)
.with_cost(1)
.with_flags()
.with_latency(1),
99,
&format!("AND reg, imm — {}", tname),
));
self.register(ISelPattern::new(
&format!("and_neg1_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(-1),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
200,
&format!("AND x, -1 -> MOV (no-op) — {}", tname),
));
self.register(ISelPattern::new(
&format!("and_zero_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::XOR)
.with_cost(1)
.with_flags()
.with_latency(1),
180,
&format!("AND x, 0 -> XOR x, x — {}", tname),
));
self.register(ISelPattern::new(
&format!("or_rr_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Or, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::OR)
.with_cost(1)
.with_flags()
.with_latency(1),
100,
&format!("OR reg, reg — {}", tname),
));
self.register(ISelPattern::new(
&format!("or_ri_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Or, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::OR)
.with_cost(1)
.with_flags()
.with_latency(1),
99,
&format!("OR reg, imm — {}", tname),
));
self.register(ISelPattern::new(
&format!("or_zero_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Or, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
200,
&format!("OR x, 0 -> MOV — {}", tname),
));
self.register(ISelPattern::new(
&format!("xor_rr_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Xor, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::XOR)
.with_cost(1)
.with_flags()
.with_latency(1),
100,
&format!("XOR reg, reg — {}", tname),
));
self.register(ISelPattern::new(
&format!("xor_self_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Xor, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::XOR)
.with_cost(1)
.with_flags()
.with_latency(1),
105,
&format!("XOR x, x -> zero — {}", tname),
));
self.register(ISelPattern::new(
&format!("xor_neg1_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Xor, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(-1),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::NOT)
.with_cost(1)
.with_latency(1),
110,
&format!("XOR x, -1 -> NOT — {}", tname),
));
self.register(ISelPattern::new(
&format!("test_rr_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::TEST)
.with_cost(1)
.with_flags()
.with_latency(1),
98,
&format!("TEST reg, reg — {}", tname),
));
}
}
fn build_shift_rotate_patterns(&mut self) {
let sizes = [("i8", "8"), ("i16", "16"), ("i32", "32"), ("i64", "64")];
let shift_ops = [
(IROpcodeKind::Shl, DeepX86Opcode::SHL, "shl"),
(IROpcodeKind::LShr, DeepX86Opcode::SHR, "shr"),
(IROpcodeKind::AShr, DeepX86Opcode::SAR, "sar"),
];
for &(tname, suffix) in &sizes {
for &(ir_op, x86_op, op_name) in &shift_ops {
self.register(ISelPattern::new(
&format!("{}_ri_{}", op_name, suffix),
ISelPatternNode::tree(ir_op, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternCondition::ValidShiftAmt(64),
]),
ISelPatternResult::new(x86_op)
.with_cost(1)
.with_flags()
.with_latency(1),
100,
&format!("{} reg, imm — {}", op_name.to_uppercase(), tname),
));
self.register(ISelPattern::new(
&format!("{}_rcl_{}", op_name, suffix),
ISelPatternNode::tree(ir_op, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(x86_op)
.with_cost(2)
.with_flags()
.with_latency(2),
95,
&format!("{} reg, cl — {}", op_name.to_uppercase(), tname),
));
self.register(ISelPattern::new(
&format!("{}_r1_{}", op_name, suffix),
ISelPatternNode::tree(ir_op, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(x86_op)
.with_cost(1)
.with_flags()
.with_latency(1),
105,
&format!("{} reg, 1 — {}", op_name.to_uppercase(), tname),
));
}
}
for &(tname, suffix) in &sizes {
self.register(ISelPattern::new(
&format!("rol_ri_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Shl, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::ROL)
.with_cost(1)
.with_flags()
.with_latency(1),
85,
&format!("ROL reg, imm — {}", tname),
));
self.register(ISelPattern::new(
&format!("ror_ri_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::LShr, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::ROR)
.with_cost(1)
.with_flags()
.with_latency(1),
85,
&format!("ROR reg, imm — {}", tname),
));
}
self.register(ISelPattern::new(
"shld_ri_i32",
ISelPatternNode::tree(IROpcodeKind::Shl, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches("i32".to_string()),
ISelPatternResult::new(DeepX86Opcode::SHLD)
.with_cost(3)
.with_latency(3),
80,
"SHLD reg, reg, imm — i32",
));
self.register(ISelPattern::new(
"shrd_ri_i32",
ISelPatternNode::tree(IROpcodeKind::LShr, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches("i32".to_string()),
ISelPatternResult::new(DeepX86Opcode::SHRD)
.with_cost(3)
.with_latency(3),
80,
"SHRD reg, reg, imm — i32",
));
for &(op_name, x86_op) in &[
("shlx", DeepX86Opcode::SHLX),
("shrx", DeepX86Opcode::SHRX),
("sarx", DeepX86Opcode::SARX),
] {
self.register(ISelPattern::new(
&format!("{}_i64", &op_name[..op_name.len() - 1]),
ISelPatternNode::tree(IROpcodeKind::Shl, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("bmi2".into()),
ISelPatternCondition::TypeMatches("i64".into()),
]),
ISelPatternResult::new(x86_op)
.with_cost(1)
.with_feature("bmi2")
.with_latency(1),
200,
&format!("{} — BMI2", op_name),
));
}
self.register(ISelPattern::new(
"rorx_i64",
ISelPatternNode::tree(IROpcodeKind::LShr, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("bmi2".into()),
ISelPatternCondition::TypeMatches("i64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::RORX)
.with_cost(1)
.with_feature("bmi2")
.with_latency(1),
200,
"RORX — BMI2",
));
for &(tname, suffix) in &sizes {
self.register(ISelPattern::new(
&format!("bswap_{}", suffix),
ISelPatternNode::Reg,
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::BSWAP)
.with_cost(1)
.with_latency(1),
75,
&format!("BSWAP — {}", tname),
));
}
}
fn build_floating_point_patterns(&mut self) {
let fp_sizes = [("f32", "ss", "ps"), ("f64", "sd", "pd")];
for &(tname, scalar_suffix, packed_suffix) in &fp_sizes {
let is_64bit_mode = self.is_64bit;
let (add_ss, add_ps) = match tname {
"f32" => (DeepX86Opcode::ADDSS, DeepX86Opcode::ADDPS),
"f64" => (DeepX86Opcode::ADDSD, DeepX86Opcode::ADDPD),
_ => continue,
};
self.register(ISelPattern::new(
&format!("fadd_{}", scalar_suffix),
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(add_ss)
.with_cost(3)
.with_latency(3),
100,
&format!("ADD{} — scalar {}", scalar_suffix.to_uppercase(), tname),
));
self.register(ISelPattern::new(
&format!("fadd_{}", packed_suffix),
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(format!("v4{}", tname)),
ISelPatternResult::new(add_ps)
.with_cost(3)
.with_latency(3),
95,
&format!("ADD{} — packed {}", packed_suffix.to_uppercase(), tname),
));
let (sub_ss, sub_ps) = match tname {
"f32" => (DeepX86Opcode::SUBSS, DeepX86Opcode::SUBPS),
"f64" => (DeepX86Opcode::SUBSD, DeepX86Opcode::SUBPD),
_ => continue,
};
self.register(ISelPattern::new(
&format!("fsub_{}", scalar_suffix),
ISelPatternNode::tree(IROpcodeKind::FSub, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(sub_ss)
.with_cost(3)
.with_latency(3),
100,
&format!("SUB{} — scalar {}", scalar_suffix.to_uppercase(), tname),
));
let (mul_ss, mul_ps) = match tname {
"f32" => (DeepX86Opcode::MULSS, DeepX86Opcode::MULPS),
"f64" => (DeepX86Opcode::MULSD, DeepX86Opcode::MULPD),
_ => continue,
};
self.register(ISelPattern::new(
&format!("fmul_{}", scalar_suffix),
ISelPatternNode::tree(IROpcodeKind::FMul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(mul_ss)
.with_cost(3)
.with_latency(3),
100,
&format!("MUL{} — scalar {}", scalar_suffix.to_uppercase(), tname),
));
let (div_ss, div_ps) = match tname {
"f32" => (DeepX86Opcode::DIVSS, DeepX86Opcode::DIVPS),
"f64" => (DeepX86Opcode::DIVSD, DeepX86Opcode::DIVPD),
_ => continue,
};
self.register(ISelPattern::new(
&format!("fdiv_{}", scalar_suffix),
ISelPatternNode::tree(IROpcodeKind::FDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(div_ss)
.with_cost(10)
.with_latency(10),
100,
&format!("DIV{} — scalar {}", scalar_suffix.to_uppercase(), tname),
));
let sqrt_ss = match tname {
"f32" => DeepX86Opcode::SQRTSS,
"f64" => DeepX86Opcode::SQRTSD,
_ => continue,
};
self.register(ISelPattern::new(
&format!("fsqrt_{}", scalar_suffix),
ISelPatternNode::Reg,
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(sqrt_ss)
.with_cost(12)
.with_latency(12),
80,
&format!("SQRT{} — scalar {}", scalar_suffix.to_uppercase(), tname),
));
let fma_variants = [
DeepX86Opcode::VFMADD132PS,
DeepX86Opcode::VFMADD213PS,
DeepX86Opcode::VFMADD231PS,
DeepX86Opcode::VFMADD132PD,
DeepX86Opcode::VFMADD213PD,
DeepX86Opcode::VFMADD231PD,
DeepX86Opcode::VFMSUB132PS,
DeepX86Opcode::VFMSUB213PS,
DeepX86Opcode::VFMSUB231PS,
DeepX86Opcode::VFMSUB132PD,
DeepX86Opcode::VFMSUB213PD,
DeepX86Opcode::VFMSUB231PD,
DeepX86Opcode::VFNMADD132PS,
DeepX86Opcode::VFNMADD213PS,
DeepX86Opcode::VFNMADD231PS,
DeepX86Opcode::VFNMADD132PD,
DeepX86Opcode::VFNMADD213PD,
DeepX86Opcode::VFNMADD231PD,
DeepX86Opcode::VFNMSUB132PS,
DeepX86Opcode::VFNMSUB213PS,
DeepX86Opcode::VFNMSUB231PS,
DeepX86Opcode::VFNMSUB132PD,
DeepX86Opcode::VFNMSUB213PD,
DeepX86Opcode::VFNMSUB231PD,
];
for &fma_op in &fma_variants {
let mnem = fma_op.mnemonic();
let is_f32 = mnem.contains("ps") && !mnem.contains("pd");
self.register(ISelPattern::new(
&format!("fma_{}", mnem),
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::tree(IROpcodeKind::FMul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("fma".into()),
if is_f32 {
ISelPatternCondition::TypeMatches("f32".into())
} else {
ISelPatternCondition::TypeMatches("f64".into())
},
]),
ISelPatternResult::new(fma_op)
.with_cost(4)
.with_feature("fma")
.with_latency(4),
250,
&format!("{} — FMA", mnem),
));
}
self.register(ISelPattern::new(
&format!("frem_{}", scalar_suffix),
ISelPatternNode::tree(IROpcodeKind::FRem, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(50)
.with_latency(50),
50,
&format!("FREM -> call fmod — {}", tname),
));
}
if self.features.contains("fma") {
for &(scalar_mnem, _ps_op, pd_op) in &[
("vfmadd132ss", DeepX86Opcode::VFMADD132PS, DeepX86Opcode::VFMADD132PD),
("vfmadd213ss", DeepX86Opcode::VFMADD213PS, DeepX86Opcode::VFMADD213PD),
("vfmadd231ss", DeepX86Opcode::VFMADD231PS, DeepX86Opcode::VFMADD231PD),
] {
self.register(ISelPattern::new(
scalar_mnem,
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::tree(IROpcodeKind::FMul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("fma".into()),
ISelPatternResult::new(DeepX86Opcode::VFMADD132SS)
.with_cost(4)
.with_feature("fma")
.with_latency(4),
250,
scalar_mnem,
));
}
}
}
fn build_conversion_patterns(&mut self) {
let trunc_pairs = [("i64", "i32", "32"), ("i32", "i16", "16"), ("i16", "i8", "8")];
for &(from, to, suffix) in &trunc_pairs {
self.register(ISelPattern::new(
&format!("trunc_{}_{}", from, to),
ISelPatternNode::tree(IROpcodeKind::Trunc, vec![ISelPatternNode::Reg]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(to.to_string()),
]),
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
100,
&format!("Trunc {} -> {} (implicit)", from, to),
));
}
for &(from, to, suffix) in &trunc_pairs {
self.register(ISelPattern::new(
&format!("zext_{}_{}", from, to),
ISelPatternNode::tree(IROpcodeKind::ZExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(to.to_string()),
]),
ISelPatternResult::new(DeepX86Opcode::MOVZX)
.with_cost(1)
.with_latency(1),
100,
&format!("MOVZX {} -> {}", from, to),
));
}
self.register(ISelPattern::new(
"sext_i32_i64",
ISelPatternNode::tree(IROpcodeKind::SExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSXD)
.with_cost(1)
.with_latency(1),
100,
"MOVSXD i32 -> i64",
));
self.register(ISelPattern::new(
"sext_i16_i32",
ISelPatternNode::tree(IROpcodeKind::SExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSX)
.with_cost(1)
.with_latency(1),
100,
"MOVSX i16 -> i32",
));
self.register(ISelPattern::new(
"sext_i8_i32",
ISelPatternNode::tree(IROpcodeKind::SExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSX)
.with_cost(1)
.with_latency(1),
100,
"MOVSX i8 -> i32",
));
self.register(ISelPattern::new(
"fptrunc_f64_f32",
ISelPatternNode::tree(IROpcodeKind::FPTrunc, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("f32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSD2SS)
.with_cost(3)
.with_latency(3),
100,
"CVTSD2SS f64 -> f32",
));
self.register(ISelPattern::new(
"fpext_f32_f64",
ISelPatternNode::tree(IROpcodeKind::FPExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSS2SD)
.with_cost(3)
.with_latency(3),
100,
"CVTSS2SD f32 -> f64",
));
self.register(ISelPattern::new(
"fptosi_f64_i64",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSD2SI)
.with_cost(4)
.with_latency(4),
100,
"CVTSD2SI f64 -> i64",
));
self.register(ISelPattern::new(
"fptosi_f32_i64",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSS2SI)
.with_cost(4)
.with_latency(4),
100,
"CVTSS2SI f32 -> i64",
));
self.register(ISelPattern::new(
"fptosi_f64_i32",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSD2SI)
.with_cost(4)
.with_latency(4),
100,
"CVTSD2SI f64 -> i32",
));
self.register(ISelPattern::new(
"sitofp_i64_f64",
ISelPatternNode::tree(IROpcodeKind::SIToFP, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSI2SD)
.with_cost(4)
.with_latency(4),
100,
"CVTSI2SD i64 -> f64",
));
self.register(ISelPattern::new(
"sitofp_i32_f64",
ISelPatternNode::tree(IROpcodeKind::SIToFP, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSI2SD)
.with_cost(4)
.with_latency(4),
100,
"CVTSI2SD i32 -> f64",
));
self.register(ISelPattern::new(
"sitofp_i64_f32",
ISelPatternNode::tree(IROpcodeKind::SIToFP, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("f32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTSI2SS)
.with_cost(4)
.with_latency(4),
100,
"CVTSI2SS i64 -> f32",
));
self.register(ISelPattern::new(
"bitcast_f32_i32",
ISelPatternNode::tree(IROpcodeKind::BitCast, vec![ISelPatternNode::Reg]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOVD)
.with_cost(1)
.with_latency(1),
90,
"MOVD (bitcast f32 <-> i32)",
));
self.register(ISelPattern::new(
"bitcast_f64_i64",
ISelPatternNode::tree(IROpcodeKind::BitCast, vec![ISelPatternNode::Reg]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOVQ)
.with_cost(1)
.with_latency(1),
90,
"MOVQ (bitcast f64 <-> i64)",
));
self.register(ISelPattern::new(
"cvtdq2ps",
ISelPatternNode::tree(IROpcodeKind::SIToFP, vec![
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("v4f32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTDQ2PS)
.with_cost(3)
.with_latency(3),
95,
"CVTDQ2PS v4i32 -> v4f32",
));
self.register(ISelPattern::new(
"cvtps2dq",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("v4i32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTPS2DQ)
.with_cost(3)
.with_latency(3),
95,
"CVTPS2DQ v4f32 -> v4i32",
));
self.register(ISelPattern::new(
"cvtdq2pd",
ISelPatternNode::tree(IROpcodeKind::SIToFP, vec![
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("v2f64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTDQ2PD)
.with_cost(3)
.with_latency(3),
95,
"CVTDQ2PD v2i32 -> v2f64",
));
self.register(ISelPattern::new(
"cvtpd2dq",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("v4i32".into()),
ISelPatternResult::new(DeepX86Opcode::CVTPD2DQ)
.with_cost(3)
.with_latency(3),
95,
"CVTPD2DQ v2f64 -> v4i32",
));
self.register(ISelPattern::new(
"cvttsd2si",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTTSD2SI)
.with_cost(4)
.with_latency(4),
98,
"CVTTSD2SI f64 -> i64 (truncating)",
));
self.register(ISelPattern::new(
"cvttss2si",
ISelPatternNode::tree(IROpcodeKind::FPToSI, vec![ISelPatternNode::Reg]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::CVTTSS2SI)
.with_cost(4)
.with_latency(4),
98,
"CVTTSS2SI f32 -> i64 (truncating)",
));
if self.features.contains("avx") {
self.register(ISelPattern::new(
"vcvtsd2ss",
ISelPatternNode::tree(IROpcodeKind::FPTrunc, vec![ISelPatternNode::Reg]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternCondition::TypeMatches("f32".into()),
]),
ISelPatternResult::new(DeepX86Opcode::VCVTSD2SS)
.with_cost(3)
.with_feature("avx")
.with_latency(3),
200,
"VCVTSD2SS f64 -> f32",
));
self.register(ISelPattern::new(
"vcvtss2sd",
ISelPatternNode::tree(IROpcodeKind::FPExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternCondition::TypeMatches("f64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::VCVTSS2SD)
.with_cost(3)
.with_feature("avx")
.with_latency(3),
200,
"VCVTSS2SD f32 -> f64",
));
}
}
fn build_memory_patterns(&mut self) {
let load_sizes = [
("i8", 1u8, "8"),
("i16", 2, "16"),
("i32", 4, "32"),
("i64", 8, "64"),
("f32", 4, "f32"),
("f64", 8, "f64"),
];
for &(tname, size, suffix) in &load_sizes {
self.register(ISelPattern::new(
&format!("load_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Load, vec![
ISelPatternNode::MemRef,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
ISelPatternCondition::OperandSize(size),
]),
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(3)
.with_latency(4),
100,
&format!("MOV reg, [mem] — {} bit load", size * 8),
));
self.register(ISelPattern::new(
&format!("store_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Store, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(format!("void")),
ISelPatternCondition::OperandSize(size),
]),
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(2)
.with_latency(1),
100,
&format!("MOV [mem], reg — {} bit store", size * 8),
));
}
let simd_sizes = [
("v16i8", DeepX86Opcode::MOVDQA, "movdqa"),
("v8i16", DeepX86Opcode::MOVDQA, "movdqa_w"),
("v4i32", DeepX86Opcode::MOVDQA, "movdqa_d"),
("v2i64", DeepX86Opcode::MOVDQA, "movdqa_q"),
("v4f32", DeepX86Opcode::MOVAPS, "movaps"),
("v2f64", DeepX86Opcode::MOVAPD, "movapd"),
("v8f32", DeepX86Opcode::VMOVAPS, "vmovaps"),
("v4f64", DeepX86Opcode::VMOVAPD, "vmovapd"),
];
for &(tname, opcode, suffix) in &simd_sizes {
self.register(ISelPattern::new(
&format!("load_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Load, vec![
ISelPatternNode::MemRef,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches(tname.to_string()),
]),
ISelPatternResult::new(opcode)
.with_cost(3)
.with_latency(4),
95,
&format!("{} [mem] — SIMD load", opcode.mnemonic().to_uppercase()),
));
self.register(ISelPattern::new(
&format!("store_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Store, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("void".to_string()),
]),
ISelPatternResult::new(opcode)
.with_cost(2)
.with_latency(1),
95,
&format!("{} [mem] — SIMD store", opcode.mnemonic().to_uppercase()),
));
}
self.register(ISelPattern::new(
"load_movss",
ISelPatternNode::tree(IROpcodeKind::Load, vec![ISelPatternNode::MemRef]),
ISelPatternCondition::TypeMatches("f32".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSS)
.with_cost(3)
.with_latency(4),
100,
"MOVSS xmm, [mem]",
));
self.register(ISelPattern::new(
"load_movsd",
ISelPatternNode::tree(IROpcodeKind::Load, vec![ISelPatternNode::MemRef]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSD)
.with_cost(3)
.with_latency(4),
100,
"MOVSD xmm, [mem]",
));
self.register(ISelPattern::new(
"alloca",
ISelPatternNode::tree(IROpcodeKind::Alloca, vec![ISelPatternNode::ConstAny]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SUB)
.with_cost(1)
.with_flags(),
80,
"Alloca -> SUB rsp, size",
));
}
fn build_control_flow_patterns(&mut self) {
self.register(ISelPattern::new(
"ret_void",
ISelPatternNode::Op(IROpcodeKind::Ret),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::RET)
.with_cost(1)
.with_latency(1),
100,
"RET",
));
self.register(ISelPattern::new(
"ret_val",
ISelPatternNode::tree(IROpcodeKind::Ret, vec![ISelPatternNode::Reg]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::RET)
.with_cost(1)
.with_latency(1),
100,
"RET (value in RAX/XMM0)",
));
self.register(ISelPattern::new(
"br_uncond",
ISelPatternNode::Op(IROpcodeKind::Br),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::JMP)
.with_cost(1)
.with_latency(1),
100,
"JMP — unconditional branch",
));
let icmp_to_jcc: [(CmpPredicate, DeepX86Opcode); 10] = [
(CmpPredicate::EQ, DeepX86Opcode::JE),
(CmpPredicate::NE, DeepX86Opcode::JNE),
(CmpPredicate::UGT, DeepX86Opcode::JA),
(CmpPredicate::UGE, DeepX86Opcode::JAE),
(CmpPredicate::ULT, DeepX86Opcode::JB),
(CmpPredicate::ULE, DeepX86Opcode::JBE),
(CmpPredicate::SGT, DeepX86Opcode::JG),
(CmpPredicate::SGE, DeepX86Opcode::JGE),
(CmpPredicate::SLT, DeepX86Opcode::JL),
(CmpPredicate::SLE, DeepX86Opcode::JLE),
];
for &(pred, jcc_op) in &icmp_to_jcc {
let cc = pred.to_icmp_cc().unwrap();
self.register(ISelPattern::new(
&format!("br_icmp_{}", cc.suffix()),
ISelPatternNode::tree(IROpcodeKind::Br, vec![
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Any, ISelPatternNode::Any, ]),
ISelPatternCondition::Always,
ISelPatternResult::new(jcc_op)
.with_cost(1)
.with_conditional()
.with_latency(1),
100,
&format!("J{} — conditional branch on {}", cc.suffix(), cc.suffix()),
));
}
self.register(ISelPattern::new(
"switch",
ISelPatternNode::Op(IROpcodeKind::Switch),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::JMP)
.with_cost(5)
.with_latency(5),
90,
"Switch -> JMP *(jump table)",
));
self.register(ISelPattern::new(
"indirectbr",
ISelPatternNode::Op(IROpcodeKind::IndirectBr),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::JMP)
.with_cost(2)
.with_latency(2),
85,
"JMP reg — indirect branch",
));
self.register(ISelPattern::new(
"unreachable",
ISelPatternNode::Op(IROpcodeKind::Unreachable),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::UD2)
.with_cost(1)
.with_latency(1),
100,
"UD2 — unreachable",
));
self.register(ISelPattern::new(
"loop_ne",
ISelPatternNode::tree(IROpcodeKind::Br, vec![
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternNode::Any,
ISelPatternNode::Any,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LOOPNE)
.with_cost(1)
.with_latency(1),
90,
"LOOPNE — dec ECX and jump if not zero",
));
}
fn build_vector_patterns(&mut self) {
self.register(ISelPattern::new(
"shufps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SHUFPS)
.with_cost(1)
.with_latency(1),
100,
"SHUFPS — shuffle packed single",
));
self.register(ISelPattern::new(
"pshufd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PSHUFD)
.with_cost(1)
.with_latency(1),
100,
"PSHUFD — shuffle doublewords",
));
self.register(ISelPattern::new(
"pshufhw",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PSHUFHW)
.with_cost(1)
.with_latency(1),
98,
"PSHUFHW — shuffle high words",
));
self.register(ISelPattern::new(
"pshuflw",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PSHUFLW)
.with_cost(1)
.with_latency(1),
98,
"PSHUFLW — shuffle low words",
));
self.register(ISelPattern::new(
"unpcklps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::UNPCKLPS)
.with_cost(1)
.with_latency(1),
95,
"UNPCKLPS — unpack low packed single",
));
self.register(ISelPattern::new(
"unpckhps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::UNPCKHPS)
.with_cost(1)
.with_latency(1),
95,
"UNPCKHPS — unpack high packed single",
));
self.register(ISelPattern::new(
"unpcklpd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::UNPCKLPD)
.with_cost(1)
.with_latency(1),
95,
"UNPCKLPD — unpack low packed double",
));
self.register(ISelPattern::new(
"unpckhpd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::UNPCKHPD)
.with_cost(1)
.with_latency(1),
95,
"UNPCKHPD — unpack high packed double",
));
self.register(ISelPattern::new(
"pextrb",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("i8".into()),
ISelPatternCondition::ImmFits(4),
]),
ISelPatternResult::new(DeepX86Opcode::PEXTRB)
.with_cost(2)
.with_latency(2),
100,
"PEXTRB — extract byte",
));
self.register(ISelPattern::new(
"pextrw",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("i16".into()),
ISelPatternCondition::ImmFits(8),
]),
ISelPatternResult::new(DeepX86Opcode::PEXTRW)
.with_cost(2)
.with_latency(2),
100,
"PEXTRW — extract word",
));
self.register(ISelPattern::new(
"pextrd",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternCondition::ImmFits(4),
]),
ISelPatternResult::new(DeepX86Opcode::PEXTRD)
.with_cost(2)
.with_latency(2),
100,
"PEXTRD — extract dword",
));
self.register(ISelPattern::new(
"pextrq",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternCondition::ImmFits(2),
]),
ISelPatternResult::new(DeepX86Opcode::PEXTRQ)
.with_cost(2)
.with_latency(2),
100,
"PEXTRQ — extract qword",
));
self.register(ISelPattern::new(
"pinsrb",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::ImmFits(16),
]),
ISelPatternResult::new(DeepX86Opcode::PINSRB)
.with_cost(2)
.with_latency(2),
100,
"PINSRB — insert byte",
));
self.register(ISelPattern::new(
"pinsrw",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::ImmFits(8),
]),
ISelPatternResult::new(DeepX86Opcode::PINSRW)
.with_cost(2)
.with_latency(2),
100,
"PINSRW — insert word",
));
self.register(ISelPattern::new(
"pinsrd",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::ImmFits(4),
]),
ISelPatternResult::new(DeepX86Opcode::PINSRD)
.with_cost(2)
.with_latency(2),
100,
"PINSRD — insert dword",
));
self.register(ISelPattern::new(
"pinsrq",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::ImmFits(2),
]),
ISelPatternResult::new(DeepX86Opcode::PINSRQ)
.with_cost(2)
.with_latency(2),
100,
"PINSRQ — insert qword",
));
self.register(ISelPattern::new(
"blendps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::BLENDPS)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
200,
"BLENDPS — blend packed single (SSE4.1)",
));
self.register(ISelPattern::new(
"blendpd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::BLENDPD)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
200,
"BLENDPD — blend packed double (SSE4.1)",
));
self.register(ISelPattern::new(
"blendvps",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg, ISelPatternNode::Reg, ISelPatternNode::Reg, ]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::BLENDVPS)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
200,
"BLENDVPS — variable blend (SSE4.1)",
));
self.register(ISelPattern::new(
"blendvpd",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::BLENDVPD)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
200,
"BLENDVPD — variable blend double (SSE4.1)",
));
let minmax_ops = [
(DeepX86Opcode::PMINSB, "pminsb"),
(DeepX86Opcode::PMINSD, "pminsd"),
(DeepX86Opcode::PMINUW, "pminuw"),
(DeepX86Opcode::PMINUD, "pminud"),
(DeepX86Opcode::PMAXSB, "pmaxsb"),
(DeepX86Opcode::PMAXSD, "pmaxsd"),
(DeepX86Opcode::PMAXUW, "pmaxuw"),
(DeepX86Opcode::PMAXUD, "pmaxud"),
];
for &(op, name) in &minmax_ops {
self.register(ISelPattern::new(
name,
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(op)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
195,
&format!("{} (SSE4.1)", op.mnemonic()),
));
}
self.register(ISelPattern::new(
"dpps",
ISelPatternNode::tree(IROpcodeKind::FMul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::DPPS)
.with_cost(4)
.with_feature("sse4.1")
.with_latency(4),
195,
"DPPS — dot product packed single (SSE4.1)",
));
if self.features.contains("avx") {
self.register(ISelPattern::new(
"vshufps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VSHUFPS)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VSHUFPS — AVX shuffle packed single",
));
self.register(ISelPattern::new(
"vshufpd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VSHUFPD)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VSHUFPD — AVX shuffle packed double",
));
self.register(ISelPattern::new(
"vpermilps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VPERMILPS)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VPERMILPS — AVX permute single",
));
self.register(ISelPattern::new(
"vinsertf128",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VINSERTF128)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VINSERTF128 — AVX insert 128-bit lane",
));
self.register(ISelPattern::new(
"vextractf128",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VEXTRACTF128)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VEXTRACTF128 — AVX extract 128-bit lane",
));
self.register(ISelPattern::new(
"vperm2f128",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VPERM2F128)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VPERM2F128 — AVX permute 128-bit float lanes",
));
self.register(ISelPattern::new(
"vzeroall",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VZEROALL)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
80,
"VZEROALL — zero all YMM registers",
));
self.register(ISelPattern::new(
"vzeroupper",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VZEROUPPER)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
80,
"VZEROUPPER — zero upper YMM bits",
));
}
if self.features.contains("avx2") {
self.register(ISelPattern::new(
"vpermq",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPERMQ)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
"VPERMQ — AVX2 permute quadwords",
));
self.register(ISelPattern::new(
"vpermpd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPERMPD)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
"VPERMPD — AVX2 permute packed double",
));
self.register(ISelPattern::new(
"vpbroadcastd",
ISelPatternNode::Reg,
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPBROADCASTD)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
"VPBROADCASTD — AVX2 broadcast dword",
));
self.register(ISelPattern::new(
"vpgatherdd",
ISelPatternNode::tree(IROpcodeKind::Load, vec![
ISelPatternNode::MemRef,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPGATHERDD)
.with_cost(5)
.with_feature("avx2")
.with_latency(5),
200,
"VPGATHERDD — AVX2 gather dwords",
));
}
if self.features.contains("avx512f") {
self.register(ISelPattern::new(
"vblendmps",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VBLENDMPS)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VBLENDMPS — AVX-512 blend packed single using mask",
));
self.register(ISelPattern::new(
"vblendmpd",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VBLENDMPD)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VBLENDMPD — AVX-512 blend packed double using mask",
));
self.register(ISelPattern::new(
"vpblendmd",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VPBLENDMD)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VPBLENDMD — AVX-512 blend dwords using mask",
));
self.register(ISelPattern::new(
"vpblendmq",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VPBLENDMQ)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VPBLENDMQ — AVX-512 blend quadwords using mask",
));
self.register(ISelPattern::new(
"vshufi32x4",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VSHUFI32X4)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VSHUFI32X4 — AVX-512 shuffle 128-bit integer lanes",
));
self.register(ISelPattern::new(
"vinserti32x4",
ISelPatternNode::tree(IROpcodeKind::InsertElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VINSERTI32X4)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VINSERTI32X4 — AVX-512 insert 128-bit lane",
));
self.register(ISelPattern::new(
"vextracti32x4",
ISelPatternNode::tree(IROpcodeKind::ExtractElement, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VEXTRACTI32X4)
.with_cost(1)
.with_feature("avx512f")
.with_latency(1),
250,
"VEXTRACTI32X4 — AVX-512 extract 128-bit lane",
));
self.register(ISelPattern::new(
"vpcompressd",
ISelPatternNode::tree(IROpcodeKind::Store, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VPCOMPRESSD)
.with_cost(3)
.with_feature("avx512f")
.with_latency(3),
250,
"VPCOMPRESSD — AVX-512 compress dwords to memory",
));
self.register(ISelPattern::new(
"vpexpandd",
ISelPatternNode::tree(IROpcodeKind::Load, vec![
ISelPatternNode::MemRef,
]),
ISelPatternCondition::HasFeature("avx512f".into()),
ISelPatternResult::new(DeepX86Opcode::VPEXPANDD)
.with_cost(3)
.with_feature("avx512f")
.with_latency(3),
250,
"VPEXPANDD — AVX-512 expand dwords from memory",
));
}
self.register(ISelPattern::new(
"haddps",
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse3".into()),
ISelPatternResult::new(DeepX86Opcode::HADDPS)
.with_cost(2)
.with_feature("sse3")
.with_latency(2),
195,
"HADDPS — horizontal add packed single (SSE3)",
));
self.register(ISelPattern::new(
"haddpd",
ISelPatternNode::tree(IROpcodeKind::FAdd, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse3".into()),
ISelPatternResult::new(DeepX86Opcode::HADDPD)
.with_cost(2)
.with_feature("sse3")
.with_latency(2),
195,
"HADDPD — horizontal add packed double (SSE3)",
));
self.register(ISelPattern::new(
"palignr",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("ssse3".into()),
ISelPatternResult::new(DeepX86Opcode::PALIGNR)
.with_cost(1)
.with_feature("ssse3")
.with_latency(1),
195,
"PALIGNR — align right (SSSE3)",
));
}
fn build_atomic_patterns(&mut self) {
let atomic_ops = [
("add", DeepX86Opcode::LOCK_ADD),
("sub", DeepX86Opcode::LOCK_SUB),
("and", DeepX86Opcode::LOCK_AND),
("or", DeepX86Opcode::LOCK_OR),
("xor", DeepX86Opcode::LOCK_XOR),
];
for &(ir_name, x86_op) in &atomic_ops {
self.register(ISelPattern::new(
&format!("atomic_{}", ir_name),
ISelPatternNode::tree(IROpcodeKind::AtomicRMW, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(x86_op)
.with_cost(15)
.with_latency(15),
100,
&format!("LOCK {} — atomic {}", ir_name.to_uppercase(), ir_name),
));
}
self.register(ISelPattern::new(
"atomic_xchg",
ISelPatternNode::tree(IROpcodeKind::AtomicRMW, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LOCK_XCHG)
.with_cost(15)
.with_latency(15),
100,
"LOCK XCHG — atomic exchange",
));
self.register(ISelPattern::new(
"atomic_inc",
ISelPatternNode::tree(IROpcodeKind::AtomicRMW, vec![
ISelPatternNode::MemRef,
ISelPatternNode::ConstInt(1),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LOCK_INC)
.with_cost(15)
.with_latency(15),
105,
"LOCK INC",
));
self.register(ISelPattern::new(
"atomic_dec",
ISelPatternNode::tree(IROpcodeKind::AtomicRMW, vec![
ISelPatternNode::MemRef,
ISelPatternNode::ConstInt(-1),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LOCK_DEC)
.with_cost(15)
.with_latency(15),
105,
"LOCK DEC",
));
self.register(ISelPattern::new(
"cmpxchg",
ISelPatternNode::tree(IROpcodeKind::CmpXchg, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg, ISelPatternNode::Reg, ]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LOCK_CMPXCHG)
.with_cost(15)
.with_latency(15),
100,
"LOCK CMPXCHG — atomic compare-and-exchange",
));
self.register(ISelPattern::new(
"cmpxchg8b",
ISelPatternNode::tree(IROpcodeKind::CmpXchg, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::CMPXCHG8B)
.with_cost(20)
.with_latency(20),
95,
"CMPXCHG8B — 64-bit compare-and-exchange",
));
self.register(ISelPattern::new(
"cmpxchg16b",
ISelPatternNode::tree(IROpcodeKind::CmpXchg, vec![
ISelPatternNode::MemRef,
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("i128".into()),
ISelPatternResult::new(DeepX86Opcode::CMPXCHG16B)
.with_cost(25)
.with_latency(25),
95,
"CMPXCHG16B — 128-bit compare-and-exchange",
));
self.register(ISelPattern::new(
"fence_seqcst",
ISelPatternNode::Op(IROpcodeKind::Fence),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MFENCE)
.with_cost(10)
.with_latency(10),
100,
"MFENCE — full memory fence",
));
self.register(ISelPattern::new(
"fence_acquire",
ISelPatternNode::Op(IROpcodeKind::Fence),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::LFENCE)
.with_cost(5)
.with_latency(5),
90,
"LFENCE — load fence",
));
self.register(ISelPattern::new(
"fence_release",
ISelPatternNode::Op(IROpcodeKind::Fence),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SFENCE)
.with_cost(5)
.with_latency(5),
90,
"SFENCE — store fence",
));
}
fn build_comparison_patterns(&mut self) {
let icmp_preds: [(CmpPredicate, DeepX86Opcode); 10] = [
(CmpPredicate::EQ, DeepX86Opcode::SETE),
(CmpPredicate::NE, DeepX86Opcode::SETNE),
(CmpPredicate::UGT, DeepX86Opcode::SETA),
(CmpPredicate::UGE, DeepX86Opcode::SETAE),
(CmpPredicate::ULT, DeepX86Opcode::SETB),
(CmpPredicate::ULE, DeepX86Opcode::SETBE),
(CmpPredicate::SGT, DeepX86Opcode::SETG),
(CmpPredicate::SGE, DeepX86Opcode::SETGE),
(CmpPredicate::SLT, DeepX86Opcode::SETL),
(CmpPredicate::SLE, DeepX86Opcode::SETLE),
];
for &(pred, setcc_op) in &icmp_preds {
let cc = pred.to_icmp_cc().unwrap();
self.register(ISelPattern::new(
&format!("icmp_{}", cc.suffix()),
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CMP)
.with_cost(1)
.with_flags(),
100,
&format!("CMP + SET{} — integer compare {}", cc.suffix(), cc.suffix()),
));
self.register(ISelPattern::new(
&format!("cmov_{}", cc.suffix()),
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(match_pred_to_cmov(pred))
.with_cost(2)
.with_conditional()
.with_latency(2),
120,
&format!("CMOV{} — conditional move", cc.suffix()),
));
}
for &(pred, setcc_op) in &icmp_preds {
let cc = pred.to_icmp_cc().unwrap();
self.register(ISelPattern::new(
&format!("icmp_imm_{}", cc.suffix()),
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::ImmFits(32),
ISelPatternResult::new(DeepX86Opcode::CMP)
.with_cost(1)
.with_flags(),
98,
&format!("CMP reg, imm + SET{}", cc.suffix()),
));
}
let fcmp_preds = [
CmpPredicate::OEQ,
CmpPredicate::OGT,
CmpPredicate::OGE,
CmpPredicate::OLT,
CmpPredicate::OLE,
CmpPredicate::ONE,
CmpPredicate::ORD,
CmpPredicate::UNO,
CmpPredicate::UEQ,
CmpPredicate::UNE,
];
for &pred in &fcmp_preds {
let pred_name = format!("{:?}", pred).to_lowercase();
self.register(ISelPattern::new(
&format!("fcmp_ucomiss_{}", pred_name),
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("f32".into()),
ISelPatternResult::new(DeepX86Opcode::UCOMISS)
.with_cost(2)
.with_flags(),
100,
&format!("UCOMISS + SETcc — float compare {}", pred_name),
));
self.register(ISelPattern::new(
&format!("fcmp_ucomisd_{}", pred_name),
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::UCOMISD)
.with_cost(2)
.with_flags(),
100,
&format!("UCOMISD + SETcc — float compare {}", pred_name),
));
}
self.register(ISelPattern::new(
"cmpss_eq",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("f32".into()),
ISelPatternResult::new(DeepX86Opcode::CMPSS)
.with_cost(2)
.with_flags(),
95,
"CMPSS — SSE scalar compare",
));
self.register(ISelPattern::new(
"cmpsd_eq",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::TypeMatches("f64".into()),
ISelPatternResult::new(DeepX86Opcode::CMPSD)
.with_cost(2)
.with_flags(),
95,
"CMPSD — SSE scalar compare",
));
self.register(ISelPattern::new(
"icmp_eq_zero",
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::TEST)
.with_cost(1)
.with_flags(),
110,
"TEST reg, reg — compare with zero",
));
if self.features.contains("avx") {
self.register(ISelPattern::new(
"vcmpps",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VCMPPS)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VCMPPS — AVX packed compare",
));
self.register(ISelPattern::new(
"vcmppd",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternResult::new(DeepX86Opcode::VCMPPD)
.with_cost(1)
.with_feature("avx")
.with_latency(1),
200,
"VCMPPD — AVX packed double compare",
));
self.register(ISelPattern::new(
"vucomiss",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternCondition::TypeMatches("f32".into()),
]),
ISelPatternResult::new(DeepX86Opcode::VUCOMISS)
.with_cost(2)
.with_feature("avx")
.with_latency(2),
200,
"VUCOMISS — AVX unordered compare scalar single",
));
self.register(ISelPattern::new(
"vucomisd",
ISelPatternNode::tree(IROpcodeKind::FCmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("avx".into()),
ISelPatternCondition::TypeMatches("f64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::VUCOMISD)
.with_cost(2)
.with_feature("avx")
.with_latency(2),
200,
"VUCOMISD — AVX unordered compare scalar double",
));
}
}
fn build_call_patterns(&mut self) {
self.register(ISelPattern::new(
"call_direct",
ISelPatternNode::tree(IROpcodeKind::Call, vec![ISelPatternNode::Reg]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(1)
.with_latency(1),
100,
"CALL — direct call",
));
self.register(ISelPattern::new(
"call_indirect",
ISelPatternNode::tree(IROpcodeKind::Call, vec![ISelPatternNode::Reg]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(2)
.with_latency(2),
98,
"CALL reg — indirect call",
));
self.register(ISelPattern::new(
"call_mem",
ISelPatternNode::tree(IROpcodeKind::Call, vec![ISelPatternNode::MemRef]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(3)
.with_latency(3),
95,
"CALL [mem] — indirect call via memory",
));
self.register(ISelPattern::new(
"tail_call",
ISelPatternNode::tree(IROpcodeKind::Ret, vec![
ISelPatternNode::tree(IROpcodeKind::Call, vec![
ISelPatternNode::Reg,
]),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::JMP)
.with_cost(1)
.with_latency(1),
150,
"Tail call -> JMP (sibcall optimization)",
));
self.register(ISelPattern::new(
"invoke",
ISelPatternNode::Op(IROpcodeKind::Invoke),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(2)
.with_latency(2),
90,
"Invoke -> CALL (with landing pad setup)",
));
self.register(ISelPattern::new(
"callbr",
ISelPatternNode::Op(IROpcodeKind::CallBr),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CALL)
.with_cost(2)
.with_latency(2),
90,
"CallBr -> CALL",
));
self.register(ISelPattern::new(
"call_frame_setup",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PUSH)
.with_cost(0)
.with_latency(0),
60,
"PUSH rbp — frame setup for call",
));
self.register(ISelPattern::new(
"call_stack_adjust",
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::TypeMatches("i64".to_string()),
]),
ISelPatternResult::new(DeepX86Opcode::ADD)
.with_cost(1)
.with_flags(),
55,
"ADD rsp, N — stack cleanup after call",
));
}
fn build_misc_patterns(&mut self) {
self.register(ISelPattern::new(
"select_reg",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg, ISelPatternNode::Reg, ISelPatternNode::Reg, ]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CMOVNE)
.with_cost(2)
.with_conditional()
.with_latency(2),
100,
"Select -> CMOVNE (conditional move)",
));
self.register(ISelPattern::new(
"select_const",
ISelPatternNode::tree(IROpcodeKind::Select, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SETNE)
.with_cost(1)
.with_latency(1),
105,
"Select 1, 0 -> SETNE",
));
self.register(ISelPattern::new(
"phi",
ISelPatternNode::Op(IROpcodeKind::Phi),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
100,
"Phi -> MOV (register coalescing)",
));
for &(tname, suffix) in &[("i32", "32"), ("i64", "64")] {
self.register(ISelPattern::new(
&format!("gep_lea_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::GetElementPtr, vec![
ISelPatternNode::Reg, ISelPatternNode::Reg, ]),
ISelPatternCondition::TypeMatches(tname.into()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
100,
&format!("GEP -> LEA [base + index] — {}", tname),
));
self.register(ISelPattern::new(
&format!("gep_lea_const_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::GetElementPtr, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::TypeMatches(tname.into()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
100,
&format!("GEP -> LEA [base + const] — {}", tname),
));
self.register(ISelPattern::new(
&format!("gep_lea_scaled_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::GetElementPtr, vec![
ISelPatternNode::Reg, ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg, ISelPatternNode::ConstAny, ]),
]),
ISelPatternCondition::TypeMatches(tname.into()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
105,
&format!("GEP -> LEA [base + index*scale] — {}", tname),
));
}
self.register(ISelPattern::new(
"extractvalue",
ISelPatternNode::Op(IROpcodeKind::ExtractValue),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(1)
.with_latency(1),
85,
"ExtractValue -> MOV",
));
self.register(ISelPattern::new(
"insertvalue",
ISelPatternNode::Op(IROpcodeKind::InsertValue),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(1)
.with_latency(1),
85,
"InsertValue -> MOV",
));
self.register(ISelPattern::new(
"freeze",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
50,
"Freeze -> MOV (no-op)",
));
self.register(ISelPattern::new(
"popcnt",
ISelPatternNode::Reg,
ISelPatternCondition::HasFeature("popcnt".into()),
ISelPatternResult::new(DeepX86Opcode::POPCNT)
.with_cost(3)
.with_feature("popcnt")
.with_latency(3),
200,
"POPCNT (population count)",
));
self.register(ISelPattern::new(
"lzcnt",
ISelPatternNode::Reg,
ISelPatternCondition::HasFeature("lzcnt".into()),
ISelPatternResult::new(DeepX86Opcode::LZCNT)
.with_cost(3)
.with_feature("lzcnt")
.with_latency(3),
200,
"LZCNT (leading zero count)",
));
self.register(ISelPattern::new(
"tzcnt",
ISelPatternNode::Reg,
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::TZCNT)
.with_cost(3)
.with_feature("bmi1")
.with_latency(3),
200,
"TZCNT (trailing zero count)",
));
self.register(ISelPattern::new(
"blsi",
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::BLSI)
.with_cost(1)
.with_feature("bmi1")
.with_latency(1),
200,
"BLSI — extract lowest set bit (BMI1)",
));
self.register(ISelPattern::new(
"blsr",
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::BLSR)
.with_cost(1)
.with_feature("bmi1")
.with_latency(1),
200,
"BLSR — reset lowest set bit (BMI1)",
));
self.register(ISelPattern::new(
"blsmsk",
ISelPatternNode::tree(IROpcodeKind::Xor, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::BLSMSK)
.with_cost(1)
.with_feature("bmi1")
.with_latency(1),
200,
"BLSMSK — mask up to lowest set bit (BMI1)",
));
self.register(ISelPattern::new(
"bextr",
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::BEXTR)
.with_cost(1)
.with_feature("bmi1")
.with_latency(1),
200,
"BEXTR — bit field extract (BMI1)",
));
self.register(ISelPattern::new(
"pdep",
ISelPatternNode::tree(IROpcodeKind::Or, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("bmi2".into()),
ISelPatternResult::new(DeepX86Opcode::PDEP)
.with_cost(3)
.with_feature("bmi2")
.with_latency(3),
200,
"PDEP — parallel bit deposit (BMI2)",
));
self.register(ISelPattern::new(
"pext",
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("bmi2".into()),
ISelPatternResult::new(DeepX86Opcode::PEXT)
.with_cost(3)
.with_feature("bmi2")
.with_latency(3),
200,
"PEXT — parallel bit extract (BMI2)",
));
self.register(ISelPattern::new(
"andn",
ISelPatternNode::tree(IROpcodeKind::And, vec![
ISelPatternNode::Not(Box::new(ISelPatternNode::Reg)),
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("bmi1".into()),
ISelPatternResult::new(DeepX86Opcode::ANDN)
.with_cost(1)
.with_feature("bmi1")
.with_latency(1),
200,
"ANDN — logical and-not (BMI1)",
));
self.register(ISelPattern::new(
"memcpy_movsb",
ISelPatternNode::tree(IROpcodeKind::Call, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny, ]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::REP_MOVSB)
.with_cost(10)
.with_latency(10),
80,
"REP MOVSB — memory copy (memcpy intrinsic)",
));
self.register(ISelPattern::new(
"memset_stosb",
ISelPatternNode::tree(IROpcodeKind::Call, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstAny, ISelPatternNode::ConstAny, ]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::REP_STOSB)
.with_cost(10)
.with_latency(10),
80,
"REP STOSB — memory set (memset intrinsic)",
));
self.register(ISelPattern::new(
"movsxd",
ISelPatternNode::tree(IROpcodeKind::SExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::All(vec![
ISelPatternCondition::Is64Bit,
ISelPatternCondition::TypeMatches("i64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::MOVSXD)
.with_cost(1)
.with_latency(1),
100,
"MOVSXD — move with sign-extension (32->64)",
));
self.register(ISelPattern::new(
"cdq",
ISelPatternNode::Reg,
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternResult::new(DeepX86Opcode::CDQ)
.with_cost(1)
.with_latency(1),
75,
"CDQ — sign-extend EAX to EDX:EAX",
));
self.register(ISelPattern::new(
"cqo",
ISelPatternNode::Reg,
ISelPatternCondition::All(vec![
ISelPatternCondition::Is64Bit,
ISelPatternCondition::TypeMatches("i64".into()),
]),
ISelPatternResult::new(DeepX86Opcode::CQO)
.with_cost(1)
.with_latency(1),
75,
"CQO — sign-extend RAX to RDX:RAX",
));
}
fn build_extended_integer_patterns(&mut self) {
for &suffix in &["8", "16", "32", "64"] {
self.register(ISelPattern::new(
&format!("add_zero_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::MOV)
.with_cost(0)
.with_latency(0),
200,
&format!("ADD x, 0 -> MOV (no-op) {}", suffix),
));
}
for &suffix in &["8", "16", "32", "64"] {
self.register(ISelPattern::new(
&format!("sub_self_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::XOR)
.with_cost(1)
.with_flags()
.with_latency(1),
200,
&format!("SUB x, x -> XOR x, x (zero idiom) {}", suffix),
));
}
for &(pow, shift) in &[(2, 1), (4, 2), (8, 3), (16, 4), (32, 5), (64, 6), (128, 7), (256, 8)] {
for &suffix in &["8", "16", "32", "64"] {
self.register(ISelPattern::new(
&format!("udiv_pow2_{}_{}", pow, suffix),
ISelPatternNode::tree(IROpcodeKind::UDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(pow),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SHR)
.with_cost(1)
.with_flags()
.with_latency(1),
160,
&format!("UDiv by {} -> SHR by {}", pow, shift),
));
}
}
for &(pow, shift) in &[(2, 1), (4, 2), (8, 3), (16, 4), (32, 5), (64, 6)] {
self.register(ISelPattern::new(
&format!("sdiv_pow2_{}_32", pow),
ISelPatternNode::tree(IROpcodeKind::SDiv, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(pow),
]),
ISelPatternCondition::TypeMatches("i32".into()),
ISelPatternResult::new(DeepX86Opcode::SAR)
.with_cost(3)
.with_flags()
.with_latency(3),
150,
&format!("SDiv by {} -> SAR + adjust (i32)", pow),
));
}
for &(mul_const, base_reg, index_reg, lea_scale) in &[
(3, DeepX86Reg::RAX, DeepX86Reg::RAX, 2u8),
(5, DeepX86Reg::RAX, DeepX86Reg::RAX, 4u8),
(9, DeepX86Reg::RAX, DeepX86Reg::RAX, 8u8),
(6, DeepX86Reg::RAX, DeepX86Reg::RAX, 1u8), (10, DeepX86Reg::RAX, DeepX86Reg::RAX, 1u8), (12, DeepX86Reg::RAX, DeepX86Reg::RAX, 1u8), ] {
self.register(ISelPattern::new(
&format!("mul{}_lea", mul_const),
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(mul_const),
]),
ISelPatternCondition::TypeMatches("i64".into()),
ISelPatternResult::new(DeepX86Opcode::LEA)
.with_cost(1)
.with_latency(1),
155,
&format!("MUL by {} -> LEA optimization", mul_const),
));
}
for &(pow, mask) in &[(2, 1i64), (4, 3), (8, 7), (16, 15), (32, 31), (64, 63), (128, 127)] {
self.register(ISelPattern::new(
&format!("urem_pow2_{}", pow),
ISelPatternNode::tree(IROpcodeKind::URem, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(pow),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::AND)
.with_cost(1)
.with_flags()
.with_latency(1),
160,
&format!("URem by {} -> AND with {}", pow, mask),
));
}
self.register(ISelPattern::new(
"add_neg_to_sub",
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstIntRange(i64::MIN, -1),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::SUB)
.with_cost(1)
.with_flags()
.with_latency(1),
120,
"ADD x, -N -> SUB x, N",
));
self.register(ISelPattern::new(
"sub_neg_to_add",
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstIntRange(i64::MIN, -1),
]),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::ADD)
.with_cost(1)
.with_flags()
.with_latency(1),
120,
"SUB x, -N -> ADD x, N",
));
}
fn build_extended_vector_patterns(&mut self) {
self.register(ISelPattern::new(
"pmulld_sse41",
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternCondition::TypeMatches("v4i32".into()),
]),
ISelPatternResult::new(DeepX86Opcode::PMULLD)
.with_cost(2)
.with_feature("sse4.1")
.with_latency(2),
195,
"PMULLD — packed multiply dwords (SSE4.1)",
));
self.register(ISelPattern::new(
"packusdw",
ISelPatternNode::tree(IROpcodeKind::Trunc, vec![
ISelPatternNode::Reg,
]),
ISelPatternCondition::All(vec![
ISelPatternCondition::HasFeature("sse4.1".into()),
]),
ISelPatternResult::new(DeepX86Opcode::PACKUSDW)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
195,
"PACKUSDW — pack with unsigned saturation (SSE4.1)",
));
for &(suffix, opcode, desc) in &[
("bw", DeepX86Opcode::PMOVSXBW, "PMOVSXBW — sign extend byte to word"),
("wd", DeepX86Opcode::PMOVSXWD, "PMOVSXWD — sign extend word to dword"),
("dq", DeepX86Opcode::PMOVSXDQ, "PMOVSXDQ — sign extend dword to qword"),
("bd", DeepX86Opcode::PMOVSXBD, "PMOVSXBD — sign extend byte to dword"),
("wq", DeepX86Opcode::PMOVSXWQ, "PMOVSXWQ — sign extend word to qword"),
("bq", DeepX86Opcode::PMOVSXBQ, "PMOVSXBQ — sign extend byte to qword"),
] {
self.register(ISelPattern::new(
&format!("pmovsx{}", suffix),
ISelPatternNode::tree(IROpcodeKind::SExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
195,
desc,
));
}
for &(suffix, opcode, desc) in &[
("bw", DeepX86Opcode::PMOVZXBW, "PMOVZXBW — zero extend byte to word"),
("wd", DeepX86Opcode::PMOVZXWD, "PMOVZXWD — zero extend word to dword"),
("dq", DeepX86Opcode::PMOVZXDQ, "PMOVZXDQ — zero extend dword to qword"),
("bd", DeepX86Opcode::PMOVZXBD, "PMOVZXBD — zero extend byte to dword"),
("wq", DeepX86Opcode::PMOVZXWQ, "PMOVZXWQ — zero extend word to qword"),
("bq", DeepX86Opcode::PMOVZXBQ, "PMOVZXBQ — zero extend byte to qword"),
] {
self.register(ISelPattern::new(
&format!("pmovzx{}", suffix),
ISelPatternNode::tree(IROpcodeKind::ZExt, vec![ISelPatternNode::Reg]),
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
195,
desc,
));
}
for &suffix in &["8", "16", "32", "64"] {
self.register(ISelPattern::new(
&format!("crc32_{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Xor, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse4.2".into()),
ISelPatternResult::new(DeepX86Opcode::CRC32)
.with_cost(3)
.with_feature("sse4.2")
.with_latency(3),
195,
&format!("CRC32 — hardware CRC-32C {}", suffix),
));
}
self.register(ISelPattern::new(
"ptest",
ISelPatternNode::tree(IROpcodeKind::ICmp, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(0),
]),
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternResult::new(DeepX86Opcode::PTEST)
.with_cost(1)
.with_feature("sse4.1")
.with_latency(1),
195,
"PTEST — packed bit test (SSE4.1)",
));
self.register(ISelPattern::new(
"vpermd",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPERMD)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
"VPERMD — AVX2 permute dwords",
));
self.register(ISelPattern::new(
"vpermps",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
ISelPatternNode::ConstAny,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPERMPS)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
"VPERMPS — AVX2 permute singles",
));
for &(suffix, opcode, desc) in &[
("d", DeepX86Opcode::VPSLLVD, "VPSLLVD — variable shift left logical dwords"),
("q", DeepX86Opcode::VPSLLVQ, "VPSLLVQ — variable shift left logical qwords"),
] {
self.register(ISelPattern::new(
&format!("vpsllv{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Shl, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
desc,
));
}
for &(suffix, opcode, desc) in &[
("d", DeepX86Opcode::VPSRLVD, "VPSRLVD — variable shift right logical dwords"),
("q", DeepX86Opcode::VPSRLVQ, "VPSRLVQ — variable shift right logical qwords"),
] {
self.register(ISelPattern::new(
&format!("vpsrlv{}", suffix),
ISelPatternNode::tree(IROpcodeKind::LShr, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
desc,
));
}
for &(suffix, opcode, desc) in &[
("d", DeepX86Opcode::VPSRAVD, "VPSRAVD — variable shift right arithmetic dwords"),
] {
self.register(ISelPattern::new(
&format!("vpsrav{}", suffix),
ISelPatternNode::tree(IROpcodeKind::AShr, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(opcode)
.with_cost(1)
.with_feature("avx2")
.with_latency(1),
200,
desc,
));
}
for &(suffix, opcode, desc) in &[
("dps", DeepX86Opcode::VGATHERDPS, "VGATHERDPS — gather single-precision floats"),
("dpd", DeepX86Opcode::VGATHERDPD, "VGATHERDPD — gather double-precision floats"),
] {
self.register(ISelPattern::new(
&format!("vgather{}", suffix),
ISelPatternNode::tree(IROpcodeKind::Load, vec![
ISelPatternNode::MemRef,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(opcode)
.with_cost(5)
.with_feature("avx2")
.with_latency(5),
200,
desc,
));
}
self.register(ISelPattern::new(
"vpmaddwd",
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::tree(IROpcodeKind::Mul, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("avx2".into()),
ISelPatternResult::new(DeepX86Opcode::VPMADD)
.with_cost(3)
.with_feature("avx2")
.with_latency(3),
195,
"VPMADDWD — multiply and add packed words (AVX2)",
));
self.register(ISelPattern::new(
"movddup",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse3".into()),
ISelPatternResult::new(DeepX86Opcode::MOVDDUP)
.with_cost(1)
.with_feature("sse3")
.with_latency(1),
195,
"MOVDDUP — move and duplicate double (SSE3)",
));
self.register(ISelPattern::new(
"movsldup",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse3".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSLDUP)
.with_cost(1)
.with_feature("sse3")
.with_latency(1),
195,
"MOVSLDUP — move and duplicate low singles (SSE3)",
));
self.register(ISelPattern::new(
"movshdup",
ISelPatternNode::tree(IROpcodeKind::ShuffleVector, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse3".into()),
ISelPatternResult::new(DeepX86Opcode::MOVSHDUP)
.with_cost(1)
.with_feature("sse3")
.with_latency(1),
195,
"MOVSHDUP — move and duplicate high singles (SSE3)",
));
self.register(ISelPattern::new(
"phminposuw",
ISelPatternNode::Reg,
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternResult::new(DeepX86Opcode::PHMINPOSUW)
.with_cost(3)
.with_feature("sse4.1")
.with_latency(3),
190,
"PHMINPOSUW — horizontal minimum of packed unsigned words (SSE4.1)",
));
self.register(ISelPattern::new(
"mpsadbw",
ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::tree(IROpcodeKind::Sub, vec![
ISelPatternNode::Reg,
ISelPatternNode::Reg,
]),
ISelPatternNode::Reg,
]),
ISelPatternCondition::HasFeature("sse4.1".into()),
ISelPatternResult::new(DeepX86Opcode::MPSADBW)
.with_cost(3)
.with_feature("sse4.1")
.with_latency(3),
190,
"MPSADBW — sum of absolute differences (SSE4.1)",
));
}
fn build_system_patterns(&mut self) {
self.register(ISelPattern::new(
"prefetch",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PREFETCH)
.with_cost(3)
.with_latency(3),
70,
"PREFETCH — prefetch cache line",
));
self.register(ISelPattern::new(
"prefetchw",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PREFETCHW)
.with_cost(3)
.with_latency(3),
70,
"PREFETCHW — prefetch with intent to write",
));
self.register(ISelPattern::new(
"prefetchwt1",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PREFETCHWT1)
.with_cost(3)
.with_latency(3),
70,
"PREFETCHWT1 — prefetch into L1 cache with intent to write",
));
self.register(ISelPattern::new(
"clflush",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CLFLUSH)
.with_cost(5)
.with_latency(5),
70,
"CLFLUSH — flush cache line",
));
self.register(ISelPattern::new(
"clflushopt",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CLFLUSHOPT)
.with_cost(3)
.with_latency(3),
70,
"CLFLUSHOPT — optimized cache line flush",
));
self.register(ISelPattern::new(
"clwb",
ISelPatternNode::MemRef,
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CLWB)
.with_cost(3)
.with_latency(3),
70,
"CLWB — cache line write back",
));
self.register(ISelPattern::new(
"rdtsc",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::RDTSC)
.with_cost(15)
.with_latency(15),
60,
"RDTSC — read time-stamp counter",
));
self.register(ISelPattern::new(
"rdtscp",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::RDTSCP)
.with_cost(20)
.with_latency(20),
60,
"RDTSCP — read time-stamp counter and processor ID",
));
self.register(ISelPattern::new(
"rdpmc",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::RDPMC)
.with_cost(15)
.with_latency(15),
60,
"RDPMC — read performance monitoring counter",
));
self.register(ISelPattern::new(
"cpuid",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::CPUID)
.with_cost(100)
.with_latency(100),
50,
"CPUID — CPU identification",
));
self.register(ISelPattern::new(
"pause",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::PAUSE)
.with_cost(140)
.with_latency(140),
50,
"PAUSE — spin loop hint",
));
self.register(ISelPattern::new(
"hlt",
ISelPatternNode::Op(IROpcodeKind::Unreachable),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::HLT)
.with_cost(1)
.with_latency(1),
50,
"HLT — halt",
));
self.register(ISelPattern::new(
"endbr64",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::All(vec![
ISelPatternCondition::Is64Bit,
ISelPatternCondition::HasFeature("cet".into()),
]),
ISelPatternResult::new(DeepX86Opcode::ENDBR64)
.with_cost(1)
.with_feature("cet")
.with_latency(1),
70,
"ENDBR64 — end branch 64 (CET)",
));
self.register(ISelPattern::new(
"endbr32",
ISelPatternNode::Op(IROpcodeKind::Freeze),
ISelPatternCondition::All(vec![
ISelPatternCondition::Is32Bit,
ISelPatternCondition::HasFeature("cet".into()),
]),
ISelPatternResult::new(DeepX86Opcode::ENDBR32)
.with_cost(1)
.with_feature("cet")
.with_latency(1),
70,
"ENDBR32 — end branch 32 (CET)",
));
self.register(ISelPattern::new(
"hreset",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::HasFeature("hreset".into()),
ISelPatternResult::new(DeepX86Opcode::HRESET)
.with_cost(100)
.with_feature("hreset")
.with_latency(100),
60,
"HRESET — history reset",
));
self.register(ISelPattern::new(
"xgetbv",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::XGETBV)
.with_cost(10)
.with_latency(10),
60,
"XGETBV — get value of extended control register",
));
self.register(ISelPattern::new(
"xsetbv",
ISelPatternNode::Op(IROpcodeKind::Call),
ISelPatternCondition::Always,
ISelPatternResult::new(DeepX86Opcode::XSETBV)
.with_cost(10)
.with_latency(10),
60,
"XSETBV — set value of extended control register",
));
}
pub fn build_extended(&mut self) {
self.build_extended_integer_patterns();
self.build_extended_vector_patterns();
self.build_system_patterns();
}
pub fn len(&self) -> usize {
self.patterns.len()
}
pub fn is_empty(&self) -> bool {
self.patterns.is_empty()
}
pub fn patterns_for(&self, opcode_name: &str) -> Vec<&ISelPattern> {
match self.by_opcode.get(opcode_name) {
Some(indices) => indices.iter().map(|&i| &self.patterns[i]).collect(),
None => Vec::new(),
}
}
}
fn match_pred_to_cmov(pred: CmpPredicate) -> DeepX86Opcode {
match pred {
CmpPredicate::EQ => DeepX86Opcode::CMOVE,
CmpPredicate::NE => DeepX86Opcode::CMOVNE,
CmpPredicate::UGT => DeepX86Opcode::CMOVA,
CmpPredicate::UGE => DeepX86Opcode::CMOVAE,
CmpPredicate::ULT => DeepX86Opcode::CMOVB,
CmpPredicate::ULE => DeepX86Opcode::CMOVBE,
CmpPredicate::SGT => DeepX86Opcode::CMOVG,
CmpPredicate::SGE => DeepX86Opcode::CMOVGE,
CmpPredicate::SLT => DeepX86Opcode::CMOVL,
CmpPredicate::SLE => DeepX86Opcode::CMOVLE,
_ => DeepX86Opcode::CMOVNE, }
}
#[derive(Debug, Clone)]
pub struct MatchContext {
pub is_64bit: bool,
pub features: HashSet<String>,
pub type_name: Option<String>,
pub operand_size: Option<u8>,
}
impl MatchContext {
pub fn new(is_64bit: bool, features: HashSet<String>) -> Self {
Self {
is_64bit,
features,
type_name: None,
operand_size: None,
}
}
pub fn with_type(mut self, type_name: &str) -> Self {
self.type_name = Some(type_name.to_string());
self
}
pub fn with_operand_size(mut self, size: u8) -> Self {
self.operand_size = Some(size);
self
}
}
#[derive(Debug, Clone)]
struct MatchNode {
ir_opcode: Option<IROpcodeKind>,
children: Vec<MatchNode>,
matched_pattern: Option<usize>,
cost: u32,
selected_opcode: Option<DeepX86Opcode>,
}
impl MatchNode {
fn new() -> Self {
Self {
ir_opcode: None,
children: Vec::new(),
matched_pattern: None,
cost: 0,
selected_opcode: None,
}
}
fn with_opcode(op: IROpcodeKind) -> Self {
Self {
ir_opcode: Some(op),
children: Vec::new(),
matched_pattern: None,
cost: u32::MAX,
selected_opcode: None,
}
}
}
pub struct PatternMatcher {
database: PatternDatabase,
context: MatchContext,
pub stats: MatcherStats,
}
#[derive(Debug, Default, Clone)]
pub struct MatcherStats {
pub nodes_matched: usize,
pub nodes_failed: usize,
pub nodes_multiple: usize,
pub avg_cost: f64,
pub patterns_considered: usize,
pub priority_wins: usize,
}
#[derive(Debug, Clone)]
pub struct MatchResult {
pub pattern_index: usize,
pub pattern_name: String,
pub opcode: DeepX86Opcode,
pub all_opcodes: Vec<DeepX86Opcode>,
pub cost: u32,
pub operand_mapping: Vec<OperandMapEntry>,
pub subtree_cost: u32,
}
impl PatternMatcher {
pub fn new(database: PatternDatabase, context: MatchContext) -> Self {
Self {
database,
context,
stats: MatcherStats::default(),
}
}
pub fn match_node(
&self,
ir_opcode: &IROpcodeKind,
_children_count: usize,
) -> Vec<MatchResult> {
let opcode_name = ir_opcode.as_str();
let patterns = self.database.patterns_for(opcode_name);
let mut results = Vec::new();
for pattern in &patterns {
let condition_holds = pattern.condition.evaluate(
self.context.is_64bit,
&self.context.features,
self.context.operand_size,
None,
self.context.type_name.as_deref(),
);
if !condition_holds {
continue;
}
results.push(MatchResult {
pattern_index: 0, pattern_name: pattern.name.clone(),
opcode: pattern.result.opcodes.first().copied().unwrap_or(
DeepX86Opcode::NOP,
),
all_opcodes: pattern.result.opcodes.clone(),
cost: pattern.result.cost,
operand_mapping: pattern.result.operand_mapping.clone(),
subtree_cost: pattern.result.cost,
});
}
results.sort_by(|a, b| {
let a_pat = &self.database.patterns[a.pattern_index];
let b_pat = &self.database.patterns[b.pattern_index];
b_pat
.priority
.cmp(&a_pat.priority)
.then_with(|| a.cost.cmp(&b.cost))
});
results
}
pub fn tree_cover(
&mut self,
ir_opcode: &IROpcodeKind,
child_matches: &[Vec<MatchResult>],
) -> Option<MatchResult> {
let candidates = self.match_node(ir_opcode, child_matches.len());
let num_candidates = candidates.len();
if candidates.is_empty() {
self.stats.nodes_failed += 1;
return None;
}
if candidates.len() > 1 {
self.stats.nodes_multiple += 1;
}
let mut best: Option<MatchResult> = None;
for mut candidate in candidates {
let mut total_cost = candidate.cost;
for child_results in child_matches {
if let Some(best_child) = child_results.first() {
total_cost += best_child.subtree_cost;
}
}
candidate.subtree_cost = total_cost;
if best.is_none() || total_cost < best.as_ref().unwrap().subtree_cost {
best = Some(candidate);
} else if best.is_some() {
self.stats.priority_wins += 1;
}
}
if let Some(ref result) = best {
self.stats.nodes_matched += 1;
self.stats.patterns_considered += num_candidates;
let old_total = self.stats.avg_cost * (self.stats.nodes_matched as f64 - 1.0);
self.stats.avg_cost =
(old_total + result.subtree_cost as f64) / self.stats.nodes_matched as f64;
} else {
self.stats.nodes_failed += 1;
}
best
}
pub fn match_cmp_predicate(&self, pred: &CmpPredicate) -> Option<DeepX86CondCode> {
pred.to_icmp_cc()
}
pub fn match_jcc(&self, pred: &CmpPredicate) -> Option<DeepX86Opcode> {
match pred {
CmpPredicate::EQ => Some(DeepX86Opcode::JE),
CmpPredicate::NE => Some(DeepX86Opcode::JNE),
CmpPredicate::UGT => Some(DeepX86Opcode::JA),
CmpPredicate::UGE => Some(DeepX86Opcode::JAE),
CmpPredicate::ULT => Some(DeepX86Opcode::JB),
CmpPredicate::ULE => Some(DeepX86Opcode::JBE),
CmpPredicate::SGT => Some(DeepX86Opcode::JG),
CmpPredicate::SGE => Some(DeepX86Opcode::JGE),
CmpPredicate::SLT => Some(DeepX86Opcode::JL),
CmpPredicate::SLE => Some(DeepX86Opcode::JLE),
_ => None,
}
}
pub fn immediate_fits_bits(&self, value: i64, bits: u32) -> bool {
let max = (1i64 << (bits - 1)) - 1;
let min = -(1i64 << (bits - 1));
value >= min && value <= max
}
pub fn is_power_of_two(&self, value: i64) -> bool {
value > 0 && (value & (value - 1)) == 0
}
pub fn select_lea_pattern(
&self,
base: Option<DeepX86Reg>,
index: Option<DeepX86Reg>,
scale: u8,
disp: i32,
) -> (DeepX86Opcode, Vec<DeepX86Operand>) {
let mut operands = Vec::new();
if let Some(b) = base {
operands.push(DeepX86Operand::reg(b));
}
if let Some(idx) = index {
operands.push(DeepX86Operand::base_index_scale_disp(
base.unwrap_or(DeepX86Reg::RSP),
idx,
scale,
disp,
8,
));
} else if disp != 0 {
operands.push(DeepX86Operand::imm(disp as i64));
}
(DeepX86Opcode::LEA, operands)
}
pub fn database(&self) -> &PatternDatabase {
&self.database
}
}
pub struct OperandMapper {
pub vreg_to_phys: HashMap<u32, DeepX86Reg>,
pub used_regs: HashSet<DeepX86Reg>,
pub vreg_to_stack: HashMap<u32, i32>,
pub next_stack_offset: i32,
pub is_64bit: bool,
}
impl OperandMapper {
pub fn new(is_64bit: bool) -> Self {
Self {
vreg_to_phys: HashMap::new(),
used_regs: HashSet::new(),
vreg_to_stack: HashMap::new(),
next_stack_offset: -8, is_64bit,
}
}
pub fn assign_reg(&mut self, vreg: u32, preferred: Option<DeepX86Reg>) -> DeepX86Reg {
if let Some(&phys) = self.vreg_to_phys.get(&vreg) {
return phys;
}
let reg = if let Some(pref) = preferred {
if !self.used_regs.contains(&pref) {
pref
} else {
self.find_free_gpr()
}
} else {
self.find_free_gpr()
};
self.vreg_to_phys.insert(vreg, reg);
self.used_regs.insert(reg);
reg
}
fn find_free_gpr(&self) -> DeepX86Reg {
let caller_saved = if self.is_64bit {
vec![
DeepX86Reg::RAX,
DeepX86Reg::RCX,
DeepX86Reg::RDX,
DeepX86Reg::RSI,
DeepX86Reg::RDI,
DeepX86Reg::R8,
DeepX86Reg::R9,
DeepX86Reg::R10,
DeepX86Reg::R11,
]
} else {
vec![
DeepX86Reg::EAX,
DeepX86Reg::ECX,
DeepX86Reg::EDX,
DeepX86Reg::ESI,
DeepX86Reg::EDI,
]
};
for ® in &caller_saved {
if !self.used_regs.contains(®) {
return reg;
}
}
let callee_saved = if self.is_64bit {
vec![
DeepX86Reg::RBX,
DeepX86Reg::R12,
DeepX86Reg::R13,
DeepX86Reg::R14,
DeepX86Reg::R15,
]
} else {
vec![DeepX86Reg::EBX, DeepX86Reg::ESI, DeepX86Reg::EDI]
};
for ® in &callee_saved {
if !self.used_regs.contains(®) {
return reg;
}
}
DeepX86Reg::RAX }
pub fn find_free_xmm(&self) -> DeepX86Reg {
let xmm_regs = [
DeepX86Reg::XMM0,
DeepX86Reg::XMM1,
DeepX86Reg::XMM2,
DeepX86Reg::XMM3,
DeepX86Reg::XMM4,
DeepX86Reg::XMM5,
DeepX86Reg::XMM6,
DeepX86Reg::XMM7,
DeepX86Reg::XMM8,
DeepX86Reg::XMM9,
DeepX86Reg::XMM10,
DeepX86Reg::XMM11,
DeepX86Reg::XMM12,
DeepX86Reg::XMM13,
DeepX86Reg::XMM14,
DeepX86Reg::XMM15,
];
for ® in &xmm_regs {
if !self.used_regs.contains(®) {
return reg;
}
}
DeepX86Reg::XMM0 }
pub fn ensure_reg(&mut self, vreg: u32, preferred: Option<DeepX86Reg>) -> (DeepX86Reg, bool) {
if let Some(&phys) = self.vreg_to_phys.get(&vreg) {
(phys, false) } else if let Some(&stack_off) = self.vreg_to_stack.get(&vreg) {
let reg = self.assign_reg(vreg, preferred);
(reg, true) } else {
let reg = self.assign_reg(vreg, preferred);
(reg, false)
}
}
pub fn spill_to_stack(&mut self, vreg: u32) -> i32 {
if let Some(&offset) = self.vreg_to_stack.get(&vreg) {
return offset;
}
let slot_size: i32 = if self.is_64bit { 8 } else { 4 };
let offset = self.next_stack_offset;
self.next_stack_offset -= slot_size;
self.vreg_to_stack.insert(vreg, offset);
if let Some(&phys) = self.vreg_to_phys.get(&vreg) {
self.used_regs.remove(&phys);
self.vreg_to_phys.remove(&vreg);
}
offset
}
pub fn get_phys_reg(&self, vreg: u32) -> Option<DeepX86Reg> {
self.vreg_to_phys.get(&vreg).copied()
}
pub fn get_stack_offset(&self, vreg: u32) -> Option<i32> {
self.vreg_to_stack.get(&vreg).copied()
}
pub fn stack_slot_size(&self) -> i32 {
if self.is_64bit {
8
} else {
4
}
}
pub fn spill_mem_operand(&self, vreg: u32) -> Option<DeepX86Operand> {
let offset = self.get_stack_offset(vreg)?;
Some(DeepX86Operand::base_disp(
if self.is_64bit {
DeepX86Reg::RBP
} else {
DeepX86Reg::EBP
},
offset,
if self.is_64bit { 8 } else { 4 },
))
}
pub fn free_reg(&mut self, reg: DeepX86Reg) {
self.used_regs.remove(®);
}
pub fn reset(&mut self) {
self.vreg_to_phys.clear();
self.used_regs.clear();
self.vreg_to_stack.clear();
self.next_stack_offset = -8;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArgClass {
NoClass,
Integer,
SSE,
SSEUp,
X87,
X87Up,
ComplexX87,
Memory,
}
impl ArgClass {
pub fn as_str(&self) -> &'static str {
match self {
Self::NoClass => "NOCLASS",
Self::Integer => "INTEGER",
Self::SSE => "SSE",
Self::SSEUp => "SSEUP",
Self::X87 => "X87",
Self::X87Up => "X87UP",
Self::ComplexX87 => "COMPLEX_X87",
Self::Memory => "MEMORY",
}
}
}
#[derive(Debug, Clone)]
pub struct ArgInfo {
pub class: Vec<ArgClass>,
pub num_eightbytes: usize,
pub in_regs: bool,
pub on_stack: bool,
pub stack_offset: i32,
pub reg_assignments: Vec<Option<DeepX86Reg>>,
}
impl ArgInfo {
pub fn new() -> Self {
Self {
class: Vec::new(),
num_eightbytes: 0,
in_regs: false,
on_stack: false,
stack_offset: 0,
reg_assignments: Vec::new(),
}
}
}
pub struct CallingConventionISel {
pub is_64bit: bool,
next_int_reg: usize,
next_sse_reg: usize,
next_stack_offset: i32,
max_stack_offset: i32,
has_stack_args: bool,
return_info: ArgInfo,
}
impl CallingConventionISel {
pub const INT_ARG_REGS_64: [DeepX86Reg; 6] = [
DeepX86Reg::RDI,
DeepX86Reg::RSI,
DeepX86Reg::RDX,
DeepX86Reg::RCX,
DeepX86Reg::R8,
DeepX86Reg::R9,
];
pub const SSE_ARG_REGS: [DeepX86Reg; 8] = [
DeepX86Reg::XMM0,
DeepX86Reg::XMM1,
DeepX86Reg::XMM2,
DeepX86Reg::XMM3,
DeepX86Reg::XMM4,
DeepX86Reg::XMM5,
DeepX86Reg::XMM6,
DeepX86Reg::XMM7,
];
pub const FASTCALL_INT_REGS_32: [DeepX86Reg; 2] = [DeepX86Reg::ECX, DeepX86Reg::EDX];
pub fn new(is_64bit: bool) -> Self {
Self {
is_64bit,
next_int_reg: 0,
next_sse_reg: 0,
next_stack_offset: 0,
max_stack_offset: 0,
has_stack_args: false,
return_info: ArgInfo::new(),
}
}
pub fn reset(&mut self) {
self.next_int_reg = 0;
self.next_sse_reg = 0;
self.next_stack_offset = 0;
self.max_stack_offset = 0;
self.has_stack_args = false;
self.return_info = ArgInfo::new();
}
pub fn classify_arg(&mut self, type_name: &str, size_bytes: usize) -> ArgInfo {
let mut info = ArgInfo::new();
match type_name {
"i8" | "i16" | "i32" | "i64" | "ptr" => {
info.class = vec![ArgClass::Integer];
info.num_eightbytes = 1;
}
"f32" | "f64" => {
info.class = vec![ArgClass::SSE];
info.num_eightbytes = 1;
}
"v4f32" | "v2f64" | "v16i8" | "v8i16" | "v4i32" | "v2i64" => {
info.class = vec![ArgClass::SSE];
info.num_eightbytes = 2;
}
"v8f32" | "v4f64" | "v32i8" | "v16i16" | "v8i32" | "v4i64" => {
info.class = vec![ArgClass::SSE, ArgClass::SSE, ArgClass::SSE, ArgClass::SSE];
info.num_eightbytes = 4;
}
_ if size_bytes > 16 => {
info.class = vec![ArgClass::Memory];
info.num_eightbytes = 0;
}
_ => {
let num_eightbytes = (size_bytes + 7) / 8;
info.num_eightbytes = num_eightbytes;
for _ in 0..num_eightbytes {
info.class.push(ArgClass::Integer);
}
}
}
info.reg_assignments = Vec::new();
let mut needs_stack = false;
for cls in &info.class {
match cls {
ArgClass::Integer => {
if self.is_64bit && self.next_int_reg < Self::INT_ARG_REGS_64.len() {
let reg = Self::INT_ARG_REGS_64[self.next_int_reg];
info.reg_assignments.push(Some(reg));
self.next_int_reg += 1;
} else if !self.is_64bit
&& self.next_int_reg < Self::FASTCALL_INT_REGS_32.len()
{
let reg = Self::FASTCALL_INT_REGS_32[self.next_int_reg];
info.reg_assignments.push(Some(reg));
self.next_int_reg += 1;
} else {
needs_stack = true;
info.reg_assignments.push(None);
}
}
ArgClass::SSE => {
if self.next_sse_reg < Self::SSE_ARG_REGS.len() {
let reg = Self::SSE_ARG_REGS[self.next_sse_reg];
info.reg_assignments.push(Some(reg));
self.next_sse_reg += 1;
} else {
needs_stack = true;
info.reg_assignments.push(None);
}
}
ArgClass::Memory => {
needs_stack = true;
info.reg_assignments.push(None);
}
_ => {
info.reg_assignments.push(None);
}
}
}
if needs_stack {
info.on_stack = true;
info.stack_offset = self.next_stack_offset;
let arg_size_aligned = ((size_bytes as i32) + 7) / 8 * 8;
self.next_stack_offset += arg_size_aligned;
self.max_stack_offset = self.max_stack_offset.max(self.next_stack_offset);
self.has_stack_args = true;
} else {
info.in_regs = true;
}
info
}
pub fn classify_return(&mut self, type_name: &str, size_bytes: usize) -> ArgInfo {
let mut info = ArgInfo::new();
match type_name {
"i8" | "i16" | "i32" | "i64" | "ptr" => {
info.class = vec![ArgClass::Integer];
info.num_eightbytes = 1;
info.in_regs = true;
info.reg_assignments = vec![Some(DeepX86Reg::RAX)];
}
"f32" | "f64" => {
info.class = vec![ArgClass::SSE];
info.num_eightbytes = 1;
info.in_regs = true;
info.reg_assignments = vec![Some(DeepX86Reg::XMM0)];
}
"v4f32" | "v2f64" | "v16i8" | "v8i16" | "v4i32" | "v2i64" => {
info.class = vec![ArgClass::SSE];
info.num_eightbytes = 2;
info.in_regs = true;
info.reg_assignments = vec![Some(DeepX86Reg::XMM0)];
}
_ if size_bytes == 0 => {
info.class = vec![ArgClass::NoClass];
info.num_eightbytes = 0;
info.in_regs = true;
info.reg_assignments = vec![];
}
_ if size_bytes <= 16 && size_bytes > 8 => {
info.class = vec![ArgClass::Integer, ArgClass::Integer];
info.num_eightbytes = 2;
info.in_regs = true;
info.reg_assignments =
vec![Some(DeepX86Reg::RAX), Some(DeepX86Reg::RDX)];
}
_ if size_bytes <= 8 && size_bytes > 0 => {
info.class = vec![ArgClass::Integer];
info.num_eightbytes = 1;
info.in_regs = true;
info.reg_assignments = vec![Some(DeepX86Reg::RAX)];
}
_ => {
info.class = vec![ArgClass::Memory];
info.num_eightbytes = 0;
info.in_regs = false;
info.on_stack = true;
info.reg_assignments = vec![Some(DeepX86Reg::RDI)];
}
}
self.return_info = info.clone();
info
}
pub fn needs_sret(&self) -> bool {
self.return_info.class.contains(&ArgClass::Memory)
}
pub fn demote_sret(&mut self, ret_size_bytes: usize) -> ArgInfo {
let mut info = ArgInfo::new();
info.class = vec![ArgClass::Integer]; info.num_eightbytes = 1;
info.in_regs = true;
info.reg_assignments = vec![Some(DeepX86Reg::RDI)];
info.on_stack = true;
info.stack_offset = 0;
info
}
pub fn callee_saved_regs(&self) -> Vec<DeepX86Reg> {
if self.is_64bit {
vec![
DeepX86Reg::RBX,
DeepX86Reg::RBP,
DeepX86Reg::R12,
DeepX86Reg::R13,
DeepX86Reg::R14,
DeepX86Reg::R15,
]
} else {
vec![
DeepX86Reg::EBX,
DeepX86Reg::EBP,
DeepX86Reg::ESI,
DeepX86Reg::EDI,
]
}
}
pub fn caller_saved_regs(&self) -> Vec<DeepX86Reg> {
if self.is_64bit {
vec![
DeepX86Reg::RAX,
DeepX86Reg::RCX,
DeepX86Reg::RDX,
DeepX86Reg::RSI,
DeepX86Reg::RDI,
DeepX86Reg::R8,
DeepX86Reg::R9,
DeepX86Reg::R10,
DeepX86Reg::R11,
]
} else {
vec![DeepX86Reg::EAX, DeepX86Reg::ECX, DeepX86Reg::EDX]
}
}
pub fn stack_alignment(&self) -> u32 {
if self.is_64bit {
16
} else {
4
}
}
pub fn stack_slot_size(&self) -> u32 {
if self.is_64bit {
8
} else {
4
}
}
pub fn total_stack_args_space(&self) -> i32 {
let raw = self.max_stack_offset;
let align = self.stack_alignment() as i32;
((raw + align - 1) / align) * align
}
pub fn return_reg(&self) -> Option<DeepX86Reg> {
self.return_info
.reg_assignments
.first()
.and_then(|r| *r)
}
pub fn return_xmm_reg(&self) -> Option<DeepX86Reg> {
if self.return_info.class.first() == Some(&ArgClass::SSE) {
Some(DeepX86Reg::XMM0)
} else {
None
}
}
}
pub struct ISelDriver {
pub matcher: PatternMatcher,
pub operand_mapper: OperandMapper,
pub calling_conv: CallingConventionISel,
pub instructions: Vec<X86MachineInstruction>,
pub current_bb_label: Option<String>,
pub driver_stats: ISelDriverStats,
}
#[derive(Debug, Clone, PartialEq)]
pub struct X86MachineInstruction {
pub opcode: DeepX86Opcode,
pub operands: Vec<DeepX86Operand>,
pub comment: Option<String>,
pub is_terminator: bool,
pub is_branch: bool,
pub branch_target: Option<String>,
}
impl X86MachineInstruction {
pub fn new(opcode: DeepX86Opcode) -> Self {
Self {
opcode,
operands: Vec::new(),
comment: None,
is_terminator: false,
is_branch: false,
branch_target: None,
}
}
pub fn with_operands(mut self, ops: Vec<DeepX86Operand>) -> Self {
self.operands = ops;
self
}
pub fn with_comment(mut self, comment: &str) -> Self {
self.comment = Some(comment.to_string());
self
}
pub fn as_terminator(mut self) -> Self {
self.is_terminator = true;
self
}
pub fn as_branch(mut self, target: &str) -> Self {
self.is_branch = true;
self.is_terminator = true;
self.branch_target = Some(target.to_string());
self
}
pub fn to_asm(&self) -> String {
let mut s = String::new();
if let Some(ref comment) = self.comment {
s.push_str(&format!("# {}\n", comment));
}
s.push_str(&format!(" {}", self.opcode.mnemonic()));
if !self.operands.is_empty() {
let ops: Vec<String> = self.operands.iter().map(|o| format!("{}", o)).collect();
s.push_str(&format!(" {}", ops.join(", ")));
}
if let Some(ref target) = self.branch_target {
s.push_str(&format!(" {}", target));
}
s
}
}
#[derive(Debug, Default, Clone)]
pub struct ISelDriverStats {
pub instructions_emitted: usize,
pub basic_blocks_processed: usize,
pub spills_inserted: usize,
pub reloads_inserted: usize,
pub tail_calls_optimized: usize,
pub lea_optimizations: usize,
}
impl ISelDriver {
pub fn new(
database: PatternDatabase,
context: MatchContext,
is_64bit: bool,
) -> Self {
let matcher = PatternMatcher::new(database, context);
let operand_mapper = OperandMapper::new(is_64bit);
let calling_conv = CallingConventionISel::new(is_64bit);
Self {
matcher,
operand_mapper,
calling_conv,
instructions: Vec::new(),
current_bb_label: None,
driver_stats: ISelDriverStats::default(),
}
}
pub fn begin_basic_block(&mut self, label: &str) {
self.current_bb_label = Some(label.to_string());
self.instructions.push(
X86MachineInstruction::new(DeepX86Opcode::NOP)
.with_comment(&format!("BB: {}", label)),
);
}
pub fn select_add(
&mut self,
lhs_vreg: u32,
rhs_vreg: u32,
result_vreg: u32,
is_64bit: bool,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
let (lhs_reg, lhs_reload) = self.operand_mapper.ensure_reg(
lhs_vreg,
None,
);
let (rhs_reg, rhs_reload) = self.operand_mapper.ensure_reg(
rhs_vreg,
None,
);
let (result_reg, _) = self.operand_mapper.ensure_reg(
result_vreg,
if is_64bit { Some(DeepX86Reg::RAX) } else { Some(DeepX86Reg::EAX) },
);
if lhs_reload {
instrs.push(self.emit_reload(lhs_vreg, lhs_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
if rhs_reload {
instrs.push(self.emit_reload(rhs_vreg, rhs_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(result_reg),
DeepX86Operand::reg(lhs_reg),
])
.with_comment("copy lhs to result"),
);
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::ADD)
.with_operands(vec![
DeepX86Operand::reg(result_reg),
DeepX86Operand::reg(rhs_reg),
])
.with_comment("add rhs"),
);
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
pub fn select_load(
&mut self,
addr_vreg: u32,
result_vreg: u32,
size_bytes: u8,
is_64bit: bool,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
let (addr_reg, addr_reload) = self.operand_mapper.ensure_reg(
addr_vreg,
None,
);
let (result_reg, _) = self.operand_mapper.ensure_reg(
result_vreg,
None,
);
if addr_reload {
instrs.push(self.emit_reload(addr_vreg, addr_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(result_reg),
DeepX86Operand::base_disp(addr_reg, 0, size_bytes),
])
.with_comment(&format!("load {} bytes", size_bytes)),
);
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
pub fn select_store(
&mut self,
value_vreg: u32,
addr_vreg: u32,
size_bytes: u8,
is_64bit: bool,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
let (val_reg, val_reload) = self.operand_mapper.ensure_reg(
value_vreg,
None,
);
let (addr_reg, addr_reload) = self.operand_mapper.ensure_reg(
addr_vreg,
None,
);
if val_reload {
instrs.push(self.emit_reload(value_vreg, val_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
if addr_reload {
instrs.push(self.emit_reload(addr_vreg, addr_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::base_disp(addr_reg, 0, size_bytes),
DeepX86Operand::reg(val_reg),
])
.with_comment(&format!("store {} bytes", size_bytes)),
);
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
pub fn select_call(
&mut self,
target: &str,
arg_vregs: &[u32],
arg_types: &[(&str, usize)],
ret_vreg: Option<u32>,
ret_type: Option<(&str, usize)>,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
self.calling_conv.reset();
let mut arg_moves = Vec::new();
for (i, (&vreg, &(type_name, size))) in
arg_vregs.iter().zip(arg_types.iter()).enumerate()
{
let arg_info = self.calling_conv.classify_arg(type_name, size);
let (phys_reg, needs_reload) = self.operand_mapper.ensure_reg(
vreg,
arg_info.reg_assignments.first().and_then(|r| *r),
);
if needs_reload {
instrs.push(self.emit_reload(
vreg,
phys_reg,
self.calling_conv.is_64bit,
));
self.driver_stats.reloads_inserted += 1;
}
if let Some(target_reg) =
arg_info.reg_assignments.first().and_then(|r| *r)
{
if phys_reg != target_reg {
arg_moves.push((phys_reg, target_reg));
}
} else if arg_info.on_stack {
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::PUSH)
.with_operands(vec![DeepX86Operand::reg(phys_reg)])
.with_comment(&format!("push arg {}", i)),
);
}
}
for (src, dst) in &arg_moves {
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(*dst),
DeepX86Operand::reg(*src),
])
.with_comment("move arg to correct register"),
);
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::CALL)
.with_operands(vec![DeepX86Operand::Symbol(target.to_string())])
.with_comment(&format!("call {}", target)),
);
if let (Some(ret_vreg), Some((ret_type_name, _))) = (ret_vreg, ret_type) {
let ret_info = self.calling_conv.classify_return(ret_type_name, 8);
if let Some(ret_reg) =
ret_info.reg_assignments.first().and_then(|r| *r)
{
let (dest_reg, _) = self.operand_mapper.ensure_reg(ret_vreg, None);
if dest_reg != ret_reg {
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(dest_reg),
DeepX86Operand::reg(ret_reg),
])
.with_comment("move return value"),
);
}
}
}
if ret_vreg.is_none() {
self.driver_stats.tail_calls_optimized += 1;
}
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
pub fn select_return(
&mut self,
ret_vreg: Option<u32>,
ret_type: Option<&str>,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
if let (Some(vreg), Some(type_name)) = (ret_vreg, ret_type) {
let ret_info = self.calling_conv.classify_return(type_name, 8);
if let Some(ret_reg) =
ret_info.reg_assignments.first().and_then(|r| *r)
{
let (src_reg, needs_reload) =
self.operand_mapper.ensure_reg(vreg, Some(ret_reg));
if needs_reload {
instrs.push(self.emit_reload(
vreg,
src_reg,
self.calling_conv.is_64bit,
));
self.driver_stats.reloads_inserted += 1;
}
if src_reg != ret_reg {
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(ret_reg),
DeepX86Operand::reg(src_reg),
])
.with_comment("move to return register"),
);
}
}
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::RET)
.as_terminator()
.with_comment("return"),
);
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
pub fn select_conditional_branch(
&mut self,
cond_vreg: u32,
true_target: &str,
false_target: &str,
pred: CmpPredicate,
is_64bit: bool,
) -> Vec<X86MachineInstruction> {
let mut instrs = Vec::new();
let (cond_reg, cond_reload) =
self.operand_mapper.ensure_reg(cond_vreg, None);
if cond_reload {
instrs.push(self.emit_reload(cond_vreg, cond_reg, is_64bit));
self.driver_stats.reloads_inserted += 1;
}
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::TEST)
.with_operands(vec![
DeepX86Operand::reg(cond_reg),
DeepX86Operand::reg(cond_reg),
])
.with_comment("test condition"),
);
let jcc_op = match pred {
CmpPredicate::NE => DeepX86Opcode::JNE,
CmpPredicate::EQ => DeepX86Opcode::JE,
CmpPredicate::UGT => DeepX86Opcode::JA,
CmpPredicate::UGE => DeepX86Opcode::JAE,
CmpPredicate::ULT => DeepX86Opcode::JB,
CmpPredicate::ULE => DeepX86Opcode::JBE,
CmpPredicate::SGT => DeepX86Opcode::JG,
CmpPredicate::SGE => DeepX86Opcode::JGE,
CmpPredicate::SLT => DeepX86Opcode::JL,
CmpPredicate::SLE => DeepX86Opcode::JLE,
_ => DeepX86Opcode::JNE,
};
instrs.push(
X86MachineInstruction::new(jcc_op)
.as_branch(true_target)
.with_comment(&format!("branch if condition to {}", true_target)),
);
instrs.push(
X86MachineInstruction::new(DeepX86Opcode::JMP)
.as_branch(false_target)
.with_comment(&format!("unconditional branch to {}", false_target)),
);
self.driver_stats.instructions_emitted += instrs.len();
self.instructions.extend(instrs.clone());
instrs
}
fn emit_reload(
&self,
vreg: u32,
target_reg: DeepX86Reg,
is_64bit: bool,
) -> X86MachineInstruction {
let offset = self.operand_mapper.get_stack_offset(vreg).unwrap_or(-8);
let base = if is_64bit {
DeepX86Reg::RBP
} else {
DeepX86Reg::EBP
};
let size = if is_64bit { 8u8 } else { 4u8 };
X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::reg(target_reg),
DeepX86Operand::base_disp(base, offset, size),
])
.with_comment(&format!("reload vreg{} from stack", vreg))
}
pub fn spill_register(
&mut self,
vreg: u32,
) -> Option<X86MachineInstruction> {
let phys_reg = self.operand_mapper.get_phys_reg(vreg)?;
let offset = self.operand_mapper.spill_to_stack(vreg);
let base = if self.calling_conv.is_64bit {
DeepX86Reg::RBP
} else {
DeepX86Reg::EBP
};
let size = self.operand_mapper.stack_slot_size() as u8;
let instr = X86MachineInstruction::new(DeepX86Opcode::MOV)
.with_operands(vec![
DeepX86Operand::base_disp(base, offset, size),
DeepX86Operand::reg(phys_reg),
])
.with_comment(&format!("spill vreg{} to stack", vreg));
self.driver_stats.spills_inserted += 1;
self.instructions.push(instr.clone());
Some(instr)
}
pub fn get_instructions(&self) -> &[X86MachineInstruction] {
&self.instructions
}
pub fn render_assembly(&self) -> String {
let mut output = String::new();
for instr in &self.instructions {
output.push_str(&instr.to_asm());
output.push('\n');
}
output
}
pub fn reset(&mut self) {
self.instructions.clear();
self.operand_mapper.reset();
self.calling_conv.reset();
self.current_bb_label = None;
self.driver_stats = ISelDriverStats::default();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_64bit_features() -> HashSet<String> {
let mut features = HashSet::new();
features.insert("sse".into());
features.insert("sse2".into());
features.insert("sse3".into());
features.insert("ssse3".into());
features.insert("sse4.1".into());
features.insert("sse4.2".into());
features.insert("avx".into());
features.insert("avx2".into());
features.insert("fma".into());
features.insert("bmi1".into());
features.insert("bmi2".into());
features.insert("popcnt".into());
features.insert("lzcnt".into());
features
}
fn build_test_database(is_64bit: bool) -> PatternDatabase {
let features = make_64bit_features();
let mut db = PatternDatabase::new(is_64bit, features);
db.build();
db
}
#[test]
fn test_pattern_database_has_500_plus_patterns() {
let db = build_test_database(true);
assert!(
db.len() >= 500,
"Pattern database should have at least 500 patterns, got {}",
db.len()
);
}
#[test]
fn test_pattern_database_not_empty() {
let db = build_test_database(true);
assert!(!db.is_empty());
assert!(db.len() > 0);
}
#[test]
fn test_add_patterns_exist() {
let db = build_test_database(true);
let add_pats = db.patterns_for("add");
assert!(!add_pats.is_empty(), "ADD patterns should exist");
assert!(add_pats.len() >= 4, "Expected at least 4 ADD patterns");
}
#[test]
fn test_sub_patterns_exist() {
let db = build_test_database(true);
let sub_pats = db.patterns_for("sub");
assert!(!sub_pats.is_empty(), "SUB patterns should exist");
}
#[test]
fn test_mul_patterns_exist() {
let db = build_test_database(true);
let mul_pats = db.patterns_for("mul");
assert!(!mul_pats.is_empty(), "MUL patterns should exist");
}
#[test]
fn test_and_patterns_exist() {
let db = build_test_database(true);
let and_pats = db.patterns_for("and");
assert!(!and_pats.is_empty(), "AND patterns should exist");
}
#[test]
fn test_or_patterns_exist() {
let db = build_test_database(true);
let or_pats = db.patterns_for("or");
assert!(!or_pats.is_empty(), "OR patterns should exist");
}
#[test]
fn test_xor_patterns_exist() {
let db = build_test_database(true);
let xor_pats = db.patterns_for("xor");
assert!(!xor_pats.is_empty(), "XOR patterns should exist");
}
#[test]
fn test_shl_patterns_exist() {
let db = build_test_database(true);
let shl_pats = db.patterns_for("shl");
assert!(!shl_pats.is_empty(), "SHL patterns should exist");
}
#[test]
fn test_lshr_patterns_exist() {
let db = build_test_database(true);
let lshr_pats = db.patterns_for("lshr");
assert!(!lshr_pats.is_empty(), "LSHR patterns should exist");
}
#[test]
fn test_fadd_patterns_exist() {
let db = build_test_database(true);
let fadd_pats = db.patterns_for("fadd");
assert!(!fadd_pats.is_empty(), "FADD patterns should exist");
}
#[test]
fn test_fsub_patterns_exist() {
let db = build_test_database(true);
let fsub_pats = db.patterns_for("fsub");
assert!(!fsub_pats.is_empty(), "FSUB patterns should exist");
}
#[test]
fn test_fmul_patterns_exist() {
let db = build_test_database(true);
let fmul_pats = db.patterns_for("fmul");
assert!(!fmul_pats.is_empty(), "FMUL patterns should exist");
}
#[test]
fn test_fdiv_patterns_exist() {
let db = build_test_database(true);
let fdiv_pats = db.patterns_for("fdiv");
assert!(!fdiv_pats.is_empty(), "FDIV patterns should exist");
}
#[test]
fn test_trunc_patterns_exist() {
let db = build_test_database(true);
let trunc_pats = db.patterns_for("trunc");
assert!(!trunc_pats.is_empty(), "Trunc patterns should exist");
}
#[test]
fn test_zext_patterns_exist() {
let db = build_test_database(true);
let zext_pats = db.patterns_for("zext");
assert!(!zext_pats.is_empty(), "ZExt patterns should exist");
}
#[test]
fn test_sext_patterns_exist() {
let db = build_test_database(true);
let sext_pats = db.patterns_for("sext");
assert!(!sext_pats.is_empty(), "SExt patterns should exist");
}
#[test]
fn test_load_patterns_exist() {
let db = build_test_database(true);
let load_pats = db.patterns_for("load");
assert!(!load_pats.is_empty(), "Load patterns should exist");
assert!(load_pats.len() >= 3, "Expected at least 3 load patterns");
}
#[test]
fn test_store_patterns_exist() {
let db = build_test_database(true);
let store_pats = db.patterns_for("store");
assert!(!store_pats.is_empty(), "Store patterns should exist");
}
#[test]
fn test_ret_patterns_exist() {
let db = build_test_database(true);
let ret_pats = db.patterns_for("ret");
assert!(!ret_pats.is_empty(), "Ret patterns should exist");
assert!(ret_pats.len() >= 2, "Expected at least 2 ret patterns");
}
#[test]
fn test_br_patterns_exist() {
let db = build_test_database(true);
let br_pats = db.patterns_for("br");
assert!(!br_pats.is_empty(), "Br patterns should exist");
}
#[test]
fn test_icmp_patterns_exist() {
let db = build_test_database(true);
let icmp_pats = db.patterns_for("icmp");
assert!(!icmp_pats.is_empty(), "ICmp patterns should exist");
assert!(icmp_pats.len() >= 10, "Expected at least 10 ICmp patterns");
}
#[test]
fn test_fcmp_patterns_exist() {
let db = build_test_database(true);
let fcmp_pats = db.patterns_for("fcmp");
assert!(!fcmp_pats.is_empty(), "FCmp patterns should exist");
}
#[test]
fn test_call_patterns_exist() {
let db = build_test_database(true);
let call_pats = db.patterns_for("call");
assert!(!call_pats.is_empty(), "Call patterns should exist");
}
#[test]
fn test_select_patterns_exist() {
let db = build_test_database(true);
let select_pats = db.patterns_for("select");
assert!(!select_pats.is_empty(), "Select patterns should exist");
}
#[test]
fn test_gep_patterns_exist() {
let db = build_test_database(true);
let gep_pats = db.patterns_for("getelementptr");
assert!(!gep_pats.is_empty(), "GEP patterns should exist");
}
#[test]
fn test_phi_patterns_exist() {
let db = build_test_database(true);
let phi_pats = db.patterns_for("phi");
assert!(!phi_pats.is_empty(), "Phi patterns should exist");
}
#[test]
fn test_shufflevector_patterns_exist() {
let db = build_test_database(true);
let sv_pats = db.patterns_for("shufflevector");
assert!(!sv_pats.is_empty(), "ShuffleVector patterns should exist");
assert!(sv_pats.len() >= 6, "Expected at least 6 shuffle patterns");
}
#[test]
fn test_atomicrmw_patterns_exist() {
let db = build_test_database(true);
let atm_pats = db.patterns_for("atomicrmw");
assert!(!atm_pats.is_empty(), "AtomicRMW patterns should exist");
}
#[test]
fn test_cmpxchg_patterns_exist() {
let db = build_test_database(true);
let cmpx_pats = db.patterns_for("cmpxchg");
assert!(!cmpx_pats.is_empty(), "CmpXchg patterns should exist");
}
#[test]
fn test_matcher_matches_add() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features())
.with_type("i32");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Add, 2);
assert!(!results.is_empty(), "Matcher should find ADD patterns");
let first = &results[0];
assert_eq!(first.opcode, DeepX86Opcode::ADD);
}
#[test]
fn test_matcher_matches_sub() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features())
.with_type("i64");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Sub, 2);
assert!(!results.is_empty(), "Matcher should find SUB patterns");
let first = &results[0];
assert_eq!(first.opcode, DeepX86Opcode::SUB);
}
#[test]
fn test_cmp_predicate_to_icmp_cc() {
assert_eq!(CmpPredicate::EQ.to_icmp_cc(), Some(DeepX86CondCode::E));
assert_eq!(CmpPredicate::NE.to_icmp_cc(), Some(DeepX86CondCode::NE));
assert_eq!(CmpPredicate::UGT.to_icmp_cc(), Some(DeepX86CondCode::A));
assert_eq!(CmpPredicate::UGE.to_icmp_cc(), Some(DeepX86CondCode::AE));
assert_eq!(CmpPredicate::ULT.to_icmp_cc(), Some(DeepX86CondCode::B));
assert_eq!(CmpPredicate::ULE.to_icmp_cc(), Some(DeepX86CondCode::BE));
assert_eq!(CmpPredicate::SGT.to_icmp_cc(), Some(DeepX86CondCode::G));
assert_eq!(CmpPredicate::SGE.to_icmp_cc(), Some(DeepX86CondCode::GE));
assert_eq!(CmpPredicate::SLT.to_icmp_cc(), Some(DeepX86CondCode::L));
assert_eq!(CmpPredicate::SLE.to_icmp_cc(), Some(DeepX86CondCode::LE));
assert_eq!(CmpPredicate::OEQ.to_icmp_cc(), None);
}
#[test]
fn test_cond_code_invert() {
assert_eq!(DeepX86CondCode::E.invert(), DeepX86CondCode::NE);
assert_eq!(DeepX86CondCode::NE.invert(), DeepX86CondCode::E);
assert_eq!(DeepX86CondCode::B.invert(), DeepX86CondCode::AE);
assert_eq!(DeepX86CondCode::A.invert(), DeepX86CondCode::BE);
assert_eq!(DeepX86CondCode::L.invert(), DeepX86CondCode::GE);
assert_eq!(DeepX86CondCode::G.invert(), DeepX86CondCode::LE);
assert_eq!(DeepX86CondCode::O.invert(), DeepX86CondCode::NO);
assert_eq!(DeepX86CondCode::P.invert(), DeepX86CondCode::NP);
}
#[test]
fn test_operand_mapper_assign_reg() {
let mut mapper = OperandMapper::new(true);
let reg = mapper.assign_reg(1, None);
assert_eq!(reg, DeepX86Reg::RAX, "First free GPR should be RAX");
let reg2 = mapper.assign_reg(2, None);
assert_eq!(reg2, DeepX86Reg::RCX, "Second free GPR should be RCX");
}
#[test]
fn test_operand_mapper_spill() {
let mut mapper = OperandMapper::new(true);
let reg = mapper.assign_reg(1, None);
assert_eq!(reg, DeepX86Reg::RAX);
let offset = mapper.spill_to_stack(1);
assert_eq!(offset, -8, "First spill should be at -8");
assert_eq!(mapper.get_phys_reg(1), None);
let (reloaded_reg, needs_reload) = mapper.ensure_reg(1, None);
assert!(needs_reload, "Should indicate reload is needed after spill");
assert_eq!(reloaded_reg, DeepX86Reg::RAX, "Should get a register back");
}
#[test]
fn test_calling_convention_classify_int_arg() {
let mut cc = CallingConventionISel::new(true);
let info = cc.classify_arg("i64", 8);
assert_eq!(info.class[0], ArgClass::Integer);
assert!(info.reg_assignments[0].is_some());
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::RDI));
}
#[test]
fn test_calling_convention_classify_float_arg() {
let mut cc = CallingConventionISel::new(true);
let info = cc.classify_arg("f64", 8);
assert_eq!(info.class[0], ArgClass::SSE);
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::XMM0));
}
#[test]
fn test_calling_convention_return_int() {
let mut cc = CallingConventionISel::new(true);
let info = cc.classify_return("i64", 8);
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::RAX));
}
#[test]
fn test_calling_convention_return_float() {
let mut cc = CallingConventionISel::new(true);
let info = cc.classify_return("f64", 8);
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::XMM0));
}
#[test]
fn test_calling_convention_sret_demotion() {
let mut cc = CallingConventionISel::new(true);
cc.classify_return("struct_large", 32);
assert!(cc.needs_sret());
let info = cc.demote_sret(32);
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::RDI));
}
#[test]
fn test_opcode_mnemonics() {
assert_eq!(DeepX86Opcode::ADD.mnemonic(), "add");
assert_eq!(DeepX86Opcode::SUB.mnemonic(), "sub");
assert_eq!(DeepX86Opcode::MOV.mnemonic(), "mov");
assert_eq!(DeepX86Opcode::JMP.mnemonic(), "jmp");
assert_eq!(DeepX86Opcode::CALL.mnemonic(), "call");
assert_eq!(DeepX86Opcode::RET.mnemonic(), "ret");
assert_eq!(DeepX86Opcode::ADDSS.mnemonic(), "addss");
assert_eq!(DeepX86Opcode::VMULPS.mnemonic(), "vmulps");
assert_eq!(DeepX86Opcode::VFMADD132PD.mnemonic(), "vfmadd132pd");
}
#[test]
fn test_reg_is_gpr() {
assert!(DeepX86Reg::RAX.is_gpr());
assert!(DeepX86Reg::R8.is_gpr());
assert!(DeepX86Reg::EAX.is_gpr());
assert!(DeepX86Reg::AX.is_gpr());
assert!(DeepX86Reg::AL.is_gpr());
assert!(!DeepX86Reg::XMM0.is_gpr());
assert!(!DeepX86Reg::K0.is_gpr());
}
#[test]
fn test_reg_is_simd() {
assert!(DeepX86Reg::XMM0.is_simd());
assert!(DeepX86Reg::YMM15.is_simd());
assert!(DeepX86Reg::ZMM31.is_simd());
assert!(!DeepX86Reg::RAX.is_simd());
}
#[test]
fn test_reg_callee_saved() {
assert!(DeepX86Reg::RBX.is_callee_saved());
assert!(DeepX86Reg::RBP.is_callee_saved());
assert!(DeepX86Reg::R12.is_callee_saved());
assert!(!DeepX86Reg::RAX.is_callee_saved() || DeepX86Reg::RAX.is_caller_saved());
}
#[test]
fn test_operand_display_reg() {
let op = DeepX86Operand::reg(DeepX86Reg::RAX);
assert_eq!(format!("{}", op), "%rax");
}
#[test]
fn test_operand_display_imm() {
let op = DeepX86Operand::imm(42);
assert_eq!(format!("{}", op), "$42");
}
#[test]
fn test_operand_is_methods() {
let reg_op = DeepX86Operand::reg(DeepX86Reg::RCX);
assert!(reg_op.is_reg());
assert!(!reg_op.is_imm());
assert!(!reg_op.is_mem());
let imm_op = DeepX86Operand::imm(100);
assert!(imm_op.is_imm());
assert!(!imm_op.is_reg());
let mem_op = DeepX86Operand::base_disp(DeepX86Reg::RSP, 8, 4);
assert!(mem_op.is_mem());
assert!(!mem_op.is_reg());
}
#[test]
fn test_condition_always() {
assert!(ISelPatternCondition::Always.evaluate(true, &HashSet::new(), None, None, None));
}
#[test]
fn test_condition_is_64bit() {
assert!(ISelPatternCondition::Is64Bit.evaluate(true, &HashSet::new(), None, None, None));
assert!(!ISelPatternCondition::Is64Bit.evaluate(false, &HashSet::new(), None, None, None));
}
#[test]
fn test_condition_is_32bit() {
assert!(!ISelPatternCondition::Is32Bit.evaluate(true, &HashSet::new(), None, None, None));
assert!(ISelPatternCondition::Is32Bit.evaluate(false, &HashSet::new(), None, None, None));
}
#[test]
fn test_condition_imm_fits() {
let cond = ISelPatternCondition::ImmFits(8);
assert!(cond.evaluate(true, &HashSet::new(), None, Some(100), None));
assert!(cond.evaluate(true, &HashSet::new(), None, Some(-128), None));
assert!(!cond.evaluate(true, &HashSet::new(), None, Some(128), None));
assert!(!cond.evaluate(true, &HashSet::new(), None, None, None));
}
#[test]
fn test_condition_is_power_of_two() {
assert!(ISelPatternCondition::IsPowerOfTwo.evaluate(true, &HashSet::new(), None, Some(8), None));
assert!(ISelPatternCondition::IsPowerOfTwo.evaluate(true, &HashSet::new(), None, Some(1), None));
assert!(!ISelPatternCondition::IsPowerOfTwo.evaluate(true, &HashSet::new(), None, Some(3), None));
assert!(!ISelPatternCondition::IsPowerOfTwo.evaluate(true, &HashSet::new(), None, Some(0), None));
}
#[test]
fn test_condition_has_feature() {
let mut features = HashSet::new();
features.insert("avx".into());
assert!(ISelPatternCondition::HasFeature("avx".into()).evaluate(true, &features, None, None, None));
assert!(!ISelPatternCondition::HasFeature("avx512f".into()).evaluate(true, &features, None, None, None));
}
#[test]
fn test_fma_patterns_exist_with_feature() {
let mut features = make_64bit_features();
let db = PatternDatabase::new(true, features.clone());
let mut fma_db = PatternDatabase::new(true, features.clone());
fma_db.build_floating_point_patterns();
let fadd_pats = fma_db.patterns_for("fadd");
let has_fma = fadd_pats.iter().any(|p| p.name.contains("fma"));
assert!(has_fma, "FMA patterns should exist in fadd patterns");
}
#[test]
fn test_calling_convention_multiple_args() {
let mut cc = CallingConventionISel::new(true);
let info1 = cc.classify_arg("i64", 8);
assert_eq!(info1.reg_assignments[0], Some(DeepX86Reg::RDI));
let info2 = cc.classify_arg("i64", 8);
assert_eq!(info2.reg_assignments[0], Some(DeepX86Reg::RSI));
let info3 = cc.classify_arg("i32", 4);
assert_eq!(info3.reg_assignments[0], Some(DeepX86Reg::RDX));
let info4 = cc.classify_arg("i64", 8);
assert_eq!(info4.reg_assignments[0], Some(DeepX86Reg::RCX));
let info5 = cc.classify_arg("i64", 8);
assert_eq!(info5.reg_assignments[0], Some(DeepX86Reg::R8));
let info6 = cc.classify_arg("i64", 8);
assert_eq!(info6.reg_assignments[0], Some(DeepX86Reg::R9));
let info7 = cc.classify_arg("i64", 8);
assert!(info7.on_stack);
assert!(info7.reg_assignments[0].is_none());
}
#[test]
fn test_callee_saved_regs_64() {
let cc = CallingConventionISel::new(true);
let saved = cc.callee_saved_regs();
assert!(saved.contains(&DeepX86Reg::RBX));
assert!(saved.contains(&DeepX86Reg::RBP));
assert!(saved.contains(&DeepX86Reg::R12));
assert!(saved.contains(&DeepX86Reg::R13));
assert!(saved.contains(&DeepX86Reg::R14));
assert!(saved.contains(&DeepX86Reg::R15));
assert!(!saved.contains(&DeepX86Reg::RAX));
assert!(!saved.contains(&DeepX86Reg::RCX));
assert_eq!(saved.len(), 6);
}
#[test]
fn test_cmp_predicate_swap_operands() {
assert_eq!(CmpPredicate::UGT.swap_operands(), CmpPredicate::ULT);
assert_eq!(CmpPredicate::ULT.swap_operands(), CmpPredicate::UGT);
assert_eq!(CmpPredicate::SGT.swap_operands(), CmpPredicate::SLT);
assert_eq!(CmpPredicate::SLT.swap_operands(), CmpPredicate::SGT);
assert_eq!(CmpPredicate::EQ.swap_operands(), CmpPredicate::EQ);
assert_eq!(CmpPredicate::NE.swap_operands(), CmpPredicate::NE);
}
#[test]
fn test_isel_pattern_tree_construction() {
let tree = ISelPatternNode::tree(
IROpcodeKind::Add,
vec![ISelPatternNode::Reg, ISelPatternNode::ConstInt(42)],
);
assert!(matches!(tree, ISelPatternNode::Tree(..)));
let not_pattern = ISelPatternNode::not(ISelPatternNode::Reg);
assert!(matches!(not_pattern, ISelPatternNode::Not(..)));
}
#[test]
fn test_matcher_tree_cover() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features())
.with_type("i32");
let mut matcher = PatternMatcher::new(db, ctx);
let child_matches = vec![
vec![],
vec![],
];
let result = matcher.tree_cover(&IROpcodeKind::Add, &child_matches);
assert!(result.is_some(), "Tree cover should find a match for ADD");
let result = result.unwrap();
assert_eq!(result.opcode, DeepX86Opcode::ADD);
}
#[test]
fn test_matcher_stats() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features())
.with_type("i64");
let mut matcher = PatternMatcher::new(db, ctx);
let child_matches = vec![];
let _ = matcher.tree_cover(&IROpcodeKind::Add, &child_matches);
assert!(matcher.stats.nodes_matched > 0);
}
#[test]
fn test_immediate_fits_bits() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, HashSet::new());
let matcher = PatternMatcher::new(db, ctx);
assert!(matcher.immediate_fits_bits(127, 8));
assert!(!matcher.immediate_fits_bits(128, 8));
assert!(matcher.immediate_fits_bits(-128, 8));
assert!(matcher.immediate_fits_bits(0x7FFF_FFFF, 32));
assert!(!matcher.immediate_fits_bits(0x8000_0000, 32));
}
#[test]
fn test_is_power_of_two() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, HashSet::new());
let matcher = PatternMatcher::new(db, ctx);
assert!(matcher.is_power_of_two(1));
assert!(matcher.is_power_of_two(2));
assert!(matcher.is_power_of_two(4));
assert!(matcher.is_power_of_two(64));
assert!(!matcher.is_power_of_two(0));
assert!(!matcher.is_power_of_two(3));
assert!(!matcher.is_power_of_two(63));
}
#[test]
fn test_calling_convention_stack_alignment() {
let cc64 = CallingConventionISel::new(true);
assert_eq!(cc64.stack_alignment(), 16);
let cc32 = CallingConventionISel::new(false);
assert_eq!(cc32.stack_alignment(), 4);
}
#[test]
fn test_calling_convention_stack_slot_size() {
let cc64 = CallingConventionISel::new(true);
assert_eq!(cc64.stack_slot_size(), 8);
let cc32 = CallingConventionISel::new(false);
assert_eq!(cc32.stack_slot_size(), 4);
}
#[test]
fn test_operand_mapper_find_free_xmm() {
let mut mapper = OperandMapper::new(true);
let xmm = mapper.find_free_xmm();
assert_eq!(xmm, DeepX86Reg::XMM0);
mapper.used_regs.insert(DeepX86Reg::XMM0);
let xmm2 = mapper.find_free_xmm();
assert_eq!(xmm2, DeepX86Reg::XMM1);
}
#[test]
fn test_operand_mapper_stack_ops() {
let mapper = OperandMapper::new(true);
assert_eq!(mapper.stack_slot_size(), 8);
}
#[test]
fn test_cmp_predicate_properties() {
assert!(CmpPredicate::SGT.is_signed_int());
assert!(CmpPredicate::SGE.is_signed_int());
assert!(CmpPredicate::SLT.is_signed_int());
assert!(CmpPredicate::SLE.is_signed_int());
assert!(!CmpPredicate::UGT.is_signed_int());
assert!(!CmpPredicate::EQ.is_signed_int());
assert!(CmpPredicate::UGT.is_unsigned_int());
assert!(CmpPredicate::EQ.is_unsigned_int());
assert!(!CmpPredicate::SGT.is_unsigned_int());
}
#[test]
fn test_cond_code_swap_unsigned() {
assert_eq!(DeepX86CondCode::B.swap_unsigned(), DeepX86CondCode::A);
assert_eq!(DeepX86CondCode::A.swap_unsigned(), DeepX86CondCode::B);
assert_eq!(DeepX86CondCode::BE.swap_unsigned(), DeepX86CondCode::AE);
assert_eq!(DeepX86CondCode::AE.swap_unsigned(), DeepX86CondCode::BE);
assert_eq!(DeepX86CondCode::E.swap_unsigned(), DeepX86CondCode::E);
}
#[test]
fn test_cond_code_swap_signed() {
assert_eq!(DeepX86CondCode::L.swap_signed(), DeepX86CondCode::G);
assert_eq!(DeepX86CondCode::G.swap_signed(), DeepX86CondCode::L);
assert_eq!(DeepX86CondCode::LE.swap_signed(), DeepX86CondCode::GE);
assert_eq!(DeepX86CondCode::GE.swap_signed(), DeepX86CondCode::LE);
}
#[test]
fn test_ir_opcode_classification() {
assert!(IROpcodeKind::Add.is_binary());
assert!(IROpcodeKind::Sub.is_binary());
assert!(IROpcodeKind::FAdd.is_floating_arith());
assert!(IROpcodeKind::FSub.is_floating_arith());
assert!(!IROpcodeKind::Add.is_floating_arith());
assert!(IROpcodeKind::And.is_bitwise());
assert!(IROpcodeKind::Or.is_bitwise());
assert!(IROpcodeKind::Xor.is_bitwise());
assert!(!IROpcodeKind::Add.is_bitwise());
assert!(IROpcodeKind::Trunc.is_cast());
assert!(IROpcodeKind::ZExt.is_cast());
assert!(IROpcodeKind::SExt.is_cast());
assert!(!IROpcodeKind::Add.is_cast());
assert!(IROpcodeKind::Load.is_memory());
assert!(IROpcodeKind::Store.is_memory());
assert!(IROpcodeKind::Ret.is_control_flow());
assert!(IROpcodeKind::Br.is_control_flow());
assert!(!IROpcodeKind::Add.is_memory());
assert!(!IROpcodeKind::Add.is_control_flow());
}
#[test]
fn test_matcher_matches_fadd() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features())
.with_type("f32");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::FAdd, 2);
assert!(!results.is_empty(), "FADD should match patterns");
let has_addss = results.iter().any(|r| r.opcode == DeepX86Opcode::ADDSS);
assert!(has_addss, "Should match ADDSS for f32");
}
#[test]
fn test_calling_convention_reset() {
let mut cc = CallingConventionISel::new(true);
cc.classify_arg("i64", 8);
cc.classify_arg("i64", 8);
assert_eq!(cc.next_int_reg, 2);
cc.reset();
assert_eq!(cc.next_int_reg, 0);
assert_eq!(cc.next_sse_reg, 0);
assert_eq!(cc.next_stack_offset, 0);
}
#[test]
fn test_opcode_is_floating_point() {
assert!(DeepX86Opcode::ADDSS.is_floating_point());
assert!(DeepX86Opcode::MULSD.is_floating_point());
assert!(!DeepX86Opcode::ADD.is_floating_point());
assert!(!DeepX86Opcode::MOV.is_floating_point());
}
#[test]
fn test_opcode_is_cmov() {
assert!(DeepX86Opcode::CMOVE.is_cmov());
assert!(DeepX86Opcode::CMOVNE.is_cmov());
assert!(DeepX86Opcode::CMOVG.is_cmov());
assert!(!DeepX86Opcode::SETE.is_cmov());
assert!(!DeepX86Opcode::JE.is_cmov());
}
#[test]
fn test_opcode_is_setcc() {
assert!(DeepX86Opcode::SETE.is_setcc());
assert!(DeepX86Opcode::SETNE.is_setcc());
assert!(!DeepX86Opcode::CMOVE.is_setcc());
}
#[test]
fn test_opcode_is_jcc() {
assert!(DeepX86Opcode::JE.is_jcc());
assert!(DeepX86Opcode::JNE.is_jcc());
assert!(!DeepX86Opcode::JMP.is_jcc());
}
#[test]
fn test_opcode_is_atomic() {
assert!(DeepX86Opcode::LOCK_ADD.is_atomic());
assert!(DeepX86Opcode::CMPXCHG.is_atomic());
assert!(!DeepX86Opcode::ADD.is_atomic());
}
#[test]
fn test_operand_memory_creation() {
let mem = DeepX86Operand::base_disp(DeepX86Reg::RSP, -8, 8);
assert!(mem.is_mem());
let mem2 = DeepX86Operand::base_index_scale_disp(
DeepX86Reg::RAX,
DeepX86Reg::RCX,
4,
16,
4,
);
assert!(mem2.is_mem());
let rip = DeepX86Operand::rip_mem(0x1000, 4);
assert!(rip.is_mem());
}
#[test]
fn test_pattern_condition_valid_shift_amt() {
assert!(ISelPatternCondition::ValidShiftAmt(32).evaluate(
true,
&HashSet::new(),
None,
Some(5),
None
));
assert!(!ISelPatternCondition::ValidShiftAmt(32).evaluate(
true,
&HashSet::new(),
None,
Some(32),
None
));
}
#[test]
fn test_reg_dwarf_numbers() {
assert_eq!(DeepX86Reg::RAX.dwarf_num(), 0);
assert_eq!(DeepX86Reg::RDX.dwarf_num(), 1);
assert_eq!(DeepX86Reg::RCX.dwarf_num(), 2);
assert_eq!(DeepX86Reg::XMM0.dwarf_num(), 17);
assert_eq!(DeepX86Reg::XMM15.dwarf_num(), 32);
assert_eq!(DeepX86Reg::RIP.dwarf_num(), 16);
}
#[test]
fn test_arg_info_defaults() {
let info = ArgInfo::new();
assert!(info.class.is_empty());
assert_eq!(info.num_eightbytes, 0);
assert!(!info.in_regs);
assert!(!info.on_stack);
}
#[test]
fn test_calling_convention_total_stack_space() {
let mut cc = CallingConventionISel::new(true);
for _ in 0..10 {
cc.classify_arg("i64", 8);
}
let space = cc.total_stack_args_space();
assert!(space >= 32, "Stack space should be at least 32 bytes");
}
#[test]
fn test_matcher_matches_select_icmp() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, make_64bit_features());
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Select, 3);
assert!(!results.is_empty(), "Select should have patterns");
}
#[test]
fn test_extended_integer_patterns() {
let db = build_test_database(true);
let add_pats = db.patterns_for("add");
let has_add_zero = add_pats.iter().any(|p| p.name.contains("add_zero"));
assert!(has_add_zero, "Should have ADD x, 0 -> MOV patterns");
let sub_pats = db.patterns_for("sub");
let has_sub_self = sub_pats.iter().any(|p| p.name.contains("sub_self"));
assert!(has_sub_self, "Should have SUB x, x -> XOR patterns");
}
#[test]
fn test_div_pow2_patterns() {
let db = build_test_database(true);
let udiv_pats = db.patterns_for("udiv");
let has_shr = udiv_pats.iter().any(|p| p.name.contains("pow2"));
assert!(has_shr, "Should have UDIV by pow2 -> SHR patterns");
let urem_pats = db.patterns_for("urem");
let has_and = urem_pats.iter().any(|p| p.name.contains("pow2"));
assert!(has_and, "Should have UREM by pow2 -> AND patterns");
}
#[test]
fn test_pmovsx_pmovzx_patterns() {
let db = build_test_database(true);
let sext_pats = db.patterns_for("sext");
let has_pmovsx = sext_pats.iter().any(|p| p.name.contains("pmovsx"));
assert!(has_pmovsx, "Should have PMOVSX patterns");
let zext_pats = db.patterns_for("zext");
let has_pmovzx = zext_pats.iter().any(|p| p.name.contains("pmovzx"));
assert!(has_pmovzx, "Should have PMOVZX patterns");
}
#[test]
fn test_system_patterns() {
let db = build_test_database(true);
let any_pats = db.patterns_for("any");
let has_prefetch = db.patterns.iter().any(|p| {
p.name.contains("prefetch") || p.name.contains("clflush")
|| p.name.contains("rdtsc") || p.name.contains("cpuid")
});
assert!(has_prefetch, "Should have system instruction patterns");
}
#[test]
fn test_avx2_variable_shift_patterns() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_vector_patterns();
let shl_pats = db.patterns_for("shl");
let has_vpsllv = shl_pats.iter().any(|p| p.name.contains("vpsllv"));
assert!(has_vpsllv, "Should have VPSLLV patterns");
}
#[test]
fn test_vgather_patterns() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_vector_patterns();
let load_pats = db.patterns_for("load");
let has_gather = load_pats.iter().any(|p| p.name.contains("vgather"));
assert!(has_gather, "Should have VGATHER patterns");
}
#[test]
fn test_add_negative_to_sub() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_integer_patterns();
let add_pats = db.patterns_for("add");
let has_neg = add_pats.iter().any(|p| p.name == "add_neg_to_sub");
assert!(has_neg, "Should have ADD x, -N -> SUB x, N pattern");
let sub_pats = db.patterns_for("sub");
let has_neg_sub = sub_pats.iter().any(|p| p.name == "sub_neg_to_add");
assert!(has_neg_sub, "Should have SUB x, -N -> ADD x, N pattern");
}
#[test]
fn test_crc32_patterns() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_vector_patterns();
let xor_pats = db.patterns_for("xor");
let has_crc32 = xor_pats.iter().any(|p| p.name.contains("crc32"));
assert!(has_crc32, "Should have CRC32 patterns");
}
#[test]
fn test_ptest_pattern() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_vector_patterns();
let icmp_pats = db.patterns_for("icmp");
let has_ptest = icmp_pats.iter().any(|p| p.name == "ptest");
assert!(has_ptest, "Should have PTEST pattern");
}
#[test]
fn test_sse3_movdup_patterns() {
let db = build_test_database(true);
let sv_pats = db.patterns_for("shufflevector");
let has_movddup = sv_pats.iter().any(|p| p.name == "movddup");
let has_movsldup = sv_pats.iter().any(|p| p.name == "movsldup");
let has_movshdup = sv_pats.iter().any(|p| p.name == "movshdup");
assert!(has_movddup, "Should have MOVDDUP pattern");
assert!(has_movsldup, "Should have MOVSLDUP pattern");
assert!(has_movshdup, "Should have MOVSHDUP pattern");
}
#[test]
fn test_sse41_extended_patterns() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_vector_patterns();
let has_phmin = db.patterns.iter().any(|p| p.name == "phminposuw");
let has_mpsadbw = db.patterns.iter().any(|p| p.name == "mpsadbw");
assert!(has_phmin, "Should have PHMINPOSUW pattern");
assert!(has_mpsadbw, "Should have MPSADBW pattern");
}
#[test]
fn test_opcode_num_operands() {
assert_eq!(DeepX86Opcode::NOP.num_operands(), 0);
assert_eq!(DeepX86Opcode::RET.num_operands(), 0);
assert_eq!(DeepX86Opcode::PUSH.num_operands(), 1);
assert_eq!(DeepX86Opcode::POP.num_operands(), 1);
assert_eq!(DeepX86Opcode::INC.num_operands(), 1);
assert_eq!(DeepX86Opcode::ADD.num_operands(), 2);
assert_eq!(DeepX86Opcode::MOV.num_operands(), 2);
assert_eq!(DeepX86Opcode::CMP.num_operands(), 2);
assert_eq!(DeepX86Opcode::SHUFPS.num_operands(), 3);
assert_eq!(DeepX86Opcode::BLENDPS.num_operands(), 3);
assert_eq!(DeepX86Opcode::SHL.num_operands(), 2);
assert_eq!(DeepX86Opcode::IMUL.num_operands(), 1); assert_eq!(DeepX86Opcode::SETO.num_operands(), 1);
assert_eq!(DeepX86Opcode::VFMADD132PS.num_operands(), 3);
assert_eq!(DeepX86Opcode::VADDPD.num_operands(), 3);
}
#[test]
fn test_memory_operand_display() {
let mem1 = DeepX86Operand::base_disp(DeepX86Reg::RSP, -8, 8);
let s1 = format!("{}", mem1);
assert!(s1.contains("rsp") && s1.contains("-8"));
let mem2 = DeepX86Operand::base_index_scale_disp(
DeepX86Reg::RAX, DeepX86Reg::RCX, 4, 16, 4,
);
let s2 = format!("{}", mem2);
assert!(s2.contains("rax") && s2.contains("rcx") && s2.contains("*4"));
let rip = DeepX86Operand::rip_mem(0x1000, 4);
let s3 = format!("{}", rip);
assert!(s3.contains("rip") && s3.contains("4096"));
}
#[test]
fn test_calling_convention_overflow_to_stack() {
let mut cc = CallingConventionISel::new(true);
for i in 0..6 {
let info = cc.classify_arg("i64", 8);
if i < 6 {
assert!(info.in_regs, "Arg {} should be in register", i);
}
}
let info_stack = cc.classify_arg("i64", 8);
assert!(info_stack.on_stack, "7th arg should be on stack");
assert_eq!(info_stack.stack_offset, 0);
}
#[test]
fn test_calling_convention_sse_overflow() {
let mut cc = CallingConventionISel::new(true);
for i in 0..8 {
let info = cc.classify_arg("f64", 8);
assert_eq!(info.reg_assignments[0], Some(DeepX86Reg::XMM0));
}
}
#[test]
fn test_operand_mapper_multiple_regs() {
let mut mapper = OperandMapper::new(true);
let regs: Vec<DeepX86Reg> = (0..8)
.map(|vreg| mapper.assign_reg(vreg, None))
.collect();
let unique: HashSet<_> = regs.iter().collect();
assert_eq!(unique.len(), 8, "Should have 8 unique registers");
assert!(regs[0].is_caller_saved() || regs[0] == DeepX86Reg::RAX);
}
#[test]
fn test_operand_mapper_ensure_after_spill() {
let mut mapper = OperandMapper::new(true);
mapper.assign_reg(1, None);
let offset = mapper.spill_to_stack(1);
assert_eq!(offset, -8);
let (reg, needs_reload) = mapper.ensure_reg(1, None);
assert!(needs_reload, "Should signal reload needed");
assert_eq!(reg, DeepX86Reg::RAX);
}
#[test]
fn test_matcher_32bit_mode() {
let db = build_test_database(false);
let ctx = MatchContext::new(false, make_64bit_features())
.with_type("i32");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Add, 2);
assert!(!results.is_empty(), "Should match ADD in 32-bit mode");
assert_eq!(results[0].opcode, DeepX86Opcode::ADD);
}
#[test]
fn test_pattern_matching_feature_check() {
let mut features = HashSet::new();
features.insert("sse".into());
features.insert("sse2".into());
let db = PatternDatabase::new(true, features);
let ctx = MatchContext::new(true, HashSet::new())
.with_type("v4f32");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Select, 3);
let has_blend = results.iter().any(|r| r.opcode == DeepX86Opcode::BLENDPS);
assert!(!has_blend, "BLENDPS should not match without sse4.1 feature");
}
#[test]
fn test_immediate_fits_bits_edge_cases() {
let db = build_test_database(true);
let ctx = MatchContext::new(true, HashSet::new());
let matcher = PatternMatcher::new(db, ctx);
assert!(matcher.immediate_fits_bits(0, 8));
assert!(matcher.immediate_fits_bits(127, 8));
assert!(matcher.immediate_fits_bits(-128, 8));
assert!(!matcher.immediate_fits_bits(128, 8));
assert!(!matcher.immediate_fits_bits(-129, 8));
assert!(matcher.immediate_fits_bits(32767, 16));
assert!(!matcher.immediate_fits_bits(32768, 16));
assert!(matcher.immediate_fits_bits(0x7FFFFFFF, 32));
assert!(!matcher.immediate_fits_bits(0x80000000u32 as i64, 32));
}
#[test]
fn test_cond_code_suffix_string() {
assert_eq!(DeepX86CondCode::E.suffix(), "e");
assert_eq!(DeepX86CondCode::NE.suffix(), "ne");
assert_eq!(DeepX86CondCode::B.suffix(), "b");
assert_eq!(DeepX86CondCode::AE.suffix(), "ae");
assert_eq!(DeepX86CondCode::A.suffix(), "a");
assert_eq!(DeepX86CondCode::BE.suffix(), "be");
assert_eq!(DeepX86CondCode::G.suffix(), "g");
assert_eq!(DeepX86CondCode::GE.suffix(), "ge");
assert_eq!(DeepX86CondCode::L.suffix(), "l");
assert_eq!(DeepX86CondCode::LE.suffix(), "le");
assert_eq!(DeepX86CondCode::O.suffix(), "o");
assert_eq!(DeepX86CondCode::NO.suffix(), "no");
assert_eq!(DeepX86CondCode::P.suffix(), "p");
assert_eq!(DeepX86CondCode::NP.suffix(), "np");
assert_eq!(DeepX86CondCode::S.suffix(), "s");
assert_eq!(DeepX86CondCode::NS.suffix(), "ns");
}
#[test]
fn test_add_sub_neg_canonicalization() {
let features = make_64bit_features();
let mut db = PatternDatabase::new(true, features.clone());
db.build_extended_integer_patterns();
let add_pats = db.patterns_for("add");
let has_canon = add_pats.iter().any(|p| p.name == "add_neg_to_sub");
assert!(has_canon);
let sub_pats = db.patterns_for("sub");
let has_canon2 = sub_pats.iter().any(|p| p.name == "sub_neg_to_add");
assert!(has_canon2);
}
#[test]
fn test_isel_pattern_node_helpers() {
let tree = ISelPatternNode::tree(IROpcodeKind::Add, vec![
ISelPatternNode::Reg,
ISelPatternNode::ConstInt(1),
]);
assert!(matches!(tree, ISelPatternNode::Tree(..)));
let not_node = ISelPatternNode::not(ISelPatternNode::Reg);
assert!(matches!(not_node, ISelPatternNode::Not(..)));
let any = ISelPatternNode::Any;
assert!(any.is_any());
assert!(!ISelPatternNode::Reg.is_any());
}
#[test]
fn test_match_context_builder() {
let ctx = MatchContext::new(true, HashSet::new())
.with_type("i64")
.with_operand_size(8);
assert_eq!(ctx.type_name, Some("i64".to_string()));
assert_eq!(ctx.operand_size, Some(8));
assert!(ctx.is_64bit);
}
#[test]
fn test_calling_convention_return_info() {
let mut cc = CallingConventionISel::new(true);
cc.classify_return("i64", 8);
assert_eq!(cc.return_reg(), Some(DeepX86Reg::RAX));
let mut cc2 = CallingConventionISel::new(true);
cc2.classify_return("f64", 8);
assert_eq!(cc2.return_xmm_reg(), Some(DeepX86Reg::XMM0));
}
#[test]
fn test_operand_mapper_reset() {
let mut mapper = OperandMapper::new(true);
mapper.assign_reg(1, None);
mapper.assign_reg(2, None);
mapper.spill_to_stack(1);
assert!(!mapper.used_regs.is_empty());
mapper.reset();
assert!(mapper.used_regs.is_empty());
assert!(mapper.vreg_to_phys.is_empty());
assert!(mapper.vreg_to_stack.is_empty());
assert_eq!(mapper.next_stack_offset, -8);
}
#[test]
fn test_operand_symbol() {
let sym = DeepX86Operand::Symbol("printf".to_string());
assert_eq!(format!("{}", sym), "printf");
assert!(!sym.is_reg());
assert!(!sym.is_imm());
assert!(!sym.is_mem());
}
#[test]
fn test_operand_map_entry() {
let e1 = OperandMapEntry::ResultToDest;
let e2 = OperandMapEntry::SrcToOperand(0, 0);
let e3 = OperandMapEntry::FixedReg(DeepX86Reg::RAX, 0);
let e4 = OperandMapEntry::FixedImm(0, 1);
let e5 = OperandMapEntry::SrcToTwoOperands(0, 0, 1);
assert_ne!(e1, e2);
assert_ne!(e3, e4);
}
#[test]
fn test_matcher_avx512_features() {
let mut features = make_64bit_features();
features.insert("avx512f".into());
let db = PatternDatabase::new(true, features.clone());
let ctx = MatchContext::new(true, features)
.with_type("v16f32");
let matcher = PatternMatcher::new(db, ctx);
let results = matcher.match_node(&IROpcodeKind::Select, 3);
let has_blend = results.iter().any(|r| {
r.opcode == DeepX86Opcode::VBLENDMPS
|| r.opcode == DeepX86Opcode::VBLENDMPD
});
assert!(has_blend, "Should have AVX-512 blend patterns");
}
#[test]
fn test_all_opcode_families_present() {
let db = build_test_database(true);
let required_opcodes = vec![
"add", "sub", "mul", "udiv", "sdiv", "urem", "srem",
"fadd", "fsub", "fmul", "fdiv", "frem",
"shl", "lshr", "ashr",
"and", "or", "xor",
"trunc", "zext", "sext", "fptrunc", "fpext", "fptosi", "sitofp",
"load", "store", "getelementptr",
"ret", "br", "switch",
"icmp", "fcmp",
"select", "phi",
"call", "cmpxchg", "atomicrmw",
"shufflevector", "extractelement", "insertelement",
];
for op in &required_opcodes {
let pats = db.patterns_for(op);
assert!(
!pats.is_empty(),
"Opcode '{}' should have at least one pattern",
op
);
}
}
}