aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/x86_64/Encoding.zig
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alex@alexrp.com>2025-08-20 15:45:53 +0200
committerAlex Rønne Petersen <alex@alexrp.com>2025-09-26 02:02:07 +0200
commit86077fe6bdac34fe610f4c0b6bac3d6d1b97c22d (patch)
treed8f58b4d4e034d5770c816e886690387a1db7ffe /src/codegen/x86_64/Encoding.zig
parent212715f62d3b22a2da18904f570dbc918ca8470a (diff)
downloadzig-86077fe6bdac34fe610f4c0b6bac3d6d1b97c22d.tar.gz
zig-86077fe6bdac34fe610f4c0b6bac3d6d1b97c22d.zip
compiler: move self-hosted backends from src/arch to src/codegen
Diffstat (limited to 'src/codegen/x86_64/Encoding.zig')
-rw-r--r--src/codegen/x86_64/Encoding.zig1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/src/codegen/x86_64/Encoding.zig b/src/codegen/x86_64/Encoding.zig
new file mode 100644
index 0000000000..3b41e0b07a
--- /dev/null
+++ b/src/codegen/x86_64/Encoding.zig
@@ -0,0 +1,1072 @@
+const Encoding = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const math = std.math;
+
+const bits = @import("bits.zig");
+const encoder = @import("encoder.zig");
+const Instruction = encoder.Instruction;
+const Operand = Instruction.Operand;
+const Prefix = Instruction.Prefix;
+const Register = bits.Register;
+const Rex = encoder.Rex;
+const LegacyPrefixes = encoder.LegacyPrefixes;
+
+mnemonic: Mnemonic,
+data: Data,
+
+const Data = struct {
+ op_en: OpEn,
+ ops: [4]Op,
+ opc_len: u3,
+ opc: [7]u8,
+ modrm_ext: u3,
+ mode: Mode,
+ feature: Feature,
+};
+
+pub fn findByMnemonic(
+ prefix: Instruction.Prefix,
+ mnemonic: Mnemonic,
+ ops: []const Instruction.Operand,
+ target: *const std.Target,
+) !?Encoding {
+ var input_ops: [4]Op = @splat(.none);
+ for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op, target);
+
+ const rex_required = for (ops) |op| switch (op) {
+ .reg => |r| switch (r) {
+ .spl, .bpl, .sil, .dil => break true,
+ else => {},
+ },
+ else => {},
+ } else false;
+ const rex_invalid = for (ops) |op| switch (op) {
+ .reg => |r| switch (r) {
+ .ah, .bh, .ch, .dh => break true,
+ else => {},
+ },
+ else => {},
+ } else false;
+ const rex_extended = for (ops) |op| {
+ if (op.baseExtEnc() != 0b00 or op.indexExtEnc() != 0b00) break true;
+ } else false;
+
+ if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode;
+
+ var shortest_enc: ?Encoding = null;
+ var shortest_len: ?usize = null;
+ next: for (mnemonic_to_encodings_map[@intFromEnum(mnemonic)]) |data| {
+ if (!switch (data.feature) {
+ .none => true,
+ .@"32bit" => switch (target.cpu.arch) {
+ else => unreachable,
+ .x86 => true,
+ .x86_64 => false,
+ },
+ .@"64bit" => switch (target.cpu.arch) {
+ else => unreachable,
+ .x86 => false,
+ .x86_64 => true,
+ },
+ inline .@"invpcid 32bit", .@"rdpid 32bit" => |tag| switch (target.cpu.arch) {
+ else => unreachable,
+ .x86 => target.cpu.has(
+ .x86,
+ @field(std.Target.x86.Feature, @tagName(tag)[0 .. @tagName(tag).len - " 32bit".len]),
+ ),
+ .x86_64 => false,
+ },
+ inline .@"invpcid 64bit", .@"rdpid 64bit", .@"prefetchi 64bit" => |tag| switch (target.cpu.arch) {
+ else => unreachable,
+ .x86 => false,
+ .x86_64 => target.cpu.has(
+ .x86,
+ @field(std.Target.x86.Feature, @tagName(tag)[0 .. @tagName(tag).len - " 64bit".len]),
+ ),
+ },
+ .prefetch => target.cpu.hasAny(.x86, &.{ .sse, .prfchw, .prefetchi, .prefetchwt1 }),
+ inline else => |tag| has_features: {
+ comptime var feature_it = std.mem.splitScalar(u8, @tagName(tag), ' ');
+ comptime var features: []const std.Target.x86.Feature = &.{};
+ inline while (comptime feature_it.next()) |feature| features = features ++ .{@field(std.Target.x86.Feature, feature)};
+ break :has_features target.cpu.hasAll(.x86, features);
+ },
+ }) continue;
+
+ switch (data.mode) {
+ .none, .short => if (rex_required) continue,
+ .rex, .rex_short => if (!rex_required) continue,
+ else => {},
+ }
+ for (input_ops, data.ops) |input_op, data_op| if (!input_op.isSubset(data_op)) continue :next;
+
+ const enc: Encoding = .{ .mnemonic = mnemonic, .data = data };
+ if (shortest_enc) |previous_shortest_enc| {
+ const len = estimateInstructionLength(prefix, enc, ops);
+ const previous_shortest_len = shortest_len orelse
+ estimateInstructionLength(prefix, previous_shortest_enc, ops);
+ if (len < previous_shortest_len) {
+ shortest_enc = enc;
+ shortest_len = len;
+ } else shortest_len = previous_shortest_len;
+ } else shortest_enc = enc;
+ }
+ return shortest_enc;
+}
+
+/// Returns first matching encoding by opcode.
+pub fn findByOpcode(opc: []const u8, prefixes: struct {
+ legacy: LegacyPrefixes,
+ rex: Rex,
+}, modrm_ext: ?u3) ?Encoding {
+ for (mnemonic_to_encodings_map, 0..) |encs, mnemonic_int| for (encs) |data| {
+ const enc = Encoding{ .mnemonic = @as(Mnemonic, @enumFromInt(mnemonic_int)), .data = data };
+ if (modrm_ext) |ext| if (ext != data.modrm_ext) continue;
+ if (!std.mem.eql(u8, opc, enc.opcode())) continue;
+ if (prefixes.rex.w) {
+ if (!data.mode.isLong()) continue;
+ } else if (prefixes.rex.present and !prefixes.rex.isSet()) {
+ if (!data.mode.isRex()) continue;
+ } else if (prefixes.legacy.prefix_66) {
+ if (!data.mode.isShort()) continue;
+ } else {
+ if (data.mode.isShort()) continue;
+ }
+ return enc;
+ };
+ return null;
+}
+
+pub fn opcode(encoding: *const Encoding) []const u8 {
+ return encoding.data.opc[0..encoding.data.opc_len];
+}
+
+pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
+ const prefix = encoding.data.opc[0];
+ return switch (prefix) {
+ 0x66, 0xf2, 0xf3 => prefix,
+ else => null,
+ };
+}
+
+pub fn modRmExt(encoding: Encoding) u3 {
+ return switch (encoding.data.op_en) {
+ .ia, .m, .mi, .m1, .mc, .vm, .vmi => encoding.data.modrm_ext,
+ else => unreachable,
+ };
+}
+
+pub fn format(encoding: Encoding, writer: *std.Io.Writer) std.Io.Writer.Error!void {
+ var opc = encoding.opcode();
+ if (encoding.data.mode.isVex()) {
+ try writer.writeAll("VEX.");
+
+ try writer.writeAll(switch (encoding.data.mode) {
+ .vex_128_w0, .vex_128_w1, .vex_128_wig => "128",
+ .vex_256_w0, .vex_256_w1, .vex_256_wig => "256",
+ .vex_lig_w0, .vex_lig_w1, .vex_lig_wig => "LIG",
+ .vex_lz_w0, .vex_lz_w1, .vex_lz_wig => "LZ",
+ else => unreachable,
+ });
+
+ switch (opc[0]) {
+ else => {},
+ 0x66, 0xf3, 0xf2 => {
+ try writer.print(".{X:0>2}", .{opc[0]});
+ opc = opc[1..];
+ },
+ }
+
+ try writer.print(".{X}", .{opc[0 .. opc.len - 1]});
+ opc = opc[opc.len - 1 ..];
+
+ try writer.writeAll(".W");
+ try writer.writeAll(switch (encoding.data.mode) {
+ .vex_128_w0, .vex_256_w0, .vex_lig_w0, .vex_lz_w0 => "0",
+ .vex_128_w1, .vex_256_w1, .vex_lig_w1, .vex_lz_w1 => "1",
+ .vex_128_wig, .vex_256_wig, .vex_lig_wig, .vex_lz_wig => "IG",
+ else => unreachable,
+ });
+
+ try writer.writeByte(' ');
+ } else if (encoding.data.mode.isLong()) try writer.writeAll("REX.W + ");
+ for (opc) |byte| try writer.print("{x:0>2} ", .{byte});
+
+ switch (encoding.data.op_en) {
+ .z, .fd, .td, .i, .zi, .ii, .d => {},
+ .o, .zo, .oz, .oi => {
+ const op = switch (encoding.data.op_en) {
+ .o, .oz, .oi => encoding.data.ops[0],
+ .zo => encoding.data.ops[1],
+ else => unreachable,
+ };
+ const tag = switch (op) {
+ .r8 => "rb",
+ .r16 => "rw",
+ .r32 => "rd",
+ .r64 => "rd",
+ else => unreachable,
+ };
+ try writer.print("+{s} ", .{tag});
+ },
+ .ia, .m, .mi, .m1, .mc, .vm, .vmi => try writer.print("/{d} ", .{encoding.modRmExt()}),
+ .mr, .rm, .rmi, .mri, .mrc, .rm0, .rvm, .rvmr, .rvmi, .mvr, .rmv => try writer.writeAll("/r "),
+ }
+
+ switch (encoding.data.op_en) {
+ .i, .d, .zi, .ii, .ia, .oi, .mi, .rmi, .mri, .vmi, .rvmi => for (0..2) |i| {
+ const op = switch (i) {
+ 0 => switch (encoding.data.op_en) {
+ .i, .ii, .ia, .d => encoding.data.ops[0],
+ .zi, .oi, .mi => encoding.data.ops[1],
+ .rmi, .mri, .vmi => encoding.data.ops[2],
+ .rvmi => encoding.data.ops[3],
+ else => unreachable,
+ },
+ 1 => switch (encoding.data.op_en) {
+ .ii => encoding.data.ops[1],
+ else => break,
+ },
+ else => unreachable,
+ };
+ const tag = switch (op) {
+ .imm8, .imm8s => "ib",
+ .imm16, .imm16s => "iw",
+ .imm32, .imm32s => "id",
+ .imm64 => "io",
+ .rel8 => "cb",
+ .rel16 => "cw",
+ .rel32 => "cd",
+ else => unreachable,
+ };
+ try writer.print("{s} ", .{tag});
+ },
+ .rvmr => try writer.writeAll("/is4 "),
+ .z, .fd, .td, .o, .zo, .oz, .m, .m1, .mc, .mr, .rm, .mrc, .rm0, .vm, .rvm, .mvr, .rmv => {},
+ }
+
+ try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
+
+ for (encoding.data.ops) |op| switch (op) {
+ .none => break,
+ else => try writer.print("{s} ", .{@tagName(op)}),
+ };
+
+ const op_en = switch (encoding.data.op_en) {
+ .zi => .i,
+ else => |op_en| op_en,
+ };
+ try writer.print("{s}", .{@tagName(op_en)});
+}
+
+pub const Mnemonic = enum {
+ // Directives
+ @".cfi_def_cfa",
+ @".cfi_def_cfa_register",
+ @".cfi_def_cfa_offset",
+ @".cfi_adjust_cfa_offset",
+ @".cfi_offset",
+ @".cfi_val_offset",
+ @".cfi_rel_offset",
+ @".cfi_register",
+ @".cfi_restore",
+ @".cfi_undefined",
+ @".cfi_same_value",
+ @".cfi_remember_state",
+ @".cfi_restore_state",
+ @".cfi_escape",
+ // zig fmt: off
+ // General-purpose
+ aaa, aad, aam, aas, adc, add, @"and", arpl,
+ bound, bsf, bsr, bswap, bt, btc, btr, bts,
+ call, cbw, cdq, cdqe,
+ clac, clc, cld, cldemote, clflush, clflushopt, cli, clts, clui, clrssbsy, clwb, cmc,
+ cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
+ cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
+ cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz,
+ cmp, cmps, cmpsb, cmpsd, cmpsq, cmpsw, cmpxchg, cmpxchg8b, cmpxchg16b,
+ cpuid, cqo, cwd, cwde,
+ daa, das, dec, div,
+ endbr32, endbr64, enqcmd, enqcmds, enter,
+ hlt, hreset,
+ idiv, imul, in, inc, incsspd, incsspq, ins, insb, insd, insw,
+ int, int1, int3, into, invd, invlpg, invpcid, iret, iretd, iretq, iretw,
+ ja, jae, jb, jbe, jc, jcxz, je, jecxz, jg, jge, jl, jle, jmp, jna, jnae, jnb, jnbe,
+ jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, jrcxz, js, jz,
+ lahf, lar, lea, leave, lfence, lgdt, lidt, lldt, lmsw, loop, loope, loopne,
+ lods, lodsb, lodsd, lodsq, lodsw,
+ lsl, ltr,
+ mfence, mov, movbe,
+ movs, movsb, movsd, movsq, movsw,
+ movsx, movsxd, movzx, mul,
+ neg, nop, not,
+ @"or", out, outs, outsb, outsd, outsw,
+ pause, pop, popf, popfd, popfq, push, pushfq,
+ rcl, rcr,
+ rdfsbase, rdgsbase, rdmsr, rdpid, rdpkru, rdpmc, rdrand, rdseed, rdsspd, rdsspq, rdtsc, rdtscp,
+ ret, rol, ror, rsm,
+ sahf, sal, sar, sbb,
+ scas, scasb, scasd, scasq, scasw,
+ senduipi, serialize,
+ shl, shld, shr, shrd,
+ stac, stc, std, sti, str, stui,
+ sub, swapgs, syscall, sysenter, sysexit, sysret,
+ seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae,
+ setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns,
+ setnz, seto, setp, setpe, setpo, sets, setz,
+ sfence, sidt, sldt, smsw,
+ stos, stosb, stosd, stosq, stosw,
+ @"test", testui, tpause,
+ ud0, ud1, ud2, uiret, umonitor, umwait,
+ verr, verw, wrfsbase, wrgsbase, wrmsr, wrpkru, wrssd, wrssq, wrussd, wrussq,
+ xadd, xchg, xgetbv, xlat, xlatb, xor,
+ // X87
+ f2xm1, fabs, fadd, faddp, fbld, fbstp, fchs, fclex,
+ fcmovb, fcmovbe, fcmove, fcmovnb, fcmovnbe, fcmovne, fcmovnu, fcmovu,
+ fcom, fcomi, fcomip, fcomp, fcompp, fcos,
+ fdecstp, fdiv, fdivp, fdivr, fdivrp, ffree,
+ fiadd, ficom, ficomp, fidiv, fidivr, fild, fimul, fincstp, finit,
+ fist, fistp, fisub, fisubr,
+ fld, fld1, fldcw, fldenv, fldl2e, fldl2t, fldlg2, fldln2, fldpi, fldz,
+ fmul, fmulp,
+ fnclex, fninit, fnop, fnsave, fnstcw, fnstenv, fnstsw,
+ fpatan, fprem, fprem1, fptan, frndint, frstor,
+ fsave, fscale, fsin, fsincos, fsqrt,
+ fst, fstcw, fstenv, fstp, fstsw,
+ fsub, fsubp, fsubr, fsubrp,
+ ftst, fucom, fucomi, fucomip, fucomp, fucompp,
+ fwait, fxam, fxch, fxtract, fyl2x, fyl2xp1, wait,
+ // MMX
+ emms, movd, movq,
+ packssdw, packsswb, packuswb,
+ paddb, paddd, paddsb, paddsw, paddusb, paddusw, paddw,
+ pand, pandn, por, pxor,
+ pcmpeqb, pcmpeqd, pcmpeqw,
+ pcmpgtb, pcmpgtd, pcmpgtw,
+ pmaddwd, pmulhw, pmullw,
+ pslld, psllq, psllw,
+ psrad, psraw,
+ psrld, psrlq, psrlw,
+ psubb, psubd, psubsb, psubsw, psubusb, psubusw, psubw,
+ // SSE
+ addps, addss,
+ andnps, andps,
+ cmpps, cmpss, comiss,
+ cvtpi2ps, cvtps2pi, cvtsi2ss, cvtss2si, cvttps2pi, cvttss2si,
+ divps, divss,
+ fxrstor, fxrstor64, fxsave, fxsave64,
+ ldmxcsr,
+ maxps, maxss,
+ minps, minss,
+ movaps, movhlps, movhps, movlhps, movlps,
+ movmskps,
+ movss, movups,
+ mulps, mulss,
+ orps,
+ pavgb, pavgw,
+ pextrw, pinsrw,
+ pmaxsw, pmaxub, pminsw, pminub, pmovmskb, pmulhuw,
+ prefetchit0, prefetchit1, prefetchnta, prefetcht0, prefetcht1, prefetcht2, prefetchw, prefetchwt1,
+ psadbw, pshufw,
+ shufps,
+ sqrtps, sqrtss,
+ stmxcsr,
+ subps, subss,
+ ucomiss, unpckhps, unpcklps,
+ xorps,
+ // SSE2
+ addpd, addsd,
+ andpd,
+ andnpd,
+ cmppd, //cmpsd,
+ comisd,
+ cvtdq2pd, cvtdq2ps, cvtpd2dq, cvtpd2pi, cvtpd2ps, cvtpi2pd,
+ cvtps2dq, cvtps2pd, cvtsd2si, cvtsd2ss, cvtsi2sd, cvtss2sd,
+ cvttpd2dq, cvttpd2pi, cvttps2dq, cvttsd2si,
+ divpd, divsd,
+ gf2p8affineinvqb, gf2p8affineqb, gf2p8mulb,
+ maxpd, maxsd,
+ minpd, minsd,
+ movapd,
+ movdq2q, movdqa, movdqu,
+ movhpd, movlpd,
+ movmskpd, movq2dq,
+ //movsd,
+ movupd,
+ mulpd, mulsd,
+ orpd,
+ paddq, pmuludq,
+ pshufd, pshufhw, pshuflw,
+ pslldq, psrldq, psubq,
+ punpckhbw, punpckhdq, punpckhqdq, punpckhwd,
+ punpcklbw, punpckldq, punpcklqdq, punpcklwd,
+ shufpd,
+ sqrtpd, sqrtsd,
+ subpd, subsd,
+ ucomisd, unpckhpd, unpcklpd,
+ xorpd,
+ // SSE3
+ addsubpd, addsubps,
+ fisttp,
+ haddpd, haddps,
+ hsubpd, hsubps,
+ lddqu,
+ movddup, movshdup, movsldup,
+ // SSSE3
+ pabsb, pabsd, pabsw, palignr,
+ phaddw, phaddsw, phaddd, phsubw, phsubsw, phsubd,
+ pmaddubsw, pmulhrsw, pshufb,
+ psignb, psignd, psignw,
+ // SSE4.1
+ blendpd, blendps, blendvpd, blendvps,
+ dppd, dpps,
+ extractps,
+ insertps,
+ packusdw,
+ pblendvb, pblendw,
+ pcmpeqq,
+ pextrb, pextrd, pextrq,
+ phminposuw,
+ pinsrb, pinsrd, pinsrq,
+ pmaxsb, pmaxsd, pmaxud, pmaxuw, pminsb, pminsd, pminud, pminuw,
+ pmovsxbd, pmovsxbq, pmovsxbw, pmovsxdq, pmovsxwd, pmovsxwq,
+ pmovzxbd, pmovzxbq, pmovzxbw, pmovzxdq, pmovzxwd, pmovzxwq,
+ pmuldq, pmulld,
+ ptest,
+ roundpd, roundps, roundsd, roundss,
+ // SSE4.2
+ crc32, pcmpgtq,
+ // ABM
+ lzcnt, popcnt,
+ // PCLMUL
+ pclmulqdq,
+ // AES
+ aesdec, aesdeclast, aesenc, aesenclast, aesimc, aeskeygenassist,
+ // SHA
+ sha1rnds4, sha1nexte, sha1msg1, sha1msg2, sha256msg1, sha256msg2, sha256rnds2,
+ // AVX
+ vaddpd, vaddps, vaddsd, vaddss, vaddsubpd, vaddsubps,
+ vaesdec, vaesdeclast, vaesenc, vaesenclast, vaesimc, vaeskeygenassist,
+ vandnpd, vandnps, vandpd, vandps,
+ vblendpd, vblendps, vblendvpd, vblendvps,
+ vbroadcastf128, vbroadcastsd, vbroadcastss,
+ vcmppd, vcmpps, vcmpsd, vcmpss, vcomisd, vcomiss,
+ vcvtdq2pd, vcvtdq2ps, vcvtpd2dq, vcvtpd2ps,
+ vcvtps2dq, vcvtps2pd, vcvtsd2si, vcvtsd2ss,
+ vcvtsi2sd, vcvtsi2ss, vcvtss2sd, vcvtss2si,
+ vcvttpd2dq, vcvttps2dq, vcvttsd2si, vcvttss2si,
+ vdivpd, vdivps, vdivsd, vdivss,
+ vdppd, vdpps,
+ vextractf128, vextractps,
+ vgf2p8affineinvqb, vgf2p8affineqb, vgf2p8mulb,
+ vhaddpd, vhaddps, vhsubpd, vhsubps,
+ vinsertf128, vinsertps,
+ vlddqu, vldmxcsr,
+ vmaskmovpd, vmaskmovps,
+ vmaxpd, vmaxps, vmaxsd, vmaxss,
+ vminpd, vminps, vminsd, vminss,
+ vmovapd, vmovaps,
+ vmovd,
+ vmovddup,
+ vmovdqa, vmovdqu,
+ vmovhlps, vmovhpd, vmovhps, vmovlhps, vmovlpd, vmovlps,
+ vmovmskpd, vmovmskps,
+ vmovq,
+ vmovsd,
+ vmovshdup, vmovsldup,
+ vmovss,
+ vmovupd, vmovups,
+ vmulpd, vmulps, vmulsd, vmulss,
+ vorpd, vorps,
+ vpabsb, vpabsd, vpabsw,
+ vpackssdw, vpacksswb, vpackusdw, vpackuswb,
+ vpaddb, vpaddd, vpaddq, vpaddsb, vpaddsw, vpaddusb, vpaddusw, vpaddw,
+ vpalignr, vpand, vpandn, vpavgb, vpavgw,
+ vpblendvb, vpblendw, vpclmulqdq,
+ vpcmpeqb, vpcmpeqd, vpcmpeqq, vpcmpeqw,
+ vpcmpgtb, vpcmpgtd, vpcmpgtq, vpcmpgtw,
+ vperm2f128, vpermilpd, vpermilps,
+ vpextrb, vpextrd, vpextrq, vpextrw,
+ vphaddw, vphaddsw, vphaddd, vphminposuw, vphsubw, vphsubsw, vphsubd,
+ vpinsrb, vpinsrd, vpinsrq, vpinsrw,
+ vpmaddubsw, vpmaddwd,
+ vpmaxsb, vpmaxsd, vpmaxsw, vpmaxub, vpmaxud, vpmaxuw,
+ vpminsb, vpminsd, vpminsw, vpminub, vpminud, vpminuw,
+ vpmovmskb,
+ vpmovsxbd, vpmovsxbq, vpmovsxbw, vpmovsxdq, vpmovsxwd, vpmovsxwq,
+ vpmovzxbd, vpmovzxbq, vpmovzxbw, vpmovzxdq, vpmovzxwd, vpmovzxwq,
+ vpmuldq, vpmulhrsw, vpmulhuw, vpmulhw, vpmulld, vpmullw, vpmuludq,
+ vpor,
+ vpsadbw, vpshufb, vpshufd, vpshufhw, vpshuflw,
+ vpsignb, vpsignd, vpsignw,
+ vpslld, vpslldq, vpsllq, vpsllw,
+ vpsrad, vpsraq, vpsraw,
+ vpsrld, vpsrldq, vpsrlq, vpsrlw,
+ vpsubb, vpsubd, vpsubq, vpsubsb, vpsubsw, vpsubusb, vpsubusw, vpsubw,
+ vptest,
+ vpunpckhbw, vpunpckhdq, vpunpckhqdq, vpunpckhwd,
+ vpunpcklbw, vpunpckldq, vpunpcklqdq, vpunpcklwd,
+ vpxor,
+ vroundpd, vroundps, vroundsd, vroundss,
+ vshufpd, vshufps,
+ vsqrtpd, vsqrtps, vsqrtsd, vsqrtss,
+ vstmxcsr,
+ vsubpd, vsubps, vsubsd, vsubss,
+ vtestpd, vtestps,
+ vucomisd, vucomiss, vunpckhpd, vunpckhps, vunpcklpd, vunpcklps,
+ vxorpd, vxorps,
+ // BMI
+ andn, bextr, blsi, blsmsk, blsr, tzcnt,
+ // BMI2
+ bzhi, mulx, pdep, pext, rorx, sarx, shlx, shrx,
+ // F16C
+ vcvtph2ps, vcvtps2ph,
+ // FMA
+ vfmadd132pd, vfmadd213pd, vfmadd231pd,
+ vfmadd132ps, vfmadd213ps, vfmadd231ps,
+ vfmadd132sd, vfmadd213sd, vfmadd231sd,
+ vfmadd132ss, vfmadd213ss, vfmadd231ss,
+ // AVX2
+ vbroadcasti128, vpbroadcastb, vpbroadcastd, vpbroadcastq, vpbroadcastw,
+ vextracti128, vinserti128, vpblendd,
+ vperm2i128, vpermd, vpermpd, vpermps, vpermq,
+ vpmaskmovd, vpmaskmovq,
+ vpsllvd, vpsllvq, vpsravd, vpsrlvd, vpsrlvq,
+ // ADX
+ adcx, adox,
+ // AESKLE
+ aesdec128kl, aesdec256kl, aesenc128kl, aesenc256kl, encodekey128, encodekey256, loadiwkey,
+ // AESKLEWIDE_KL
+ aesdecwide128kl, aesdecwide256kl, aesencwide128kl, aesencwide256kl,
+ // zig fmt: on
+};
+
+pub const OpEn = enum {
+ // zig fmt: off
+ z,
+ o, zo, oz, oi,
+ i, zi, ii, ia,
+ d, m,
+ fd, td,
+ m1, mc, mi, mr, rm,
+ rmi, mri, mrc,
+ rm0, vm, vmi, rvm, rvmr, rvmi, mvr, rmv,
+ // zig fmt: on
+};
+
+pub const Op = enum {
+ // zig fmt: off
+ none,
+ unity,
+ imm8, imm16, imm32, imm64,
+ imm8s, imm16s, imm32s,
+ al, ax, eax, rax,
+ cl, dx,
+ rip, eip, ip,
+ r8, r16, r32, r64,
+ rm8, rm16, rm32, rm64,
+ r32_m8, r32_m16, r64_m16,
+ m8, m16, m32, m64, m80, m128, m256,
+ rel8, rel16, rel32,
+ m, moffs, mrip8,
+ sreg,
+ st0, st, mm, mm_m64,
+ xmm0, xmm, xmm_m8, xmm_m16, xmm_m32, xmm_m64, xmm_m128,
+ ymm, ymm_m256,
+ cr, dr,
+ // zig fmt: on
+
+ pub fn fromOperand(operand: Instruction.Operand, target: *const std.Target) Op {
+ return switch (operand) {
+ .none => .none,
+
+ .reg => |reg| switch (reg.class()) {
+ .general_purpose => switch (reg) {
+ .al => .al,
+ .ax => .ax,
+ .eax => .eax,
+ .rax => .rax,
+ .cl => .cl,
+ .dx => .dx,
+ else => switch (reg.size().bitSize(target)) {
+ 8 => .r8,
+ 16 => .r16,
+ 32 => .r32,
+ 64 => .r64,
+ else => unreachable,
+ },
+ },
+ .gphi => .r8,
+ .segment => .sreg,
+ .x87 => switch (reg) {
+ .st0 => .st0,
+ else => .st,
+ },
+ .mmx => .mm,
+ .sse => switch (reg) {
+ .xmm0 => .xmm0,
+ else => switch (reg.size().bitSize(target)) {
+ 128 => .xmm,
+ 256 => .ymm,
+ else => unreachable,
+ },
+ },
+ .ip => switch (reg) {
+ .rip => .rip,
+ .eip => .eip,
+ .ip => .ip,
+ else => unreachable,
+ },
+ .cr => .cr,
+ .dr => .dr,
+ },
+
+ .mem => |mem| switch (mem) {
+ .moffs => .moffs,
+ .sib => switch (mem.bitSize(target)) {
+ 0 => .m,
+ 8 => .m8,
+ 16 => .m16,
+ 32 => .m32,
+ 64 => .m64,
+ 80 => .m80,
+ 128 => .m128,
+ 256 => .m256,
+ else => unreachable,
+ },
+ .rip => switch (mem.bitSize(target)) {
+ 0, 8 => .mrip8,
+ 16 => .m16,
+ 32 => .m32,
+ 64 => .m64,
+ 80 => .m80,
+ 128 => .m128,
+ 256 => .m256,
+ else => unreachable,
+ },
+ },
+
+ .imm => |imm| switch (imm) {
+ .signed => |x| if (x == 1)
+ .unity
+ else if (math.cast(i8, x)) |_|
+ .imm8s
+ else if (math.cast(i16, x)) |_|
+ .imm16s
+ else
+ .imm32s,
+ .unsigned => |x| if (x == 1)
+ .unity
+ else if (math.cast(i8, x)) |_|
+ .imm8s
+ else if (math.cast(u8, x)) |_|
+ .imm8
+ else if (math.cast(i16, x)) |_|
+ .imm16s
+ else if (math.cast(u16, x)) |_|
+ .imm16
+ else if (math.cast(i32, x)) |_|
+ .imm32s
+ else if (math.cast(u32, x)) |_|
+ .imm32
+ else
+ .imm64,
+ },
+
+ .bytes => unreachable,
+ };
+ }
+
+ pub fn toReg(op: Op) Register {
+ return switch (op) {
+ else => .none,
+ .al => .al,
+ .ax => .ax,
+ .eax => .eax,
+ .rax => .rax,
+ .cl => .cl,
+ .dx => .dx,
+ .rip => .rip,
+ .eip => .eip,
+ .ip => .ip,
+ .st0 => .st0,
+ .xmm0 => .xmm0,
+ };
+ }
+
+ pub fn immBitSize(op: Op) u64 {
+ return switch (op) {
+ .none, .m, .moffs, .mrip8, .sreg => unreachable,
+ .al, .cl, .dx, .rip, .eip, .ip, .r8, .rm8, .r32_m8 => unreachable,
+ .ax, .r16, .rm16 => unreachable,
+ .eax, .r32, .rm32, .r32_m16 => unreachable,
+ .rax, .r64, .rm64, .r64_m16 => unreachable,
+ .st0, .st, .mm, .mm_m64 => unreachable,
+ .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
+ .ymm, .ymm_m256 => unreachable,
+ .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
+ .cr, .dr => unreachable,
+ .unity => 1,
+ .imm8, .imm8s, .rel8 => 8,
+ .imm16, .imm16s, .rel16 => 16,
+ .imm32, .imm32s, .rel32 => 32,
+ .imm64 => 64,
+ };
+ }
+
+ pub fn regBitSize(op: Op) u64 {
+ return switch (op) {
+ .none, .m, .moffs, .mrip8, .sreg => unreachable,
+ .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
+ .rel8, .rel16, .rel32 => unreachable,
+ .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
+ .al, .cl, .r8, .rm8 => 8,
+ .ax, .dx, .ip, .r16, .rm16 => 16,
+ .eax, .eip, .r32, .rm32, .r32_m8, .r32_m16 => 32,
+ .rax, .rip, .r64, .rm64, .r64_m16, .mm, .mm_m64, .cr, .dr => 64,
+ .st0, .st => 80,
+ .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
+ .ymm, .ymm_m256 => 256,
+ };
+ }
+
+ pub fn memBitSize(op: Op) u64 {
+ return switch (op) {
+ .none, .m, .moffs, .sreg => unreachable,
+ .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
+ .rel8, .rel16, .rel32 => unreachable,
+ .al, .cl, .r8, .ax, .dx, .ip, .r16, .eax, .eip, .r32, .rax, .rip, .r64 => unreachable,
+ .st0, .st, .mm, .xmm0, .xmm, .ymm => unreachable,
+ .cr, .dr => unreachable,
+ .mrip8, .m8, .rm8, .r32_m8, .xmm_m8 => 8,
+ .m16, .rm16, .r32_m16, .r64_m16, .xmm_m16 => 16,
+ .m32, .rm32, .xmm_m32 => 32,
+ .m64, .rm64, .mm_m64, .xmm_m64 => 64,
+ .m80 => 80,
+ .m128, .xmm_m128 => 128,
+ .m256, .ymm_m256 => 256,
+ };
+ }
+
+ pub fn isSigned(op: Op) bool {
+ return switch (op) {
+ .unity, .imm8, .imm16, .imm32, .imm64 => false,
+ .imm8s, .imm16s, .imm32s => true,
+ .rel8, .rel16, .rel32 => true,
+ else => unreachable,
+ };
+ }
+
+ pub fn isUnsigned(op: Op) bool {
+ return !op.isSigned();
+ }
+
+ pub fn isRegister(op: Op) bool {
+ // zig fmt: off
+ return switch (op) {
+ .al, .ax, .eax, .rax,
+ .cl, .dx,
+ .ip, .eip, .rip,
+ .r8, .r16, .r32, .r64,
+ .rm8, .rm16, .rm32, .rm64,
+ .r32_m8, .r32_m16, .r64_m16,
+ .st0, .st, .mm, .mm_m64,
+ .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
+ .ymm, .ymm_m256,
+ .cr, .dr,
+ => true,
+ else => false,
+ };
+ // zig fmt: on
+ }
+
+ pub fn isImmediate(op: Op) bool {
+ // zig fmt: off
+ return switch (op) {
+ .imm8, .imm16, .imm32, .imm64,
+ .imm8s, .imm16s, .imm32s,
+ .rel8, .rel16, .rel32,
+ .unity,
+ => true,
+ else => false,
+ };
+ // zig fmt: on
+ }
+
+ pub fn isMemory(op: Op) bool {
+ // zig fmt: off
+ return switch (op) {
+ .rm8, .rm16, .rm32, .rm64,
+ .r32_m8, .r32_m16, .r64_m16,
+ .m8, .m16, .m32, .m64, .m80, .m128, .m256,
+ .m, .moffs, .mrip8,
+ .mm_m64,
+ .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
+ .ymm_m256,
+ => true,
+ else => false,
+ };
+ // zig fmt: on
+ }
+
+ pub fn isSegmentRegister(op: Op) bool {
+ return switch (op) {
+ .moffs, .sreg => true,
+ else => false,
+ };
+ }
+
+ pub fn class(op: Op) bits.Register.Class {
+ return switch (op) {
+ else => unreachable,
+ .al, .ax, .eax, .rax, .cl, .dx => .general_purpose,
+ .r8, .r16, .r32, .r64 => .general_purpose,
+ .rm8, .rm16, .rm32, .rm64 => .general_purpose,
+ .r32_m8, .r32_m16, .r64_m16 => .general_purpose,
+ .sreg => .segment,
+ .st0, .st => .x87,
+ .mm, .mm_m64 => .mmx,
+ .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
+ .ymm, .ymm_m256 => .sse,
+ .rip, .eip, .ip => .ip,
+ .cr => .cr,
+ .dr => .dr,
+ };
+ }
+
+ /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding.
+ pub fn isSubset(op: Op, target: Op) bool {
+ switch (op) {
+ .none, .m, .moffs, .sreg => return op == target,
+ else => {
+ if (op.isRegister() and target.isRegister()) {
+ return switch (target.toReg()) {
+ .none => op.class() == target.class() and op.regBitSize() == target.regBitSize(),
+ else => op == target,
+ };
+ }
+ if (op.isMemory() and target.isMemory()) {
+ switch (target) {
+ .m => return true,
+ .moffs, .mrip8 => return op == target,
+ else => return op.memBitSize() == target.memBitSize(),
+ }
+ }
+ if (op.isImmediate() and target.isImmediate()) {
+ switch (target) {
+ .imm64 => if (op.immBitSize() <= 64) return true,
+ .imm32s, .rel32 => if (op.immBitSize() < 32 or (op.immBitSize() == 32 and op.isSigned()))
+ return true,
+ .imm32 => if (op.immBitSize() <= 32) return true,
+ .imm16s, .rel16 => if (op.immBitSize() < 16 or (op.immBitSize() == 16 and op.isSigned()))
+ return true,
+ .imm16 => if (op.immBitSize() <= 16) return true,
+ .imm8s, .rel8 => if (op.immBitSize() < 8 or (op.immBitSize() == 8 and op.isSigned()))
+ return true,
+ .imm8 => if (op.immBitSize() <= 8) return true,
+ else => {},
+ }
+ return op == target;
+ }
+ return false;
+ },
+ }
+ }
+};
+
+pub const Mode = enum {
+ // zig fmt: off
+ none,
+ short, long,
+ rex, rex_short,
+ wait,
+ vex_128_w0, vex_128_w1, vex_128_wig,
+ vex_256_w0, vex_256_w1, vex_256_wig,
+ vex_lig_w0, vex_lig_w1, vex_lig_wig,
+ vex_lz_w0, vex_lz_w1, vex_lz_wig,
+ // zig fmt: on
+
+ pub fn isShort(mode: Mode) bool {
+ return switch (mode) {
+ .short, .rex_short => true,
+ else => false,
+ };
+ }
+
+ pub fn isLong(mode: Mode) bool {
+ return switch (mode) {
+ .long,
+ .vex_128_w1,
+ .vex_256_w1,
+ .vex_lig_w1,
+ .vex_lz_w1,
+ => true,
+ else => false,
+ };
+ }
+
+ pub fn isRex(mode: Mode) bool {
+ return switch (mode) {
+ else => false,
+ .rex, .rex_short => true,
+ };
+ }
+
+ pub fn isVex(mode: Mode) bool {
+ return switch (mode) {
+ // zig fmt: off
+ else => false,
+ .vex_128_w0, .vex_128_w1, .vex_128_wig,
+ .vex_256_w0, .vex_256_w1, .vex_256_wig,
+ .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
+ .vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
+ => true,
+ // zig fmt: on
+ };
+ }
+
+ pub fn isVecLong(mode: Mode) bool {
+ return switch (mode) {
+ // zig fmt: off
+ else => unreachable,
+ .vex_128_w0, .vex_128_w1, .vex_128_wig,
+ .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
+ .vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
+ => false,
+ .vex_256_w0, .vex_256_w1, .vex_256_wig,
+ => true,
+ // zig fmt: on
+ };
+ }
+};
+
+pub const Feature = enum {
+ none,
+ @"32bit",
+ @"64bit",
+ adx,
+ aes,
+ @"aes avx",
+ avx,
+ avx2,
+ bmi,
+ bmi2,
+ cldemote,
+ clflushopt,
+ clwb,
+ cmov,
+ @"cmov x87",
+ crc32,
+ enqcmd,
+ f16c,
+ fma,
+ fsgsbase,
+ fxsr,
+ gfni,
+ @"gfni avx",
+ hreset,
+ @"invpcid 32bit",
+ @"invpcid 64bit",
+ kl,
+ lzcnt,
+ mmx,
+ movbe,
+ pclmul,
+ @"pclmul avx",
+ pku,
+ popcnt,
+ prefetch,
+ @"prefetchi 64bit",
+ prefetchwt1,
+ prfchw,
+ rdrnd,
+ rdseed,
+ @"rdpid 32bit",
+ @"rdpid 64bit",
+ sahf,
+ serialize,
+ shstk,
+ smap,
+ sse,
+ sse2,
+ sse3,
+ @"sse3 x87",
+ sse4_1,
+ sse4_2,
+ ssse3,
+ sha,
+ uintr,
+ vaes,
+ vpclmulqdq,
+ waitpkg,
+ widekl,
+ x87,
+};
+
+fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize {
+ var inst: Instruction = .{
+ .prefix = prefix,
+ .encoding = encoding,
+ .ops = @splat(.none),
+ };
+ @memcpy(inst.ops[0..ops.len], ops);
+
+ // By using a buffer with maximum length of encoded instruction, we can use
+ // the `end` field of the Writer for the count.
+ var buf: [16]u8 = undefined;
+ var trash: std.Io.Writer.Discarding = .init(&buf);
+ inst.encode(&trash.writer, .{
+ .allow_frame_locs = true,
+ .allow_symbols = true,
+ }) catch {
+ // Since the function signature for encode() does not mention under what
+ // conditions it can fail, I have changed `unreachable` to `@panic` here.
+ // This is a TODO item since it indicates this function
+ // (`estimateInstructionLength`) has the wrong function signature.
+ @panic("unexpected failure to encode");
+ };
+ return trash.writer.end;
+}
+
+const mnemonic_to_encodings_map = init: {
+ @setEvalBranchQuota(5_900);
+ const ModrmExt = u3;
+ const Entry = struct { Mnemonic, OpEn, []const Op, []const u8, ModrmExt, Mode, Feature };
+ const encodings: []const Entry = @import("encodings.zon");
+
+ const mnemonic_count = @typeInfo(Mnemonic).@"enum".fields.len;
+ var mnemonic_map: [mnemonic_count][]Data = @splat(&.{});
+ for (encodings) |entry| mnemonic_map[@intFromEnum(entry[0])].len += 1;
+ var data_storage: [encodings.len]Data = undefined;
+ var storage_index: usize = 0;
+ for (&mnemonic_map) |*value| {
+ value.ptr = data_storage[storage_index..].ptr;
+ storage_index += value.len;
+ }
+ var mnemonic_index: [mnemonic_count]usize = @splat(0);
+ const ops_len = @typeInfo(@FieldType(Data, "ops")).array.len;
+ const opc_len = @typeInfo(@FieldType(Data, "opc")).array.len;
+ for (encodings) |entry| {
+ const index = &mnemonic_index[@intFromEnum(entry[0])];
+ mnemonic_map[@intFromEnum(entry[0])][index.*] = .{
+ .op_en = entry[1],
+ .ops = (entry[2] ++ .{.none} ** (ops_len - entry[2].len)).*,
+ .opc_len = entry[3].len,
+ .opc = (entry[3] ++ .{undefined} ** (opc_len - entry[3].len)).*,
+ .modrm_ext = entry[4],
+ .mode = entry[5],
+ .feature = entry[6],
+ };
+ index.* += 1;
+ }
+ const final_storage = data_storage;
+ var final_map: [mnemonic_count][]const Data = @splat(&.{});
+ storage_index = 0;
+ for (&final_map, mnemonic_map) |*final_value, value| {
+ final_value.* = final_storage[storage_index..][0..value.len];
+ storage_index += value.len;
+ }
+ break :init final_map;
+};