odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

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();