tablegen-x86.js (86938B)
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 "use strict"; 7 8 const fs = require("fs"); 9 const path = require("path"); 10 11 const commons = require("./generator-commons.js"); 12 const cxx = require("./generator-cxx.js"); 13 const core = require("./tablegen.js"); 14 15 const asmdb = core.asmdb; 16 17 const DEBUG = commons.DEBUG; 18 const FATAL = commons.FATAL; 19 const kIndent = commons.kIndent; 20 const ArrayUtils = commons.ArrayUtils; 21 const IndexedArray = commons.IndexedArray; 22 const ObjectUtils = commons.ObjectUtils; 23 const StringUtils = commons.StringUtils; 24 25 const disclaimer = StringUtils.disclaimer; 26 const decToHex = StringUtils.decToHex; 27 28 function readJSON(fileName) { 29 const content = fs.readFileSync(fileName); 30 return JSON.parse(content); 31 } 32 33 const x86data = readJSON(path.join(__dirname, "..", "db", asmdb.x86.dbName)); 34 35 // ============================================================================ 36 // [tablegen.x86.x86isa] 37 // ============================================================================ 38 39 // Create the X86 database and add some special cases recognized by AsmJit. 40 const x86isa = new asmdb.x86.ISA(x86data); 41 42 // ============================================================================ 43 // [tablegen.x86.Filter] 44 // ============================================================================ 45 46 class Filter { 47 static unique(instArray) { 48 const result = []; 49 const known = {}; 50 51 for (let i = 0; i < instArray.length; i++) { 52 const inst = instArray[i]; 53 if (inst.altForm) 54 continue; 55 56 const s = inst.operands.map((op) => { return op.isImm() ? "imm" : op.toString(); }).join(", "); 57 if (known[s] === true) 58 continue; 59 60 known[s] = true; 61 result.push(inst); 62 } 63 64 return result; 65 } 66 67 static noAltForm(instArray) { 68 const result = []; 69 for (let i = 0; i < instArray.length; i++) { 70 const inst = instArray[i]; 71 if (inst.alt) 72 continue; 73 result.push(inst); 74 } 75 return result; 76 } 77 78 static byArch(instArray, arch) { 79 return instArray.filter(function(inst) { 80 return inst.arch === "ANY" || inst.arch === arch; 81 }); 82 } 83 } 84 85 // ============================================================================ 86 // [tablegen.x86.GenUtils] 87 // ============================================================================ 88 89 const VexToEvexMap = { 90 "vbroadcastf128": "vbroadcastf32x4", 91 "vbroadcasti128": "vbroadcasti32x4", 92 "vextractf128": "vextractf32x4", 93 "vextracti128": "vextracti32x4", 94 "vinsertf128": "vinsertf32x4", 95 "vinserti128": "vinserti32x4", 96 "vmovdqa": "vmovdqa32", 97 "vmovdqu": "vmovdqu32", 98 "vpand": "vpandd", 99 "vpandn": "vpandnd", 100 "vpor": "vpord", 101 "vpxor": "vpxord", 102 "vroundpd": "vrndscalepd", 103 "vroundps": "vrndscaleps", 104 "vroundsd": "vrndscalesd", 105 "vroundss": "vrndscaless" 106 }; 107 108 class GenUtils { 109 static cpuArchOf(dbInsts) { 110 let anyArch = false; 111 let x86Arch = false; 112 let x64Arch = false; 113 114 for (let i = 0; i < dbInsts.length; i++) { 115 const dbInst = dbInsts[i]; 116 if (dbInst.arch === "ANY") anyArch = true; 117 if (dbInst.arch === "X86") x86Arch = true; 118 if (dbInst.arch === "X64") x64Arch = true; 119 } 120 121 return anyArch || (x86Arch && x64Arch) ? "" : x86Arch ? "(X86)" : "(X64)"; 122 } 123 124 static cpuFeaturesOf(dbInsts) { 125 function cmp(a, b) { 126 if (a.startsWith("AVX512") && !b.startsWith("AVX512")) 127 return 1; 128 if (b.startsWith("AVX512") && !a.startsWith("AVX512")) 129 return -1; 130 131 if (a.startsWith("AVX") && !b.startsWith("AVX")) 132 return 1; 133 if (b.startsWith("AVX") && !a.startsWith("AVX")) 134 return -1; 135 136 if (a === "FPU" && b !== "FPU") 137 return 1; 138 if (b === "FPU" && a !== "FPU") 139 return -1; 140 141 return a < b ? -1 : a === b ? 0 : 1; 142 } 143 144 const features = Object.getOwnPropertyNames(dbInsts.unionCpuFeatures()); 145 features.sort(cmp); 146 return features; 147 } 148 149 static assignVexEvexCompatibilityFlags(f, dbInsts) { 150 const vexInsts = dbInsts.filter((inst) => { return inst.prefix === "VEX"; }); 151 const evexInsts = dbInsts.filter((inst) => { return inst.prefix === "EVEX"; }); 152 153 function isCompatible(vexInst, evexInst) { 154 if (vexInst.operands.length !== evexInst.operands.length) 155 return false; 156 157 for (let i = 0; i < vexInst.operands.length; i++) { 158 const vexOp = vexInst.operands[i]; 159 const evexOp = evexInst.operands[i]; 160 161 if (vexOp.data === evexOp.data) 162 continue; 163 164 if (vexOp.reg && vexOp.reg === evexOp.reg) 165 continue; 166 if (vexOp.mem && vexOp.mem === evexOp.mem) 167 continue; 168 169 return false; 170 } 171 return true; 172 } 173 174 let compatible = 0; 175 for (const vexInst of vexInsts) { 176 for (const evexInst of evexInsts) { 177 if (isCompatible(vexInst, evexInst)) { 178 compatible++; 179 break; 180 } 181 } 182 } 183 184 if (compatible == vexInsts.length) { 185 f.EvexCompat = true; 186 return true; 187 } 188 189 if (evexInsts[0].operands[0].reg === "k") { 190 f.EvexKReg = true; 191 return true; 192 } 193 194 if (evexInsts[0].operands.length == 2 && vexInsts[0].operands.length === 3) { 195 f.EvexTwoOp = true; 196 return true; 197 } 198 199 return false; 200 } 201 202 static flagsOf(dbInsts) { 203 const f = Object.create(null); 204 205 let mib = dbInsts.length > 0 && /^(?:bndldx|bndstx)$/.test(dbInsts[0].name); 206 if (mib) 207 f.Mib = true; 208 209 let mmx = false; 210 let vec = false; 211 212 for (let i = 0; i < dbInsts.length; i++) { 213 const dbInst = dbInsts[i]; 214 const operands = dbInst.operands; 215 216 if (dbInst.name === "emms") 217 mmx = true; 218 219 if (dbInst.name === "vzeroall" || dbInst.name === "vzeroupper") 220 vec = true; 221 222 for (let j = 0; j < operands.length; j++) { 223 const op = operands[j]; 224 if (op.reg === "mm") 225 mmx = true; 226 else if (/^(xmm|ymm|zmm)$/.test(op.reg)) { 227 vec = true; 228 } 229 } 230 } 231 232 if (mmx) f.Mmx = true; 233 if (vec) f.Vec = true; 234 235 for (let i = 0; i < dbInsts.length; i++) { 236 const dbInst = dbInsts[i]; 237 const operands = dbInst.operands; 238 239 if (dbInst.prefixes.lock ) f.Lock = true; 240 if (dbInst.prefixes.xacquire ) f.XAcquire = true; 241 if (dbInst.prefixes.xrelease ) f.XRelease = true; 242 if (dbInst.prefixes.bnd ) f.Rep = true; 243 if (dbInst.prefixes.rep ) f.Rep = true; 244 if (dbInst.prefixes.repne ) f.Rep = true; 245 if (dbInst.prefixes.repIgnore ) f.RepIgnored = true; 246 if (dbInst.k === "zeroing" ) f.Avx512ImplicitZ = true; 247 248 if (dbInst.category.FPU) { 249 for (let j = 0; j < operands.length; j++) { 250 const op = operands[j]; 251 if (op.memSize === 16) f.FpuM16 = true; 252 if (op.memSize === 32) f.FpuM32 = true; 253 if (op.memSize === 64) f.FpuM64 = true; 254 if (op.memSize === 80) f.FpuM80 = true; 255 } 256 } 257 258 if (dbInst.tsib) 259 f.Tsib = true; 260 261 if (dbInst.vsibReg) 262 f.Vsib = true; 263 264 if (dbInst.prefix === "VEX" || dbInst.prefix === "XOP") 265 f.Vex = true; 266 267 if (dbInst.encodingPreference === "EVEX") 268 f.PreferEvex = true; 269 270 if (dbInst.prefix === "EVEX") { 271 f.Evex = true; 272 if (dbInst.kmask) f.Avx512K = true; 273 if (dbInst.zmask) f.Avx512Z = true; 274 275 if (dbInst.er) f.Avx512ER = true; 276 if (dbInst.sae) f.Avx512SAE = true; 277 278 if (dbInst.broadcast) f["Avx512B" + String(dbInst.elementSize)] = true; 279 if (dbInst.tupleType === "T1_4X") f.Avx512T4X = true; 280 } 281 282 if (VexToEvexMap[dbInst.name]) 283 f.EvexTransformable = true; 284 } 285 286 if (f.Vex && f.Evex) { 287 GenUtils.assignVexEvexCompatibilityFlags(f, dbInsts) 288 } 289 290 const result = Object.getOwnPropertyNames(f); 291 result.sort(); 292 return result; 293 } 294 295 static eqOps(aOps, aFrom, bOps, bFrom) { 296 let x = 0; 297 for (;;) { 298 const aIndex = x + aFrom; 299 const bIndex = x + bFrom; 300 301 const aOut = aIndex >= aOps.length; 302 const bOut = bIndex >= bOps.length; 303 304 if (aOut || bOut) 305 return !!(aOut && bOut); 306 307 const aOp = aOps[aIndex]; 308 const bOp = bOps[bIndex]; 309 310 if (aOp.data !== bOp.data) 311 return false; 312 313 x++; 314 } 315 } 316 317 // Prevent some instructions from having implicit memory size if that would 318 // make them ambiguous. There are some instructions where the ambiguity is 319 // okay, but some like 'push' and 'pop' where it isn't. 320 static canUseImplicitMemSize(name) { 321 switch (name) { 322 case "pop": 323 case "push": 324 return false; 325 326 default: 327 return true; 328 } 329 } 330 331 static singleRegCase(name) { 332 switch (name) { 333 case "xchg" : 334 335 case "and" : 336 case "pand" : case "vpand" : case "vpandd" : case "vpandq" : 337 case "andpd" : case "vandpd" : 338 case "andps" : case "vandps" : 339 340 case "or" : 341 case "por" : case "vpor" : case "vpord" : case "vporq" : 342 case "orpd" : case "vorpd" : 343 case "orps" : case "vorps" : 344 345 case "pminsb" : case "vpminsb": case "pmaxsb" : case "vpmaxsb" : 346 case "pminsw" : case "vpminsw": case "pmaxsw" : case "vpmaxsw" : 347 case "pminsd" : case "vpminsd": case "pmaxsd" : case "vpmaxsd" : 348 case "pminub" : case "vpminub": case "pmaxub" : case "vpmaxub" : 349 case "pminuw" : case "vpminuw": case "pmaxuw" : case "vpmaxuw" : 350 case "pminud" : case "vpminud": case "pmaxud" : case "vpmaxud" : 351 return "RO"; 352 353 case "pandn" : case "vpandn" : case "vpandnd" : case "vpandnq" : 354 355 case "xor" : 356 case "pxor" : case "vpxor" : case "vpxord" : case "vpxorq" : 357 case "xorpd" : case "vxorpd" : 358 case "xorps" : case "vxorps" : 359 360 case "kxnorb": 361 case "kxnord": 362 case "kxnorw": 363 case "kxnorq": 364 365 case "kxorb": 366 case "kxord": 367 case "kxorw": 368 case "kxorq": 369 370 case "sub" : 371 case "sbb" : 372 case "psubb" : case "vpsubb" : 373 case "psubw" : case "vpsubw" : 374 case "psubd" : case "vpsubd" : 375 case "psubq" : case "vpsubq" : 376 case "psubsb" : case "vpsubsb": case "psubusb" : case "vpsubusb" : 377 case "psubsw" : case "vpsubsw": case "psubusw" : case "vpsubusw" : 378 379 case "vpcmpeqb": case "pcmpeqb": case "vpcmpgtb": case "pcmpgtb" : 380 case "vpcmpeqw": case "pcmpeqw": case "vpcmpgtw": case "pcmpgtw" : 381 case "vpcmpeqd": case "pcmpeqd": case "vpcmpgtd": case "pcmpgtd" : 382 case "vpcmpeqq": case "pcmpeqq": case "vpcmpgtq": case "pcmpgtq" : 383 384 case "vpcmpb" : case "vpcmpub": 385 case "vpcmpd" : case "vpcmpud": 386 case "vpcmpw" : case "vpcmpuw": 387 case "vpcmpq" : case "vpcmpuq": 388 return "WO"; 389 390 default: 391 return "None"; 392 } 393 } 394 395 static fixedRegOfRegName(reg) { 396 switch (reg) { 397 case "es" : return 1; 398 case "cs" : return 2; 399 case "ss" : return 3; 400 case "ds" : return 4; 401 case "fs" : return 5; 402 case "gs" : return 6; 403 case "ah" : return 0; 404 case "ch" : return 1; 405 case "dh" : return 2; 406 case "bh" : return 3; 407 case "al" : case "ax": case "eax": case "rax": case "zax": return 0; 408 case "cl" : case "cx": case "ecx": case "rcx": case "zcx": return 1; 409 case "dl" : case "dx": case "edx": case "rdx": case "zdx": return 2; 410 case "bl" : case "bx": case "ebx": case "rbx": case "zbx": return 3; 411 case "spl" : case "sp": case "esp": case "rsp": case "zsp": return 4; 412 case "bpl" : case "bp": case "ebp": case "rbp": case "zbp": return 5; 413 case "sil" : case "si": case "esi": case "rsi": case "zsi": return 6; 414 case "dil" : case "di": case "edi": case "rdi": case "zdi": return 7; 415 case "st0" : return 0; 416 case "xmm0": return 0; 417 case "ymm0": return 0; 418 case "zmm0": return 0; 419 default: 420 return -1; 421 } 422 } 423 424 static fixedRegOf(op) { 425 if (op.isReg()) { 426 return GenUtils.fixedRegOfRegName(op.reg); 427 } 428 else if (op.isMem() && op.memRegOnly) { 429 return GenUtils.fixedRegOfRegName(op.memRegOnly); 430 } 431 else { 432 return -1; 433 } 434 } 435 436 static controlFlow(dbInsts) { 437 if (dbInsts.checkAttribute("control", "jump")) return "Jump"; 438 if (dbInsts.checkAttribute("control", "call")) return "Call"; 439 if (dbInsts.checkAttribute("control", "branch")) return "Branch"; 440 if (dbInsts.checkAttribute("control", "return")) return "Return"; 441 return "Regular"; 442 } 443 } 444 445 // ============================================================================ 446 // [tablegen.x86.X86TableGen] 447 // ============================================================================ 448 449 class X86TableGen extends core.TableGen { 450 constructor() { 451 super("X86"); 452 453 this.emitMissingString = ""; 454 } 455 456 // -------------------------------------------------------------------------- 457 // [Query] 458 // -------------------------------------------------------------------------- 459 460 // Get instructions (dbInsts) having the same name as understood by AsmJit. 461 query(name) { 462 return x86isa.query({ name: name, filter: function(inst) { 463 return !inst.ext.APX_F && !inst.ext.AVX10_1 && !inst.ext.AVX10_2; 464 }}); 465 } 466 467 // -------------------------------------------------------------------------- 468 // [Parse / Merge] 469 // -------------------------------------------------------------------------- 470 471 parse() { 472 const data = this.dataOfFile("src/asmjit/x86/x86instdb.cpp"); 473 const re = new RegExp( 474 "INST\\(" + 475 "([A-Za-z0-9_]+)\\s*" + "," + // [01] Instruction. 476 "([^,]+)" + "," + // [02] Encoding. 477 "(.{26}[^,]*)" + "," + // [03] Opcode[0]. 478 "(.{26}[^,]*)" + "," + // [04] Opcode[1]. 479 // --- autogenerated fields --- 480 "([^\\)]+)" + "," + // [05] MainOpcodeIndex. 481 "([^\\)]+)" + "," + // [06] AltOpcodeIndex. 482 "([^\\)]+)" + "," + // [07] CommonDataIndex. 483 "([^\\)]+)" + "\\)", // [08] OperationDataIndex. 484 "g"); 485 486 let m; 487 while ((m = re.exec(data)) !== null) { 488 let enum_ = m[1]; 489 let name = enum_ === "None" ? "" : enum_.toLowerCase(); 490 let encoding = m[2].trim(); 491 let opcode0 = m[3].trim(); 492 let opcode1 = m[4].trim(); 493 494 const dbInsts = this.query(name); 495 if (name && !dbInsts.length) 496 FATAL(`Instruction '${name}' not found in asmdb`); 497 498 const flags = GenUtils.flagsOf(dbInsts); 499 const controlFlow = GenUtils.controlFlow(dbInsts); 500 const singleRegCase = GenUtils.singleRegCase(name); 501 502 const aliasData = x86isa.aliasData(name); 503 504 this.addInstruction({ 505 id : 0, // Instruction id (numeric value). 506 name : name, // Instruction name. 507 displayName : name, // Instruction name to display. 508 enum : enum_, // Instruction enum without `kId` prefix. 509 dbInsts : dbInsts, // All dbInsts returned from asmdb query. 510 encoding : encoding, // Instruction encoding. 511 opcode0 : opcode0, // Primary opcode. 512 opcode1 : opcode1, // Secondary opcode. 513 flags : flags, 514 signatures : null, // Instruction signatures. 515 controlFlow : controlFlow, 516 singleRegCase : singleRegCase, 517 518 aliases : aliasData, 519 520 mainOpcodeValue : -1, // Main opcode value (0.255 hex). 521 mainOpcodeIndex : -1, // Index to InstDB::main_opcode_table. 522 altOpcodeIndex : -1, // Index to InstDB::alt_opcode_table. 523 nameIndex : -1, // Index to InstDB::_nameData. 524 commonInfoIndex : -1, 525 additionalInfoIndex: -1, 526 527 signatureIndex : -1, 528 signatureCount : -1 529 }); 530 } 531 532 if (this.insts.length === 0) 533 FATAL("X86TableGen.parse(): Invalid parsing regexp (no data parsed)"); 534 535 console.log("Number of Instructions: " + this.insts.length); 536 } 537 538 merge() { 539 let s = StringUtils.format(this.insts, "", true, function(inst) { 540 return "INST(" + 541 String(inst.enum ).padEnd(17) + ", " + 542 String(inst.encoding ).padEnd(19) + ", " + 543 String(inst.opcode0 ).padEnd(26) + ", " + 544 String(inst.opcode1 ).padEnd(26) + ", " + 545 String(inst.mainOpcodeIndex ).padEnd( 3) + ", " + 546 String(inst.altOpcodeIndex ).padEnd( 3) + ", " + 547 String(inst.commonInfoIndex ).padEnd( 3) + ", " + 548 String(inst.additionalInfoIndex).padEnd( 3) + ")"; 549 }) + "\n"; 550 this.inject("InstInfo", s, this.insts.length * 8); 551 } 552 553 // -------------------------------------------------------------------------- 554 // [Other] 555 // -------------------------------------------------------------------------- 556 557 printMissing() { 558 const ignored = ArrayUtils.toDict([ 559 "cmpsb", "cmpsw", "cmpsd", "cmpsq", 560 "lodsb", "lodsw", "lodsd", "lodsq", 561 "movsb", "movsw", "movsd", "movsq", 562 "scasb", "scasw", "scasd", "scasq", 563 "stosb", "stosw", "stosd", "stosq", 564 "insb" , "insw" , "insd" , 565 "outsb", "outsw", "outsd", 566 "wait" // Maps to `fwait`, which AsmJit uses instead. 567 ]); 568 569 let out = ""; 570 x86isa.instructionNames.forEach(function(name) { 571 let dbInsts = x86isa.query(name); 572 if (!this.instMap[name] && ignored[name] !== true) { 573 console.log(`MISSING INSTRUCTION '${name}'`); 574 let inst = this.newInstFromGroup(dbInsts); 575 if (inst) { 576 out += " INST(" + 577 String(inst.enum ).padEnd(17) + ", " + 578 String(inst.encoding ).padEnd(19) + ", " + 579 String(inst.opcode0 ).padEnd(26) + ", " + 580 String(inst.opcode1 ).padEnd(26) + ", " + 581 String("0" ).padEnd( 3) + ", " + 582 String("0" ).padEnd( 3) + ", " + 583 String("0" ).padEnd( 5) + ", " + 584 String("0" ).padEnd( 3) + ", " + 585 String("0" ).padEnd( 3) + "),\n"; 586 } 587 } 588 }, this); 589 console.log(out); 590 console.log(this.emitMissingString); 591 } 592 593 newInstFromGroup(dbInsts) { 594 function composeOpCode(obj) { 595 return `${obj.type}(${obj.prefix},${obj.opcode},${obj.o},${obj.l},${obj.w},${obj.ew},${obj.en},${obj.tt})`; 596 } 597 598 function GetAccess(dbInst) { 599 let operands = dbInst.operands; 600 if (!operands.length) return ""; 601 602 let op = operands[0]; 603 if (op.read && op.write) 604 return "RW"; 605 else if (op.read) 606 return "RO"; 607 else 608 return "WO"; 609 } 610 611 function isVecPrefix(s) { 612 return s === "VEX" || s === "EVEX" || s === "XOP"; 613 } 614 615 function formatEmit(dbi) { 616 const results = []; 617 const nameUp = dbi.name[0].toUpperCase() + dbi.name.substr(1); 618 619 for (let choice = 0; choice < 2; choice++) { 620 let s = `ASMJIT_INST_${dbi.operands.length}x(${dbi.name}, ${nameUp}`; 621 for (let j = 0; j < dbi.operands.length; j++) { 622 s += ", "; 623 const op = dbi.operands[j]; 624 let reg = op.reg; 625 let mem = op.mem; 626 627 if (op.isReg() && op.isMem()) { 628 if (choice == 0) mem = null; 629 if (choice == 1) reg = null; 630 } 631 632 if (reg) { 633 if (reg === "xmm" || reg === "ymm" || reg === "zmm") 634 s += "Vec"; 635 else if (reg === "k") 636 s += "KReg"; 637 else if (reg === "r32" || reg === "r64" || reg === "r16" || reg === "r8") 638 s += "Gp"; 639 else 640 s += reg; 641 } 642 else if (mem) { 643 s += "Mem"; 644 } 645 else if (op.isImm()) { 646 s += "Imm"; 647 } 648 else { 649 s += "Unknown"; 650 } 651 } 652 s += `)`; 653 results.push(s); 654 } 655 656 return results; 657 } 658 659 let dbi = dbInsts[0]; 660 661 let id = this.insts.length; 662 let name = dbi.name; 663 let enum_ = name[0].toUpperCase() + name.substr(1); 664 665 let opcode = dbi.opcode.byte; 666 let modR = dbi.opcode.modr; 667 let mm = dbi.opcode.mm; 668 let pp = dbi.opcode.pp; 669 let encoding = dbi.encoding; 670 let isVec = isVecPrefix(dbi.prefix); 671 let evexCount = 0; 672 673 let access = GetAccess(dbi); 674 675 let vexL = undefined; 676 let vexW = undefined; 677 let evexW = undefined; 678 let cdshl = "_"; 679 let tupleType = "_"; 680 681 const tupleTypeToCDSHL = { 682 "FVM": "4", 683 "FV": "4", 684 "HVM": "3", 685 "HV": "3", 686 "QVM": "2", 687 "QV": "2", 688 "T1S": "?" 689 } 690 691 const emitMap = {}; 692 693 for (let i = 0; i < dbInsts.length; i++) { 694 dbi = dbInsts[i]; 695 696 if (dbi.prefix === "VEX" || dbi.prefix === "XOP") { 697 let newVexL = String(dbi.opcode.l === "128" ? 0 : dbi.opcode.l === "256" ? 1 : dbi.opcode.l === "512" ? 2 : "_"); 698 let newVexW = String(dbi.opcode.w === "W0" ? 0 : dbi.opcode.w === "W1" ? 1 : "_"); 699 700 if (vexL !== undefined && vexL !== newVexL) 701 vexL = "x"; 702 else 703 vexL = newVexL; 704 if (vexW !== undefined && vexW !== newVexW) 705 vexW = "x"; 706 else 707 vexW = newVexW; 708 } 709 710 if (dbi.prefix === "EVEX") { 711 evexCount++; 712 let newEvexW = String(dbi.opcode.w === "W0" ? 0 : dbi.opcode.w === "W1" ? 1 : "_"); 713 if (evexW !== undefined && evexW !== newEvexW) 714 evexW = "x"; 715 else 716 evexW = newEvexW; 717 718 if (dbi.tupleType) { 719 if (tupleType !== "_" && tupleType !== dbi.tupleType) { 720 console.log(`${dbi.name}: WARNING: TupleType ${tupleType} != ${dbi.tupleType}`); 721 } 722 723 tupleType = dbi.tupleType; 724 } 725 } 726 727 if (opcode !== dbi.opcode.byte) { console.log(`${dbi.name}: ISSUE: Opcode ${opcode} != ${dbi.opcode.byte}`); return null; } 728 if (modR !== dbi.opcode.modr) { console.log(`${dbi.name}: ISSUE: ModR ${modR} != ${dbi.opcode.modr}`); return null; } 729 if (mm !== dbi.opcode.mm ) { console.log(`${dbi.name}: ISSUE: MM ${mm} != ${dbi.opcode.mm}`); return null; } 730 if (pp !== dbi.opcode.pp ) { console.log(`${dbi.name}: ISSUE: PP ${pp} != ${dbi.opcode.pp}`); return null; } 731 if (encoding !== dbi.encoding ) { console.log(`${dbi.name}: ISSUE: Enc ${encoding} != ${dbi.encoding}`); return null; } 732 if (access !== GetAccess(dbi) ) { console.log(`${dbi.name}: ISSUE: Access ${access} != ${GetAccess(dbi)}`); return null; } 733 if (isVec != isVecPrefix(dbi.prefix)) { console.log(`${dbi.name}: ISSUE: Vex/Non-Vex mismatch`); return null; } 734 735 formatEmit(dbi).forEach((emit) => { 736 if (!emitMap[emit]) { 737 emitMap[emit] = true; 738 this.emitMissingString += emit + "\n"; 739 } 740 }); 741 } 742 743 if (tupleType !== "_") 744 cdshl = tupleTypeToCDSHL[tupleType] || "?"; 745 746 let ppmm = pp.padEnd(2).replace(/ /g, "0") + 747 mm.padEnd(4).replace(/ /g, "0") ; 748 749 let composed = composeOpCode({ 750 type : evexCount == dbInsts.length ? "E" : isVec ? "V" : "O", 751 prefix: ppmm, 752 opcode: opcode, 753 o : modR === "r" ? "_" : (modR ? modR : "_"), 754 l : vexL !== undefined ? vexL : "_", 755 w : vexW !== undefined ? vexW : "_", 756 ew : evexW !== undefined ? evexW : "_", 757 en : cdshl, 758 tt : dbi.modRM ? dbi.modRM + " " : tupleType.padEnd(3) 759 }); 760 761 return { 762 id : id, 763 name : name, 764 enum : enum_, 765 encoding : encoding, 766 opcode0 : composed, 767 opcode1 : "0", 768 nameIndex : -1, 769 commonInfoIndex : -1, 770 additionalInfoIndex: -1 771 }; 772 } 773 774 // -------------------------------------------------------------------------- 775 // [Hooks] 776 // -------------------------------------------------------------------------- 777 778 onBeforeRun() { 779 this.load([ 780 "src/asmjit/x86/x86globals.h", 781 "src/asmjit/x86/x86instdb.cpp", 782 "src/asmjit/x86/x86instdb.h", 783 "src/asmjit/x86/x86instdb_p.h" 784 ]); 785 this.parse(); 786 } 787 788 onAfterRun() { 789 this.merge(); 790 this.save(); 791 this.dumpTableSizes(); 792 this.printMissing(); 793 } 794 } 795 796 // ============================================================================ 797 // [tablegen.x86.IdEnum] 798 // ============================================================================ 799 800 class IdEnum extends core.IdEnum { 801 constructor() { 802 super("IdEnum"); 803 } 804 805 comment(inst) { 806 function filterAVX(features, avx) { 807 return features.filter(function(item) { return /^(AVX|FMA)/.test(item) === avx; }); 808 } 809 810 let dbInsts = inst.dbInsts; 811 if (!dbInsts.length) return "Invalid instruction id."; 812 813 let text = ""; 814 let features = GenUtils.cpuFeaturesOf(dbInsts); 815 816 const priorityFeatures = ["AVX_VNNI", "AVX_VNNI_INT8", "AVX_IFMA", "AVX_NE_CONVERT"]; 817 818 if (features.length) { 819 text += "{"; 820 const avxFeatures = filterAVX(features, true); 821 const otherFeatures = filterAVX(features, false); 822 823 for (const pf of priorityFeatures) { 824 const index = avxFeatures.indexOf(pf); 825 if (index != -1) { 826 avxFeatures.splice(index, 1); 827 avxFeatures.unshift(pf); 828 } 829 } 830 831 const vl = avxFeatures.indexOf("AVX512_VL"); 832 if (vl !== -1) avxFeatures.splice(vl, 1); 833 834 const fma = avxFeatures.indexOf("FMA"); 835 if (fma !== -1) { avxFeatures.splice(fma, 1); avxFeatures.splice(0, 0, "FMA"); } 836 837 text += avxFeatures.join("|"); 838 if (vl !== -1) text += "+VL"; 839 840 if (otherFeatures.length) 841 text += (avxFeatures.length ? " & " : "") + otherFeatures.join("|"); 842 843 text += "}"; 844 } 845 846 let arch = GenUtils.cpuArchOf(dbInsts); 847 if (arch) 848 text += (text ? " " : "") + arch; 849 850 return `Instruction '${inst.name}'${(text ? " " + text : "")}.`; 851 } 852 } 853 854 // ============================================================================ 855 // [tablegen.x86.NameTable] 856 // ============================================================================ 857 858 class NameTable extends core.NameTable { 859 constructor() { 860 super("NameTable", null, true); 861 } 862 } 863 864 // ============================================================================ 865 // [tablegen.x86.AltOpcodeTable] 866 // ============================================================================ 867 868 class AltOpcodeTable extends core.Task { 869 constructor() { 870 super("AltOpcodeTable"); 871 } 872 873 run() { 874 const insts = this.ctx.insts; 875 876 const mainOpcodeTable = new IndexedArray(); 877 const altOpcodeTable = new IndexedArray(); 878 879 const cdttSimplification = { 880 "0" : "None", 881 "_" : "None", 882 "FV" : "ByLL", 883 "HV" : "ByLL", 884 "QV" : "ByLL", 885 "FVM" : "ByLL", 886 "T1S" : "None", 887 "T1F" : "None", 888 "T1_4X": "None", 889 "T2" : "None", 890 "T4" : "None", 891 "T8" : "None", 892 "HVM" : "ByLL", 893 "QVM" : "ByLL", 894 "OVM" : "ByLL", 895 "128" : "None", 896 "T4X" : "None" 897 } 898 899 const noOp = "O(000000,00,0,0,0,0,0,0 )"; 900 901 mainOpcodeTable.addIndexed(noOp); 902 903 function splitOpcodeToComponents(opcode) { 904 const i = opcode.indexOf("("); 905 const prefix = opcode.substr(0, i); 906 return [prefix].concat(opcode.substring(i + 1, opcode.length - 1).split(",")); 907 } 908 909 function normalizeOpcodeComponents(components) { 910 for (let i = 1; i < components.length; i++) { 911 components[i] = components[i].trim(); 912 // These all are zeros that only have some contextual meaning in the table, but the assembler doesn't care. 913 if (components[i] === "_" || components[i] === "I" || components[i] === "x") 914 components[i] = "0"; 915 } 916 917 // Simplify CDTT (compressed displacement TupleType). 918 if (components.length >= 9) { 919 if (components[0] === "V" || components[0] === "E") { 920 const cdtt = components[8]; 921 if (cdttSimplification[cdtt] !== undefined) 922 components[8] = cdttSimplification[cdtt]; 923 } 924 } 925 return components; 926 } 927 928 function joinOpcodeComponents(components) { 929 const prefix = components[0]; 930 const values = components.slice(1); 931 if (values.length >= 8) 932 values[7] = values[7].padEnd(4); 933 return prefix + "(" + values.join(",") + ")"; 934 } 935 936 function indexMainOpcode(opcode) { 937 if (opcode === "0") 938 return ["00", 0]; 939 940 let opcodeByte = ""; 941 const components = normalizeOpcodeComponents(splitOpcodeToComponents(opcode)); 942 943 if (components[0] === "O_FPU") { 944 // Reset opcode byte, this is stored in the instruction data itself. 945 opcodeByte = components[2].substr(2, 2); 946 components[2] = components[2].substr(0, 2) + "00"; 947 } 948 else if (components[0] === "O" || components[0] === "V" || components[0] === "E") { 949 // Reset opcode byte, this is stored in the instruction data itself. 950 opcodeByte = components[2]; 951 components[2] = "00"; 952 } 953 else { 954 FATAL(`Failed to process opcode '${opcode}'`); 955 } 956 957 const newOpcode = joinOpcodeComponents(components); 958 return [opcodeByte, mainOpcodeTable.addIndexed(newOpcode.padEnd(27))]; 959 } 960 961 function indexAltOpcode(opcode) { 962 if (opcode === "0") 963 opcode = noOp; 964 else 965 opcode = joinOpcodeComponents(normalizeOpcodeComponents(splitOpcodeToComponents(opcode))); 966 return altOpcodeTable.addIndexed(opcode.padEnd(27)); 967 } 968 969 insts.map((inst) => { 970 const [value, index] = indexMainOpcode(inst.opcode0); 971 inst.mainOpcodeValue = value; 972 inst.mainOpcodeIndex = index; 973 inst.altOpcodeIndex = indexAltOpcode(inst.opcode1); 974 }); 975 976 // console.log(mainOpcodeTable.length); 977 // console.log(StringUtils.format(mainOpcodeTable, kIndent, true)); 978 979 this.inject("MainOpcodeTable", 980 disclaimer(`const uint32_t InstDB::main_opcode_table[] = {\n${StringUtils.format(mainOpcodeTable, kIndent, true)}\n};\n`), 981 mainOpcodeTable.length * 4); 982 983 this.inject("AltOpcodeTable", 984 disclaimer(`const uint32_t InstDB::alt_opcode_table[] = {\n${StringUtils.format(altOpcodeTable, kIndent, true)}\n};\n`), 985 altOpcodeTable.length * 4); 986 } 987 } 988 989 // ============================================================================ 990 // [tablegen.x86.InstSignatureTable] 991 // ============================================================================ 992 993 const RegOp = ArrayUtils.toDict(["al", "ah", "ax", "eax", "rax", "cl", "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd"]); 994 const MemOp = ArrayUtils.toDict(["m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"]); 995 996 const cmpOp = StringUtils.makePriorityCompare([ 997 "RegGpbLo", "RegGpbHi", "RegGpw", "RegGpd", "RegGpq", "RegXmm", "RegYmm", "RegZmm", "RegMm", "RegKReg", "RegSReg", "RegCReg", "RegDReg", "RegSt", "RegBnd", "RegTmm", 998 "MemUnspecified", "Mem8", "Mem16", "Mem32", "Mem48", "Mem64", "Mem80", "Mem128", "Mem256", "Mem512", "Mem1024", 999 "Vm32x", "Vm32y", "Vm32z", "Vm64x", "Vm64y", "Vm64z", 1000 "ImmI4", "ImmU4", "ImmI8", "ImmU8", "ImmI16", "ImmU16", "ImmI32", "ImmU32", "ImmI64", "ImmU64", 1001 "Rel8", "Rel32", 1002 "FlagMemBase", 1003 "FlagMemDs", 1004 "FlagMemEs", 1005 "FlagMib", 1006 "FlagTMem", 1007 "FlagConsecutive", 1008 "FlagImplicit" 1009 ]); 1010 1011 function StringifyOpArray(a, map) { 1012 let s = ""; 1013 for (let i = 0; i < a.length; i++) { 1014 const op = a[i]; 1015 let mapped = null; 1016 if (typeof map === "function") 1017 mapped = map(op); 1018 else if (Object.hasOwn(map, op)) 1019 mapped = map[op]; 1020 else 1021 FATAL(`UNHANDLED OPERAND '${op}'`); 1022 s += (s ? " | " : "") + mapped; 1023 } 1024 return s ? s : "0"; 1025 } 1026 1027 class OSignature { 1028 constructor() { 1029 this.flags = Object.create(null); 1030 } 1031 1032 equals(other) { 1033 return ObjectUtils.equals(this.flags, other.flags); 1034 } 1035 1036 xor(other) { 1037 const result = ObjectUtils.xor(this.flags, other.flags); 1038 return Object.getOwnPropertyNames(result).length === 0 ? null : result; 1039 } 1040 1041 mergeWith(other) { 1042 const af = this.flags; 1043 const bf = other.flags; 1044 1045 let hasReg = false; 1046 let indexKind = ""; 1047 1048 for (let k in af) { 1049 const index = asmdb.x86.Utils.regIndexOf(k); 1050 const kind = asmdb.x86.Utils.regKindOf(k); 1051 1052 if (kind) 1053 hasReg = true; 1054 1055 if (index !== null && index !== -1) 1056 indexKind = kind; 1057 } 1058 1059 if (hasReg) { 1060 for (let k in bf) { 1061 const index = asmdb.x86.Utils.regIndexOf(k); 1062 if (index !== null && index !== -1) { 1063 const kind = asmdb.x86.Utils.regKindOf(k); 1064 if (indexKind !== kind) 1065 return false; 1066 } 1067 } 1068 } 1069 1070 // Can merge... 1071 for (let k in bf) 1072 af[k] = true; 1073 return true; 1074 } 1075 1076 toString() { 1077 let s = ""; 1078 let flags = this.flags; 1079 1080 for (let k in flags) { 1081 if (k === "read" || k === "write" || k === "implicit" || k === "memDS" || k === "memES") 1082 continue; 1083 1084 let x = k; 1085 if (x === "memZAX") x = "zax"; 1086 if (x === "memZDI") x = "zdi"; 1087 if (x === "memZSI") x = "zsi"; 1088 s += (s ? "|" : "") + x; 1089 } 1090 1091 if (flags.memDS) s = "ds:[" + s + "]"; 1092 if (flags.memES) s = "es:[" + s + "]"; 1093 1094 if (flags.implicit) 1095 s = "<" + s + ">"; 1096 1097 return s; 1098 } 1099 1100 toAsmJitOpData() { 1101 let opFlags = Object.create(null); 1102 let regMask = 0; 1103 1104 for (let k in this.flags) { 1105 switch (k) { 1106 case "r8lo" : opFlags.RegGpbLo = true; break; 1107 case "r8hi" : opFlags.RegGpbHi = true; break; 1108 case "r16" : opFlags.RegGpw = true; break; 1109 case "r32" : opFlags.RegGpd = true; break; 1110 case "r64" : opFlags.RegGpq = true; break; 1111 case "creg" : opFlags.RegCReg = true; break; 1112 case "dreg" : opFlags.RegDReg = true; break; 1113 case "sreg" : opFlags.RegSReg = true; break; 1114 case "bnd" : opFlags.RegBnd = true; break; 1115 case "st" : opFlags.RegSt = true; break; 1116 case "k" : opFlags.RegKReg = true; break; 1117 case "mm" : opFlags.RegMm = true; break; 1118 case "xmm" : opFlags.RegXmm = true; break; 1119 case "ymm" : opFlags.RegYmm = true; break; 1120 case "zmm" : opFlags.RegZmm = true; break; 1121 case "tmm" : opFlags.RegTmm = true; break; 1122 1123 case "m8" : opFlags.Mem8 = true; break; 1124 case "m16" : opFlags.Mem16 = true; break; 1125 case "m32" : opFlags.Mem32 = true; break; 1126 case "m48" : opFlags.Mem48 = true; break; 1127 case "m64" : opFlags.Mem64 = true; break; 1128 case "m80" : opFlags.Mem80 = true; break; 1129 case "m128" : opFlags.Mem128 = true; break; 1130 case "m256" : opFlags.Mem256 = true; break; 1131 case "m512" : opFlags.Mem512 = true; break; 1132 case "m1024" : opFlags.Mem1024 = true; break; 1133 1134 case "mem" : opFlags.MemUnspecified = true; break; 1135 case "mib" : opFlags.MemUnspecified = true; opFlags.FlagMib = true; break; 1136 case "tmem" : opFlags.MemUnspecified = true; opFlags.FlagTMem = true; break; 1137 1138 case "memBase" : opFlags.FlagMemBase = true; break; 1139 case "memDS" : opFlags.FlagMemDs = true; break; 1140 case "memES" : opFlags.FlagMemEs = true; break; 1141 case "memZAX" : regMask |= 1 << 0; break; 1142 case "memZSI" : regMask |= 1 << 6; break; 1143 case "memZDI" : regMask |= 1 << 7; break; 1144 1145 case "vm32x" : opFlags.Vm32x = true; break; 1146 case "vm32y" : opFlags.Vm32y = true; break; 1147 case "vm32z" : opFlags.Vm32z = true; break; 1148 case "vm64x" : opFlags.Vm64x = true; break; 1149 case "vm64y" : opFlags.Vm64y = true; break; 1150 case "vm64z" : opFlags.Vm64z = true; break; 1151 1152 case "i4" : opFlags.ImmI4 = true; break; 1153 case "u4" : opFlags.ImmU4 = true; break; 1154 case "i8" : opFlags.ImmI8 = true; break; 1155 case "u8" : opFlags.ImmU8 = true; break; 1156 case "i16" : opFlags.ImmI16 = true; break; 1157 case "u16" : opFlags.ImmU16 = true; break; 1158 case "i32" : opFlags.ImmI32 = true; break; 1159 case "u32" : opFlags.ImmU32 = true; break; 1160 case "i64" : opFlags.ImmI64 = true; break; 1161 case "u64" : opFlags.ImmU64 = true; break; 1162 1163 case "rel8" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel8 = true; break; 1164 case "rel16" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel32 = true; break; 1165 case "rel32" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel32 = true; break; 1166 1167 case "es" : opFlags.RegSReg = true; regMask |= 1 << 1; break; 1168 case "cs" : opFlags.RegSReg = true; regMask |= 1 << 2; break; 1169 case "ss" : opFlags.RegSReg = true; regMask |= 1 << 3; break; 1170 case "ds" : opFlags.RegSReg = true; regMask |= 1 << 4; break; 1171 case "fs" : opFlags.RegSReg = true; regMask |= 1 << 5; break; 1172 case "gs" : opFlags.RegSReg = true; regMask |= 1 << 6; break; 1173 case "al" : opFlags.RegGpbLo = true; regMask |= 1 << 0; break; 1174 case "ah" : opFlags.RegGpbHi = true; regMask |= 1 << 0; break; 1175 case "ax" : opFlags.RegGpw = true; regMask |= 1 << 0; break; 1176 case "eax" : opFlags.RegGpd = true; regMask |= 1 << 0; break; 1177 case "rax" : opFlags.RegGpq = true; regMask |= 1 << 0; break; 1178 case "cl" : opFlags.RegGpbLo = true; regMask |= 1 << 1; break; 1179 case "ch" : opFlags.RegGpbHi = true; regMask |= 1 << 1; break; 1180 case "cx" : opFlags.RegGpw = true; regMask |= 1 << 1; break; 1181 case "ecx" : opFlags.RegGpd = true; regMask |= 1 << 1; break; 1182 case "rcx" : opFlags.RegGpq = true; regMask |= 1 << 1; break; 1183 case "dl" : opFlags.RegGpbLo = true; regMask |= 1 << 2; break; 1184 case "dh" : opFlags.RegGpbHi = true; regMask |= 1 << 2; break; 1185 case "dx" : opFlags.RegGpw = true; regMask |= 1 << 2; break; 1186 case "edx" : opFlags.RegGpd = true; regMask |= 1 << 2; break; 1187 case "rdx" : opFlags.RegGpq = true; regMask |= 1 << 2; break; 1188 case "bl" : opFlags.RegGpbLo = true; regMask |= 1 << 3; break; 1189 case "bh" : opFlags.RegGpbHi = true; regMask |= 1 << 3; break; 1190 case "bx" : opFlags.RegGpw = true; regMask |= 1 << 3; break; 1191 case "ebx" : opFlags.RegGpd = true; regMask |= 1 << 3; break; 1192 case "rbx" : opFlags.RegGpq = true; regMask |= 1 << 3; break; 1193 case "si" : opFlags.RegGpw = true; regMask |= 1 << 6; break; 1194 case "esi" : opFlags.RegGpd = true; regMask |= 1 << 6; break; 1195 case "rsi" : opFlags.RegGpq = true; regMask |= 1 << 6; break; 1196 case "di" : opFlags.RegGpw = true; regMask |= 1 << 7; break; 1197 case "edi" : opFlags.RegGpd = true; regMask |= 1 << 7; break; 1198 case "rdi" : opFlags.RegGpq = true; regMask |= 1 << 7; break; 1199 case "st0" : opFlags.RegSt = true; regMask |= 1 << 0; break; 1200 case "xmm0" : opFlags.RegXmm = true; regMask |= 1 << 0; break; 1201 case "ymm0" : opFlags.RegYmm = true; regMask |= 1 << 0; break; 1202 1203 case "implicit": opFlags.FlagImplicit = true; break; 1204 1205 default: 1206 console.log(`UNKNOWN OPERAND '${k}'`); 1207 } 1208 } 1209 1210 const outputFlags = StringifyOpArray(ArrayUtils.sorted(opFlags, cmpOp), function(k) { return `F(${k})`; }); 1211 return `ROW(${outputFlags || 0}, ${decToHex(regMask, 2)})`; 1212 } 1213 } 1214 1215 class ISignature extends Array { 1216 constructor(name) { 1217 super(); 1218 this.name = name; 1219 this.x86 = false; 1220 this.x64 = false; 1221 this.implicit = 0; // Number of implicit operands. 1222 } 1223 1224 opEquals(other) { 1225 const len = this.length; 1226 if (len !== other.length) return false; 1227 1228 for (let i = 0; i < len; i++) 1229 if (!this[i].equals(other[i])) 1230 return false; 1231 1232 return true; 1233 } 1234 1235 mergeWith(other) { 1236 // If both architectures are the same, it's fine to merge. 1237 const sameArch = this.x86 === other.x86 && this.x64 === other.x64; 1238 1239 // If the first arch is [X86|X64] and the second [X64] it's also fine. 1240 // if (!ok && this.x86 && this.x64 && !other.x86 && other.x64) 1241 // ok = true; 1242 1243 // It's not ok if both signatures have different number of implicit operands. 1244 if (!sameArch || this.implicit !== other.implicit) { 1245 return false; 1246 } 1247 1248 // It's not ok if both signatures have different number of operands. 1249 const len = this.length; 1250 if (len !== other.length) 1251 return false; 1252 1253 let xorIndex = -1; 1254 for (let i = 0; i < len; i++) { 1255 const xor = this[i].xor(other[i]); 1256 if (xor === null) 1257 continue; 1258 1259 if (xorIndex === -1) 1260 xorIndex = i; 1261 else 1262 return false; 1263 } 1264 1265 // Bail if mergeWith at operand-level failed. 1266 if (xorIndex === -1 || !this[xorIndex].mergeWith(other[xorIndex])) 1267 return false; 1268 1269 return true; 1270 } 1271 1272 toString() { 1273 return "{" + this.join(", ") + "}"; 1274 } 1275 } 1276 1277 class SignatureArray extends Array { 1278 constructor(instructionName) { 1279 super(); 1280 this.instructionName = instructionName; 1281 } 1282 // Iterate over all signatures and check which operands don't need explicit memory size. 1283 calcImplicitMemSize(instName) { 1284 // Calculates a hash-value (aka key) of all register operands specified by `regOps` in `inst`. 1285 function keyOf(inst, regOps) { 1286 let s = ""; 1287 for (let i = 0; i < inst.length; i++) { 1288 const op = inst[i]; 1289 if (regOps & (1 << i)) 1290 s += "{" + ArrayUtils.sorted(ObjectUtils.and(op.flags, RegOp)).join("|") + "}"; 1291 } 1292 return s || "?"; 1293 } 1294 1295 for (let aIndex = 0; aIndex < this.length; aIndex++) { 1296 const aInst = this[aIndex]; 1297 const len = aInst.length; 1298 1299 let memOp = ""; 1300 let memPos = -1; 1301 let regOps = 0; 1302 1303 // Check if this instruction signature has a memory operand of explicit size. 1304 for (let i = 0; i < len; i++) { 1305 const aOp = aInst[i]; 1306 const mem = ObjectUtils.findKey(aOp.flags, MemOp); 1307 1308 if (mem) { 1309 // Stop if the memory operand has implicit-size or if there is more than one. 1310 if (aOp.flags.mem || memPos >= 0) { 1311 memPos = -1; 1312 break; 1313 } 1314 else { 1315 memOp = mem; 1316 memPos = i; 1317 } 1318 } 1319 else if (ObjectUtils.hasAny(aOp.flags, RegOp)) { 1320 // Doesn't consider 'r/m' as we already checked 'm'. 1321 regOps |= (1 << i); 1322 } 1323 } 1324 1325 if (memPos < 0) 1326 continue; 1327 1328 // Create a `sameSizeSet` set of all instructions having the exact 1329 // explicit memory operand at the same position and registers at 1330 // positions matching `regOps` bits and `diffSizeSet` having memory 1331 // operand of different size, but registers at the same positions. 1332 const sameSizeSet = [aInst]; 1333 const diffSizeSet = []; 1334 const diffSizeHash = Object.create(null); 1335 1336 for (let bIndex = 0; bIndex < this.length; bIndex++) { 1337 const bInst = this[bIndex]; 1338 if (aIndex === bIndex || len !== bInst.length) continue; 1339 1340 let hasMatch = 1; 1341 for (let i = 0; i < len; i++) { 1342 if (i === memPos) continue; 1343 1344 const reg = ObjectUtils.hasAny(bInst[i].flags, RegOp); 1345 if (regOps & (1 << i)) 1346 hasMatch &= reg; 1347 else if (reg) 1348 hasMatch = 0; 1349 } 1350 1351 if (hasMatch) { 1352 const bOp = bInst[memPos]; 1353 if (bOp.flags.mem) continue; 1354 1355 const mem = ObjectUtils.findKey(bOp.flags, MemOp); 1356 if (mem === memOp) { 1357 sameSizeSet.push(bInst); 1358 } 1359 else if (mem) { 1360 const key = keyOf(bInst, regOps); 1361 diffSizeSet.push(bInst); 1362 if (!diffSizeHash[key]) 1363 diffSizeHash[key] = [bInst]; 1364 else 1365 diffSizeHash[key].push(bInst); 1366 } 1367 } 1368 } 1369 1370 // Two cases. 1371 // A) The memory operand has implicit-size if `diffSizeSet` is empty. That 1372 // means that the instruction only uses one size for all reg combinations. 1373 // 1374 // B) The memory operand has implicit-size if `diffSizeSet` contains different 1375 // register signatures than `sameSizeSet`. 1376 let implicit = true; 1377 1378 if (!diffSizeSet.length) { 1379 // Case A: 1380 } 1381 else { 1382 // Case B: Find collisions in `sameSizeSet` and `diffSizeSet`. 1383 for (let bIndex = 0; bIndex < sameSizeSet.length; bIndex++) { 1384 const bInst = sameSizeSet[bIndex]; 1385 const key = keyOf(bInst, regOps); 1386 1387 const diff = diffSizeHash[key]; 1388 if (diff) { 1389 diff.forEach((diffInst) => { 1390 if ((bInst.x86 && !diffInst.x86) || (!bInst.x86 && diffInst.x86)) { 1391 // If this is X86|ANY instruction and the other is X64, or vice-versa, 1392 // then keep this implicit as it won't do any harm. These instructions 1393 // cannot be mixed and it will make implicit the 32-bit one in cases 1394 // where X64 introduced 64-bit ones like `cvtsi2ss`. 1395 if (!/^(bndcl|bndcn|bndcu|ptwrite|(v)?cvtsi2ss|(v)?cvtsi2sd|vcvtusi2ss|vcvtusi2sd)$/.test(instName)) 1396 implicit = false; 1397 } 1398 else { 1399 implicit = false; 1400 } 1401 }); 1402 } 1403 } 1404 } 1405 1406 // Patch all instructions to accept implicit-size memory operand. 1407 for (let bIndex = 0; bIndex < sameSizeSet.length; bIndex++) { 1408 const bInst = sameSizeSet[bIndex]; 1409 if (implicit) { 1410 bInst[memPos].flags.mem = true; 1411 } 1412 1413 if (!implicit) { 1414 DEBUG(`${this.name}: Explicit: ${bInst}`); 1415 } 1416 } 1417 } 1418 } 1419 1420 compact() { 1421 let didSomething = true; 1422 while (didSomething) { 1423 didSomething = false; 1424 for (let i = 0; i < this.length; i++) { 1425 let row = this[i]; 1426 let j = i + 1; 1427 while (j < this.length) { 1428 if (row.mergeWith(this[j])) { 1429 this.splice(j, 1); 1430 didSomething = true; 1431 continue; 1432 } 1433 j++; 1434 } 1435 } 1436 } 1437 } 1438 1439 toString() { 1440 return `[${this.join(",\n")}]`; 1441 } 1442 } 1443 1444 class InstSignatureTable extends core.Task { 1445 constructor() { 1446 super("InstSignatureTable"); 1447 this.maxOpRows = 0; 1448 } 1449 1450 run() { 1451 const insts = this.ctx.insts; 1452 1453 insts.forEach((inst) => { 1454 inst.signatures = this.makeSignatures(Filter.noAltForm(inst.dbInsts)); 1455 this.maxOpRows = Math.max(this.maxOpRows, inst.signatures.length); 1456 }); 1457 1458 const iSignatureMap = Object.create(null); 1459 const iSignatureArr = []; 1460 1461 const oSignatureMap = Object.create(null); 1462 const oSignatureArr = []; 1463 1464 // Must be first to be assigned to zero. 1465 const oSignatureNone = "ROW(0, 0xFF)"; 1466 oSignatureMap[oSignatureNone] = [0]; 1467 oSignatureArr.push(oSignatureNone); 1468 1469 function findSignaturesIndex(rows) { 1470 const len = rows.length; 1471 if (!len) return 0; 1472 1473 const indexes = iSignatureMap[rows[0].data]; 1474 if (indexes === undefined) return -1; 1475 1476 for (let i = 0; i < indexes.length; i++) { 1477 const index = indexes[i]; 1478 if (index + len > iSignatureArr.length) continue; 1479 1480 let ok = true; 1481 for (let j = 0; j < len; j++) { 1482 if (iSignatureArr[index + j].data !== rows[j].data) { 1483 ok = false; 1484 break; 1485 } 1486 } 1487 1488 if (ok) 1489 return index; 1490 } 1491 1492 return -1; 1493 } 1494 1495 function indexSignatures(signatures) { 1496 const result = iSignatureArr.length; 1497 1498 for (let i = 0; i < signatures.length; i++) { 1499 const signature = signatures[i]; 1500 const idx = iSignatureArr.length; 1501 1502 if (!Object.hasOwn(iSignatureMap, signature.data)) 1503 iSignatureMap[signature.data] = []; 1504 1505 iSignatureMap[signature.data].push(idx); 1506 iSignatureArr.push(signature); 1507 } 1508 1509 return result; 1510 } 1511 1512 for (let len = this.maxOpRows; len >= 0; len--) { 1513 insts.forEach((inst) => { 1514 const signatures = inst.signatures; 1515 if (signatures.length === len) { 1516 const signatureEntries = []; 1517 for (let j = 0; j < len; j++) { 1518 const signature = signatures[j]; 1519 1520 let signatureEntry = `ROW(${signature.length}, ${signature.x86 ? 1 : 0}, ${signature.x64 ? 1 : 0}, ${signature.implicit}`; 1521 let signatureComment = signature.toString(); 1522 1523 let x = 0; 1524 while (x < signature.length) { 1525 const h = signature[x].toAsmJitOpData(); 1526 let index = -1; 1527 if (!Object.hasOwn(oSignatureMap, h)) { 1528 index = oSignatureArr.length; 1529 oSignatureMap[h] = index; 1530 oSignatureArr.push(h); 1531 } 1532 else { 1533 index = oSignatureMap[h]; 1534 } 1535 1536 signatureEntry += `, ${String(index).padEnd(3)}`; 1537 x++; 1538 } 1539 1540 while (x < 6) { 1541 signatureEntry += `, ${String(0).padEnd(3)}`; 1542 x++; 1543 } 1544 1545 signatureEntry += `)`; 1546 signatureEntries.push({ data: signatureEntry, comment: signatureComment, refs: 0 }); 1547 } 1548 1549 let count = signatureEntries.length; 1550 let index = findSignaturesIndex(signatureEntries); 1551 1552 if (index === -1) 1553 index = indexSignatures(signatureEntries); 1554 1555 iSignatureArr[index].refs++; 1556 inst.signatureIndex = index; 1557 inst.signatureCount = count; 1558 } 1559 }); 1560 } 1561 1562 let s = `#define ROW(count, x86, x64, implicit, o0, o1, o2, o3, o4, o5) \\\n` + 1563 ` { count, uint8_t(x86 ? uint8_t(InstDB::Mode::kX86) : uint8_t(0)) | \\\n` + 1564 ` (x64 ? uint8_t(InstDB::Mode::kX64) : uint8_t(0)) , \\\n` + 1565 ` implicit, \\\n` + 1566 ` 0, \\\n` + 1567 ` { o0, o1, o2, o3, o4, o5 } \\\n` + 1568 ` }\n` + 1569 StringUtils.makeCxxArrayWithComment(iSignatureArr, "const InstDB::InstSignature InstDB::_inst_signature_table[]") + 1570 `#undef ROW\n` + 1571 `\n` + 1572 `#define ROW(op_flags, reg_id) { op_flags, uint8_t(reg_id) }\n` + 1573 `#define F(VAL) uint64_t(InstDB::OpFlags::k##VAL)\n` + 1574 StringUtils.makeCxxArray(oSignatureArr, "const InstDB::OpSignature InstDB::_op_signature_table[]") + 1575 `#undef F\n` + 1576 `#undef ROW\n`; 1577 this.inject("InstSignatureTable", disclaimer(s), oSignatureArr.length * 8 + iSignatureArr.length * 8); 1578 } 1579 1580 makeSignatures(dbInsts) { 1581 const instName = dbInsts.length ? dbInsts[0].name : ""; 1582 const signatures = new SignatureArray(instName); 1583 1584 for (let i = 0; i < dbInsts.length; i++) { 1585 const inst = dbInsts[i]; 1586 const ops = inst.operands; 1587 1588 // NOTE: This changed from having reg|mem merged into creating two signatures 1589 // instead. Imagine two instructions in one `dbInsts` array: 1590 // 1591 // 1. mov reg, reg/mem 1592 // 2. mov reg/mem, reg 1593 // 1594 // If we merge them and then unmerge, we will have 4 signatures, when iterated: 1595 // 1596 // 1a. mov reg, reg 1597 // 1b. mov reg, mem 1598 // 2a. mov reg, reg 1599 // 2b. mov mem, reg 1600 // 1601 // So, instead of merging them here, we insert separated signatures and let 1602 // the tool merge them in a way that can be easily unmerged at runtime into: 1603 // 1604 // 1a. mov reg, reg 1605 // 1b. mov reg, mem 1606 // 2b. mov mem, reg 1607 let modrmCount = 1; 1608 for (let modrm = 0; modrm < modrmCount; modrm++) { 1609 let row = new ISignature(inst.name); 1610 1611 row.x86 = (inst.arch === "ANY" || inst.arch === "X86"); 1612 row.x64 = (inst.arch === "ANY" || inst.arch === "X64"); 1613 1614 let j; 1615 for (j = 0; j < ops.length; j++) { 1616 let iop = ops[j]; 1617 1618 let reg = iop.reg; 1619 let mem = iop.mem; 1620 let imm = iop.imm; 1621 let rel = iop.rel; 1622 1623 // Skip all instructions having implicit `imm` operand of `1`. 1624 if (iop.immValue !== null) 1625 break; 1626 1627 // Shorten the number of signatures of 'mov' instruction. 1628 if (inst.name === "mov" && mem.startsWith("moff")) 1629 break; 1630 1631 if (reg === "seg") reg = "sreg"; 1632 if (reg === "st(i)") reg = "st"; 1633 if (reg === "st(0)") reg = "st0"; 1634 1635 if (mem === "moff8") mem = "m8"; 1636 if (mem === "moff16") mem = "m16"; 1637 if (mem === "moff32") mem = "m32"; 1638 if (mem === "moff64") mem = "m64"; 1639 1640 if (mem === "m32fp") mem = "m32"; 1641 if (mem === "m64fp") mem = "m64"; 1642 if (mem === "m80fp") mem = "m80"; 1643 if (mem === "m80bcd") mem = "m80"; 1644 if (mem === "m80dec") mem = "m80"; 1645 if (mem === "m16int") mem = "m16"; 1646 if (mem === "m32int") mem = "m32"; 1647 if (mem === "m64int") mem = "m64"; 1648 1649 if (mem === "m16_16") mem = "m32"; 1650 if (mem === "m16_32") mem = "m48"; 1651 if (mem === "m16_64") mem = "m80"; 1652 1653 if (reg && mem) { 1654 if (modrmCount === 1) { 1655 mem = null; 1656 modrmCount++; 1657 } 1658 else { 1659 reg = null; 1660 } 1661 } 1662 1663 const op = new OSignature(); 1664 if (iop.implicit) { 1665 row.implicit++; 1666 op.flags.implicit = true; 1667 } 1668 1669 const seg = iop.memSegment; 1670 if (seg) { 1671 switch (inst.name) { 1672 case "insb": op.flags.m8 = true; break; 1673 case "insw": op.flags.m16 = true; break; 1674 case "insd": op.flags.m32 = true; break; 1675 case "outsb": op.flags.m8 = true; break; 1676 case "outsw": op.flags.m16 = true; break; 1677 case "outsd": op.flags.m32 = true; break; 1678 case "clzero": op.flags.mem = true; op.flags.m512 = true; break; 1679 case "enqcmd": op.flags.mem = true; op.flags.m512 = true; break; 1680 case "enqcmds": op.flags.mem = true; op.flags.m512 = true; break; 1681 case "movdir64b": op.flags.mem = true; op.flags.m512 = true; break; 1682 case "maskmovq": op.flags.mem = true; op.flags.m64 = true; break; 1683 case "maskmovdqu": op.flags.mem = true; op.flags.m128 = true; break; 1684 case "vmaskmovdqu": op.flags.mem = true; op.flags.m128 = true; break; 1685 case "monitor": op.flags.mem = true; break; 1686 case "monitorx": op.flags.mem = true; break; 1687 case "umonitor": op.flags.mem = true; break; 1688 default: console.log(`UNKNOWN MEM IN INSTRUCTION '${inst.name}'`); break; 1689 } 1690 1691 if (iop.memRegOnly) 1692 reg = iop.memRegOnly; 1693 1694 if (seg === "ds") op.flags.memDS = true; 1695 if (seg === "es") op.flags.memES = true; 1696 if (reg === "reg") { op.flags.memBase = true; } 1697 if (reg === "r32") { op.flags.memBase = true; } 1698 if (reg === "r64") { op.flags.memBase = true; } 1699 if (reg === "zax") { op.flags.memBase = true; op.flags.memZAX = true; } 1700 if (reg === "zsi") { op.flags.memBase = true; op.flags.memZSI = true; } 1701 if (reg === "zdi") { op.flags.memBase = true; op.flags.memZDI = true; } 1702 } 1703 else if (reg) { 1704 if (reg == "r8") { 1705 op.flags["r8lo"] = true; 1706 1707 if (!inst.w || inst.w === "W0") 1708 op.flags["r8hi"] = true; 1709 } 1710 else { 1711 op.flags[reg] = true; 1712 } 1713 } 1714 1715 if (mem) { 1716 op.flags[mem] = true; 1717 // HACK: Allow LEA to use any memory size. 1718 if (/^(lea)$/.test(inst.name)) { 1719 op.flags.mem = true; 1720 Object.assign(op.flags, MemOp); 1721 } 1722 1723 // HACK: These instructions specify explicit memory size, but it's just informational. 1724 if (/^(call|enqcmd|enqcmds|lcall|ljmp|movdir64b)$/.test(inst.name)) { 1725 op.flags.mem = true; 1726 } 1727 } 1728 1729 if (imm) { 1730 if (iop.immSign === "any" || iop.immSign === "signed" ) op.flags["i" + imm] = true; 1731 if (iop.immSign === "any" || iop.immSign === "unsigned") op.flags["u" + imm] = true; 1732 } 1733 1734 if (rel) { 1735 op.flags["rel" + rel] = true; 1736 } 1737 1738 row.push(op); 1739 } 1740 1741 // Not equal if we terminated the loop. 1742 if (j === ops.length) { 1743 signatures.push(row); 1744 } 1745 } 1746 } 1747 1748 if (signatures.length && GenUtils.canUseImplicitMemSize(instName)) 1749 signatures.calcImplicitMemSize(instName); 1750 1751 signatures.compact(); 1752 return signatures; 1753 } 1754 } 1755 1756 // ============================================================================ 1757 // [tablegen.x86.AdditionalInfoTable] 1758 // ============================================================================ 1759 1760 class AdditionalInfoTable extends core.Task { 1761 constructor() { 1762 super("AdditionalInfoTable"); 1763 } 1764 1765 run() { 1766 const insts = this.ctx.insts; 1767 const rwInfoTable = new IndexedArray(); 1768 const instFlagsTable = new IndexedArray(); 1769 const additionaInfoTable = new IndexedArray(); 1770 1771 // If the instruction doesn't read any flags it should point to the first index. 1772 rwInfoTable.addIndexed(`{ 0, 0 }`); 1773 1774 insts.forEach((inst) => { 1775 const dbInsts = inst.dbInsts; 1776 1777 let features = GenUtils.cpuFeaturesOf(dbInsts).map(function(f) { return `EXT(${f})`; }).join(", "); 1778 if (!features) features = "0"; 1779 1780 let [r, w] = this.rwFlagsOf(dbInsts); 1781 const rData = r.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0"; 1782 const wData = w.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0"; 1783 const instFlags = Object.create(null); 1784 1785 switch (inst.name) { 1786 case "kmovb": 1787 case "kmovd": 1788 case "kmovq": 1789 case "kmovw": 1790 case "mov": 1791 case "movq": 1792 case "movsd": 1793 case "movss": 1794 case "movapd": 1795 case "movaps": 1796 case "movdqa": 1797 case "movdqu": 1798 case "movupd": 1799 case "movups": 1800 case "vmovapd": 1801 case "vmovaps": 1802 case "vmovdqa": 1803 case "vmovdqa8": 1804 case "vmovdqa16": 1805 case "vmovdqa32": 1806 case "vmovdqa64": 1807 case "vmovdqu": 1808 case "vmovdqu8": 1809 case "vmovdqu16": 1810 case "vmovdqu32": 1811 case "vmovdqu64": 1812 case "vmovq": 1813 case "vmovsd": 1814 case "vmovss": 1815 case "vmovupd": 1816 case "vmovups": 1817 instFlags["MovOp"] = true; 1818 break; 1819 } 1820 1821 const instFlagsIndex = instFlagsTable.addIndexed("InstRWFlags(" + StringUtils.formatCppFlags(instFlags, (f) => { return `FLAG(${f})`; }, "FLAG(None)") + ")"); 1822 const rwInfoIndex = rwInfoTable.addIndexed(`{ ${rData}, ${wData} }`); 1823 1824 inst.additionalInfoIndex = additionaInfoTable.addIndexed(`{ ${instFlagsIndex}, ${rwInfoIndex}, { ${features} } }`); 1825 }); 1826 1827 let s = `#define EXT(VAL) uint32_t(CpuFeatures::X86::k##VAL)\n` + 1828 `const InstDB::AdditionalInfo InstDB::additional_info_table[] = {\n${StringUtils.format(additionaInfoTable, kIndent, true)}\n};\n` + 1829 `#undef EXT\n` + 1830 `\n` + 1831 `#define FLAG(VAL) uint32_t(CpuRWFlags::kX86_##VAL)\n` + 1832 `const InstDB::RWFlagsInfoTable InstDB::rw_flags_info_table[] = {\n${StringUtils.format(rwInfoTable, kIndent, true)}\n};\n` + 1833 `#undef FLAG\n` + 1834 `\n` + 1835 `#define FLAG(VAL) uint32_t(InstRWFlags::k##VAL)\n` + 1836 `const InstRWFlags InstDB::inst_flags_table[] = {\n${StringUtils.format(instFlagsTable, kIndent, true)}\n};\n` + 1837 `#undef FLAG\n`; 1838 this.inject("AdditionalInfoTable", disclaimer(s), additionaInfoTable.length * 8 + rwInfoTable.length * 8 + instFlagsTable.length * 4); 1839 } 1840 1841 rwFlagsOf(dbInsts) { 1842 const r = Object.create(null); 1843 const w = Object.create(null); 1844 1845 for (let i = 0; i < dbInsts.length; i++) { 1846 const dbInst = dbInsts[i]; 1847 1848 // Omit special cases, this is handled well in C++ code. 1849 if (dbInst.name === "mov") 1850 continue; 1851 1852 const regs = dbInst.io; 1853 1854 // Mov is a special case, moving to/from control regs makes flags undefined, 1855 // which we don't want to have in `X86InstDB::operationData`. This is, thus, 1856 // a special case instruction analyzer must deal with. 1857 if (dbInst.name === "mov") 1858 continue; 1859 1860 for (let reg in regs) { 1861 let flag = ""; 1862 switch (reg) { 1863 case "CF": flag = "CF"; break; 1864 case "OF": flag = "OF"; break; 1865 case "SF": flag = "SF"; break; 1866 case "ZF": flag = "ZF"; break; 1867 case "AF": flag = "AF"; break; 1868 case "PF": flag = "PF"; break; 1869 case "DF": flag = "DF"; break; 1870 case "IF": flag = "IF"; break; 1871 //case "TF": flag = "TF"; break; 1872 case "AC": flag = "AC"; break; 1873 case "C0": flag = "C0"; break; 1874 case "C1": flag = "C1"; break; 1875 case "C2": flag = "C2"; break; 1876 case "C3": flag = "C3"; break; 1877 default: 1878 continue; 1879 } 1880 1881 switch (regs[reg]) { 1882 case "R": 1883 r[flag] = true; 1884 break; 1885 case "X": 1886 r[flag] = true; 1887 // ... fallthrough ... 1888 case "W": 1889 case "U": 1890 case "0": 1891 case "1": 1892 w[flag] = true; 1893 break; 1894 } 1895 } 1896 } 1897 1898 return [ArrayUtils.sorted(r), ArrayUtils.sorted(w)]; 1899 } 1900 } 1901 1902 // ============================================================================ 1903 // [tablegen.x86.InstRWInfoTable] 1904 // ============================================================================ 1905 1906 const NOT_MEM_AMBIGUOUS = ArrayUtils.toDict([ 1907 "call", "movq" 1908 ]); 1909 1910 class InstRWInfoTable extends core.Task { 1911 constructor() { 1912 super("InstRWInfoTable"); 1913 1914 this.rwInfoIndexA = []; 1915 this.rwInfoIndexB = []; 1916 this.rwInfoTableA = new IndexedArray(); 1917 this.rwInfoTableB = new IndexedArray(); 1918 1919 this.rmInfoTable = new IndexedArray(); 1920 this.opInfoTable = new IndexedArray(); 1921 1922 this.rwCategoryByName = { 1923 "imul" : "Imul", 1924 "mov" : "Mov", 1925 "movabs" : "Movabs", 1926 "movhpd" : "Movh64", 1927 "movhps" : "Movh64", 1928 "punpcklbw" : "Punpcklxx", 1929 "punpckldq" : "Punpcklxx", 1930 "punpcklwd" : "Punpcklxx", 1931 "vmaskmovpd": "Vmaskmov", 1932 "vmaskmovps": "Vmaskmov", 1933 "vmovddup" : "Vmovddup", 1934 "vmovmskpd" : "Vmovmskpd", 1935 "vmovmskps" : "Vmovmskps", 1936 "vpmaskmovd": "Vmaskmov", 1937 "vpmaskmovq": "Vmaskmov" 1938 }; 1939 1940 const _ = null; 1941 this.rwCategoryByData = { 1942 Vmov1_8: [ 1943 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 8}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_], 1944 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 16}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_], 1945 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_], 1946 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_] 1947 ], 1948 Vmov1_4: [ 1949 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_], 1950 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_], 1951 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_] 1952 ], 1953 Vmov1_2: [ 1954 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_], 1955 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_], 1956 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_] 1957 ], 1958 Vmov2_1: [ 1959 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_], 1960 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_], 1961 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_] 1962 ], 1963 Vmov4_1: [ 1964 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_], 1965 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_], 1966 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_] 1967 ], 1968 Vmov8_1: [ 1969 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 16},_,_,_,_], 1970 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_], 1971 [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_] 1972 ] 1973 }; 1974 } 1975 1976 run() { 1977 const insts = this.ctx.insts; 1978 1979 const noRmInfo = StringUtils.formatCppStruct( 1980 "InstDB::RWInfoRm::kCategory" + "None".padEnd(10), 1981 StringUtils.decToHex(0, 2), 1982 String(0).padEnd(2), 1983 StringUtils.formatCppFlags({}), 1984 "0" 1985 ); 1986 1987 const noOpInfo = StringUtils.formatCppStruct( 1988 "0x0000000000000000u", 1989 "0x0000000000000000u", 1990 "0xFF", 1991 "0", 1992 StringUtils.formatCppStruct(0), 1993 "OpRWFlags::kNone" 1994 ); 1995 1996 this.rmInfoTable.addIndexed(noRmInfo); 1997 this.opInfoTable.addIndexed(noOpInfo); 1998 1999 insts.forEach((inst) => { 2000 // Alternate forms would only mess this up, so filter them out. 2001 const dbInsts = Filter.noAltForm(inst.dbInsts); 2002 2003 // The best we can do is to divide instructions that have 2 operands and others. 2004 // This gives us the highest chance of preventing special cases (which were not 2005 // entirely avoided). 2006 const o2Insts = dbInsts.filter((inst) => { return inst.operands.length === 2; }); 2007 const oxInsts = dbInsts.filter((inst) => { return inst.operands.length !== 2; }); 2008 2009 const rwInfoArray = [this.rwInfo(inst, o2Insts), this.rwInfo(inst, oxInsts)]; 2010 const rmInfoArray = [this.rmInfo(inst, o2Insts), this.rmInfo(inst, oxInsts)]; 2011 2012 for (let i = 0; i < 2; i++) { 2013 const rwInfo = rwInfoArray[i]; 2014 const rmInfo = rmInfoArray[i]; 2015 2016 const rwOps = rwInfo.rwOps; 2017 const rwOpsIndex = []; 2018 for (let j = 0; j < rwOps.length; j++) { 2019 const op = rwOps[j]; 2020 if (!op) { 2021 rwOpsIndex.push(this.opInfoTable.addIndexed(noOpInfo)); 2022 continue; 2023 } 2024 2025 const flags = {}; 2026 const opAcc = op.access; 2027 2028 if (opAcc === "R") flags.Read = true; 2029 if (opAcc === "W") flags.Write = true; 2030 if (opAcc === "X") flags.RW = true; 2031 ObjectUtils.merge(flags, op.flags); 2032 2033 const rIndex = opAcc === "X" || opAcc === "R" ? op.index : -1; 2034 const rWidth = opAcc === "X" || opAcc === "R" ? op.width : -1; 2035 const wIndex = opAcc === "X" || opAcc === "W" ? op.index : -1; 2036 const wWidth = opAcc === "X" || opAcc === "W" ? op.width : -1; 2037 2038 const consecutiveLeadCount = op.clc; 2039 2040 const opData = StringUtils.formatCppStruct( 2041 this.byteMaskFromBitRanges([{ start: rIndex, end: rIndex + rWidth - 1 }]) + "u", 2042 this.byteMaskFromBitRanges([{ start: wIndex, end: wIndex + wWidth - 1 }]) + "u", 2043 StringUtils.decToHex(op.fixed === -1 ? 0xFF : op.fixed, 2), 2044 String(consecutiveLeadCount), 2045 StringUtils.formatCppStruct(0), 2046 StringUtils.formatCppFlags(flags, function(flag) { return "OpRWFlags::k" + flag; }, "OpRWFlags::kNone") 2047 ); 2048 2049 rwOpsIndex.push(this.opInfoTable.addIndexed(opData)); 2050 } 2051 2052 const rmData = StringUtils.formatCppStruct( 2053 "InstDB::RWInfoRm::kCategory" + rmInfo.category.padEnd(10), 2054 StringUtils.decToHex(rmInfo.rmIndexes, 2), 2055 String(Math.max(rmInfo.memFixed, 0)).padEnd(2), 2056 StringUtils.formatCppFlags({ 2057 "InstDB::RWInfoRm::kFlagAmbiguous": Boolean(rmInfo.memAmbiguous), 2058 "InstDB::RWInfoRm::kFlagMovssMovsd": Boolean(inst.name === "movss" || inst.name === "movsd"), 2059 "InstDB::RWInfoRm::kFlagPextrw": Boolean(inst.name === "pextrw"), 2060 "InstDB::RWInfoRm::kFlagFeatureIfRMI": Boolean(rmInfo.memExtensionIfRMI) 2061 }), 2062 rmInfo.memExtension === "None" ? "0" : "uint32_t(CpuFeatures::X86::k" + rmInfo.memExtension + ")" 2063 ); 2064 2065 const rwData = StringUtils.formatCppStruct( 2066 "InstDB::RWInfo::kCategory" + rwInfo.category.padEnd(10), 2067 String(this.rmInfoTable.addIndexed(rmData)).padEnd(2), 2068 StringUtils.formatCppStruct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); }))) 2069 ); 2070 2071 if (i == 0) 2072 this.rwInfoIndexA.push(this.rwInfoTableA.addIndexed(rwData)); 2073 else 2074 this.rwInfoIndexB.push(this.rwInfoTableB.addIndexed(rwData)); 2075 } 2076 }); 2077 2078 let s = ""; 2079 s += "const uint8_t InstDB::rw_info_index_a_table[Inst::_kIdCount] = {\n" + StringUtils.format(this.rwInfoIndexA, kIndent, -1) + "\n};\n"; 2080 s += "\n"; 2081 s += "const uint8_t InstDB::rw_info_index_b_table[Inst::_kIdCount] = {\n" + StringUtils.format(this.rwInfoIndexB, kIndent, -1) + "\n};\n"; 2082 s += "\n"; 2083 s += "const InstDB::RWInfo InstDB::rw_info_a_table[] = {\n" + StringUtils.format(this.rwInfoTableA, kIndent, true) + "\n};\n"; 2084 s += "\n"; 2085 s += "const InstDB::RWInfo InstDB::rw_info_b_table[] = {\n" + StringUtils.format(this.rwInfoTableB, kIndent, true) + "\n};\n"; 2086 s += "\n"; 2087 s += "const InstDB::RWInfoOp InstDB::rw_info_op_table[] = {\n" + StringUtils.format(this.opInfoTable, kIndent, true) + "\n};\n"; 2088 s += "\n"; 2089 s += "const InstDB::RWInfoRm InstDB::rw_info_rm_table[] = {\n" + StringUtils.format(this.rmInfoTable, kIndent, true) + "\n};\n"; 2090 2091 const size = this.rwInfoIndexA.length + 2092 this.rwInfoIndexB.length + 2093 this.rwInfoTableA.length * 8 + 2094 this.rwInfoTableB.length * 8 + 2095 this.rmInfoTable.length * 4 + 2096 this.opInfoTable.length * 24; 2097 2098 this.inject("InstRWInfoTable", disclaimer(s), size); 2099 } 2100 2101 byteMaskFromBitRanges(ranges) { 2102 const arr = []; 2103 for (let i = 0; i < 64; i++) 2104 arr.push(0); 2105 2106 for (let i = 0; i < ranges.length; i++) { 2107 const start = ranges[i].start; 2108 const end = ranges[i].end; 2109 2110 if (start < 0) 2111 continue; 2112 2113 for (let j = start; j <= end; j++) { 2114 const bytePos = j >> 3; 2115 if (bytePos < 0 || bytePos >= arr.length) 2116 FATAL(`Range ${start}:${end} cannot be used to create a byte-mask`); 2117 arr[bytePos] = 1; 2118 } 2119 } 2120 2121 let s = "0x"; 2122 for (let i = arr.length - 4; i >= 0; i -= 4) { 2123 const value = (arr[i + 3] << 3) | (arr[i + 2] << 2) | (arr[i + 1] << 1) | arr[i]; 2124 s += value.toString(16).toUpperCase(); 2125 } 2126 return s; 2127 } 2128 2129 // Read/Write Info 2130 // --------------- 2131 2132 rwInfo(asmInst, dbInsts) { 2133 const self = this; 2134 2135 function nullOps() { 2136 return [null, null, null, null, null, null]; 2137 } 2138 2139 function makeRwFromOp(op) { 2140 if (!op.isRegOrMem()) 2141 return null; 2142 2143 return { 2144 access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?", 2145 clc: 0, 2146 flags: {}, 2147 fixed: GenUtils.fixedRegOf(op), 2148 index: op.rwxIndex, 2149 width: op.rwxWidth 2150 }; 2151 } 2152 2153 function queryRwGeneric(dbInsts, step) { 2154 let rwOps = nullOps(); 2155 for (let i = 0; i < dbInsts.length; i++) { 2156 const dbInst = dbInsts[i]; 2157 const operands = dbInst.operands; 2158 2159 for (let j = 0; j < operands.length; j++) { 2160 const op = operands[j]; 2161 if (!op.isRegOrMem()) 2162 continue; 2163 2164 const opSize = op.isReg() ? op.regSize : op.memSize; 2165 let d = { 2166 access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?", 2167 clc: 0, 2168 flags: {}, 2169 fixed: -1, 2170 index: -1, 2171 width: -1 2172 }; 2173 2174 if (op.consecutiveLeadCount) 2175 d.clc = op.consecutiveLeadCount; 2176 2177 const instName = dbInst.name; 2178 // NOTE: Avoid push/pop here as PUSH/POP has many variations for segment registers, 2179 // which would set 'd.fixed' field even for GP variation of the instuction. 2180 if (instName !== "push" && instName !== "pop") { 2181 d.fixed = GenUtils.fixedRegOf(op); 2182 } 2183 2184 switch (instName) { 2185 case "vfcmaddcph": 2186 case "vfmaddcph": 2187 case "vfcmaddcsh": 2188 case "vfmaddcsh": 2189 case "vfcmulcsh": 2190 case "vfmulcsh": 2191 case "vfcmulcph": 2192 case "vfmulcph": 2193 if (j === 0) 2194 d.flags.Unique = true; 2195 break; 2196 } 2197 2198 if (op.zext) 2199 d.flags.ZExt = true; 2200 2201 if (op.regIndexRel) 2202 d.flags.Consecutive = true; 2203 2204 for (let k in self.rwOpFlagsForInstruction(asmInst.name, j)) 2205 d.flags[k] = true; 2206 2207 if ((step === -1 || step === j) || op.rwxIndex !== 0 || op.rwxWidth !== opSize) { 2208 d.index = op.rwxIndex; 2209 d.width = op.rwxWidth; 2210 } 2211 2212 if (d.fixed !== -1) { 2213 if (op.memSegment) 2214 d.flags.MemPhysId = true; 2215 else 2216 d.flags.RegPhysId = true; 2217 } 2218 2219 if (rwOps[j] === null) { 2220 rwOps[j] = d; 2221 } 2222 else { 2223 if (!ObjectUtils.equalsExcept(rwOps[j], d, { "fixed": true, "flags": true })) 2224 return null; 2225 2226 if (rwOps[j].fixed === -1) 2227 rwOps[j].fixed = d.fixed; 2228 ObjectUtils.merge(rwOps[j].flags, d.flags); 2229 } 2230 } 2231 } 2232 2233 const name = dbInsts.length ? dbInsts[0].name : ""; 2234 2235 switch (name) { 2236 case "vpternlogd": 2237 case "vpternlogq": 2238 return { category: "GenericEx", rwOps }; 2239 2240 default: 2241 return { category: "Generic", rwOps }; 2242 } 2243 } 2244 2245 function queryRwByData(dbInsts, rwOpsArray) { 2246 for (let i = 0; i < dbInsts.length; i++) { 2247 const dbInst = dbInsts[i]; 2248 const operands = dbInst.operands; 2249 const rwOps = nullOps(); 2250 2251 for (let j = 0; j < operands.length; j++) { 2252 rwOps[j] = makeRwFromOp(operands[j]) 2253 } 2254 2255 let match = 0; 2256 for (let j = 0; j < rwOpsArray.length; j++) 2257 match |= ObjectUtils.equals(rwOps, rwOpsArray[j]); 2258 2259 if (!match) 2260 return false; 2261 } 2262 2263 return true; 2264 } 2265 2266 function dumpRwToData(dbInsts) { 2267 const out = []; 2268 for (let i = 0; i < dbInsts.length; i++) { 2269 const dbInst = dbInsts[i]; 2270 const operands = dbInst.operands; 2271 const rwOps = nullOps(); 2272 2273 for (let j = 0; j < operands.length; j++) 2274 rwOps[j] = makeRwFromOp(operands[j]) 2275 2276 if (ArrayUtils.deepIndexOf(out, rwOps) !== -1) 2277 continue; 2278 2279 out.push(rwOps); 2280 } 2281 return out; 2282 } 2283 2284 // Some instructions are just special... 2285 const name = dbInsts.length ? dbInsts[0].name : ""; 2286 if (name in this.rwCategoryByName) 2287 return { category: this.rwCategoryByName[name], rwOps: nullOps() }; 2288 2289 // Generic rules. 2290 for (let i = -1; i <= 6; i++) { 2291 const rwInfo = queryRwGeneric(dbInsts, i); 2292 if (rwInfo) 2293 return rwInfo; 2294 } 2295 2296 // Specific rules. 2297 for (let k in this.rwCategoryByData) 2298 if (queryRwByData(dbInsts, this.rwCategoryByData[k])) 2299 return { category: k, rwOps: nullOps() }; 2300 2301 // FATAL: Missing data to categorize this instruction. 2302 if (name) { 2303 const items = dumpRwToData(dbInsts) 2304 console.log(`RW: ${dbInsts.length ? dbInsts[0].name : ""}:`); 2305 items.forEach((item) => { 2306 console.log(" " + JSON.stringify(item)); 2307 }); 2308 } 2309 2310 return null; 2311 } 2312 2313 rwOpFlagsForInstruction(instName, opIndex) { 2314 const toMap = ArrayUtils.toDict; 2315 2316 // TODO: We should be able to get this information from asmdb. 2317 switch (instName + "@" + opIndex) { 2318 case "cmps@0": return toMap(['MemBaseRW', 'MemBasePostModify']); 2319 case "cmps@1": return toMap(['MemBaseRW', 'MemBasePostModify']); 2320 case "movs@0": return toMap(['MemBaseRW', 'MemBasePostModify']); 2321 case "movs@1": return toMap(['MemBaseRW', 'MemBasePostModify']); 2322 case "lods@1": return toMap(['MemBaseRW', 'MemBasePostModify']); 2323 case "stos@0": return toMap(['MemBaseRW', 'MemBasePostModify']); 2324 case "scas@1": return toMap(['MemBaseRW', 'MemBasePostModify']); 2325 case "bndstx@0": return toMap(['MemBaseWrite', 'MemIndexWrite']); 2326 2327 default: 2328 return {}; 2329 } 2330 } 2331 2332 // Reg/Mem Info 2333 // ------------ 2334 2335 rmInfo(asmInst, dbInsts) { 2336 const info = { 2337 category: "None", 2338 rmIndexes: this.rmReplaceableIndexes(dbInsts), 2339 memFixed: this.rmFixedSize(dbInsts), 2340 memAmbiguous: this.rmIsAmbiguous(dbInsts), 2341 memConsistent: this.rmIsConsistent(dbInsts), 2342 memExtension: this.rmExtension(dbInsts), 2343 memExtensionIfRMI: this.rmExtensionIfRMI(dbInsts) 2344 }; 2345 2346 if (info.memFixed !== -1) 2347 info.category = "Fixed"; 2348 else if (info.memConsistent) 2349 info.category = "Consistent"; 2350 else if (info.rmIndexes) 2351 info.category = this.rmReplaceableCategory(dbInsts); 2352 2353 return info; 2354 } 2355 2356 rmReplaceableCategory(dbInsts) { 2357 let category = null; 2358 2359 for (let i = 0; i < dbInsts.length; i++) { 2360 const dbInst = dbInsts[i]; 2361 const operands = dbInst.operands; 2362 2363 let rs = -1; 2364 let ms = -1; 2365 2366 for (let j = 0; j < operands.length; j++) { 2367 const op = operands[j]; 2368 if (op.isMem()) 2369 ms = op.memSize; 2370 else if (op.isReg()) 2371 rs = Math.max(rs, op.regSize); 2372 } 2373 2374 let c = (rs === -1 ) ? "None" : 2375 (ms === -1 ) ? "None" : 2376 (ms === rs ) ? "Fixed" : 2377 (ms === rs / 2) ? "Half" : 2378 (ms === rs / 4) ? "Quarter" : 2379 (ms === rs / 8) ? "Eighth" : "Unknown"; 2380 2381 if (category === null) 2382 category = c; 2383 else if (category !== c) { 2384 // Special cases. 2385 if (dbInst.name === "mov" || dbInst.name === "vmovddup") 2386 return "None"; 2387 2388 if (/^(punpcklbw|punpckldq|punpcklwd)$/.test(dbInst.name)) 2389 return "None"; 2390 2391 return cxx.Utils.capitalize(dbInst.name); 2392 } 2393 } 2394 2395 if (category === "Unknown") 2396 console.log(`Instruction '${dbInsts[0].name}' has no RMInfo category.`); 2397 2398 return category || "Unknown"; 2399 } 2400 2401 rmReplaceableIndexes(dbInsts) { 2402 function maskOf(inst, fn) { 2403 let m = 0; 2404 let operands = inst.operands; 2405 for (let i = 0; i < operands.length; i++) 2406 if (fn(operands[i])) 2407 m |= (1 << i); 2408 return m; 2409 } 2410 2411 function getRegIndexes(inst) { return maskOf(inst, function(op) { return op.isReg(); }); }; 2412 function getMemIndexes(inst) { return maskOf(inst, function(op) { return op.isMem(); }); }; 2413 2414 let mask = 0; 2415 2416 for (let i = 0; i < dbInsts.length; i++) { 2417 const dbInst = dbInsts[i]; 2418 2419 let mi = getMemIndexes(dbInst); 2420 let ri = getRegIndexes(dbInst) & ~mi; 2421 2422 if (!mi) 2423 continue; 2424 2425 const match = dbInsts.some((inst) => { 2426 let ti = getRegIndexes(inst); 2427 return ((ri & ti) === ri && (mi & ti) === mi); 2428 }); 2429 2430 if (!match) 2431 return 0; 2432 mask |= mi; 2433 } 2434 2435 return mask; 2436 } 2437 2438 rmFixedSize(insts) { 2439 let savedOp = null; 2440 2441 for (let i = 0; i < insts.length; i++) { 2442 const inst = insts[i]; 2443 const operands = inst.operands; 2444 2445 for (let j = 0; j < operands.length; j++) { 2446 const op = operands[j]; 2447 if (op.mem) { 2448 if (savedOp && savedOp.mem !== op.mem) 2449 return -1; 2450 savedOp = op; 2451 } 2452 } 2453 } 2454 2455 return savedOp ? Math.max(savedOp.memSize, 0) / 8 : -1; 2456 } 2457 2458 rmIsConsistent(insts) { 2459 let hasMem = 0; 2460 for (let i = 0; i < insts.length; i++) { 2461 const inst = insts[i]; 2462 const operands = inst.operands; 2463 for (let j = 0; j < operands.length; j++) { 2464 const op = operands[j]; 2465 if (op.mem) { 2466 hasMem = 1; 2467 if (!op.reg) 2468 return 0; 2469 if (asmdb.x86.Utils.regSize(op.reg) !== op.memSize) 2470 return 0; 2471 } 2472 } 2473 } 2474 return hasMem; 2475 } 2476 2477 rmIsAmbiguous(dbInsts) { 2478 function isAmbiguous(dbInsts) { 2479 const memMap = {}; 2480 const immMap = {}; 2481 2482 for (let i = 0; i < dbInsts.length; i++) { 2483 const dbInst = dbInsts[i]; 2484 const operands = dbInst.operands; 2485 2486 let memStr = ""; 2487 let immStr = ""; 2488 let hasMem = false; 2489 let hasImm = false; 2490 2491 for (let j = 0; j < operands.length; j++) { 2492 const op = operands[j]; 2493 if (j) { 2494 memStr += ", "; 2495 immStr += ", "; 2496 } 2497 2498 if (op.isImm()) { 2499 immStr += "imm"; 2500 hasImm = true; 2501 } 2502 else { 2503 immStr += op.toString(); 2504 } 2505 2506 if (op.mem) { 2507 memStr += "m"; 2508 hasMem = true; 2509 } 2510 else { 2511 memStr += op.isImm() ? "imm" : op.toString(); 2512 } 2513 } 2514 2515 if (hasImm) { 2516 if (immMap[immStr] === true) 2517 continue; 2518 immMap[immStr] = true; 2519 } 2520 2521 if (hasMem) { 2522 if (memMap[memStr] === true) 2523 return 1; 2524 memMap[memStr] = true; 2525 } 2526 } 2527 return 0; 2528 } 2529 2530 const uniqueInsts = Filter.unique(dbInsts); 2531 2532 // Special cases. 2533 if (!dbInsts.length) 2534 return 0; 2535 2536 if (NOT_MEM_AMBIGUOUS[dbInsts[0].name]) 2537 return 0; 2538 2539 return (isAmbiguous(Filter.byArch(uniqueInsts, "X86")) << 0) | 2540 (isAmbiguous(Filter.byArch(uniqueInsts, "X64")) << 1) ; 2541 } 2542 2543 rmExtension(dbInsts) { 2544 if (!dbInsts.length) 2545 return "None"; 2546 2547 const name = dbInsts[0].name; 2548 switch (name) { 2549 case "pextrw": 2550 return "SSE4_1"; 2551 2552 case "vpslld": 2553 case "vpsllq": 2554 case "vpsrad": 2555 case "vpsrld": 2556 case "vpsrlq": 2557 return "AVX512_F"; 2558 2559 case "vpslldq": 2560 case "vpsllw": 2561 case "vpsraw": 2562 case "vpsrldq": 2563 case "vpsrlw": 2564 return "AVX512_BW"; 2565 2566 default: 2567 return "None"; 2568 } 2569 } 2570 2571 rmExtensionIfRMI(dbInsts) { 2572 if (!dbInsts.length) 2573 return 0; 2574 2575 const name = dbInsts[0].name; 2576 return /^(vpslld|vpsllq|vpsrad|vpsrld|vpsrlq|vpslldq|vpsllw|vpsraw|vpsrldq|vpsrlw)$/.test(name); 2577 } 2578 } 2579 2580 // ============================================================================ 2581 // [tablegen.x86.InstCommonTable] 2582 // ============================================================================ 2583 2584 class InstCommonTable extends core.Task { 2585 constructor() { 2586 super("InstCommonTable", [ 2587 "IdEnum", 2588 "NameTable", 2589 "InstSignatureTable", 2590 "AdditionalInfoTable", 2591 "InstRWInfoTable" 2592 ]); 2593 } 2594 2595 run() { 2596 const insts = this.ctx.insts; 2597 const table = new IndexedArray(); 2598 2599 insts.forEach((inst) => { 2600 const commonFlagsArray = inst.flags.filter((flag) => { return !flag.startsWith("Avx512"); }); 2601 const avx512FlagsArray = inst.flags.filter((flag) => { return flag.startsWith("Avx512"); }); 2602 2603 const commonFlags = commonFlagsArray.map(function(flag) { return `F(${flag })`; }).join("|") || "0"; 2604 const avx512Flags = avx512FlagsArray.map(function(flag) { return `X(${flag.substr(6)})`; }).join("|") || "0"; 2605 2606 const controlFlow = `CONTROL_FLOW(${inst.controlFlow})`; 2607 const singleRegCase = `SAME_REG_HINT(${inst.singleRegCase})`; 2608 2609 const row = "{ " + 2610 String(commonFlags ).padEnd(50) + ", " + 2611 String(avx512Flags ).padEnd(30) + ", " + 2612 String(inst.signatureIndex).padEnd( 3) + ", " + 2613 String(inst.signatureCount).padEnd( 2) + ", " + 2614 String(controlFlow ).padEnd(16) + ", " + 2615 String(singleRegCase ).padEnd(16) + "}"; 2616 inst.commonInfoIndex = table.addIndexed(row); 2617 }); 2618 2619 let s = `#define F(VAL) uint32_t(InstDB::InstFlags::k##VAL)\n` + 2620 `#define X(VAL) uint32_t(InstDB::Avx512Flags::k##VAL)\n` + 2621 `#define CONTROL_FLOW(VAL) uint8_t(InstControlFlow::k##VAL)\n` + 2622 `#define SAME_REG_HINT(VAL) uint8_t(InstSameRegHint::k##VAL)\n` + 2623 `const InstDB::CommonInfo InstDB::_inst_common_info_table[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` + 2624 `#undef SAME_REG_HINT\n` + 2625 `#undef CONTROL_FLOW\n` + 2626 `#undef X\n` + 2627 `#undef F\n`; 2628 this.inject("InstCommonTable", disclaimer(s), table.length * 8); 2629 } 2630 } 2631 2632 // ============================================================================ 2633 // [Main] 2634 // ============================================================================ 2635 2636 new X86TableGen() 2637 .addTask(new IdEnum()) 2638 .addTask(new NameTable()) 2639 .addTask(new AltOpcodeTable()) 2640 .addTask(new InstSignatureTable()) 2641 .addTask(new AdditionalInfoTable()) 2642 .addTask(new InstRWInfoTable()) 2643 .addTask(new InstCommonTable()) 2644 .run();