x86.js (40788B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 (function($scope, $as) { 7 "use strict"; 8 9 // Import. 10 const base = $scope.base ? $scope.base : require("./base.js"); 11 12 const dict = base.dict; 13 const NONE = base.NONE; 14 const Parsing = base.Parsing; 15 const MapUtils = base.MapUtils; 16 17 // Export. 18 const x86 = $scope[$as] = {}; 19 20 function FAIL(msg) { throw new Error("[X86] " + msg); } 21 22 // Database 23 // ======== 24 25 x86.dbName = "isa_x86.json"; 26 27 // Metadata Tables 28 // =============== 29 30 const ArchGroupInfo = dict({ 31 "ry": ["ANY", "X64"], 32 "rv": ["ANY", "ANY", "X64"] 33 }); 34 35 // Groups are used by instruction tables to group multiple operand combinations into a single record. In general 36 // X86 and X86_64 instructions can be divided into GP and SIMD groups, where GP groups use `ry/my` syntax to 37 // specify operation for 16/32/64 bit registers and "xy/mxy"/"xyz/mxyz" groups to specify a SIMD instruction that 38 // uses either XMM/YMM (AVX) or XMM/YMM/ZMM registers (AVX-512). 39 const OperandGroupInfo = dict({ 40 "ry" : { "group": "ry" , "subst": ["r32", "r64"] }, 41 "my" : { "group": "ry" , "subst": ["m32", "m64"] }, 42 "axy" : { "group": "ry" , "subst": ["eax", "rax"] }, 43 "bxy" : { "group": "ry" , "subst": ["ebx", "rbx"] }, 44 "cxy" : { "group": "ry" , "subst": ["ecx", "rcx"] }, 45 "dxy" : { "group": "ry" , "subst": ["edx", "rdx"] }, 46 47 "rv" : { "group": "rv" , "subst": ["r16", "r32", "r64"] }, 48 "mv" : { "group": "rv" , "subst": ["m16", "m32", "m64"] }, 49 "axv" : { "group": "rv" , "subst": ["ax", "eax", "rax"] }, 50 "bxv" : { "group": "rv" , "subst": ["bx", "ebx", "rbx"] }, 51 "cxv" : { "group": "rv" , "subst": ["cx", "ecx", "rcx"] }, 52 "dxv" : { "group": "rv" , "subst": ["dx", "edx", "rdx"] }, 53 "immv" : { "group": "rv" , "subst": ["imm16", "imm32", "imms32"] }, 54 55 "xy" : { "group": "xy" , "subst": ["xmm", "ymm"] }, 56 "mxy" : { "group": "xy" , "subst": ["m128", "m256"] }, 57 58 "xxx" : { "group": "xyz", "subst": ["xmm[31:0]", "xmm[63:0]", "xmm"] }, 59 "xxy" : { "group": "xyz", "subst": ["xmm[63:0]", "xmm", "ymm"] }, 60 "xyz" : { "group": "xyz", "subst": ["xmm", "ymm", "zmm"] }, 61 "mxxx" : { "group": "xyz", "subst": ["m32", "m64", "m128"] }, 62 "mxxy" : { "group": "xyz", "subst": ["m64", "m128", "m256"] }, 63 "mxyz" : { "group": "xyz", "subst": ["m128", "m256", "m512"] } 64 }); 65 66 const OpcodeGroupInfo = dict({ 67 "Wy" : { "group": "ry" , "subst": ["W0", "W1"] }, 68 "iv" : { "group": "rv" , "subst": ["iw", "id", "id"] }, 69 "Pv" : { "group": "rv" , "subst": ["66", "NP", "NP"] }, 70 "Wv" : { "group": "rv" , "subst": ["W0", "W0", "W1"] } 71 }); 72 73 // Instruction tables use various notations to specify L/LL field, which is used by VEX/EVEX/XOP encodings. This 74 // field has 1 bit (VEX/XOP) and 2 bits (EVEX) and in general the notation used is 128/256/512, which determines 75 // the size of SIMD operation, and this is also the notation we want to convert everything else into. 76 const OpcodeLLMapping = dict({ 77 "128": "128", 78 "256": "256", 79 "512": "512", 80 "LZ" : "128", 81 "LLZ": "128", 82 "L0" : "128", 83 "L1" : "256", 84 "LIG": "LIG", 85 "Lxy": "xy", 86 "xyz": "xyz" 87 }); 88 89 const RegSize = Object.freeze({ 90 "r8" : 8, 91 "r8hi": 8, 92 "r16" : 16, 93 "r32" : 32, 94 "r64" : 64, 95 "mm" : 64, 96 "xmm" : 128, 97 "ymm" : 256, 98 "zmm" : 512, 99 "tmm" : 512, // Maximum size (64 bytes). 100 "bnd" : 128, 101 "k" : 64, 102 "st" : 80 103 }); 104 105 // CpuRegs 106 // ======= 107 108 // Build an object containing CPU registers as keys mapping them to type, kind, and index. 109 function buildCpuRegs(defs) { 110 const map = dict(); 111 112 for (let type in defs) { 113 const def = defs[type]; 114 const kind = def.kind; 115 const names = def.names; 116 const group = def.group; 117 118 if (def.any) 119 map[def.any] = { type: type, kind: kind, index: -1, group: group }; 120 121 if (names) { 122 for (let i = 0; i < names.length; i++) { 123 let name = names[i]; 124 let m = /^([A-Za-z\(\)]+)(\d+)-(\d+)([A-Za-z\(\)]*)$/.exec(name); 125 126 if (m) { 127 let a = parseInt(m[2], 10); 128 let b = parseInt(m[3], 10); 129 130 for (let n = a; n <= b; n++) { 131 const index = m[1] + n + m[4]; 132 map[index] = { type: type, kind: kind, index: index }; 133 } 134 } 135 else { 136 map[name] = { type: type, kind: kind, index: i }; 137 } 138 } 139 } 140 } 141 142 // HACK: In instruction manuals `r8` denotes low 8-bit register, however, 143 // that collides with `r8`, which is a 64-bit register. Since the result 144 // of this function is only used internally we patch it to be compatible 145 // with what Intel specifies. 146 map.r8.type = "r8"; 147 148 return map; 149 } 150 151 const CpuRegisters = buildCpuRegs({ 152 "r8" : { "kind": "gp" , "any": "r8" , "names": ["al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8-15b"] }, 153 "r8hi": { "kind": "gp" , "names": ["ah", "ch", "dh", "bh"] }, 154 "r16" : { "kind": "gp" , "any": "r16" , "names": ["ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8-15w"] }, 155 "r32" : { "kind": "gp" , "any": "r32" , "names": ["eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8-15d"] }, 156 "r64" : { "kind": "gp" , "any": "r64" , "names": ["rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8-15"] }, 157 "rxx" : { "kind": "gp" , "names": ["zax", "zcx", "zdx", "zbx", "zsp", "zbp", "zsi", "zdi"] }, 158 "sreg": { "kind": "sreg", "any": "sreg" , "names": ["es", "cs", "ss", "ds", "fs", "gs" ] }, 159 "creg": { "kind": "creg", "any": "creg" , "names": ["cr0-15"] }, 160 "dreg": { "kind": "dreg", "any": "dreg" , "names": ["dr0-15"] }, 161 "bnd" : { "kind": "bnd" , "any": "bnd" , "names": ["bnd0-3"] }, 162 "st" : { "kind": "st" , "any": "st(i)", "names": ["st(0-7)"] }, 163 "mm" : { "kind": "mm" , "any": "mm" , "names": ["mm0-7"] }, 164 "k" : { "kind": "k" , "any": "k" , "names": ["k0-7"] }, 165 "xmm" : { "kind": "vec" , "any": "xmm" , "names": ["xmm0-31"] }, 166 "ymm" : { "kind": "vec" , "any": "ymm" , "names": ["ymm0-31"] }, 167 "zmm" : { "kind": "vec" , "any": "zmm" , "names": ["zmm0-31"] }, 168 "tmm" : { "kind": "tile", "any": "tmm" , "names": ["tmm0-7"] } 169 }); 170 171 // asmdb.x86.Utils 172 // =============== 173 174 // X86/X64 utilities. 175 class Utils { 176 static groupOf(op) { 177 return Object.hasOwn(OperandGroupInfo, op) ? OperandGroupInfo[op].group : null; 178 } 179 180 static splitInstructionSignature(s) { 181 let prefixes = []; 182 if (s.startsWith("[")) { 183 const prefixEnd = Parsing.matchClosingChar(s, 0); 184 prefixes = s.substring(1, prefixEnd).replace("xacqrel", "xacquire|xrelease").split("|"); 185 186 s = s.substring(prefixEnd + 1).trim(); 187 } 188 189 let nameEnd = s.indexOf(" "); 190 let names = s.substring(0, nameEnd === -1 ? s.length : nameEnd); 191 let operands = nameEnd === -1 ? "" : s.substring(nameEnd + 1).trim(); 192 193 if (names.endsWith("{nf}")) { 194 names = names.substring(0, names.length - 4); 195 prefixes.nf = true; 196 } 197 198 return { 199 names: names.split("|"), 200 prefixes: prefixes, 201 operands: operands 202 } 203 } 204 205 // Split the operand(s) string into individual operands as defined by the 206 // instruction database. 207 // 208 // NOTE: X86/X64 doesn't require anything else than separating the commas, 209 // this function is here for compatibility with other instruction sets. 210 static splitOperands(s) { 211 const array = s.split(","); 212 for (let i = 0; i < array.length; i++) 213 array[i] = array[i].trim(); 214 return array; 215 } 216 217 // Get whether the string `s` describes a register operand. 218 static isRegOp(s) { return s && Object.hasOwn(CpuRegisters, s); } 219 // Get whether the string `s` describes a memory operand. 220 static isMemOp(s) { return s && /^(?:mem|mib|tmem|moff||(?:m(?:off)?\d+(?:dec|bcd|fp|int)?)|(?:m16_\d+)|(?:vm\d+(?:x|y|z)))$/.test(s); } 221 // Get whether the string `s` describes an immediate operand. 222 static isImmOp(s) { return s && /^(?:1|imm4|imm8|imm16|imm32|imm64|imms8|imms32|immu16|immu32|immv|if|p16_16|p16_32|dfv)$/.test(s); } 223 // Get whether the string `s` describes a relative displacement (label). 224 static isRelOp(s) { return s && /^rel\d+$/.test(s); } 225 226 // Get a register type of a `s`, returns `null` if the register is unknown. 227 static regTypeOf(s) { return Object.hasOwn(CpuRegisters, s) ? CpuRegisters[s].type : null; } 228 // Get a register kind of a `s`, returns `null` if the register is unknown. 229 static regKindOf(s) { return Object.hasOwn(CpuRegisters, s) ? CpuRegisters[s].kind : null; } 230 // Get a register type of a `s`, returns `null` if the register is unknown and `-1` 231 // if the given string does only represent a register type, but not a specific reg. 232 static regIndexOf(s) { return Object.hasOwn(CpuRegisters, s) ? CpuRegisters[s].index : null; } 233 234 static regSize(s) { 235 if (s in RegSize) 236 return RegSize[s]; 237 238 const reg = CpuRegisters[s]; 239 if (reg && reg.type in RegSize) 240 return RegSize[reg.type]; 241 242 return -1; 243 } 244 245 // Get size of an immediate `s` [in bits]. 246 // 247 // Handles "ib", "iw", "id", "if", "iq", and also "/is4". 248 static immSize(s) { 249 switch (s) { 250 case "/is4" : return 4; 251 case "imm4" : return 4; 252 case "1" : return 8; 253 case "imm8" : return 8; 254 case "imm16" : return 16; 255 case "imm32" : return 32; 256 case "imm64" : return 64; 257 case "imms8" : return 8; 258 case "imms32": return 32; 259 case "immu16": return 16; 260 case "immu32": return 32; 261 case "ib" : 262 case "ub" : return 8; 263 case "iw" : 264 case "uw" : return 16; 265 case "id" : 266 case "ud" : return 32; 267 case "iq" : 268 case "uq" : return 64; 269 case "p16_16": return 32; 270 case "if" : 271 case "p16_32": return 48; 272 273 // Influences EVEX encoding, not an immediate byte. 274 case "dfv" : return 0; 275 276 // Invalid immediate. 277 default : FAIL(`Invalid immediate ${s}`); 278 } 279 } 280 281 // Get size of a relative displacement [in bits]. 282 static relSize(s) { 283 switch (s) { 284 case "rel8" : return 8; 285 case "rel16" : return 16; 286 case "rel32" : return 32; 287 default : return -1; 288 } 289 } 290 } 291 x86.Utils = Utils; 292 293 // asmdb.x86.Operand 294 // ================= 295 296 // X86/X64 operand. 297 class Operand extends base.Operand { 298 constructor() { 299 super(); 300 301 this.groupPattern = ""; // Group pattern in case this operand was created from a group. 302 this.memSegment = ""; // Segment specified with register that is used to perform a memory IO. 303 this.memOff = false; // Memory operand is an absolute offset (only a specific version of MOV). 304 this.memFar = false; // Memory is a far pointer (includes segment in first two bytes). 305 this.vsibReg = ""; // AVX VSIB register type (xmm/ymm/zmm). 306 this.vsibSize = -1; // AVX VSIB register size (32/64). 307 this.bcstSize = -1; // AVX-512 broadcast size. 308 } 309 310 _substituteGroupOp(op, groupIndex) { 311 const opPart = op.match(/^([A-Za-z]+)/); 312 if (opPart) { 313 const groupPattern = Utils.groupOf(opPart[1]); 314 if (groupPattern) { 315 this.groupPattern = groupPattern; 316 return OperandGroupInfo[opPart[1]].subst[groupIndex] + op.substring(opPart[1].length); 317 } 318 } 319 return op; 320 } 321 322 assignData(data, defaultAccess, groupIndex) { 323 let s = data; 324 this.data = data; 325 326 const type = []; 327 328 // Handle RWX decorators prefix "[RWwXx]:". 329 let access = defaultAccess; 330 const access_match = /^(R|W|w|X|x)(\?)?\:/.exec(s); 331 if (access_match) { 332 // TODO: Conditional access is ignored at the moment. 333 access = access_match[1]; 334 s = s.substring(access_match[0].length); 335 } 336 337 // Handle commutativity attribute. 338 if (Parsing.isCommutative(s)) { 339 this.commutative = true; 340 s = Parsing.clearCommutative(s); 341 } 342 343 // Handle AVX-512 broadcast possibility specified as "/bN" suffix. 344 const mBcst = /\/b(\d+)/.exec(s); 345 if (mBcst) { 346 this.bcstSize = parseInt(mBcst[1], 10); 347 348 // Remove the broadcast attribute from the definition; it's not needed anymore. 349 s = s.substring(0, mBcst.index) + s.substring(mBcst.index + mBcst[0].length); 350 } 351 352 // Handle <implicit> attribute. 353 if (Parsing.isImplicit(s)) { 354 this.implicit = true; 355 s = Parsing.clearImplicit(s); 356 } 357 358 // Support multiple operands separated by "/" (only used by r/m). 359 let ops = s.split("/"); 360 let oArr = []; 361 362 for (let i = 0; i < ops.length; i++) { 363 let origOp = ops[i].trim(); 364 let op = this._substituteGroupOp(origOp, groupIndex); 365 366 // Handle range suffix [A] or [A:B]: 367 const mRange = /\[(\d+)\s*(?:\:\s*(\d+)\s*)?\]$/.exec(op); 368 if (mRange) { 369 const a = parseInt(mRange[1], 10); 370 const b = parseInt(mRange[2] || String(a), 10); 371 372 if (a < b) 373 FAIL(`Operand '${origOp}' contains invalid range '[${a}:${b}]'`) 374 375 this.rwxIndex = b; 376 this.rwxWidth = a - b + 1; 377 378 op = op.substring(0, op.length - mRange[0].length); 379 } 380 381 // Handle a segment specification if this is an implicit register performing memory access. 382 const memSegRegM = op.match(/\((ds|es)\:\s*([\w]+)\)$/); 383 if (memSegRegM) { 384 this.memSegment = memSegRegM[1]; 385 this.memRegOnly = memSegRegM[2]; 386 op = op.substring(0, memSegRegM.index).trim(); 387 } 388 389 oArr.push(op); 390 391 let regIndexRel = 0; 392 if (op.endsWith("+1") || op.endsWith("+2") || op.endsWith("+3")) { 393 regIndexRel = parseInt(op.substr(op.length - 1, 1)); 394 op = op.substring(0, op.length - 2); 395 } 396 397 // Group substitution - when a rv/mv instruction uses 'w' or 'x' access it's only used by 398 // the 16-bit form, 32-bit and 64-bit always use 'W' and 'X' when used in a 'rv/mv' group. 399 if (this.groupPattern === "rv" && groupIndex > 0 && access !== "R") { 400 access = access.toUpperCase(); 401 } 402 403 if (Utils.isRegOp(op)) { 404 this.reg = op; 405 this.regType = Utils.regTypeOf(op); 406 this.regIndexRel = regIndexRel; 407 this.setAccess(access); 408 409 type.push("reg"); 410 continue; 411 } 412 413 if (Utils.isMemOp(op)) { 414 this.mem = op; 415 this.setAccess(access); 416 417 // Handle memory size. 418 const mOff = /^m(?:off)?(\d+)/.exec(op); 419 this.memSize = mOff ? parseInt(mOff[1], 10) : 0; 420 this.memOff = op.indexOf("moff") === 0; 421 422 const mSeg = /^m16_(\d+)/.exec(op); 423 if (mSeg) { 424 this.memFar = true; 425 this.memSize = parseInt(mSeg[1], 10) + 16; 426 } 427 428 // Handle vector addressing mode and size "vmXXr". 429 const mVM = /^vm(\d+)(x|y|z)$/.exec(op); 430 if (mVM) { 431 this.vsibReg = mVM[2] + "mm"; 432 this.vsibSize = parseInt(mVM[1], 10); 433 } 434 435 type.push("mem"); 436 continue; 437 } 438 439 if (Utils.isImmOp(op)) { 440 const size = Utils.immSize(op); 441 if (!this.imm) 442 this.imm = size; 443 else if (this.imm !== size) 444 FAIL(`Immediate size mismatch: ${this.imm} != ${size}`); 445 446 // Sign-extend / zero-extend. 447 const sign = op.startsWith("imms") ? "signed" : 448 op.startsWith("immu") ? "unsigned" : "any"; 449 this.immSign = sign; 450 451 if (op === "1") { 452 this.immValue = 1; 453 this.implicit = true; 454 } 455 456 if (type.indexOf("imm") !== -1) 457 type.push("imm"); 458 continue; 459 } 460 461 if (Utils.isRelOp(op)) { 462 this.rel = Utils.relSize(op); 463 464 type.push("rel"); 465 continue; 466 } 467 468 FAIL(`Operand '${origOp}' unhandled`); 469 } 470 471 // In case the data has been modified it's always better to use the stripped off 472 // version as we have already processed and stored all the possible decorators. 473 this.data = oArr.join("/"); 474 this.type = type.join("/"); 475 476 if (this.rwxIndex === -1) { 477 const opSize = this.isReg() ? this.regSize : 478 this.isMem() ? this.memSize : -1; 479 if (opSize !== -1) { 480 this.rwxIndex = 0; 481 this.rwxWidth = opSize; 482 } 483 } 484 } 485 486 get regSize() { 487 return Utils.regSize(this.reg); 488 } 489 490 setAccess(x) { 491 const u = x.toUpperCase(); 492 this.zext = x === "W" || x === "X"; 493 this.read = u === "R" || u === "X"; 494 this.write = u === "W" || u === "X"; 495 return this; 496 } 497 498 499 isFixedReg() { return this.reg && this.reg !== this.regType && this.reg !== "st(i)"; } 500 isFixedMem() { return this.memSegment && this.isFixedReg(); } 501 502 isPartialOp() { 503 const maybePartial = this.regType === "r8" || 504 this.regType === "r8hi" || 505 this.regType === "r16" || 506 this.regType === "xmm"; 507 return maybePartial && !this.zext; 508 } 509 510 toRegMem() { 511 if (this.reg && this.mem) 512 return this.reg + "/m"; 513 else if (this.mem && (this.vsibReg || /fp$|int$/.test(this.mem))) 514 return this.mem; 515 else if (this.mem) 516 return "m"; 517 else 518 return this.toString(); 519 } 520 521 toString() { return this.data; } 522 } 523 x86.Operand = Operand; 524 525 // asmdb.x86.Instruction 526 // ===================== 527 528 // X86/X64 instruction. 529 class Instruction extends base.Instruction { 530 constructor(db) { 531 super(db); 532 533 this.opcode = dict({ 534 byte : "", // Opcode byte (a single value specified as HEX string "00-FF"). 535 ri : false, // Instruction opcode is combined with register, "XX+r" or "XX+i". 536 _67h : false, // Opcode 67h prefix use. 537 mm : "", // Opcode MM[MMM] part (map). 538 pp : "", // Opcode PP part. 539 w : "", // Opcode W field. 540 l : "", // EVEX.LL (nothing, 128, 256, 512, LIG). 541 nd : 0, // EVEX.ND (new dest) field (default is false, specified as ND=0 or ND=1). 542 nf : 0, // EVEX.NF (no flags) field (default is false, specified as NF=0 or NF=1). 543 scc : "", // EVEX.SCC field (4 bits - condition flags). 544 mod : "", // MODRM.MOD part (2 bits) - either "xx", "11" or "!(11)". 545 modr : "", // MODRM.R part (3 bits) - either "rrr" 546 modrm: "" // MODRM.R/M part - either "bbb" 547 }); 548 549 this.prefix = ""; // Prefix - "", "3DNOW", "EVEX", "VEX", "XOP". 550 this.privilege = "L3"; // Privilege level required to execute the instruction. 551 this.groupPattern = ""; // Group pattern in case the instruction was created from a group such as "ry", "rv", "xy", "xyz". 552 this.groupIndex = -1; // Group index. 553 554 this.rel = 0; // Displacement ("cb", "cw", and "cd" parts). 555 556 this.fpuTop = 0; // FPU top index manipulation [-1, 0, 1, 2]. 557 this.fpuStack = ""; // FPU stack manipulation 558 559 this.vsibReg = ""; // AVX VSIB register type (xmm/ymm/zmm). 560 this.vsibSize = -1; // AVX VSIB register size (32/64). 561 562 this.broadcast = false; // AVX-512 broadcast support. 563 this.bcstSize = -1; // AVX-512 broadcast size. 564 565 this.k = ""; // AVX-512 K function ("", "blend", "zeroing"). 566 this.kmask = false; // AVX-512 merging {k}. 567 this.zmask = false; // AVX-512 zeroing {kz}, implies {k}. 568 this.er = false; // AVX-512 embedded rounding {er}, implies {sae}. 569 this.sae = false; // AVX-512 suppress all exceptions {sae} support. 570 571 this.tupleType = ""; // AVX-512 tuple-type. 572 this.elementSize = -1; // Instruction's element size. 573 this.encodingPreference = ""; // Encoding preference (either nothing or "EVEX"). 574 575 this.consecutiveLead = 0; // Consecutive register leading N other registers. 576 this.prefixes = dict(); // Allowed prefixes. 577 } 578 579 _substituteOpcodePart(op, groupIndex) { 580 if (Object.hasOwn(OpcodeGroupInfo, op)) { 581 return OpcodeGroupInfo[op].subst[groupIndex]; 582 } 583 else { 584 return op; 585 } 586 } 587 588 assignData(data, groupIndex) { 589 this.name = data.name; 590 this.groupIndex = groupIndex; 591 592 if (data.tt) 593 this.tupleType = data.tt; 594 595 const em = data.op.match(/^\[\s*(\w+)\s*\](.*)$/); 596 const encodingField = em ? em[1] : "NONE"; 597 const opcodeField = em ? em[2] : data.op; 598 599 this._assignOperands(data.operands, groupIndex); 600 this._assignEncoding(encodingField); 601 this._assignOpcode(opcodeField.trim(), groupIndex); 602 603 for (let k in data) { 604 if (k === "name" || k === "op" || k === "operands") 605 continue; 606 this._assignAttribute(k, data[k]); 607 } 608 609 this._updateOperandsInfo(); 610 this._postProcess(); 611 } 612 613 _assignAttribute(key, value) { 614 switch (key) { 615 case "vl": 616 if (value) { 617 this.ext["AVX512_VL"] = true; 618 } 619 return; 620 621 case "prefixes": 622 this._combineAttribute("prefixes", value); 623 return; 624 625 case "fpuStack": 626 this.fpuStack = value; 627 switch (value) { 628 case "dec" : this.fpuTop = -1; break; 629 case "inc" : this.fpuTop = 1; break; 630 case "pop" : this.fpuTop = 1; break; 631 case "pop2x": this.fpuTop = 2; break; 632 case "push" : this.fpuTop = -1; break; 633 default: 634 FAIL(`Invalid fpuStack value '${value}'`); 635 } 636 return; 637 638 case "kz": 639 this.zmask = true; 640 this.kmask = true; 641 return; 642 643 case "k": 644 this.kmask = true; 645 if (typeof value === "string") 646 super._assignAttribute(key, value); 647 return; 648 649 case "er": 650 this.er = true; 651 this.sae = true; // {er} implies {sae}. 652 return; 653 654 case "sae": 655 this.sae = true; 656 return; 657 658 case "broadcast": 659 this.broadcast = true; 660 this.elementSize = value; 661 return; 662 663 default: 664 super._assignAttribute(key, value); 665 } 666 } 667 668 _assignOperands(s, groupIndex) { 669 if (!s) return; 670 671 // First remove all flags specified as {...}. We put them into `flags` 672 // map and mix with others. This seems to be the best we can do here. 673 for (;;) { 674 let a = s.indexOf("{"); 675 let b = s.indexOf("}"); 676 677 if (a === -1 || b === -1) 678 break; 679 680 // Get the `flag` and remove it from `s`. 681 this._assignAttribute(s.substring(a + 1, b), true); 682 s = s.substring(0, a) + s.substring(b + 1); 683 } 684 685 // Split into individual operands and push them to `operands`. 686 const arr = Utils.splitOperands(s); 687 for (let i = 0; i < arr.length; i++) { 688 const operand = new Operand(); 689 operand.assignData(arr[i].trim(), i === 0 ? "X" : "R", groupIndex); 690 691 if (operand.mem == "tmem") { 692 this.tsib = true; 693 } 694 695 if (operand.groupPattern && this.groupPattern !== operand.groupPattern) { 696 if (this.groupPattern) { 697 FAIL(`Instruction ${this.name}: Operand's group pattern mismatch '${this.groupPattern}' != '${operand.groupPattern}'`); 698 } 699 this.groupPattern = operand.groupPattern; 700 } 701 702 this.operands.push(operand); 703 } 704 } 705 706 _assignEncoding(s) { 707 this.encoding = s; 708 } 709 710 _assignOpcode(s, groupIndex) { 711 this.opcodeString = s; 712 713 let parts = s.split(" "); 714 715 if (/^(VEX|EVEX|XOP)\./.test(s)) { 716 // Parse VEX/XOP and EVEX encoded instruction, which looks like "<PREFIX>.[APX-DATA].<LL>.<PP>.<MAP>.<W>" 717 let prefix = parts[0].split("."); 718 this.prefix = prefix[0]; 719 720 for (let i = 1; i < prefix.length; i++) { 721 let comp = prefix[i]; 722 723 if (/^(Pv|Wv|Wy)$/.test(comp)) { 724 comp = OpcodeGroupInfo[comp].subst[groupIndex]; 725 } 726 727 // Process APX EVEX.ND field - ND=0 or ND=1. 728 if (/^ND=[01]$/.test(comp)) { 729 this.opcode.nd = comp === "ND=1"; 730 continue; 731 } 732 733 // Process APX EVEX.NF field - NF=0 or NF=1. 734 if (/^NF=[01]$/.test(comp)) { 735 this.opcode.nf = comp === "NF=1"; 736 continue; 737 } 738 739 // Process APX EVEX.SCC field - SCC=0-F 740 if (/^SCC=[0-9A-F]$/.test(comp)) { 741 this.opcode.scc = comp.charAt(5); 742 continue; 743 } 744 745 // Process `L/LL` field. 746 if (Object.hasOwn(OpcodeLLMapping, comp)) { 747 this.opcode.l = OpcodeLLMapping[comp]; 748 continue; 749 } 750 751 // Process `PP` field - 66/F2/F3/NP (NP means no PP field used) 752 if (comp === "P0") { /* ignored, `P` is zero... */ continue; } 753 if (/^(?:66|F2|F3|NP)$/.test(comp)) { this.opcode.pp = comp; continue; } 754 755 // Process `MM` field - 0F/0F3A/0F38/MAP4/MAP5/MAP6/M8/M9. 756 if (/^(?:0F|0F3A|0F38|MAP[4-9A])$/.test(comp)) { this.opcode.mm = comp; continue; } 757 758 // Process `W` field. 759 if (/^(WIG|W0|W1|)$/.test(comp)) { this.opcode.w = comp; continue; } 760 761 // TODO: Some new APX instructions don't have W specified (ENQCMD/ENQCMDS). 762 if (comp === "W?") { this.opcode.w = "W0"; continue; } 763 764 // ERROR. 765 this.report(`'${this.opcodeString}' Unhandled component: ${comp}`); 766 } 767 768 for (let i = 1; i < parts.length; i++) { 769 let comp = parts[i]; 770 771 // Parse opcode. 772 if (/^[0-9A-Fa-f]{2}$/.test(comp)) { 773 this.opcode.byte = comp.toUpperCase(); 774 continue; 775 } 776 777 // Parse ModR/M field using "/r" or "/0-7" notation. 778 if (/^\/[r0-7]$/.test(comp)) { 779 this.opcode.mod = "xx"; 780 this.opcode.modr = comp.charAt(1); 781 this.opcode.modm = "b"; 782 continue; 783 } 784 785 // Parse ModR/M field using "11:xxx:xxx" and "!(11):xxx:xxx" notation. 786 const m = comp.match(/^(11|!\(11\)):(rrr|[01]{3}):(bbb|[01]{3})$/); 787 if (m) { 788 this.opcode.mod = m[1]; 789 this.opcode.modr = m[2] === "rrr" ? "r" : String(parseInt(m[2], 2)); 790 this.opcode.modrm = m[3] === "bbb" ? "b" : String(parseInt(m[3], 2)); 791 continue; 792 } 793 794 // Parse immediate byte, word, dword, or qword. 795 comp = this._substituteOpcodePart(comp, groupIndex); 796 if (/^(?:ib|iw|id|iq|\/is4)$/.test(comp)) { 797 this.imm += Utils.immSize(comp); 798 continue; 799 } 800 801 this.report(`'${this.opcodeString}' Unhandled opcode component: ${comp}`); 802 } 803 } 804 else { 805 // Parse X86/X64 instruction (including legacy MMX/SSE/3DNOW instructions). 806 let rex_parsed = false; 807 808 for (let i = 0; i < parts.length; i++) { 809 let comp = parts[i]; 810 811 if (comp === "NFx" || comp === "NOREP" || comp === "NO67") { 812 // Ignored for now. 813 continue; 814 } 815 816 // Parse REX or REX2 prefix. 817 if (comp.startsWith("REX2.") || comp === "REX.W") { 818 if (rex_parsed) { 819 FAIL(`'${this.opcodeString}' Multiple REX prefixes are invalid`); 820 } 821 822 rex_parsed = true; 823 824 // Instructions that force REX.W prefix or use REX2 prefix are always 64-bit instructions. 825 this.arch = "X64"; 826 827 if (comp === "REX.W") { 828 this.opcode.w = "W1"; 829 } 830 else { 831 this.prefix = "REX2"; 832 833 // REX2 has always 3 components - "REX2.<MAP>.<W>". 834 const rex2 = comp.split("."); 835 if (rex2.length !== 3) { 836 FAIL(`'${this.opcodeString}' Invalid REX2 prefix - expected exactly 3 REX2 components`); 837 } 838 839 if (rex2[1] === "MAP0") { 840 // nothing. 841 } 842 else if (rex2[1] === "MAP1") { 843 this.opcode.mm = "0F"; 844 } 845 else { 846 FAIL(`'${this.opcodeString}' Invalid REX2 prefix - REX2.MAP component could be either MAP0 or MAP1`); 847 } 848 849 this.opcode.w = rex2[2]; 850 } 851 852 continue; 853 } 854 855 // Parse `PP` prefixes. 856 if (this.opcode.mm === "") { 857 if (this.opcode.pp === "" && /^(?:66|F2|F3|NP)$/.test(comp) || 858 this.opcode.pp === "66" && /^(?:F2|F3)$/.test(comp)) { 859 this.opcode.pp += comp; 860 continue; 861 } 862 } 863 864 // Parse `MM` prefixes. 865 if ((this.opcode.mm === "" && comp === "0F") || 866 (this.opcode.mm === "0F" && /^(?:01|3A|38)$/.test(comp))) { 867 this.opcode.mm += comp; 868 continue; 869 } 870 871 // Recognize "0F 0F /r XX" encoding. 872 if (this.opcode.mm === "0F" && comp === "0F") { 873 this.prefix = "3DNOW"; 874 continue; 875 } 876 877 // Parse opcode byte. 878 if (/^[0-9A-F]{2}(?:\+[ri])?$/.test(comp)) { 879 // Parse "+r" or "+i" suffix. 880 if (comp.length > 2) { 881 this.opcode.ri = true; 882 comp = comp.substring(0, 2); 883 } 884 885 // FPU instructions are encoded as "PREFIX XX", where prefix is not the same 886 // as MM prefixes used everywhere else. AsmJit internally extends MM field in 887 // instruction tables to allow storing this prefix together with other "MM" 888 // prefixes, currently the unused indexes are used, but if X86 moves forward 889 // and starts using these we can simply use more bits in the opcode DWORD. 890 if (!this.opcode.pp && this.opcode.byte === "9B") { 891 this.opcode.pp = this.opcode.byte; 892 this.opcode.byte = comp; 893 continue; 894 } 895 896 if (!this.opcode.mm && (/^(?:D8|D9|DA|DB|DC|DD|DE|DF)$/.test(this.opcode.byte))) { 897 this.opcode.mm = this.opcode.byte; 898 this.opcode.byte = comp; 899 continue; 900 } 901 902 if (this.opcode.byte) { 903 if (this.opcode.byte === "67") { 904 this.opcode._67h = true; 905 } 906 else { 907 if (!this.opcode.modr && !this.opcode.modrm) { 908 const value = parseInt(comp, 16); 909 if ((value & 0xC0) == 0xC0) { 910 this.opcode.mod = "11"; 911 this.opcode.modr = String((value >> 3) & 0x7); 912 this.opcode.modrm = String((value >> 0) & 0x7); 913 } 914 else { 915 this.report(`'${this.opcodeString}' Unsupported secondary opcode (MOD/RM) '${comp}' value`); 916 } 917 } 918 else { 919 this.report(`'${this.opcodeString}' Multiple opcodes, have ${this.opcode.byte}, found ${comp}`); 920 } 921 } 922 } 923 924 this.opcode.byte = comp; 925 continue; 926 } 927 928 // Parse ModR/M field using "/r" or "/0-7" notation. 929 if (/^\/[r0-7]$/.test(comp) && !this.opcode.modr) { 930 this.opcode.mod = "xx"; 931 this.opcode.modr = comp.charAt(1); 932 this.opcode.modm = "b"; 933 continue; 934 } 935 936 // Parse ModR/M field using "11:xxx:xxx" and "!(11):xxx:xxx" notation. 937 const m = comp.match(/^(11|!\(11\)):(rrr|[01]{3}):(bbb|[01]{3})$/); 938 if (m) { 939 this.opcode.mod = m[1]; 940 this.opcode.modr = m[2] === "rrr" ? "r" : String(parseInt(m[2], 2)); 941 this.opcode.modrm = m[3] === "bbb" ? "b" : String(parseInt(m[3], 2)); 942 continue; 943 } 944 945 // Parse immediate byte, word, dword, fword, or qword. 946 if (/^(?:ib|iw|id|iq|iv|if)$/.test(comp)) { 947 if (comp === "iv") 948 comp = OpcodeGroupInfo[comp].subst[groupIndex]; 949 this.imm += Utils.immSize(comp); 950 continue; 951 } 952 953 if (comp === "moff") { 954 this.moff = true; 955 continue; 956 } 957 958 // Parse displacement. 959 if (/^(?:cb|cw|cd)$/.test(comp) && !this.rel) { 960 this.rel = comp === "cb" ? 1 : 961 comp === "cw" ? 2 : 962 comp === "cd" ? 4 : -1; 963 continue; 964 } 965 966 // ERROR. 967 this.report(`'${this.opcodeString}' Unhandled opcode component: ${comp}`); 968 } 969 } 970 971 // HACK: Fix instructions having opcode "01". 972 if (this.opcode.byte === "" && this.opcode.mm.indexOf("0F01") === this.opcode.mm.length - 4) { 973 this.opcode.byte = "01"; 974 this.opcode.mm = this.opcode.mm.substring(0, this.opcode.mm.length - 2); 975 } 976 977 if (this.opcode.byte) 978 this.opcodeValue = parseInt(this.opcode.byte, 16); 979 980 if (!this.opcode.byte) 981 this.report(`Couldn't parse instruction's opcode '${this.opcodeString}'`); 982 } 983 984 _updateOperandsInfo() { 985 super._updateOperandsInfo(); 986 987 let consecutiveLead = null; 988 let consecutiveLastIndex = 0; 989 990 for (let i = 0; i < this.operands.length; i++) { 991 const op = this.operands[i]; 992 993 // Instructions that use 64-bit GP registers are always 64-bit instructions. 994 if (op.reg === "r64" || op.reg === "rax" || op.reg === "rbx" || op.reg === "rcx" || op.reg === "rdx" || op.reg === "rsi" || op.reg === "rdi") 995 this.arch = "X64"; 996 997 // Propagate broadcast. 998 if (op.bcstSize > 0) 999 this._assignAttribute("broadcast", op.bcstSize); 1000 1001 // Propagate VSIB. 1002 if (op.vsibReg) { 1003 if (this.vsibReg) { 1004 this.report("Only one operand can be a vector memory address (vmNNx)"); 1005 } 1006 1007 this.vsibReg = op.vsibReg; 1008 this.vsibSize = op.vsibSize; 1009 } 1010 1011 if (op.regIndexRel) { 1012 if (i - op.regIndexRel < 0) { 1013 this.report(`The consecutive register information is invalid, index of the lead (${i - op.regIndexRel}) is out of range`); 1014 } 1015 else { 1016 const lead = this.operands[i - op.regIndexRel]; 1017 if (consecutiveLead && consecutiveLead != lead) { 1018 this.report(`The consecutive register chain is invalid`); 1019 } 1020 else { 1021 consecutiveLead = lead; 1022 consecutiveLastIndex = Math.max(consecutiveLastIndex, op.regIndexRel); 1023 } 1024 } 1025 } 1026 } 1027 1028 if (consecutiveLead) { 1029 consecutiveLead.consecutive_lead_count = consecutiveLastIndex + 1; 1030 } 1031 } 1032 1033 // Validate the instruction's definition. Common mistakes can be checked and 1034 // reported easily, however, if the mistake is just an invalid opcode or 1035 // something else it's impossible to detect. 1036 _postProcess() { 1037 if (this.groupPattern) { 1038 const archInfo = ArchGroupInfo[this.groupPattern]; 1039 if (this.arch === "ANY" && archInfo && this.arch !== archInfo[this.groupIndex]) { 1040 // TODO: Never triggered, which means it should be removed. 1041 this.arch = archInfo[this.groupIndex]; 1042 } 1043 } 1044 else { 1045 this.groupIndex = -1; 1046 } 1047 1048 if (this.privilege === "L0") 1049 this.category.SYSTEM = true; 1050 1051 let immCount = this.immCount; 1052 1053 // Verify that the immediate operand/operands are specified in instruction 1054 // encoding and opcode field. Basically if there is an "ix" in operands, 1055 // the encoding should contain "I". 1056 if (immCount > 0) { 1057 if (immCount === 1 && this.operands[this.operands.length - 1].data === "1") { 1058 // This must be one of rcl|rcr|rol|ror|sar|sal|shr. We won't validate 1059 // these as these have "1" as implicit (encoded within opcode, not after). 1060 } 1061 else { 1062 // Every immediate should have its imm byte ("ib", "iw", "id", or "iq") in the opcode data. 1063 let m = this.opcodeString.match(/(?:^|\s+)(ib|iw|id|iq|iv|if|\/is4)/g); 1064 if (!m || m.length !== immCount) { 1065 this.report(`Immediate(s) [${immCount}] not found in opcode: ${this.opcodeString}`); 1066 } 1067 } 1068 } 1069 } 1070 1071 isAVX() { return this.isVEX() || this.isEVEX(); } 1072 isVEX() { return this.prefix === "VEX" || this.prefix === "XOP"; } 1073 isEVEX() { return this.prefix === "EVEX" } 1074 1075 getWValue() { 1076 switch (this.opcode.w) { 1077 case "W0": return 0; 1078 case "W1": return 1; 1079 } 1080 return -1; 1081 } 1082 1083 // Get signature of the instruction as "ARCH PREFIX ENCODING[:operands]" form. 1084 get signature() { 1085 let operands = this.operands; 1086 let sign = this.arch; 1087 1088 if (this.prefix) { 1089 sign += " " + this.prefix; 1090 if (this.prefix !== "3DNOW") { 1091 if (this.opcode.l === "L1") 1092 sign += ".256"; 1093 else if (this.opcode.l === "256" || this.opcode.l === "512") 1094 sign += `.${this.opcode.l}`; 1095 else 1096 sign += ".128"; 1097 1098 if (this.opcode.w === "W1") 1099 sign += ".W"; 1100 } 1101 } 1102 else if (this.opcode.w === "W1") { 1103 sign += " REX.W"; 1104 } 1105 1106 sign += " " + this.encoding; 1107 1108 for (let i = 0; i < operands.length; i++) { 1109 sign += (i === 0) ? ":" : ","; 1110 1111 let operand = operands[i]; 1112 if (operand.implicit) 1113 sign += `[${operand.reg}]`; 1114 else 1115 sign += operand.toRegMem(); 1116 } 1117 1118 return sign; 1119 } 1120 1121 get immCount() { 1122 let ops = this.operands; 1123 let n = 0; 1124 for (let i = 0; i < ops.length; i++) 1125 if (ops[i].isImm()) 1126 n++; 1127 return n; 1128 } 1129 1130 get modRValue() { 1131 if (/^[0-7]$/.test(this.opcode.modr)) 1132 return parseInt(this.opcode.modr, 10); 1133 else 1134 return 0; 1135 } 1136 1137 get modRMValue() { 1138 if (/^[0-7]$/.test(this.opcode.modrm)) 1139 return parseInt(this.opcode.modrm, 10); 1140 else 1141 return 0; 1142 } 1143 } 1144 x86.Instruction = Instruction; 1145 1146 // asmdb.x86.ISA 1147 // ============= 1148 1149 const ArchKeys = MapUtils.mapFromArray(["any", "x86", "x64", "apx", "___"]); 1150 1151 function findArch(inst) { 1152 for (let a in ArchKeys) { 1153 if (typeof inst[a] === "string") { 1154 return a; 1155 } 1156 } 1157 1158 FAIL(`Instruction signature not found in record: ${JSON.stringify(inst)}`); 1159 } 1160 1161 function mergeGroupData(data, group) { 1162 for (let k in group) { 1163 switch (k) { 1164 case "group": 1165 case "instructions": 1166 break; 1167 1168 case "ext": 1169 data[k] = (data[k] ? data[k] + " " : "") + group[k]; 1170 break; 1171 1172 default: 1173 if (data[k] === undefined) 1174 data[k] = group[k] 1175 break; 1176 } 1177 } 1178 } 1179 1180 // X86/X64 instruction database - stores Instruction instances in a map and 1181 // aggregates all instructions with the same name. 1182 class ISA extends base.ISA { 1183 constructor(data) { 1184 super(data); 1185 this.addData(data || NONE); 1186 } 1187 1188 _addInstructions(groups) { 1189 for (let group of groups) { 1190 for (let record of group.instructions) { 1191 let arch = findArch(record); 1192 1193 // TODO: Ignore records having this (only used for testing purposes). 1194 if (arch === "___") 1195 continue; 1196 1197 const apx = arch === "apx"; 1198 1199 const sgn = Utils.splitInstructionSignature(record[arch]); 1200 const data = MapUtils.cloneExcept(record, arch); 1201 1202 mergeGroupData(data, group) 1203 1204 for (let j = 0; j < sgn.names.length; j++) { 1205 data.name = sgn.names[j]; 1206 data.prefixes = sgn.prefixes; 1207 data.operands = sgn.operands; 1208 1209 if (j > 0) { 1210 data.aliasOf = sgn.names[0]; 1211 } 1212 1213 let groupIndex = 0; 1214 let instruction = null; 1215 do { 1216 instruction = new Instruction(this); 1217 instruction.arch = apx ? "X64" : arch.toUpperCase(); 1218 instruction.assignData(data, groupIndex); 1219 1220 if (apx) { 1221 instruction.ext["APX_F"] = true; 1222 if (instruction.category.GP) { 1223 instruction.category.GP_EXT = true 1224 } 1225 } 1226 1227 this._addInstruction(instruction); 1228 } while (instruction.groupPattern && ++groupIndex < OperandGroupInfo[instruction.groupPattern].subst.length); 1229 } 1230 } 1231 } 1232 1233 return this; 1234 } 1235 } 1236 x86.ISA = ISA; 1237 1238 // asmdb.x86.X86DataCheck 1239 // ====================== 1240 1241 class X86DataCheck { 1242 static checkVexEvex(db) { 1243 const map = db.instructionMap; 1244 for (let name in map) { 1245 const instructions = map[name]; 1246 for (let i = 0; i < instructions.length; i++) { 1247 const instA = instructions[i]; 1248 for (let j = i + 1; j < instructions.length; j++) { 1249 const instB = instructions[j]; 1250 if (instA.operands.join("_") === instB.operands.join("_")) { 1251 const vex = instA.prefix === "VEX" ? instA : instB.prefix === "VEX" ? instB : null; 1252 const evex = instA.prefix === "EVEX" ? instA : instB.prefix === "EVEX" ? instB : null; 1253 1254 if (vex && evex && vex.opcode.byte === evex.opcode.byte) { 1255 // NOTE: There are some false positives, they will be printed as well. 1256 let ok = vex.opcode.w === evex.opcode.w && vex.opcode.l === evex.opcode.l; 1257 1258 if (!ok) { 1259 console.log(`Instruction ${name} differs:`); 1260 console.log(` ${vex.operands.join(" ")}: ${vex.opcodeString}`); 1261 console.log(` ${evex.operands.join(" ")}: ${evex.opcodeString}`); 1262 } 1263 } 1264 } 1265 } 1266 } 1267 } 1268 } 1269 } 1270 x86.X86DataCheck = X86DataCheck; 1271 1272 }).apply(this, typeof module === "object" && module && module.exports 1273 ? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "x86"]);