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};