summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/tools/tablegen.js
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/tools/tablegen.js')
-rw-r--r--3rdparty/asmjit/tools/tablegen.js568
1 files changed, 568 insertions, 0 deletions
diff --git a/3rdparty/asmjit/tools/tablegen.js b/3rdparty/asmjit/tools/tablegen.js
new file mode 100644
index 00000000000..a151805675b
--- /dev/null
+++ b/3rdparty/asmjit/tools/tablegen.js
@@ -0,0 +1,568 @@
+// This file is part of AsmJit project <https://asmjit.com>
+//
+// See asmjit.h or LICENSE.md for license and copyright information
+// SPDX-License-Identifier: Zlib
+
+// ============================================================================
+// tablegen.js
+//
+// Provides core foundation for generating tables that AsmJit requires. This
+// file should provide everything table generators need in general.
+// ============================================================================
+
+"use strict";
+
+// ============================================================================
+// [Imports]
+// ============================================================================
+
+const fs = require("fs");
+
+const commons = require("./generator-commons.js");
+const cxx = require("./generator-cxx.js");
+const asmdb = require("../db");
+
+exports.asmdb = asmdb;
+exports.exp = asmdb.base.exp;
+
+const hasOwn = Object.prototype.hasOwnProperty;
+
+const FATAL = commons.FATAL;
+const StringUtils = commons.StringUtils;
+
+const kAsmJitRoot = "..";
+exports.kAsmJitRoot = kAsmJitRoot;
+
+// ============================================================================
+// [InstructionNameData]
+// ============================================================================
+
+function charTo5Bit(c) {
+ if (c >= 'a' && c <= 'z')
+ return 1 + (c.charCodeAt(0) - 'a'.charCodeAt(0));
+ else if (c >= '0' && c <= '4')
+ return 1 + 26 + (c.charCodeAt(0) - '0'.charCodeAt(0));
+ else
+ FATAL(`Character '${c}' cannot be encoded into a 5-bit string`);
+}
+
+class InstructionNameData {
+ constructor() {
+ this.names = [];
+ this.primaryTable = [];
+ this.stringTable = "";
+ this.size = 0;
+ this.indexComment = [];
+ this.maxNameLength = 0;
+ }
+
+ add(s) {
+ // First try to encode the string with 5-bit characters that fit into a 32-bit int.
+ if (/^[a-z0-4]{0,6}$/.test(s)) {
+ let index = 0;
+ for (let i = 0; i < s.length; i++)
+ index |= charTo5Bit(s[i]) << (i * 5);
+
+ this.names.push(s);
+ this.primaryTable.push(index | (1 << 31));
+ this.indexComment.push(`Small '${s}'.`);
+ }
+ else {
+ // Put the string into a string table.
+ this.names.push(s);
+ this.primaryTable.push(-1);
+ this.indexComment.push(``);
+ }
+
+ if (this.maxNameLength < s.length)
+ this.maxNameLength = s.length;
+ }
+
+ index() {
+ const kMaxPrefixSize = 15;
+ const kMaxSuffixSize = 7;
+ const names = [];
+
+ for (let idx = 0; idx < this.primaryTable.length; idx++) {
+ if (this.primaryTable[idx] === -1) {
+ names.push({ name: this.names[idx], index: idx });
+ }
+ }
+
+ names.sort(function(a, b) {
+ if (a.name.length > b.name.length)
+ return -1;
+ if (a.name.length < b.name.length)
+ return 1;
+ return (a > b) ? 1 : (a < b) ? -1 : 0;
+ });
+
+ for (let z = 0; z < names.length; z++) {
+ const idx = names[z].index;
+ const name = names[z].name;
+
+ let done = false;
+ let longestPrefix = 0;
+ let longestSuffix = 0;
+
+ let prefix = "";
+ let suffix = "";
+
+ for (let i = Math.min(name.length, kMaxPrefixSize); i > 0; i--) {
+ prefix = name.substring(0, i);
+ suffix = name.substring(i);
+
+ const prefixIndex = this.stringTable.indexOf(prefix);
+ const suffixIndex = this.stringTable.indexOf(suffix);
+
+ // Matched both parts?
+ if (prefixIndex !== -1 && suffix === "") {
+ done = true;
+ break;
+ }
+
+ if (prefixIndex !== -1 && suffixIndex !== -1) {
+ done = true;
+ break;
+ }
+
+ if (prefixIndex !== -1 && longestPrefix === 0)
+ longestPrefix = prefix.length;
+
+ if (suffixIndex !== -1 && suffix.length > longestSuffix)
+ longestSuffix = suffix.length;
+
+ if (suffix.length === kMaxSuffixSize)
+ break;
+ }
+
+ if (!done) {
+ let minPrefixSize = name.length >= 8 ? name.length / 2 + 1 : name.length - 2;
+
+ prefix = "";
+ suffix = "";
+
+ if (longestPrefix >= minPrefixSize) {
+ prefix = name.substring(0, longestPrefix);
+ suffix = name.substring(longestPrefix);
+ }
+ else if (longestSuffix) {
+ const splitAt = Math.min(name.length - longestSuffix, kMaxPrefixSize);
+ prefix = name.substring(0, splitAt);
+ suffix = name.substring(splitAt);
+ }
+ else if (name.length > kMaxPrefixSize) {
+ prefix = name.substring(0, kMaxPrefixSize);
+ suffix = name.substring(kMaxPrefixSize);
+ }
+ else {
+ prefix = name;
+ suffix = "";
+ }
+ }
+
+ if (suffix) {
+ const prefixIndex = this.addOrReferenceString(prefix);
+ const suffixIndex = this.addOrReferenceString(suffix);
+
+ this.primaryTable[idx] = prefixIndex | (prefix.length << 12) | (suffixIndex << 16) | (suffix.length << 28);
+ this.indexComment[idx] = `Large '${prefix}|${suffix}'.`;
+ }
+ else {
+ const prefixIndex = this.addOrReferenceString(prefix);
+
+ this.primaryTable[idx] = prefixIndex | (prefix.length << 12);
+ this.indexComment[idx] = `Large '${prefix}'.`;
+ }
+ }
+ }
+
+ addOrReferenceString(s) {
+ let index = this.stringTable.indexOf(s);
+ if (index === -1) {
+ index = this.stringTable.length;
+ this.stringTable += s;
+ }
+ return index;
+ }
+
+ formatIndexTable(tableName) {
+ if (this.size === -1)
+ FATAL(`IndexedString.formatIndexTable(): Not indexed yet, call index()`);
+
+ let s = "";
+ for (let i = 0; i < this.primaryTable.length; i++) {
+ s += cxx.Utils.toHex(this.primaryTable[i], 8);
+ s += i !== this.primaryTable.length - 1 ? "," : " ";
+ s += " // " + this.indexComment[i] + "\n";
+ }
+
+ return `const uint32_t ${tableName}[] = {\n${StringUtils.indent(s, " ")}};\n`;
+ }
+
+ formatStringTable(tableName) {
+ if (this.size === -1)
+ FATAL(`IndexedString.formatStringTable(): Not indexed yet, call index()`);
+
+ let s = "";
+ for (let i = 0; i < this.stringTable.length; i += 80) {
+ if (s)
+ s += "\n"
+ s += '"' + this.stringTable.substring(i, i + 80) + '"';
+ }
+ s += ";\n";
+
+ return `const char ${tableName}[] =\n${StringUtils.indent(s, " ")}\n`;
+ }
+
+ getSize() {
+ if (this.size === -1)
+ FATAL(`IndexedString.getSize(): Not indexed yet, call index()`);
+
+ return this.primaryTable.length * 4 + this.stringTable.length;
+ }
+
+ getIndex(k) {
+ if (this.size === -1)
+ FATAL(`IndexedString.getIndex(): Not indexed yet, call index()`);
+
+ if (!hasOwn.call(this.map, k))
+ FATAL(`IndexedString.getIndex(): Key '${k}' not found.`);
+
+ return this.map[k];
+ }
+}
+exports.InstructionNameData = InstructionNameData;
+
+// ============================================================================
+// [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() {
+ FATAL("Task.run(): Must be reimplemented");
+ }
+}
+exports.Task = Task;
+
+// ============================================================================
+// [TableGen]
+// ============================================================================
+
+class Injector {
+ constructor() {
+ this.files = Object.create(null);
+ this.tableSizes = Object.create(null);
+ }
+
+ 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)
+ FATAL(`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)
+ FATAL(`TableGen.inject(): Cannot find '${key}'`);
+
+ if (size)
+ this.tableSizes[key] = size;
+
+ return this;
+ }
+
+ 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);
+ }
+}
+exports.Injector = Injector;
+
+// 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 extends Injector{
+ constructor(arch) {
+ super();
+
+ this.arch = arch;
+
+ this.tasks = [];
+ this.taskMap = Object.create(null);
+
+ this.insts = [];
+ this.instMap = Object.create(null);
+
+ this.aliases = [];
+ this.aliasMem = Object.create(null);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Task Management]
+ // --------------------------------------------------------------------------
+
+ addTask(task) {
+ if (!task.name)
+ FATAL(`TableGen.addModule(): Module must have a name`);
+
+ if (this.taskMap[task.name])
+ FATAL(`TableGen.addModule(): Module '${task.name}' already added`);
+
+ task.deps.forEach((dependency) => {
+ if (!this.taskMap[dependency])
+ FATAL(`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])
+ FATAL(`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();
+ }
+
+ // --------------------------------------------------------------------------
+ // [Hooks]
+ // --------------------------------------------------------------------------
+
+ onBeforeRun() {}
+ onAfterRun() {}
+}
+exports.TableGen = TableGen;
+
+// ============================================================================
+// [IdEnum]
+// ============================================================================
+
+class IdEnum extends Task {
+ constructor(name, deps) {
+ super(name || "IdEnum", deps);
+ }
+
+ comment(name) {
+ FATAL("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 Output {
+ constructor() {
+ this.content = Object.create(null);
+ this.tableSize = Object.create(null);
+ }
+
+ add(id, content, tableSize) {
+ this.content[id] = content;
+ this.tableSize[id] = typeof tableSize === "number" ? tableSize : 0;
+ }
+};
+exports.Output = Output;
+
+function generateNameData(out, instructions) {
+ const none = "Inst::kIdNone";
+
+ const instFirst = new Array(26);
+ const instLast = new Array(26);
+ const instNameData = new InstructionNameData();
+
+ for (let i = 0; i < instructions.length; i++)
+ instNameData.add(instructions[i].displayName);
+ instNameData.index();
+
+ for (let i = 0; i < instructions.length; i++) {
+ const inst = instructions[i];
+ const displayName = inst.displayName;
+ const alphaIndex = displayName.charCodeAt(0) - 'a'.charCodeAt(0);
+
+ if (alphaIndex < 0 || alphaIndex >= 26)
+ FATAL(`generateNameData(): Invalid lookup character '${displayName[0]}' of '${displayName}'`);
+
+ if (instFirst[alphaIndex] === undefined)
+ instFirst[alphaIndex] = `Inst::kId${inst.enum}`;
+ instLast[alphaIndex] = `Inst::kId${inst.enum}`;
+ }
+
+ var s = "";
+ s += `const InstNameIndex InstDB::instNameIndex = {{\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 += `}, uint16_t(${instNameData.maxNameLength})};\n`;
+ s += `\n`;
+ s += instNameData.formatStringTable("InstDB::_instNameStringTable");
+ s += `\n`;
+ s += instNameData.formatIndexTable("InstDB::_instNameIndexTable");
+
+ const dataSize = instNameData.getSize() + 26 * 4;
+ out.add("NameData", StringUtils.disclaimer(s), dataSize);
+ return out;
+}
+exports.generateNameData = generateNameData;
+
+class NameTable extends Task {
+ constructor(name, deps) {
+ super(name || "NameTable", deps);
+ }
+
+ run() {
+ const output = new Output();
+ generateNameData(output, this.ctx.insts);
+ this.ctx.inject("NameData", output.content["NameData"], output.tableSize["NameData"]);
+ }
+}
+exports.NameTable = NameTable;