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[@backingInt(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, @fromBackingInt(@intCast(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 inline .signed, .unsigned => |x| if (x == 1)
653 .unity
654 else if (math.cast(i8, x)) |_|
655 .imm8s
656 else if (math.cast(u8, x)) |_|
657 .imm8
658 else if (math.cast(i16, x)) |_|
659 .imm16s
660 else if (math.cast(u16, x)) |_|
661 .imm16
662 else if (math.cast(i32, x)) |_|
663 .imm32s
664 else if (math.cast(u32, x)) |_|
665 .imm32
666 else
667 .imm64,
668 },
669
670 .bytes => unreachable,
671 };
672 }
673
674 pub fn toReg(op: Op) Register {
675 return switch (op) {
676 else => .none,
677 .al => .al,
678 .ax => .ax,
679 .eax => .eax,
680 .rax => .rax,
681 .cl => .cl,
682 .dx => .dx,
683 .rip => .rip,
684 .eip => .eip,
685 .ip => .ip,
686 .st0 => .st0,
687 .xmm0 => .xmm0,
688 };
689 }
690
691 pub fn immBitSize(op: Op) u64 {
692 return switch (op) {
693 .none, .m, .moffs, .mrip8, .sreg => unreachable,
694 .al, .cl, .dx, .rip, .eip, .ip, .r8, .rm8, .r32_m8 => unreachable,
695 .ax, .r16, .rm16 => unreachable,
696 .eax, .r32, .rm32, .r32_m16 => unreachable,
697 .rax, .r64, .rm64, .r64_m16 => unreachable,
698 .st0, .st, .mm, .mm_m64 => unreachable,
699 .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
700 .ymm, .ymm_m256 => unreachable,
701 .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
702 .cr, .dr => unreachable,
703 .unity => 1,
704 .imm8, .imm8s, .rel8 => 8,
705 .imm16, .imm16s, .rel16 => 16,
706 .imm32, .imm32s, .rel32 => 32,
707 .imm64 => 64,
708 };
709 }
710
711 pub fn regBitSize(op: Op) u64 {
712 return switch (op) {
713 .none, .m, .moffs, .mrip8, .sreg => unreachable,
714 .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
715 .rel8, .rel16, .rel32 => unreachable,
716 .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
717 .al, .cl, .r8, .rm8 => 8,
718 .ax, .dx, .ip, .r16, .rm16 => 16,
719 .eax, .eip, .r32, .rm32, .r32_m8, .r32_m16 => 32,
720 .rax, .rip, .r64, .rm64, .r64_m16, .mm, .mm_m64, .cr, .dr => 64,
721 .st0, .st => 80,
722 .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
723 .ymm, .ymm_m256 => 256,
724 };
725 }
726
727 pub fn memBitSize(op: Op) u64 {
728 return switch (op) {
729 .none, .m, .moffs, .sreg => unreachable,
730 .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
731 .rel8, .rel16, .rel32 => unreachable,
732 .al, .cl, .r8, .ax, .dx, .ip, .r16, .eax, .eip, .r32, .rax, .rip, .r64 => unreachable,
733 .st0, .st, .mm, .xmm0, .xmm, .ymm => unreachable,
734 .cr, .dr => unreachable,
735 .mrip8, .m8, .rm8, .r32_m8, .xmm_m8 => 8,
736 .m16, .rm16, .r32_m16, .r64_m16, .xmm_m16 => 16,
737 .m32, .rm32, .xmm_m32 => 32,
738 .m64, .rm64, .mm_m64, .xmm_m64 => 64,
739 .m80 => 80,
740 .m128, .xmm_m128 => 128,
741 .m256, .ymm_m256 => 256,
742 };
743 }
744
745 pub fn isSigned(op: Op) bool {
746 return switch (op) {
747 .unity, .imm8, .imm16, .imm32, .imm64 => false,
748 .imm8s, .imm16s, .imm32s => true,
749 .rel8, .rel16, .rel32 => true,
750 else => unreachable,
751 };
752 }
753
754 pub fn isUnsigned(op: Op) bool {
755 return !op.isSigned();
756 }
757
758 pub fn isRegister(op: Op) bool {
759 // zig fmt: off
760 return switch (op) {
761 .al, .ax, .eax, .rax,
762 .cl, .dx,
763 .ip, .eip, .rip,
764 .r8, .r16, .r32, .r64,
765 .rm8, .rm16, .rm32, .rm64,
766 .r32_m8, .r32_m16, .r64_m16,
767 .st0, .st, .mm, .mm_m64,
768 .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
769 .ymm, .ymm_m256,
770 .cr, .dr,
771 => true,
772 else => false,
773 };
774 // zig fmt: on
775 }
776
777 pub fn isImmediate(op: Op) bool {
778 // zig fmt: off
779 return switch (op) {
780 .imm8, .imm16, .imm32, .imm64,
781 .imm8s, .imm16s, .imm32s,
782 .rel8, .rel16, .rel32,
783 .unity,
784 => true,
785 else => false,
786 };
787 // zig fmt: on
788 }
789
790 pub fn isMemory(op: Op) bool {
791 // zig fmt: off
792 return switch (op) {
793 .rm8, .rm16, .rm32, .rm64,
794 .r32_m8, .r32_m16, .r64_m16,
795 .m8, .m16, .m32, .m64, .m80, .m128, .m256,
796 .m, .moffs, .mrip8,
797 .mm_m64,
798 .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128,
799 .ymm_m256,
800 => true,
801 else => false,
802 };
803 // zig fmt: on
804 }
805
806 pub fn isSegmentRegister(op: Op) bool {
807 return switch (op) {
808 .moffs, .sreg => true,
809 else => false,
810 };
811 }
812
813 pub fn class(op: Op) bits.Register.Class {
814 return switch (op) {
815 else => unreachable,
816 .al, .ax, .eax, .rax, .cl, .dx => .general_purpose,
817 .r8, .r16, .r32, .r64 => .general_purpose,
818 .rm8, .rm16, .rm32, .rm64 => .general_purpose,
819 .r32_m8, .r32_m16, .r64_m16 => .general_purpose,
820 .sreg => .segment,
821 .st0, .st => .x87,
822 .mm, .mm_m64 => .mmx,
823 .xmm0, .xmm, .xmm_m8, .xmm_m16, .xmm_m32, .xmm_m64, .xmm_m128 => .sse,
824 .ymm, .ymm_m256 => .sse,
825 .rip, .eip, .ip => .ip,
826 .cr => .cr,
827 .dr => .dr,
828 };
829 }
830
831 /// Given an operand `op` checks if `target` is a subset for the purposes of the encoding.
832 pub fn isSubset(op: Op, target: Op) bool {
833 switch (op) {
834 .none, .m, .moffs, .sreg => return op == target,
835 else => {
836 if (op.isRegister() and target.isRegister()) {
837 return switch (target.toReg()) {
838 .none => op.class() == target.class() and op.regBitSize() == target.regBitSize(),
839 else => op == target,
840 };
841 }
842 if (op.isMemory() and target.isMemory()) {
843 switch (target) {
844 .m => return true,
845 .moffs, .mrip8 => return op == target,
846 else => return op.memBitSize() == target.memBitSize(),
847 }
848 }
849 if (op.isImmediate() and target.isImmediate()) {
850 switch (target) {
851 .imm64 => if (op.immBitSize() <= 64) return true,
852 .imm32s, .rel32 => if (op.immBitSize() < 32 or (op.immBitSize() == 32 and op.isSigned()))
853 return true,
854 .imm32 => if (op.immBitSize() <= 32) return true,
855 .imm16s, .rel16 => if (op.immBitSize() < 16 or (op.immBitSize() == 16 and op.isSigned()))
856 return true,
857 .imm16 => if (op.immBitSize() <= 16) return true,
858 .imm8s, .rel8 => if (op.immBitSize() < 8 or (op.immBitSize() == 8 and op.isSigned()))
859 return true,
860 .imm8 => if (op.immBitSize() <= 8) return true,
861 else => {},
862 }
863 return op == target;
864 }
865 return false;
866 },
867 }
868 }
869};
870
871pub const Mode = enum {
872 // zig fmt: off
873 none,
874 short, long,
875 rex, rex_short,
876 wait,
877 vex_128_w0, vex_128_w1, vex_128_wig,
878 vex_256_w0, vex_256_w1, vex_256_wig,
879 vex_lig_w0, vex_lig_w1, vex_lig_wig,
880 vex_lz_w0, vex_lz_w1, vex_lz_wig,
881 // zig fmt: on
882
883 pub fn isShort(mode: Mode) bool {
884 return switch (mode) {
885 .short, .rex_short => true,
886 else => false,
887 };
888 }
889
890 pub fn isLong(mode: Mode) bool {
891 return switch (mode) {
892 .long,
893 .vex_128_w1,
894 .vex_256_w1,
895 .vex_lig_w1,
896 .vex_lz_w1,
897 => true,
898 else => false,
899 };
900 }
901
902 pub fn isRex(mode: Mode) bool {
903 return switch (mode) {
904 else => false,
905 .rex, .rex_short => true,
906 };
907 }
908
909 pub fn isVex(mode: Mode) bool {
910 return switch (mode) {
911 // zig fmt: off
912 else => false,
913 .vex_128_w0, .vex_128_w1, .vex_128_wig,
914 .vex_256_w0, .vex_256_w1, .vex_256_wig,
915 .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
916 .vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
917 => true,
918 // zig fmt: on
919 };
920 }
921
922 pub fn isVecLong(mode: Mode) bool {
923 return switch (mode) {
924 // zig fmt: off
925 else => unreachable,
926 .vex_128_w0, .vex_128_w1, .vex_128_wig,
927 .vex_lig_w0, .vex_lig_w1, .vex_lig_wig,
928 .vex_lz_w0, .vex_lz_w1, .vex_lz_wig,
929 => false,
930 .vex_256_w0, .vex_256_w1, .vex_256_wig,
931 => true,
932 // zig fmt: on
933 };
934 }
935};
936
937pub const Feature = enum {
938 none,
939 @"32bit",
940 @"64bit",
941 adx,
942 aes,
943 @"aes avx",
944 avx,
945 avx2,
946 bmi,
947 bmi2,
948 cldemote,
949 clflushopt,
950 clwb,
951 cmov,
952 @"cmov x87",
953 crc32,
954 enqcmd,
955 f16c,
956 fma,
957 fsgsbase,
958 fxsr,
959 gfni,
960 @"gfni avx",
961 hreset,
962 @"invpcid 32bit",
963 @"invpcid 64bit",
964 kl,
965 lzcnt,
966 mmx,
967 movbe,
968 pclmul,
969 @"pclmul avx",
970 pku,
971 popcnt,
972 prefetch,
973 @"prefetchi 64bit",
974 prefetchwt1,
975 prfchw,
976 rdrnd,
977 rdseed,
978 @"rdpid 32bit",
979 @"rdpid 64bit",
980 sahf,
981 serialize,
982 shstk,
983 smap,
984 sse,
985 sse2,
986 sse3,
987 @"sse3 x87",
988 sse4_1,
989 sse4_2,
990 ssse3,
991 sha,
992 uintr,
993 vaes,
994 vpclmulqdq,
995 waitpkg,
996 widekl,
997 x87,
998};
999
1000fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize {
1001 var inst: Instruction = .{
1002 .prefix = prefix,
1003 .encoding = encoding,
1004 .ops = @splat(.none),
1005 };
1006 @memcpy(inst.ops[0..ops.len], ops);
1007
1008 // By using a buffer with maximum length of encoded instruction, we can use
1009 // the `end` field of the Writer for the count.
1010 var buf: [16]u8 = undefined;
1011 var trash: std.Io.Writer.Discarding = .init(&buf);
1012 inst.encode(&trash.writer, .{
1013 .allow_frame_locs = true,
1014 .allow_symbols = true,
1015 }) catch {
1016 // Since the function signature for encode() does not mention under what
1017 // conditions it can fail, I have changed `unreachable` to `@panic` here.
1018 // This is a TODO item since it indicates this function
1019 // (`estimateInstructionLength`) has the wrong function signature.
1020 @panic("unexpected failure to encode");
1021 };
1022 return trash.writer.end;
1023}
1024
1025const mnemonic_to_encodings_map = init: {
1026 @setEvalBranchQuota(5_900);
1027 const ModrmExt = u3;
1028 const Entry = struct { Mnemonic, OpEn, []const Op, []const u8, ModrmExt, Mode, Feature };
1029 const encodings: []const Entry = @import("encodings.zon");
1030
1031 const mnemonic_count = @typeInfo(Mnemonic).@"enum".field_names.len;
1032 var mnemonic_map: [mnemonic_count][]Data = @splat(&.{});
1033 for (encodings) |entry| mnemonic_map[@backingInt(entry[0])].len += 1;
1034 var data_storage: [encodings.len]Data = undefined;
1035 var storage_index: usize = 0;
1036 for (&mnemonic_map) |*value| {
1037 value.ptr = data_storage[storage_index..].ptr;
1038 storage_index += value.len;
1039 }
1040 var mnemonic_index: [mnemonic_count]usize = @splat(0);
1041 const ops_len = @typeInfo(@FieldType(Data, "ops")).array.len;
1042 const opc_len = @typeInfo(@FieldType(Data, "opc")).array.len;
1043 for (encodings) |entry| {
1044 const index = &mnemonic_index[@backingInt(entry[0])];
1045 mnemonic_map[@backingInt(entry[0])][index.*] = .{
1046 .op_en = entry[1],
1047 .ops = ops: {
1048 var ops: [ops_len]Op = @splat(.none);
1049 @memcpy(ops[0..entry[2].len], entry[2]);
1050 break :ops ops;
1051 },
1052 .opc_len = entry[3].len,
1053 .opc = opc: {
1054 var opc: [opc_len]u8 = @splat(undefined);
1055 @memcpy(opc[0..entry[3].len], entry[3]);
1056 break :opc opc;
1057 },
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};