| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 | // [AsmJit]// Machine Code Generation for C++.//// [License]// ZLIB - See LICENSE.md file in the package.// ============================================================================// tablegen.js//// Provides core foundation for generating tables that AsmJit requires. This// file should provide everything table generators need in general.// ============================================================================"use strict";const VERBOSE = false;// ============================================================================// [Imports]// ============================================================================const fs = require("fs");const hasOwn = Object.prototype.hasOwnProperty;const asmdb = (function() {  // Try to import a local 'asmdb' package, if available.  try {    return require("./asmdb");  }  catch (ex) {    if (ex.code !== "MODULE_NOT_FOUND") {      console.log(`FATAL ERROR: ${ex.message}`);      throw ex;    }  }  // Try to import global 'asmdb' package as local package is not available.  return require("asmdb");})();exports.asmdb = asmdb;// ============================================================================// [Constants]// ============================================================================const kIndent = "  ";const kJustify = 119;const kAsmJitRoot = "..";exports.kIndent = kIndent;exports.kJustify = kJustify;exports.kAsmJitRoot = kAsmJitRoot;// ============================================================================// [Debugging]// ============================================================================function DEBUG(msg) {  if (VERBOSE)    console.log(msg);}exports.DEBUG = DEBUG;function WARN(msg) {  console.log(msg);}exports.WARN = WARN;function FAIL(msg) {  console.log(`FATAL ERROR: ${msg}`);  throw new Error(msg);}exports.FAIL = FAIL;// ============================================================================// [Lang]// ============================================================================function nop(x) { return x; }class Lang {  static merge(a, b) {    if (a === b)      return a;    for (var k in b) {      var av = a[k];      var bv = b[k];      if (typeof av === "object" && typeof bv === "object")        Lang.merge(av, bv);      else        a[k] = bv;    }    return a;  }  static deepEq(a, b) {    if (a === b)      return true;    if (typeof a !== typeof b)      return false;    if (typeof a !== "object")      return a === b;    if (Array.isArray(a) || Array.isArray(b)) {      if (Array.isArray(a) !== Array.isArray(b))        return false;      const len = a.length;      if (b.length !== len)        return false;      for (var i = 0; i < len; i++)        if (!Lang.deepEq(a[i], b[i]))          return false;    }    else {      if (a === null || b === null)        return a === b;      for (var k in a)        if (!hasOwn.call(b, k) || !Lang.deepEq(a[k], b[k]))          return false;      for (var k in b)        if (!hasOwn.call(a, k))          return false;    }    return true;  }  static deepEqExcept(a, b, except) {    if (a === b)      return true;    if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b))      return Lang.deepEq(a, b);    for (var k in a)      if (!hasOwn.call(except, k) && (!hasOwn.call(b, k) || !Lang.deepEq(a[k], b[k])))        return false;    for (var k in b)      if (!hasOwn.call(except, k) && !hasOwn.call(a, k))        return false;    return true;  }}exports.Lang = Lang;// ============================================================================// [StringUtils]// ============================================================================class StringUtils {  static asString(x) { return String(x); }  static capitalize(s) {    s = String(s);    return !s ? s : s[0].toUpperCase() + s.substr(1);  }  static trimLeft(s) { return s.replace(/^\s+/, ""); }  static trimRight(s) { return s.replace(/\s+$/, ""); }  static upFirst(s) {    if (!s) return "";    return s[0].toUpperCase() + s.substr(1);  }  static decToHex(n, nPad) {    var hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);    while (nPad > hex.length)      hex = "0" + hex;    return "0x" + hex.toUpperCase();  }  static format(array, indent, showIndex, mapFn) {    if (!mapFn)      mapFn = StringUtils.asString;    var s = "";    var threshold = 80;    if (showIndex === -1)      s += indent;    for (var i = 0; i < array.length; i++) {      const item = array[i];      const last = i === array.length - 1;      if (showIndex !== -1)        s += indent;      s += mapFn(item);      if (showIndex > 0) {        s += `${last ? " " : ","} // #${i}`;        if (typeof array.refCountOf === "function")          s += ` [ref=${array.refCountOf(item)}x]`;      }      else if (!last) {        s += ",";      }      if (showIndex === -1) {        if (s.length >= threshold - 1 && !last) {          s += "\n" + indent;          threshold += 80;        }        else {          if (!last) s += " ";        }      }      else {        if (!last) s += "\n";      }    }    return s;  }  static makeCxxArray(array, code, indent) {    if (!indent) indent = kIndent;    return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;  }  static makeCxxArrayWithComment(array, code, indent) {    if (!indent) indent = kIndent;    var s = "";    for (var i = 0; i < array.length; i++) {      const last = i === array.length - 1;      s += indent + array[i].data +           (last ? "  // " : ", // ") + (array[i].refs ? "#" + String(i) : "").padEnd(5) + array[i].comment + "\n";    }    return `${code} = {\n${s}};\n`;  }  static disclaimer(s) {    return "// ------------------- Automatically generated, do not edit -------------------\n" +           s +           "// ----------------------------------------------------------------------------\n";  }  static indent(s, indentation) {    var lines = s.split(/\r?\n/g);    if (indentation) {      for (var i = 0; i < lines.length; i++) {        var line = lines[i];        if (line) lines[i] = indentation + line;      }    }    return lines.join("\n");  }  static inject(s, start, end, code) {    var iStart = s.indexOf(start);    var iEnd   = s.indexOf(end);    if (iStart === -1)      FAIL(`Utils.inject(): Couldn't locate start mark '${start}'`);    if (iEnd === -1)      FAIL(`Utils.inject(): Couldn't locate end mark '${end}'`);    var nIndent = 0;    while (iStart > 0 && s[iStart-1] === " ") {      iStart--;      nIndent++;    }    if (nIndent) {      const indentation = " ".repeat(nIndent);      code = StringUtils.indent(code, indentation) + indentation;    }    return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);  }  static makePriorityCompare(priorityArray) {    const map = Object.create(null);    priorityArray.forEach((str, index) => { map[str] = index; });    return function(a, b) {      const ax = hasOwn.call(map, a) ? map[a] : Infinity;      const bx = hasOwn.call(map, b) ? map[b] : Infinity;      return ax != bx ? ax - bx : a < b ? -1 : a > b ? 1 : 0;    }  }}exports.StringUtils = StringUtils;// ============================================================================// [ArrayUtils]// ============================================================================class ArrayUtils {  static min(arr, fn) {    if (!arr.length)      return null;    if (!fn)      fn = nop;    var v = fn(arr[0]);    for (var i = 1; i < arr.length; i++)      v = Math.min(v, fn(arr[i]));    return v;  }  static max(arr, fn) {    if (!arr.length)      return null;    if (!fn)      fn = nop;    var v = fn(arr[0]);    for (var i = 1; i < arr.length; i++)      v = Math.max(v, fn(arr[i]));    return v;  }  static sorted(obj, cmp) {    const out = Array.isArray(obj) ? obj.slice() : Object.getOwnPropertyNames(obj);    out.sort(cmp);    return out;  }  static deepIndexOf(arr, what) {    for (var i = 0; i < arr.length; i++)      if (Lang.deepEq(arr[i], what))        return i;    return -1;  }}exports.ArrayUtils = ArrayUtils;// ============================================================================// [MapUtils]// ============================================================================class MapUtils {  static clone(map) {    return Object.assign(Object.create(null), map);  }  static arrayToMap(arr, value) {    if (value === undefined)      value = true;    const out = Object.create(null);    for (var i = 0; i < arr.length; i++)      out[arr[i]] = value;    return out;  }  static equals(a, b) {    for (var k in a) if (!hasOwn.call(b, k)) return false;    for (var k in b) if (!hasOwn.call(a, k)) return false;    return true;  }  static firstOf(map, flags) {    for (var k in flags)      if (hasOwn.call(map, k))        return k;    return undefined;  }  static anyOf(map, flags) {    for (var k in flags)      if (hasOwn.call(map, k))        return true;    return false;  }  static add(a, b) {    for (var k in b)      a[k] = b[k];    return a;  }  static and(a, b) {    const out = Object.create(null);    for (var k in a)      if (hasOwn.call(b, k))        out[k] = true;    return out;  }  static xor(a, b) {    const out = Object.create(null);    for (var k in a) if (!hasOwn.call(b, k)) out[k] = true;    for (var k in b) if (!hasOwn.call(a, k)) out[k] = true;    return out;  }};exports.MapUtils = MapUtils;// ============================================================================// [CxxUtils]// ============================================================================class CxxUtils {  static flags(obj, fn) {    if (!fn)      fn = nop;    var out = "";    for (var k in obj) {      if (obj[k])        out += (out ? " | " : "") + fn(k);    }    return out ? out : "0";  }  static struct(...args) {    return "{ " + args.join(", ") + " }";  }};exports.CxxUtils = CxxUtils;// ============================================================================// [IndexedString]// ============================================================================// IndexedString is mostly used to merge all instruction names into a single// string with external index. It's designed mostly for generating C++ tables.//// Consider the following cases in C++:////   a) static const char* const* instNames = { "add", "mov", "vpunpcklbw" };////   b) static const char instNames[] = { "add\0" "mov\0" "vpunpcklbw\0" };//      static const uint16_t instNameIndex[] = { 0, 4, 8 };//// The latter (b) has an advantage that it doesn't have to be relocated by the// linker, which saves a lot of space in the resulting binary and a lot of CPU// cycles (and memory) when the linker loads it. AsmJit supports thousands of// instructions so each optimization like this makes it smaller and faster to// load.class IndexedString {  constructor() {    this.map = Object.create(null);    this.array = [];    this.size = -1;  }  add(s) {    this.map[s] = -1;  }  index() {    const map = this.map;    const array = this.array;    const partialMap = Object.create(null);    var k, kp;    var i, len;    // Create a map that will contain all keys and partial keys.    for (k in map) {      if (!k) {        partialMap[k] = k;      }      else {        for (i = 0, len = k.length; i < len; i++) {          kp = k.substr(i);          if (!hasOwn.call(partialMap, kp) || partialMap[kp].length < len)            partialMap[kp] = k;        }      }    }    // Create an array that will only contain keys that are needed.    for (k in map)      if (partialMap[k] === k)        array.push(k);    array.sort();    // Create valid offsets to the `array`.    var offMap = Object.create(null);    var offset = 0;    for (i = 0, len = array.length; i < len; i++) {      k = array[i];      offMap[k] = offset;      offset += k.length + 1;    }    this.size = offset;    // Assign valid offsets to `map`.    for (kp in map) {      k = partialMap[kp];      map[kp] = offMap[k] + k.length - kp.length;    }  }  format(indent, justify) {    if (this.size === -1)      FAIL(`IndexedString.format(): not indexed yet, call index()`);    const array = this.array;    if (!justify) justify = 0;    var i;    var s = "";    var line = "";    for (i = 0; i < array.length; i++) {      const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");      const newl = line + (line ? " " : indent) + item;      if (newl.length <= justify) {        line = newl;        continue;      }      else {        s += line + "\n";        line = indent + item;      }    }    return s + line;  }  getSize() {    if (this.size === -1)      FAIL(`IndexedString.getSize(): Not indexed yet, call index()`);    return this.size;  }  getIndex(k) {    if (this.size === -1)      FAIL(`IndexedString.getIndex(): Not indexed yet, call index()`);    if (!hasOwn.call(this.map, k))      FAIL(`IndexedString.getIndex(): Key '${k}' not found.`);    return this.map[k];  }}exports.IndexedString = IndexedString;// ============================================================================// [IndexedArray]// ============================================================================// IndexedArray is an Array replacement that allows to index each item inserted// to it. Its main purpose is to avoid data duplication, if an item passed to// `addIndexed()` is already within the Array then it's not inserted and the// existing index is returned instead.function IndexedArray_keyOf(item) {  return typeof item === "string" ? item : JSON.stringify(item);}class IndexedArray extends Array {  constructor() {    super();    this._index = Object.create(null);  }  refCountOf(item) {    const key = IndexedArray_keyOf(item);    const idx = this._index[key];    return idx !== undefined ? idx.refCount : 0;  }  addIndexed(item) {    const key = IndexedArray_keyOf(item);    var idx = this._index[key];    if (idx !== undefined) {      idx.refCount++;      return idx.data;    }    idx = this.length;    this._index[key] = {      data: idx,      refCount: 1    };    this.push(item);    return idx;  }}exports.IndexedArray = IndexedArray;// ============================================================================// [Task]// ============================================================================// A base runnable task that can access the TableGen through `this.ctx`.class Task {  constructor(name, deps) {    this.ctx = null;    this.name = name || "";    this.deps = deps || [];  }  inject(key, str, size) {    this.ctx.inject(key, str, size);    return this;  }  run() {    FAIL("Task.run(): Must be reimplemented");  }}exports.Task = Task;// ============================================================================// [TableGen]// ============================================================================// Main context used to load, generate, and store instruction tables. The idea// is to be extensible, so it stores 'Task's to be executed with minimal deps// management.class TableGen {  constructor(arch) {    this.arch = arch;    this.files = Object.create(null);    this.tableSizes = Object.create(null);    this.tasks = [];    this.taskMap = Object.create(null);    this.insts = [];    this.instMap = Object.create(null);    this.aliases = [];    this.aliasMem = Object.create(null);  }  // --------------------------------------------------------------------------  // [File Management]  // --------------------------------------------------------------------------  load(fileList) {    for (var i = 0; i < fileList.length; i++) {      const file = fileList[i];      const path = kAsmJitRoot + "/" + file;      const data = fs.readFileSync(path, "utf8").replace(/\r\n/g, "\n");      this.files[file] = {        prev: data,        data: data      };    }    return this;  }  save() {    for (var file in this.files) {      const obj = this.files[file];      if (obj.data !== obj.prev) {        const path = kAsmJitRoot + "/" + file;        console.log(`MODIFIED '${file}'`);        fs.writeFileSync(path + ".backup", obj.prev, "utf8");        fs.writeFileSync(path, obj.data, "utf8");      }    }  }  dataOfFile(file) {    const obj = this.files[file];    if (!obj)      FAIL(`TableGen.dataOfFile(): File '${file}' not loaded`);    return obj.data;  }  inject(key, str, size) {    const begin = "// ${" + key + ":Begin}\n";    const end   = "// ${" + key + ":End}\n";    var done = false;    for (var file in this.files) {      const obj = this.files[file];      const data = obj.data;      if (data.indexOf(begin) !== -1) {        obj.data = StringUtils.inject(data, begin, end, str);        done = true;        break;      }    }    if (!done)      FAIL(`TableGen.inject(): Cannot find '${key}'`);    if (size)      this.tableSizes[key] = size;    return this;  }  // --------------------------------------------------------------------------  // [Task Management]  // --------------------------------------------------------------------------  addTask(task) {    if (!task.name)      FAIL(`TableGen.addModule(): Module must have a name`);    if (this.taskMap[task.name])      FAIL(`TableGen.addModule(): Module '${task.name}' already added`);    task.deps.forEach((dependency) => {      if (!this.taskMap[dependency])        FAIL(`TableGen.addModule(): Dependency '${dependency}' of module '${task.name}' doesn't exist`);    });    this.tasks.push(task);    this.taskMap[task.name] = task;    task.ctx = this;    return this;  }  runTasks() {    const tasks = this.tasks;    const tasksDone = Object.create(null);    var pending = tasks.length;    while (pending) {      const oldPending = pending;      const arrPending = [];      for (var i = 0; i < tasks.length; i++) {        const task = tasks[i];        if (tasksDone[task.name])          continue;        if (task.deps.every((dependency) => { return tasksDone[dependency] === true; })) {          task.run();          tasksDone[task.name] = true;          pending--;        }        else {          arrPending.push(task.name);        }      }      if (oldPending === pending)        throw Error(`TableGen.runModules(): Modules '${arrPending.join("|")}' stuck (cyclic dependency?)`);    }  }  // --------------------------------------------------------------------------  // [Instruction Management]  // --------------------------------------------------------------------------  addInst(inst) {    if (this.instMap[inst.name])      FAIL(`TableGen.addInst(): Instruction '${inst.name}' already added`);    inst.id = this.insts.length;    this.insts.push(inst);    this.instMap[inst.name] = inst;    return this;  }  addAlias(alias, name) {    this.aliases.push(alias);    this.aliasMap[alias] = name;    return this;  }  // --------------------------------------------------------------------------  // [Run]  // --------------------------------------------------------------------------  run() {    this.onBeforeRun();    this.runTasks();    this.onAfterRun();  }  // --------------------------------------------------------------------------  // [Other]  // --------------------------------------------------------------------------  dumpTableSizes() {    const sizes = this.tableSizes;    var pad = 26;    var total = 0;    for (var name in sizes) {      const size = sizes[name];      total += size;      console.log(("Size of " + name).padEnd(pad) + ": " + size);    }    console.log("Size of all tables".padEnd(pad) + ": " + total);  }  // --------------------------------------------------------------------------  // [Hooks]  // --------------------------------------------------------------------------  onBeforeRun() {}  onAfterRun() {}}exports.TableGen = TableGen;// ============================================================================// [IdEnum]// ============================================================================class IdEnum extends Task {  constructor(name, deps) {    super(name || "IdEnum", deps);  }  comment(name) {    FAIL("IdEnum.comment(): Must be reimplemented");  }  run() {    const insts = this.ctx.insts;    var s = "";    for (var i = 0; i < insts.length; i++) {      const inst = insts[i];      var line = "kId" + inst.enum + (i ? "" : " = 0") + ",";      var text = this.comment(inst);      if (text)        line = line.padEnd(37) + "//!< " + text;      s += line + "\n";    }    s += "_kIdCount\n";    return this.ctx.inject("InstId", s);  }}exports.IdEnum = IdEnum;// ============================================================================// [NameTable]// ============================================================================class NameTable extends Task {  constructor(name, deps) {    super(name || "NameTable", deps);  }  run() {    const arch = this.ctx.arch;    const none = "Inst::kIdNone";    const insts = this.ctx.insts;    const instNames = new IndexedString();    const instFirst = new Array(26);    const instLast  = new Array(26);    var maxLength = 0;    for (var i = 0; i < insts.length; i++) {      const inst = insts[i];      instNames.add(inst.name);      maxLength = Math.max(maxLength, inst.name.length);    }    instNames.index();    for (var i = 0; i < insts.length; i++) {      const inst = insts[i];      const name = inst.name;      const nameIndex = instNames.getIndex(name);      const index = name.charCodeAt(0) - 'a'.charCodeAt(0);      if (index < 0 || index >= 26)        FAIL(`TableGen.generateNameData(): Invalid lookup character '${name[0]}' of '${name}'`);      inst.nameIndex = nameIndex;      if (instFirst[index] === undefined)        instFirst[index] = `Inst::kId${inst.enum}`;      instLast[index] = `Inst::kId${inst.enum}`;    }    var s = "";    s += `const char InstDB::_nameData[] =\n${instNames.format(kIndent, kJustify)}\n`;    s += `\n`;    s += `const InstDB::InstNameIndex InstDB::instNameIndex[26] = {\n`;    for (var i = 0; i < instFirst.length; i++) {      const firstId = instFirst[i] || none;      const lastId = instLast[i] || none;      s += `  { ${String(firstId).padEnd(22)}, ${String(lastId).padEnd(22)} + 1 }`;      if (i !== 26 - 1)        s += `,`;      s += `\n`;    }    s += `};\n`;    this.ctx.inject("NameLimits",      StringUtils.disclaimer(`enum : uint32_t { kMaxNameSize = ${maxLength} };\n`));    return this.ctx.inject("NameData", StringUtils.disclaimer(s), instNames.getSize() + 26 * 4);  }}exports.NameTable = NameTable;
 |