odin-blend2d

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

x86.js (40788B)


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