| 1 | const Encoding = @This(); |
| 2 | |
| 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; |
| 5 | const math = std.math; |
| 6 | |
| 7 | const bits = @import("bits.zig"); |
| 8 | const encoder = @import("encoder.zig"); |
| 9 | const Instruction = encoder.Instruction; |
| 10 | const Operand = Instruction.Operand; |
| 11 | const Prefix = Instruction.Prefix; |
| 12 | const Register = bits.Register; |
| 13 | const Rex = encoder.Rex; |
| 14 | const LegacyPrefixes = encoder.LegacyPrefixes; |
| 15 | |
| 16 | mnemonic: Mnemonic, |
| 17 | data: Data, |
| 18 | |
| 19 | const 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 | |
| 29 | pub 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. |
| 120 | pub 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 | |
| 142 | pub fn opcode(encoding: *const Encoding) []const u8 { |
| 143 | return encoding.data.opc[0..encoding.data.opc_len]; |
| 144 | } |
| 145 | |
| 146 | pub 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 | |
| 154 | pub 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 | |
| 161 | pub 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 | |
| 264 | pub 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 | |
| 546 | pub 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 | |
| 559 | pub 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 | |
| 871 | pub 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 | |
| 937 | pub 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 | |
| 1000 | fn 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 | |
| 1025 | const 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 | }; |