master
   1const Encoding = @This();
   2
   3const std = @import("std");
   4const assert = std.debug.assert;
   5const math = std.math;
   6
   7const bits = @import("bits.zig");
   8const encoder = @import("encoder.zig");
   9const Instruction = encoder.Instruction;
  10const Operand = Instruction.Operand;
  11const Prefix = Instruction.Prefix;
  12const Register = bits.Register;
  13const Rex = encoder.Rex;
  14const LegacyPrefixes = encoder.LegacyPrefixes;
  15
  16mnemonic: Mnemonic,
  17data: Data,
  18
  19const Data = struct {
  20    op_en: OpEn,
  21    ops: [4]Op,
  22    opc_len: u3,
  23    opc: [7]u8,
  24    modrm_ext: u3,
  25    mode: Mode,
  26    feature: Feature,
  27};
  28
  29pub fn findByMnemonic(
  30    prefix: Instruction.Prefix,
  31    mnemonic: Mnemonic,
  32    ops: []const Instruction.Operand,
  33    target: *const std.Target,
  34) !?Encoding {
  35    var input_ops: [4]Op = @splat(.none);
  36    for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op, target);
  37
  38    const rex_required = for (ops) |op| switch (op) {
  39        .reg => |r| switch (r) {
  40            .spl, .bpl, .sil, .dil => break true,
  41            else => {},
  42        },
  43        else => {},
  44    } else false;
  45    const rex_invalid = for (ops) |op| switch (op) {
  46        .reg => |r| switch (r) {
  47            .ah, .bh, .ch, .dh => break true,
  48            else => {},
  49        },
  50        else => {},
  51    } else false;
  52    const rex_extended = for (ops) |op| {
  53        if (op.baseExtEnc() != 0b00 or op.indexExtEnc() != 0b00) break true;
  54    } else false;
  55
  56    if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode;
  57
  58    var shortest_enc: ?Encoding = null;
  59    var shortest_len: ?usize = null;
  60    next: for (mnemonic_to_encodings_map[@intFromEnum(mnemonic)]) |data| {
  61        if (!switch (data.feature) {
  62            .none => true,
  63            .@"32bit" => switch (target.cpu.arch) {
  64                else => unreachable,
  65                .x86 => true,
  66                .x86_64 => false,
  67            },
  68            .@"64bit" => switch (target.cpu.arch) {
  69                else => unreachable,
  70                .x86 => false,
  71                .x86_64 => true,
  72            },
  73            inline .@"invpcid 32bit", .@"rdpid 32bit" => |tag| switch (target.cpu.arch) {
  74                else => unreachable,
  75                .x86 => target.cpu.has(
  76                    .x86,
  77                    @field(std.Target.x86.Feature, @tagName(tag)[0 .. @tagName(tag).len - " 32bit".len]),
  78                ),
  79                .x86_64 => false,
  80            },
  81            inline .@"invpcid 64bit", .@"rdpid 64bit", .@"prefetchi 64bit" => |tag| switch (target.cpu.arch) {
  82                else => unreachable,
  83                .x86 => false,
  84                .x86_64 => target.cpu.has(
  85                    .x86,
  86                    @field(std.Target.x86.Feature, @tagName(tag)[0 .. @tagName(tag).len - " 64bit".len]),
  87                ),
  88            },
  89            .prefetch => target.cpu.hasAny(.x86, &.{ .sse, .prfchw, .prefetchi, .prefetchwt1 }),
  90            inline else => |tag| has_features: {
  91                comptime var feature_it = std.mem.splitScalar(u8, @tagName(tag), ' ');
  92                comptime var features: []const std.Target.x86.Feature = &.{};
  93                inline while (comptime feature_it.next()) |feature| features = features ++ .{@field(std.Target.x86.Feature, feature)};
  94                break :has_features target.cpu.hasAll(.x86, features);
  95            },
  96        }) continue;
  97
  98        switch (data.mode) {
  99            .none, .short => if (rex_required) continue,
 100            .rex, .rex_short => if (!rex_required) continue,
 101            else => {},
 102        }
 103        for (input_ops, data.ops) |input_op, data_op| if (!input_op.isSubset(data_op)) continue :next;
 104
 105        const enc: Encoding = .{ .mnemonic = mnemonic, .data = data };
 106        if (shortest_enc) |previous_shortest_enc| {
 107            const len = estimateInstructionLength(prefix, enc, ops);
 108            const previous_shortest_len = shortest_len orelse
 109                estimateInstructionLength(prefix, previous_shortest_enc, ops);
 110            if (len < previous_shortest_len) {
 111                shortest_enc = enc;
 112                shortest_len = len;
 113            } else shortest_len = previous_shortest_len;
 114        } else shortest_enc = enc;
 115    }
 116    return shortest_enc;
 117}
 118
 119/// Returns first matching encoding by opcode.
 120pub fn findByOpcode(opc: []const u8, prefixes: struct {
 121    legacy: LegacyPrefixes,
 122    rex: Rex,
 123}, modrm_ext: ?u3) ?Encoding {
 124    for (mnemonic_to_encodings_map, 0..) |encs, mnemonic_int| for (encs) |data| {
 125        const enc = Encoding{ .mnemonic = @as(Mnemonic, @enumFromInt(mnemonic_int)), .data = data };
 126        if (modrm_ext) |ext| if (ext != data.modrm_ext) continue;
 127        if (!std.mem.eql(u8, opc, enc.opcode())) continue;
 128        if (prefixes.rex.w) {
 129            if (!data.mode.isLong()) continue;
 130        } else if (prefixes.rex.present and !prefixes.rex.isSet()) {
 131            if (!data.mode.isRex()) continue;
 132        } else if (prefixes.legacy.prefix_66) {
 133            if (!data.mode.isShort()) continue;
 134        } else {
 135            if (data.mode.isShort()) continue;
 136        }
 137        return enc;
 138    };
 139    return null;
 140}
 141
 142pub fn opcode(encoding: *const Encoding) []const u8 {
 143    return encoding.data.opc[0..encoding.data.opc_len];
 144}
 145
 146pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
 147    const prefix = encoding.data.opc[0];
 148    return switch (prefix) {
 149        0x66, 0xf2, 0xf3 => prefix,
 150        else => null,
 151    };
 152}
 153
 154pub fn modRmExt(encoding: Encoding) u3 {
 155    return switch (encoding.data.op_en) {
 156        .ia, .m, .mi, .m1, .mc, .vm, .vmi => encoding.data.modrm_ext,
 157        else => unreachable,
 158    };
 159}
 160
 161pub fn format(encoding: Encoding, writer: *std.Io.Writer) std.Io.Writer.Error!void {
 162    var opc = encoding.opcode();
 163    if (encoding.data.mode.isVex()) {
 164        try writer.writeAll("VEX.");
 165
 166        try writer.writeAll(switch (encoding.data.mode) {
 167            .vex_128_w0, .vex_128_w1, .vex_128_wig => "128",
 168            .vex_256_w0, .vex_256_w1, .vex_256_wig => "256",
 169            .vex_lig_w0, .vex_lig_w1, .vex_lig_wig => "LIG",
 170            .vex_lz_w0, .vex_lz_w1, .vex_lz_wig => "LZ",
 171            else => unreachable,
 172        });
 173
 174        switch (opc[0]) {
 175            else => {},
 176            0x66, 0xf3, 0xf2 => {
 177                try writer.print(".{X:0>2}", .{opc[0]});
 178                opc = opc[1..];
 179            },
 180        }
 181
 182        try writer.print(".{X}", .{opc[0 .. opc.len - 1]});
 183        opc = opc[opc.len - 1 ..];
 184
 185        try writer.writeAll(".W");
 186        try writer.writeAll(switch (encoding.data.mode) {
 187            .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",
 188            .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",
 189            .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",
 190            else => unreachable,
 191        });
 192
 193        try writer.writeByte(' ');
 194    } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + ");
 195    for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
 196
 197    switch (encoding.data.op_en) {
 198        .z, .fd, .td, .i, .zi, .ii, .d => {},
 199        .o, .zo, .oz, .oi => {
 200            const op = switch (encoding.data.op_en) {
 201                .o, .oz, .oi => encoding.data.ops[0],
 202                .zo => encoding.data.ops[1],
 203                else => unreachable,
 204            };
 205            const tag = switch (op) {
 206                .r8 => "rb",
 207                .r16 => "rw",
 208                .r32 => "rd",
 209                .r64 => "rd",
 210                else => unreachable,
 211            };
 212            try writer.print("+{s} ", .{tag});
 213        },
 214        .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
 215        .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "),
 216    }
 217
 218    switch (encoding.data.op_en) {
 219        .i, .d, .zi, .ii, .ia, .oi, .mi, .rmi, .mri, .vmi, .rvmi => for (0..2) |i| {
 220            const op = switch (i) {
 221                0 => switch (encoding.data.op_en) {
 222                    .i, .ii, .ia, .d => encoding.data.ops[0],
 223                    .zi, .oi, .mi => encoding.data.ops[1],
 224                    .rmi, .mri, .vmi => encoding.data.ops[2],
 225                    .rvmi => encoding.data.ops[3],
 226                    else => unreachable,
 227                },
 228                1 => switch (encoding.data.op_en) {
 229                    .ii => encoding.data.ops[1],
 230                    else => break,
 231                },
 232                else => unreachable,
 233            };
 234            const tag = switch (op) {
 235                .imm8, .imm8s => "ib",
 236                .imm16, .imm16s => "iw",
 237                .imm32, .imm32s => "id",
 238                .imm64 => "io",
 239                .rel8 => "cb",
 240                .rel16 => "cw",
 241                .rel32 => "cd",
 242                else => unreachable,
 243            };
 244            try writer.print("{s} ", .{tag});
 245        },
 246        .rvmr => try writer.writeAll("/is4 "),
 247        .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {},
 248    }
 249
 250    try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
 251
 252    for (encoding.data.ops) |op| switch (op) {
 253        .none => break,
 254        else => try writer.print("{s} ", .{@tagName(op)}),
 255    };
 256
 257    const op_en = switch (encoding.data.op_en) {
 258        .zi => .i,
 259        else => |op_en| op_en,
 260    };
 261    try writer.print("{s}", .{@tagName(op_en)});
 262}
 263
 264pub const Mnemonic = enum {
 265    // Directives
 266    @".cfi_def_cfa",
 267    @".cfi_def_cfa_register",
 268    @".cfi_def_cfa_offset",
 269    @".cfi_adjust_cfa_offset",
 270    @".cfi_offset",
 271    @".cfi_val_offset",
 272    @".cfi_rel_offset",
 273    @".cfi_register",
 274    @".cfi_restore",
 275    @".cfi_undefined",
 276    @".cfi_same_value",
 277    @".cfi_remember_state",
 278    @".cfi_restore_state",
 279    @".cfi_escape",
 280    // zig fmt: off
 281    // General-purpose
 282    aaa, aad, aam, aas, adc, add, @"and", arpl,
 283    bound, bsf, bsr, bswap, bt, btc, btr, bts,
 284    call, cbw, cdq, cdqe,
 285    clac, clc, cld, cldemote, clflush, clflushopt, cli, clts, clui, clrssbsy, clwb, cmc,
 286    cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
 287    cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
 288    cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz,
 289    cmp, cmps, cmpsb, cmpsd, cmpsq, cmpsw, cmpxchg, cmpxchg8b, cmpxchg16b,
 290    cpuid, cqo, cwd, cwde,
 291    daa, das, dec, div,
 292    endbr32, endbr64, enqcmd, enqcmds, enter,
 293    hlt, hreset,
 294    idiv, imul, in, inc, incsspd, incsspq, ins, insb, insd, insw,
 295    int, int1, int3, into, invd, invlpg, invpcid, iret, iretd, iretq, iretw,
 296    ja, jae, jb, jbe, jc, jcxz, je, jecxz, jg, jge, jl, jle, jmp, jna, jnae, jnb, jnbe,
 297    jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, jrcxz, js, jz,
 298    lahf, lar, lea, leave, lfence, lgdt, lidt, lldt, lmsw, loop, loope, loopne,
 299    lods, lodsb, lodsd, lodsq, lodsw,
 300    lret, lsl, ltr,
 301    mfence, mov, movbe,
 302    movs, movsb, movsd, movsq, movsw,
 303    movsx, movsxd, movzx, mul,
 304    neg, nop, not,
 305    @"or", out, outs, outsb, outsd, outsw,
 306    pause, pop, popf, popfd, popfq, push, pushfq,
 307    rcl, rcr,
 308    rdfsbase, rdgsbase, rdmsr, rdpid, rdpkru, rdpmc, rdrand, rdseed, rdsspd, rdsspq, rdtsc, rdtscp,
 309    ret, rol, ror, rsm,
 310    sahf, sal, sar, sbb,
 311    scas, scasb, scasd, scasq, scasw,
 312    senduipi, serialize,
 313    shl, shld, shr, shrd,
 314    stac, stc, std, sti, str, stui,
 315    sub, swapgs, syscall, sysenter, sysexit, sysret,
 316    seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae,
 317    setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns,
 318    setnz, seto, setp, setpe, setpo, sets, setz,
 319    sfence, sidt, sldt, smsw,
 320    stos, stosb, stosd, stosq, stosw,
 321    @"test", testui, tpause,
 322    ud0, ud1, ud2, uiret, umonitor, umwait,
 323    verr, verw, wrfsbase, wrgsbase, wrmsr, wrpkru, wrssd, wrssq, wrussd, wrussq,
 324    xadd, xchg, xgetbv, xlat, xlatb, xor,
 325    // X87
 326    f2xm1, fabs, fadd, faddp, fbld, fbstp, fchs, fclex,
 327    fcmovb, fcmovbe, fcmove, fcmovnb, fcmovnbe, fcmovne, fcmovnu, fcmovu,
 328    fcom, fcomi, fcomip, fcomp, fcompp, fcos,
 329    fdecstp, fdiv, fdivp, fdivr, fdivrp, ffree,
 330    fiadd, ficom, ficomp, fidiv, fidivr, fild, fimul, fincstp, finit,
 331    fist, fistp, fisub, fisubr,
 332    fld, fld1, fldcw, fldenv, fldl2e, fldl2t, fldlg2, fldln2, fldpi, fldz,
 333    fmul, fmulp,
 334    fnclex, fninit, fnop, fnsave, fnstcw, fnstenv, fnstsw,
 335    fpatan, fprem, fprem1, fptan, frndint, frstor,
 336    fsave, fscale, fsin, fsincos, fsqrt,
 337    fst, fstcw, fstenv, fstp, fstsw,
 338    fsub, fsubp, fsubr, fsubrp,
 339    ftst, fucom, fucomi, fucomip, fucomp, fucompp,
 340    fwait, fxam, fxch, fxtract, fyl2x, fyl2xp1, wait,
 341    // MMX
 342    emms, movd, movq,
 343    packssdw, packsswb, packuswb,
 344    paddb, paddd, paddsb, paddsw, paddusb, paddusw, paddw,
 345    pand, pandn, por, pxor,
 346    pcmpeqb, pcmpeqd, pcmpeqw,
 347    pcmpgtb, pcmpgtd, pcmpgtw,
 348    pmaddwd, pmulhw, pmullw,
 349    pslld, psllq, psllw,
 350    psrad, psraw,
 351    psrld, psrlq, psrlw,
 352    psubb, psubd, psubsb, psubsw, psubusb, psubusw, psubw,
 353    // SSE
 354    addps, addss,
 355    andnps, andps,
 356    cmpps, cmpss, comiss,
 357    cvtpi2ps, cvtps2pi, cvtsi2ss, cvtss2si, cvttps2pi, cvttss2si,
 358    divps, divss,
 359    fxrstor, fxrstor64, fxsave, fxsave64,
 360    ldmxcsr,
 361    maxps, maxss,
 362    minps, minss,
 363    movaps, movhlps, movhps, movlhps, movlps,
 364    movmskps,
 365    movss, movups,
 366    mulps, mulss,
 367    orps,
 368    pavgb, pavgw,
 369    pextrw, pinsrw,
 370    pmaxsw, pmaxub, pminsw, pminub, pmovmskb, pmulhuw,
 371    prefetchit0, prefetchit1, prefetchnta, prefetcht0, prefetcht1, prefetcht2, prefetchw, prefetchwt1,
 372    psadbw, pshufw,
 373    shufps,
 374    sqrtps, sqrtss,
 375    stmxcsr,
 376    subps, subss,
 377    ucomiss, unpckhps, unpcklps,
 378    xorps,
 379    // SSE2
 380    addpd, addsd,
 381    andpd,
 382    andnpd,
 383    cmppd, //cmpsd,
 384    comisd,
 385    cvtdq2pd, cvtdq2ps, cvtpd2dq, cvtpd2pi, cvtpd2ps, cvtpi2pd,
 386    cvtps2dq, cvtps2pd, cvtsd2si, cvtsd2ss, cvtsi2sd, cvtss2sd,
 387    cvttpd2dq, cvttpd2pi, cvttps2dq, cvttsd2si,
 388    divpd, divsd,
 389    gf2p8affineinvqb, gf2p8affineqb, gf2p8mulb,
 390    maxpd, maxsd,
 391    minpd, minsd,
 392    movapd,
 393    movdq2q, movdqa, movdqu,
 394    movhpd, movlpd,
 395    movmskpd, movq2dq,
 396    //movsd,
 397    movupd,
 398    mulpd, mulsd,
 399    orpd,
 400    paddq, pmuludq,
 401    pshufd, pshufhw, pshuflw,
 402    pslldq, psrldq, psubq,
 403    punpckhbw, punpckhdq, punpckhqdq, punpckhwd,
 404    punpcklbw, punpckldq, punpcklqdq, punpcklwd,
 405    shufpd,
 406    sqrtpd, sqrtsd,
 407    subpd, subsd,
 408    ucomisd, unpckhpd, unpcklpd,
 409    xorpd,
 410    // SSE3
 411    addsubpd, addsubps,
 412    fisttp,
 413    haddpd, haddps,
 414    hsubpd, hsubps,
 415    lddqu,
 416    movddup, movshdup, movsldup,
 417    // SSSE3
 418    pabsb, pabsd, pabsw, palignr,
 419    phaddw, phaddsw, phaddd, phsubw, phsubsw, phsubd,
 420    pmaddubsw, pmulhrsw, pshufb,
 421    psignb, psignd, psignw,
 422    // SSE4.1
 423    blendpd, blendps, blendvpd, blendvps,
 424    dppd, dpps,
 425    extractps,
 426    insertps,
 427    packusdw,
 428    pblendvb, pblendw,
 429    pcmpeqq,
 430    pextrb, pextrd, pextrq,
 431    phminposuw,
 432    pinsrb, pinsrd, pinsrq,
 433    pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw,
 434    pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq,
 435    pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq,
 436    pmuldq, pmulld,
 437    ptest,
 438    roundpd, roundps, roundsd, roundss,
 439    // SSE4.2
 440    crc32, pcmpgtq,
 441    // ABM
 442    lzcnt, popcnt,
 443    // PCLMUL
 444    pclmulqdq,
 445    // AES
 446    aesdec, aesdeclast, aesenc, aesenclast, aesimc, aeskeygenassist,
 447    // SHA
 448    sha1rnds4, sha1nexte, sha1msg1, sha1msg2, sha256msg1, sha256msg2, sha256rnds2,
 449    // AVX
 450    vaddpd, vaddps, vaddsd, vaddss, vaddsubpd, vaddsubps,
 451    vaesdec, vaesdeclast, vaesenc, vaesenclast, vaesimc, vaeskeygenassist,
 452    vandnpd, vandnps, vandpd, vandps,
 453    vblendpd, vblendps, vblendvpd, vblendvps,
 454    vbroadcastf128, vbroadcastsd, vbroadcastss,
 455    vcmppd, vcmpps, vcmpsd, vcmpss, vcomisd, vcomiss,
 456    vcvtdq2pd, vcvtdq2ps, vcvtpd2dq, vcvtpd2ps,
 457    vcvtps2dq, vcvtps2pd, vcvtsd2si, vcvtsd2ss,
 458    vcvtsi2sd, vcvtsi2ss, vcvtss2sd, vcvtss2si,
 459    vcvttpd2dq, vcvttps2dq, vcvttsd2si, vcvttss2si,
 460    vdivpd, vdivps, vdivsd, vdivss,
 461    vdppd, vdpps,
 462    vextractf128, vextractps,
 463    vgf2p8affineinvqb, vgf2p8affineqb, vgf2p8mulb,
 464    vhaddpd, vhaddps, vhsubpd, vhsubps,
 465    vinsertf128, vinsertps,
 466    vlddqu, vldmxcsr,
 467    vmaskmovpd, vmaskmovps,
 468    vmaxpd, vmaxps, vmaxsd, vmaxss,
 469    vminpd, vminps, vminsd, vminss,
 470    vmovapd, vmovaps,
 471    vmovd,
 472    vmovddup,
 473    vmovdqa, vmovdqu,
 474    vmovhlps, vmovhpd, vmovhps, vmovlhps, vmovlpd, vmovlps,
 475    vmovmskpd, vmovmskps,
 476    vmovq,
 477    vmovsd,
 478    vmovshdup, vmovsldup,
 479    vmovss,
 480    vmovupd, vmovups,
 481    vmulpd, vmulps, vmulsd, vmulss,
 482    vorpd, vorps,
 483    vpabsb, vpabsd, vpabsw,
 484    vpackssdw, vpacksswb, vpackusdw, vpackuswb,
 485    vpaddb, vpaddd, vpaddq, vpaddsb, vpaddsw, vpaddusb, vpaddusw, vpaddw,
 486    vpalignr, vpand, vpandn, vpavgb, vpavgw,
 487    vpblendvb, vpblendw, vpclmulqdq,
 488    vpcmpeqb, vpcmpeqd, vpcmpeqq, vpcmpeqw,
 489    vpcmpgtb, vpcmpgtd, vpcmpgtq, vpcmpgtw,
 490    vperm2f128, vpermilpd, vpermilps,
 491    vpextrb, vpextrd, vpextrq, vpextrw,
 492    vphaddw, vphaddsw, vphaddd, vphminposuw, vphsubw, vphsubsw, vphsubd,
 493    vpinsrb, vpinsrd, vpinsrq, vpinsrw,
 494    vpmaddubsw, vpmaddwd,
 495    vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw,
 496    vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw,
 497    vpmovmskb,
 498    vpmovsxbd, vpmovsxbq, vpmovsxbw, vpmovsxdq, vpmovsxwd, vpmovsxwq,
 499    vpmovzxbd, vpmovzxbq, vpmovzxbw, vpmovzxdq, vpmovzxwd, vpmovzxwq,
 500    vpmuldq, vpmulhrsw, vpmulhuw, vpmulhw, vpmulld, vpmullw, vpmuludq,
 501    vpor,
 502    vpsadbw, vpshufb, vpshufd, vpshufhw, vpshuflw,
 503    vpsignb, vpsignd, vpsignw,
 504    vpslld, vpslldq, vpsllq, vpsllw,
 505    vpsrad, vpsraq, vpsraw,
 506    vpsrld, vpsrldq, vpsrlq, vpsrlw,
 507    vpsubb, vpsubd, vpsubq, vpsubsb, vpsubsw, vpsubusb, vpsubusw, vpsubw,
 508    vptest,
 509    vpunpckhbw, vpunpckhdq, vpunpckhqdq, vpunpckhwd,
 510    vpunpcklbw, vpunpckldq, vpunpcklqdq, vpunpcklwd,
 511    vpxor,
 512    vroundpd, vroundps, vroundsd, vroundss,
 513    vshufpd, vshufps,
 514    vsqrtpd, vsqrtps, vsqrtsd, vsqrtss,
 515    vstmxcsr,
 516    vsubpd, vsubps, vsubsd, vsubss,
 517    vtestpd, vtestps,
 518    vucomisd, vucomiss, vunpckhpd, vunpckhps, vunpcklpd, vunpcklps,
 519    vxorpd, vxorps,
 520    // BMI
 521    andn, bextr, blsi, blsmsk, blsr, tzcnt,
 522    // BMI2
 523    bzhi, mulx, pdep, pext, rorx, sarx, shlx, shrx,
 524    // F16C
 525    vcvtph2ps, vcvtps2ph,
 526    // FMA
 527    vfmadd132pd, vfmadd213pd, vfmadd231pd,
 528    vfmadd132ps, vfmadd213ps, vfmadd231ps,
 529    vfmadd132sd, vfmadd213sd, vfmadd231sd,
 530    vfmadd132ss, vfmadd213ss, vfmadd231ss,
 531    // AVX2
 532    vbroadcasti128, vpbroadcastb, vpbroadcastd, vpbroadcastq, vpbroadcastw,
 533    vextracti128, vinserti128, vpblendd,
 534    vperm2i128, vpermd, vpermpd, vpermps, vpermq,
 535    vpmaskmovd, vpmaskmovq,
 536    vpsllvd, vpsllvq, vpsravd, vpsrlvd, vpsrlvq,
 537    // ADX
 538    adcx, adox,
 539    // AESKLE
 540    aesdec128kl, aesdec256kl, aesenc128kl, aesenc256kl, encodekey128, encodekey256, loadiwkey,
 541    // AESKLEWIDE_KL
 542    aesdecwide128kl, aesdecwide256kl, aesencwide128kl, aesencwide256kl,
 543    // zig fmt: on
 544};
 545
 546pub const OpEn = enum {
 547    // zig fmt: off
 548    z,
 549    o, zo, oz, oi,
 550    i, zi, ii, ia,
 551    d, m,
 552    fd, td,
 553    m1, mc, mi, mr, rm,
 554    rmi, mri, mrc,
 555    rm0, vm, vmi, rvm, rvmr, rvmi, mvr, rmv,
 556    // zig fmt: on
 557};
 558
 559pub const Op = enum {
 560    // zig fmt: off
 561    none,
 562    unity,
 563    imm8, imm16, imm32, imm64,
 564    imm8s, imm16s, imm32s,
 565    al, ax, eax, rax,
 566    cl, dx,
 567    rip, eip, ip,
 568    r8, r16, r32, r64,
 569    rm8, rm16, rm32, rm64,
 570    r32_m8, r32_m16, r64_m16,
 571    m8, m16, m32, m64, m80, m128, m256,
 572    rel8, rel16, rel32,
 573    m, moffs, mrip8,
 574    sreg,
 575    st0, st, mm, mm_m64,
 576    xmm0, xmm, xmm_m8, xmm_m16, xmm_m32, xmm_m64, xmm_m128,
 577    ymm, ymm_m256,
 578    cr, dr,
 579    // zig fmt: on
 580
 581    pub fn fromOperand(operand: Instruction.Operand, target: *const std.Target) Op {
 582        return switch (operand) {
 583            .none => .none,
 584
 585            .reg => |reg| switch (reg.class()) {
 586                .general_purpose => switch (reg) {
 587                    .al => .al,
 588                    .ax => .ax,
 589                    .eax => .eax,
 590                    .rax => .rax,
 591                    .cl => .cl,
 592                    .dx => .dx,
 593                    else => switch (reg.size().bitSize(target)) {
 594                        8 => .r8,
 595                        16 => .r16,
 596                        32 => .r32,
 597                        64 => .r64,
 598                        else => unreachable,
 599                    },
 600                },
 601                .gphi => .r8,
 602                .segment => .sreg,
 603                .x87 => switch (reg) {
 604                    .st0 => .st0,
 605                    else => .st,
 606                },
 607                .mmx => .mm,
 608                .sse => switch (reg) {
 609                    .xmm0 => .xmm0,
 610                    else => switch (reg.size().bitSize(target)) {
 611                        128 => .xmm,
 612                        256 => .ymm,
 613                        else => unreachable,
 614                    },
 615                },
 616                .ip => switch (reg) {
 617                    .rip => .rip,
 618                    .eip => .eip,
 619                    .ip => .ip,
 620                    else => unreachable,
 621                },
 622                .cr => .cr,
 623                .dr => .dr,
 624            },
 625
 626            .mem => |mem| switch (mem) {
 627                .moffs => .moffs,
 628                .sib => switch (mem.bitSize(target)) {
 629                    0 => .m,
 630                    8 => .m8,
 631                    16 => .m16,
 632                    32 => .m32,
 633                    64 => .m64,
 634                    80 => .m80,
 635                    128 => .m128,
 636                    256 => .m256,
 637                    else => unreachable,
 638                },
 639                .rip => switch (mem.bitSize(target)) {
 640                    0, 8 => .mrip8,
 641                    16 => .m16,
 642                    32 => .m32,
 643                    64 => .m64,
 644                    80 => .m80,
 645                    128 => .m128,
 646                    256 => .m256,
 647                    else => unreachable,
 648                },
 649            },
 650
 651            .imm => |imm| switch (imm) {
 652                .signed => |x| if (x == 1)
 653                    .unity
 654                else if (math.cast(i8, x)) |_|
 655                    .imm8s
 656                else if (math.cast(i16, x)) |_|
 657                    .imm16s
 658                else
 659                    .imm32s,
 660                .unsigned => |x| if (x == 1)
 661                    .unity
 662                else if (math.cast(i8, x)) |_|
 663                    .imm8s
 664                else if (math.cast(u8, x)) |_|
 665                    .imm8
 666                else if (math.cast(i16, x)) |_|
 667                    .imm16s
 668                else if (math.cast(u16, x)) |_|
 669                    .imm16
 670                else if (math.cast(i32, x)) |_|
 671                    .imm32s
 672                else if (math.cast(u32, x)) |_|
 673                    .imm32
 674                else
 675                    .imm64,
 676            },
 677
 678            .bytes => unreachable,
 679        };
 680    }
 681
 682    pub fn toReg(op: Op) Register {
 683        return switch (op) {
 684            else => .none,
 685            .al => .al,
 686            .ax => .ax,
 687            .eax => .eax,
 688            .rax => .rax,
 689            .cl => .cl,
 690            .dx => .dx,
 691            .rip => .rip,
 692            .eip => .eip,
 693            .ip => .ip,
 694            .st0 => .st0,
 695            .xmm0 => .xmm0,
 696        };
 697    }
 698
 699    pub fn immBitSize(op: Op) u64 {
 700        return switch (op) {
 701            .none, .m, .moffs, .mrip8, .sreg => unreachable,
 702            .al, .cl, .dx, .rip, .eip, .ip, .r8, .rm8, .r32_m8 => unreachable,
 703            .ax, .r16, .rm16 => unreachable,
 704            .eax, .r32, .rm32, .r32_m16 => unreachable,
 705            .rax, .r64, .rm64, .r64_m16 => unreachable,
 706            .st0, .st, .mm, .mm_m64 => unreachable,
 707            .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
 708            .ymm, .ymm_m256 => unreachable,
 709            .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
 710            .cr, .dr => unreachable,
 711            .unity => 1,
 712            .imm8, .imm8s, .rel8 => 8,
 713            .imm16, .imm16s, .rel16 => 16,
 714            .imm32, .imm32s, .rel32 => 32,
 715            .imm64 => 64,
 716        };
 717    }
 718
 719    pub fn regBitSize(op: Op) u64 {
 720        return switch (op) {
 721            .none, .m, .moffs, .mrip8, .sreg => unreachable,
 722            .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
 723            .rel8, .rel16, .rel32 => unreachable,
 724            .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
 725            .al, .cl, .r8, .rm8 => 8,
 726            .ax, .dx, .ip, .r16, .rm16 => 16,
 727            .eax, .eip, .r32, .rm32, .r32_m8, .r32_m16 => 32,
 728            .rax, .rip, .r64, .rm64, .r64_m16, .mm, .mm_m64, .cr, .dr => 64,
 729            .st0, .st => 80,
 730            .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
 731            .ymm, .ymm_m256 => 256,
 732        };
 733    }
 734
 735    pub fn memBitSize(op: Op) u64 {
 736        return switch (op) {
 737            .none, .m, .moffs, .sreg => unreachable,
 738            .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
 739            .rel8, .rel16, .rel32 => unreachable,
 740            .al, .cl, .r8, .ax, .dx, .ip, .r16, .eax, .eip, .r32, .rax, .rip, .r64 => unreachable,
 741            .st0, .st, .mm, .xmm0, .xmm, .ymm => unreachable,
 742            .cr, .dr => unreachable,
 743            .mrip8, .m8, .rm8, .r32_m8, .xmm_m8 => 8,
 744            .m16, .rm16, .r32_m16, .r64_m16, .xmm_m16 => 16,
 745            .m32, .rm32, .xmm_m32 => 32,
 746            .m64, .rm64, .mm_m64, .xmm_m64 => 64,
 747            .m80 => 80,
 748            .m128, .xmm_m128 => 128,
 749            .m256, .ymm_m256 => 256,
 750        };
 751    }
 752
 753    pub fn isSigned(op: Op) bool {
 754        return switch (op) {
 755            .unity, .imm8, .imm16, .imm32, .imm64 => false,
 756            .imm8s, .imm16s, .imm32s => true,
 757            .rel8, .rel16, .rel32 => true,
 758            else => unreachable,
 759        };
 760    }
 761
 762    pub fn isUnsigned(op: Op) bool {
 763        return !op.isSigned();
 764    }
 765
 766    pub fn isRegister(op: Op) bool {
 767        // zig fmt: off
 768        return switch (op) {
 769            .al, .ax, .eax, .rax,
 770            .cl, .dx,
 771            .ip, .eip, .rip,
 772            .r8, .r16, .r32, .r64,
 773            .rm8, .rm16, .rm32, .rm64,
 774            .r32_m8, .r32_m16, .r64_m16,
 775            .st0, .st, .mm, .mm_m64,
 776            .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
 777            .ymm, .ymm_m256,
 778            .cr, .dr,
 779            => true,
 780            else => false,
 781        };
 782        // zig fmt: on
 783    }
 784
 785    pub fn isImmediate(op: Op) bool {
 786        // zig fmt: off
 787        return switch (op) {
 788            .imm8, .imm16, .imm32, .imm64,
 789            .imm8s, .imm16s, .imm32s,
 790            .rel8, .rel16, .rel32,
 791            .unity,
 792            => true,
 793            else => false,
 794        };
 795        // zig fmt: on
 796    }
 797
 798    pub fn isMemory(op: Op) bool {
 799        // zig fmt: off
 800        return switch (op) {
 801            .rm8, .rm16, .rm32, .rm64,
 802            .r32_m8, .r32_m16, .r64_m16,
 803            .m8, .m16, .m32, .m64, .m80, .m128, .m256,
 804            .m, .moffs, .mrip8,
 805            .mm_m64,
 806            .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
 807            .ymm_m256,
 808            => true,
 809            else => false,
 810        };
 811        // zig fmt: on
 812    }
 813
 814    pub fn isSegmentRegister(op: Op) bool {
 815        return switch (op) {
 816            .moffs, .sreg => true,
 817            else => false,
 818        };
 819    }
 820
 821    pub fn class(op: Op) bits.Register.Class {
 822        return switch (op) {
 823            else => unreachable,
 824            .al, .ax, .eax, .rax, .cl, .dx => .general_purpose,
 825            .r8, .r16, .r32, .r64 => .general_purpose,
 826            .rm8, .rm16, .rm32, .rm64 => .general_purpose,
 827            .r32_m8, .r32_m16, .r64_m16 => .general_purpose,
 828            .sreg => .segment,
 829            .st0, .st => .x87,
 830            .mm, .mm_m64 => .mmx,
 831            .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
 832            .ymm, .ymm_m256 => .sse,
 833            .rip, .eip, .ip => .ip,
 834            .cr => .cr,
 835            .dr => .dr,
 836        };
 837    }
 838
 839    /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding.
 840    pub fn isSubset(op: Op, target: Op) bool {
 841        switch (op) {
 842            .none, .m, .moffs, .sreg => return op == target,
 843            else => {
 844                if (op.isRegister() and target.isRegister()) {
 845                    return switch (target.toReg()) {
 846                        .none => op.class() == target.class() and op.regBitSize() == target.regBitSize(),
 847                        else => op == target,
 848                    };
 849                }
 850                if (op.isMemory() and target.isMemory()) {
 851                    switch (target) {
 852                        .m => return true,
 853                        .moffs, .mrip8 => return op == target,
 854                        else => return op.memBitSize() == target.memBitSize(),
 855                    }
 856                }
 857                if (op.isImmediate() and target.isImmediate()) {
 858                    switch (target) {
 859                        .imm64 => if (op.immBitSize() <= 64) return true,
 860                        .imm32s, .rel32 => if (op.immBitSize() < 32 or (op.immBitSize() == 32 and op.isSigned()))
 861                            return true,
 862                        .imm32 => if (op.immBitSize() <= 32) return true,
 863                        .imm16s, .rel16 => if (op.immBitSize() < 16 or (op.immBitSize() == 16 and op.isSigned()))
 864                            return true,
 865                        .imm16 => if (op.immBitSize() <= 16) return true,
 866                        .imm8s, .rel8 => if (op.immBitSize() < 8 or (op.immBitSize() == 8 and op.isSigned()))
 867                            return true,
 868                        .imm8 => if (op.immBitSize() <= 8) return true,
 869                        else => {},
 870                    }
 871                    return op == target;
 872                }
 873                return false;
 874            },
 875        }
 876    }
 877};
 878
 879pub const Mode = enum {
 880    // zig fmt: off
 881    none,
 882    short, long,
 883    rex, rex_short,
 884    wait,
 885    vex_128_w0, vex_128_w1, vex_128_wig,
 886    vex_256_w0, vex_256_w1, vex_256_wig,
 887    vex_lig_w0, vex_lig_w1, vex_lig_wig,
 888    vex_lz_w0,  vex_lz_w1,  vex_lz_wig,
 889    // zig fmt: on
 890
 891    pub fn isShort(mode: Mode) bool {
 892        return switch (mode) {
 893            .short, .rex_short => true,
 894            else => false,
 895        };
 896    }
 897
 898    pub fn isLong(mode: Mode) bool {
 899        return switch (mode) {
 900            .long,
 901            .vex_128_w1,
 902            .vex_256_w1,
 903            .vex_lig_w1,
 904            .vex_lz_w1,
 905            => true,
 906            else => false,
 907        };
 908    }
 909
 910    pub fn isRex(mode: Mode) bool {
 911        return switch (mode) {
 912            else => false,
 913            .rex, .rex_short => true,
 914        };
 915    }
 916
 917    pub fn isVex(mode: Mode) bool {
 918        return switch (mode) {
 919            // zig fmt: off
 920            else => false,
 921            .vex_128_w0, .vex_128_w1, .vex_128_wig,
 922            .vex_256_w0, .vex_256_w1, .vex_256_wig,
 923            .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
 924            .vex_lz_w0,  .vex_lz_w1,  .vex_lz_wig,
 925            => true,
 926            // zig fmt: on
 927        };
 928    }
 929
 930    pub fn isVecLong(mode: Mode) bool {
 931        return switch (mode) {
 932            // zig fmt: off
 933            else => unreachable,
 934            .vex_128_w0, .vex_128_w1, .vex_128_wig,
 935            .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
 936            .vex_lz_w0,  .vex_lz_w1,  .vex_lz_wig,
 937            => false,
 938            .vex_256_w0, .vex_256_w1, .vex_256_wig,
 939            => true,
 940            // zig fmt: on
 941        };
 942    }
 943};
 944
 945pub const Feature = enum {
 946    none,
 947    @"32bit",
 948    @"64bit",
 949    adx,
 950    aes,
 951    @"aes avx",
 952    avx,
 953    avx2,
 954    bmi,
 955    bmi2,
 956    cldemote,
 957    clflushopt,
 958    clwb,
 959    cmov,
 960    @"cmov x87",
 961    crc32,
 962    enqcmd,
 963    f16c,
 964    fma,
 965    fsgsbase,
 966    fxsr,
 967    gfni,
 968    @"gfni avx",
 969    hreset,
 970    @"invpcid 32bit",
 971    @"invpcid 64bit",
 972    kl,
 973    lzcnt,
 974    mmx,
 975    movbe,
 976    pclmul,
 977    @"pclmul avx",
 978    pku,
 979    popcnt,
 980    prefetch,
 981    @"prefetchi 64bit",
 982    prefetchwt1,
 983    prfchw,
 984    rdrnd,
 985    rdseed,
 986    @"rdpid 32bit",
 987    @"rdpid 64bit",
 988    sahf,
 989    serialize,
 990    shstk,
 991    smap,
 992    sse,
 993    sse2,
 994    sse3,
 995    @"sse3 x87",
 996    sse4_1,
 997    sse4_2,
 998    ssse3,
 999    sha,
1000    uintr,
1001    vaes,
1002    vpclmulqdq,
1003    waitpkg,
1004    widekl,
1005    x87,
1006};
1007
1008fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize {
1009    var inst: Instruction = .{
1010        .prefix = prefix,
1011        .encoding = encoding,
1012        .ops = @splat(.none),
1013    };
1014    @memcpy(inst.ops[0..ops.len], ops);
1015
1016    // By using a buffer with maximum length of encoded instruction, we can use
1017    // the `end` field of the Writer for the count.
1018    var buf: [16]u8 = undefined;
1019    var trash: std.Io.Writer.Discarding = .init(&buf);
1020    inst.encode(&trash.writer, .{
1021        .allow_frame_locs = true,
1022        .allow_symbols = true,
1023    }) catch {
1024        // Since the function signature for encode() does not mention under what
1025        // conditions it can fail, I have changed `unreachable` to `@panic` here.
1026        // This is a TODO item since it indicates this function
1027        // (`estimateInstructionLength`) has the wrong function signature.
1028        @panic("unexpected failure to encode");
1029    };
1030    return trash.writer.end;
1031}
1032
1033const mnemonic_to_encodings_map = init: {
1034    @setEvalBranchQuota(5_900);
1035    const ModrmExt = u3;
1036    const Entry = struct { Mnemonic, OpEn, []const Op, []const u8, ModrmExt, Mode, Feature };
1037    const encodings: []const Entry = @import("encodings.zon");
1038
1039    const mnemonic_count = @typeInfo(Mnemonic).@"enum".fields.len;
1040    var mnemonic_map: [mnemonic_count][]Data = @splat(&.{});
1041    for (encodings) |entry| mnemonic_map[@intFromEnum(entry[0])].len += 1;
1042    var data_storage: [encodings.len]Data = undefined;
1043    var storage_index: usize = 0;
1044    for (&mnemonic_map) |*value| {
1045        value.ptr = data_storage[storage_index..].ptr;
1046        storage_index += value.len;
1047    }
1048    var mnemonic_index: [mnemonic_count]usize = @splat(0);
1049    const ops_len = @typeInfo(@FieldType(Data, "ops")).array.len;
1050    const opc_len = @typeInfo(@FieldType(Data, "opc")).array.len;
1051    for (encodings) |entry| {
1052        const index = &mnemonic_index[@intFromEnum(entry[0])];
1053        mnemonic_map[@intFromEnum(entry[0])][index.*] = .{
1054            .op_en = entry[1],
1055            .ops = (entry[2] ++ .{.none} ** (ops_len - entry[2].len)).*,
1056            .opc_len = entry[3].len,
1057            .opc = (entry[3] ++ .{undefined} ** (opc_len - entry[3].len)).*,
1058            .modrm_ext = entry[4],
1059            .mode = entry[5],
1060            .feature = entry[6],
1061        };
1062        index.* += 1;
1063    }
1064    const final_storage = data_storage;
1065    var final_map: [mnemonic_count][]const Data = @splat(&.{});
1066    storage_index = 0;
1067    for (&final_map, mnemonic_map) |*final_value, value| {
1068        final_value.* = final_storage[storage_index..][0..value.len];
1069        storage_index += value.len;
1070    }
1071    break :init final_map;
1072};