diff options
Diffstat (limited to '3rdparty/asmjit/tools')
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/configure-makefiles.sh | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/configure-ninja.sh | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/configure-sanitizers.sh | 4 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/configure-xcode.sh | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/enumgen.sh | 0 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/generator-commons.js | 592 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/generator-cxx.js | 270 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/tablegen-a64.js (renamed from 3rdparty/asmjit/tools/tablegen-arm.js) | 45 | ||||
-rwxr-xr-x | 3rdparty/asmjit/tools/tablegen-a64.sh | 3 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/tablegen-arm.sh | 3 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/tablegen-x86.js | 403 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/tablegen-x86.sh | 0 | ||||
-rw-r--r-- | 3rdparty/asmjit/tools/tablegen.js | 871 | ||||
-rwxr-xr-x[-rw-r--r--] | 3rdparty/asmjit/tools/tablegen.sh | 3 |
14 files changed, 1333 insertions, 861 deletions
diff --git a/3rdparty/asmjit/tools/configure-makefiles.sh b/3rdparty/asmjit/tools/configure-makefiles.sh index 69503dc28ef..69503dc28ef 100644..100755 --- a/3rdparty/asmjit/tools/configure-makefiles.sh +++ b/3rdparty/asmjit/tools/configure-makefiles.sh diff --git a/3rdparty/asmjit/tools/configure-ninja.sh b/3rdparty/asmjit/tools/configure-ninja.sh index 84808d24363..84808d24363 100644..100755 --- a/3rdparty/asmjit/tools/configure-ninja.sh +++ b/3rdparty/asmjit/tools/configure-ninja.sh diff --git a/3rdparty/asmjit/tools/configure-sanitizers.sh b/3rdparty/asmjit/tools/configure-sanitizers.sh index a9f64969b70..d35b9a4cad3 100644..100755 --- a/3rdparty/asmjit/tools/configure-sanitizers.sh +++ b/3rdparty/asmjit/tools/configure-sanitizers.sh @@ -11,3 +11,7 @@ echo "" echo "== [Configuring Build - Release_UBSAN] ==" eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Release_UBSAN" ${BUILD_OPTIONS} -DCMAKE_BUILD_TYPE=Release -DASMJIT_SANITIZE=undefined echo "" + +echo "== [Configuring Build - Release_MSAN] ==" +eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Release_MSAN" ${BUILD_OPTIONS} -DCMAKE_BUILD_TYPE=Release -DASMJIT_SANITIZE=memory +echo "" diff --git a/3rdparty/asmjit/tools/configure-xcode.sh b/3rdparty/asmjit/tools/configure-xcode.sh index d9c7d983d75..d9c7d983d75 100644..100755 --- a/3rdparty/asmjit/tools/configure-xcode.sh +++ b/3rdparty/asmjit/tools/configure-xcode.sh diff --git a/3rdparty/asmjit/tools/enumgen.sh b/3rdparty/asmjit/tools/enumgen.sh index 4783db2449d..4783db2449d 100644..100755 --- a/3rdparty/asmjit/tools/enumgen.sh +++ b/3rdparty/asmjit/tools/enumgen.sh diff --git a/3rdparty/asmjit/tools/generator-commons.js b/3rdparty/asmjit/tools/generator-commons.js new file mode 100644 index 00000000000..eb3a0721ccc --- /dev/null +++ b/3rdparty/asmjit/tools/generator-commons.js @@ -0,0 +1,592 @@ +// 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 + +const hasOwn = Object.prototype.hasOwnProperty; +function nop(x) { return x; } + +// Generator - Constants +// --------------------- + +const kIndent = " "; +exports.kIndent = kIndent; + +const kLineWidth = 120; + +// Generator - Logging +// ------------------- + +let VERBOSE = false; + +function setDebugVerbosity(value) { + VERBOSE = value; +} +exports.setDebugVerbosity = setDebugVerbosity; + +function DEBUG(msg) { + if (VERBOSE) + console.log(msg); +} +exports.DEBUG = DEBUG; + +function WARN(msg) { + console.log(msg); +} +exports.WARN = WARN; + +function FATAL(msg) { + console.log(`FATAL: ${msg}`); + throw new Error(msg); +} +exports.FATAL = FATAL; + +// Generator - Object Utilities +// ---------------------------- + +class ObjectUtils { + static clone(map) { + return Object.assign(Object.create(null), map); + } + + static merge(a, b) { + if (a === b) + return a; + + for (let k in b) { + let av = a[k]; + let bv = b[k]; + + if (typeof av === "object" && typeof bv === "object") + ObjectUtils.merge(av, bv); + else + a[k] = bv; + } + + return a; + } + + static equals(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 (let i = 0; i < len; i++) + if (!ObjectUtils.equals(a[i], b[i])) + return false; + } + else { + if (a === null || b === null) + return a === b; + + for (let k in a) + if (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k])) + return false; + + for (let k in b) + if (!hasOwn.call(a, k)) + return false; + } + + return true; + } + + static equalsExcept(a, b, except) { + if (a === b) + return true; + + if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b)) + return ObjectUtils.equals(a, b); + + for (let k in a) + if (!hasOwn.call(except, k) && (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k]))) + return false; + + for (let k in b) + if (!hasOwn.call(except, k) && !hasOwn.call(a, k)) + return false; + + return true; + } + + static findKey(map, keys) { + for (let key in keys) + if (hasOwn.call(map, key)) + return key; + return undefined; + } + + static hasAny(map, keys) { + for (let key in keys) + if (hasOwn.call(map, key)) + return true; + return false; + } + + static and(a, b) { + const out = Object.create(null); + for (let k in a) + if (hasOwn.call(b, k)) + out[k] = true; + return out; + } + + static xor(a, b) { + const out = Object.create(null); + for (let k in a) if (!hasOwn.call(b, k)) out[k] = true; + for (let k in b) if (!hasOwn.call(a, k)) out[k] = true; + return out; + } +} +exports.ObjectUtils = ObjectUtils; + +// Generator - Array Utilities +// --------------------------- + +class ArrayUtils { + static min(arr, fn) { + if (!arr.length) + return null; + + if (!fn) + fn = nop; + + let v = fn(arr[0]); + for (let 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; + + let v = fn(arr[0]); + for (let 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 (let i = 0; i < arr.length; i++) + if (ObjectUtils.equals(arr[i], what)) + return i; + return -1; + } + + static toDict(arr, value) { + if (value === undefined) + value = true; + + const out = Object.create(null); + for (let i = 0; i < arr.length; i++) + out[arr[i]] = value; + return out; + } +} +exports.ArrayUtils = ArrayUtils; + + +// Generator - String Utilities +// ---------------------------- + +class StringUtils { + static asString(x) { return String(x); } + + static countOf(s, pattern) { + if (!pattern) + FATAL(`Pattern cannot be empty`); + + let n = 0; + let pos = 0; + + while ((pos = s.indexOf(pattern, pos)) >= 0) { + n++; + pos += pattern.length; + } + + return n; + } + + 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) { + let 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; + + let s = ""; + let threshold = 80; + + if (showIndex === -1) + s += indent; + + for (let 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 (typeof indent !== "string") + indent = kIndent; + + return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`; + } + + static makeCxxArrayWithComment(array, code, indent) { + if (typeof indent !== "string") + indent = kIndent; + + let s = ""; + for (let 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 formatCppStruct(...args) { + return "{ " + args.join(", ") + " }"; + } + + static formatCppFlags(obj, fn, none) { + if (none == null) + none = "0"; + + if (!fn) + fn = nop; + + let out = ""; + for (let k in obj) { + if (obj[k]) + out += (out ? " | " : "") + fn(k); + } + return out ? out : none; + } + + static formatRecords(array, indent, fn) { + if (typeof indent !== "string") + indent = kIndent; + + if (!fn) + fn = nop; + + let s = ""; + let line = ""; + for (let i = 0; i < array.length; i++) { + const item = fn(array[i]); + const combined = line ? line + ", " + item : item; + + if (combined.length >= kLineWidth) { + s = s ? s + ",\n" + line : line; + line = item; + } + else { + line = combined; + } + } + + if (line) { + s = s ? s + ",\n" + line : line; + } + + return StringUtils.indent(s, indent); + } + + static disclaimer(s) { + return "// ------------------- Automatically generated, do not edit -------------------\n" + + s + + "// ----------------------------------------------------------------------------\n"; + } + + static indent(s, indentation) { + if (typeof indentation === "number") + indentation = " ".repeat(indentation); + + let lines = s.split(/\r?\n/g); + if (indentation) { + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + if (line) + lines[i] = indentation + line; + } + } + + return lines.join("\n"); + } + + static extract(s, start, end) { + const iStart = s.indexOf(start); + const iEnd = s.indexOf(end); + + if (iStart === -1) + FATAL(`StringUtils.extract(): Couldn't locate start mark '${start}'`); + + if (iEnd === -1) + FATAL(`StringUtils.extract(): Couldn't locate end mark '${end}'`); + + return s.substring(iStart + start.length, iEnd).trim(); + } + + static inject(s, start, end, code) { + let iStart = s.indexOf(start); + let iEnd = s.indexOf(end); + + if (iStart === -1) + FATAL(`StringUtils.inject(): Couldn't locate start mark '${start}'`); + + if (iEnd === -1) + FATAL(`StringUtils.inject(): Couldn't locate end mark '${end}'`); + + let 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; + +// Generator - Indexed Array +// ========================= + +// 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); + let 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; + +// Generator - Indexed String +// ========================== + +// 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); + + let k, kp; + let 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`. + let offMap = Object.create(null); + let 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) + FATAL(`IndexedString.format(): not indexed yet, call index()`); + + const array = this.array; + if (!justify) justify = 0; + + let i; + let s = ""; + let 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) + FATAL(`IndexedString.getSize(): Not indexed yet, call index()`); + return this.size; + } + + 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.IndexedString = IndexedString; diff --git a/3rdparty/asmjit/tools/generator-cxx.js b/3rdparty/asmjit/tools/generator-cxx.js new file mode 100644 index 00000000000..b1b1d946cae --- /dev/null +++ b/3rdparty/asmjit/tools/generator-cxx.js @@ -0,0 +1,270 @@ +// 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 + +// C++ code generation helpers. +const commons = require("./generator-commons.js"); +const FATAL = commons.FATAL; + +// Utilities to convert primitives to C++ code. +class Utils { + static toHex(val, pad) { + if (val < 0) + val = 0xFFFFFFFF + val + 1; + + let s = val.toString(16); + if (pad != null && s.length < pad) + s = "0".repeat(pad - s.length) + s; + + return "0x" + s.toUpperCase(); + } + + static capitalize(s) { + s = String(s); + return !s ? s : s[0].toUpperCase() + s.substr(1); + } + + static camelCase(s) { + if (s == null || s === "") + return s; + + s = String(s); + if (/^[A-Z]+$/.test(s)) + return s.toLowerCase(); + else + return s[0].toLowerCase() + s.substr(1); + } + + static normalizeSymbolName(s) { + switch (s) { + case "and": + case "or": + case "xor": + return s + "_"; + default: + return s; + } + } + + static indent(s, indentation) { + if (typeof indentation === "number") + indentation = " ".repeat(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"); + } +} +exports.Utils = Utils; + +// A node that represents a C++ construct. +class Node { + constructor(kind) { + this.kind = kind; + } +}; +exports.Node = Node; + +// A single line of C++ code that declares a variable with optional initialization. +class Var extends Node { + constructor(type, name, init) { + super("var"); + + this.type = type; + this.name = name; + this.init = init || ""; + } + + toString() { + let s = this.type + " " + this.name; + if (this.init) + s += " = " + this.init; + return s + ";\n"; + } +}; +exports.Var = Var; + +// A single line of C++ code, which should not contain any branch or a variable declaration. +class Line extends Node { + constructor(code) { + super("line"); + + this.code = code; + } + + toString() { + return String(this.code) + "\n"; + } +}; +exports.Line = Line; + +// A block containing an array of `Node` items (may contain nested blocks, etc...). +class Block extends Node { + constructor(nodes) { + super("block"); + + this.nodes = nodes || []; + } + + isEmpty() { + return this.nodes.length === 0; + } + + appendNode(node) { + if (!(node instanceof Node)) + FATAL("Block.appendNode(): Node must be an instance of Node"); + + this.nodes.push(node); + return this; + } + + prependNode(node) { + if (!(node instanceof Node)) + FATAL("Block.prependNode(): Node must be an instance of Node"); + + this.nodes.unshift(node); + return this; + } + + insertNode(index, node) { + if (!(node instanceof Node)) + FATAL("Block.insertNode(): Node must be an instance of Node"); + + if (index >= this.nodes.length) + this.nodes.push(node); + else + this.nodes.splice(index, 0, node); + + return this; + } + + addVarDecl(type, name, init) { + let node = type; + + if (!(node instanceof Var)) + node = new Var(type, name, init); + + let i = 0; + while (i < this.nodes.length) { + const n = this.nodes[i]; + if (n.kind === "var" && n.name === node.name && n.init === node.init) + return this; + + if (n.kind !== "var") + break; + + i++; + } + + this.insertNode(i, node); + return this; + } + + addLine(code) { + if (typeof code !== "string") + FATAL("Block.addLine(): Line must be string"); + + this.nodes.push(new Line(code)); + return this; + } + + prependEmptyLine() { + if (!this.isEmpty()) + this.nodes.splice(0, 0, new Line("")); + return this; + } + + addEmptyLine() { + if (!this.isEmpty()) + this.nodes.push(new Line("")); + return this; + } + + toString() { + let s = ""; + for (let node of this.nodes) + s += String(node); + return s; + } +} +exports.Block = Block; + +// A C++ 'condition' (if statement) and its 'body' if it's taken. +class If extends Node { + constructor(cond, body) { + super("if"); + + if (body == null) + body = new Block(); + + if (!(body instanceof Block)) + FATAL("If() - body must be a Block"); + + this.cond = cond; + this.body = body; + } + + toString() { + const cond = String(this.cond); + const body = String(this.body); + + return `if (${cond}) {\n` + Utils.indent(body, 2) + `}\n`; + } +} +exports.If = If; + +//! A C++ switch statement. +class Case extends Node { + constructor(cond, body) { + super("case"); + + this.cond = cond; + this.body = body || new Block(); + } + + toString() { + let s = ""; + for (let node of this.body.nodes) + s += String(node) + + if (this.cond !== "default") + return `case ${this.cond}: {\n` + Utils.indent(s, 2) + `}\n`; + else + return `default: {\n` + Utils.indent(s, 2) + `}\n`; + } +}; +exports.Case = Case; + +class Switch extends Node { + constructor(expression, cases) { + super("switch"); + + this.expression = expression; + this.cases = cases || []; + } + + addCase(cond, body) { + this.cases.push(new Case(cond, body)); + return this; + } + + toString() { + let s = ""; + for (let c of this.cases) { + if (s) + s += "\n"; + s += String(c); + } + + return `switch (${this.expression}) {\n` + Utils.indent(s, 2) + `}\n`; + } +} +exports.Switch = Switch; diff --git a/3rdparty/asmjit/tools/tablegen-arm.js b/3rdparty/asmjit/tools/tablegen-a64.js index e1c829358e2..1466fc63b01 100644 --- a/3rdparty/asmjit/tools/tablegen-arm.js +++ b/3rdparty/asmjit/tools/tablegen-a64.js @@ -1,37 +1,29 @@ -// [AsmJit] -// Machine Code Generation for C++. +// This file is part of AsmJit project <https://asmjit.com> // -// [License] -// ZLIB - See LICENSE.md file in the package. - -// ============================================================================ -// tablegen-arm.js -// ============================================================================ +// See asmjit.h or LICENSE.md for license and copyright information +// SPDX-License-Identifier: Zlib "use strict"; -const { executionAsyncResource } = require("async_hooks"); const core = require("./tablegen.js"); +const commons = require("./generator-commons.js"); const hasOwn = Object.prototype.hasOwnProperty; const asmdb = core.asmdb; -const kIndent = core.kIndent; -const IndexedArray = core.IndexedArray; -const StringUtils = core.StringUtils; +const kIndent = commons.kIndent; +const IndexedArray = commons.IndexedArray; +const StringUtils = commons.StringUtils; -const FAIL = core.FAIL; +const FATAL = commons.FATAL; // ============================================================================ // [ArmDB] // ============================================================================ -// Create ARM ISA. -const isa = new asmdb.arm.ISA(); - -// ============================================================================ -// [tablegen.arm.GenUtils] -// ============================================================================ +// Create AArch64 ISA. +const isa = new asmdb.aarch64.ISA(); +/* class GenUtils { // Get a list of instructions based on `name` and optional `mode`. static query(name, mode) { @@ -76,6 +68,7 @@ class GenUtils { return arr; } } +*/ // ============================================================================ // [tablegen.arm.ArmTableGen] @@ -133,11 +126,8 @@ class ArmTableGen extends core.TableGen { // [06] OpcodeDataIndex. "([^\\)]+)" + - "\\s*,\\s*" + - - // [07] NameDataIndex. - "([^\\)]+)" + "\\s*\\)" + , "g"); var m; @@ -168,14 +158,12 @@ class ArmTableGen extends core.TableGen { opcodeData : opcodeData, // Opcode data. opcodeDataIndex : -1, // Opcode data index. rwInfo : rwInfo, // RW info. - flags : instFlags, // Instruction flags. - - nameIndex : -1 // Index to InstDB::_nameData. + flags : instFlags // Instruction flags. }); } if (this.insts.length === 0 || this.insts.length !== StringUtils.countOf(stringData, "INST(")) - FAIL("ARMTableGen.parse(): Invalid parsing regexp (no data parsed)"); + FATAL("ARMTableGen.parse(): Invalid parsing regexp (no data parsed)"); console.log("Number of Instructions: " + this.insts.length); } @@ -188,8 +176,7 @@ class ArmTableGen extends core.TableGen { String(inst.opcodeData ).padEnd(86) + ", " + String(inst.rwInfo ).padEnd(10) + ", " + String(inst.flags ).padEnd(26) + ", " + - String(inst.opcodeDataIndex ).padEnd( 3) + ", " + - String(inst.nameIndex ).padEnd( 4) + ")"; + String(inst.opcodeDataIndex ).padEnd( 3) + ")" ; }) + "\n"; return this.inject("InstInfo", s, this.insts.length * 4); } diff --git a/3rdparty/asmjit/tools/tablegen-a64.sh b/3rdparty/asmjit/tools/tablegen-a64.sh new file mode 100755 index 00000000000..2cbd85ece16 --- /dev/null +++ b/3rdparty/asmjit/tools/tablegen-a64.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +node ./tablegen-a64.js diff --git a/3rdparty/asmjit/tools/tablegen-arm.sh b/3rdparty/asmjit/tools/tablegen-arm.sh deleted file mode 100644 index 8545c1e71ba..00000000000 --- a/3rdparty/asmjit/tools/tablegen-arm.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -node ./tablegen-arm.js diff --git a/3rdparty/asmjit/tools/tablegen-x86.js b/3rdparty/asmjit/tools/tablegen-x86.js index 4750edfa205..377a71045e7 100644 --- a/3rdparty/asmjit/tools/tablegen-x86.js +++ b/3rdparty/asmjit/tools/tablegen-x86.js @@ -3,86 +3,43 @@ // See asmjit.h or LICENSE.md for license and copyright information // SPDX-License-Identifier: Zlib -// ============================================================================ -// tablegen-x86.js -// -// The purpose of this script is to fetch all instructions' names into a single -// string and to optimize common patterns that appear in instruction data. It -// prevents relocation of small strings (instruction names) that has to be done -// by a linker to make all pointers the binary application/library uses valid. -// This approach decreases the final size of AsmJit binary and relocation data. -// -// NOTE: This script relies on 'asmdb' package. Either install it by using -// node.js package manager (npm) or by copying/symlinking the whole asmdb -// directory as [asmjit]/tools/asmdb. -// ============================================================================ - "use strict"; +const fs = require("fs"); +const path = require("path"); + +const commons = require("./generator-commons.js"); +const cxx = require("./generator-cxx.js"); const core = require("./tablegen.js"); + const asmdb = core.asmdb; -const kIndent = core.kIndent; -const Lang = core.Lang; -const CxxUtils = core.CxxUtils; -const MapUtils = core.MapUtils; -const ArrayUtils = core.ArrayUtils; -const StringUtils = core.StringUtils; -const IndexedArray = core.IndexedArray; +const DEBUG = commons.DEBUG; +const FATAL = commons.FATAL; +const kIndent = commons.kIndent; +const ArrayUtils = commons.ArrayUtils; +const IndexedArray = commons.IndexedArray; +const ObjectUtils = commons.ObjectUtils; +const StringUtils = commons.StringUtils; const hasOwn = Object.prototype.hasOwnProperty; const disclaimer = StringUtils.disclaimer; -const FAIL = core.FAIL; -const DEBUG = core.DEBUG; - const decToHex = StringUtils.decToHex; +function readJSON(fileName) { + const content = fs.readFileSync(fileName); + return JSON.parse(content); +} + +const x86data = readJSON(path.join(__dirname, "..", "db", asmdb.x86.dbName)); + // ============================================================================ // [tablegen.x86.x86isa] // ============================================================================ // Create the X86 database and add some special cases recognized by AsmJit. -const x86isa = new asmdb.x86.ISA({ - instructions: [ - // Imul in [reg, imm] form is encoded as [reg, reg, imm]. - ["imul", "r16, ib" , "RMI" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], - ["imul", "r32, ib" , "RMI" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], - ["imul", "r64, ib" , "RMI" , "REX.W 6B /r ib", "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], - ["imul", "r16, iw" , "RMI" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], - ["imul", "r32, id" , "RMI" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"], - ["imul", "r64, id" , "RMI" , "REX.W 69 /r id", "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"], - - // Movabs (X64 only). - ["movabs", "W:r64, iq/uq" , "I" , "REX.W B8+r iq", "X64"], - ["movabs", "w:al, moff8" , "NONE", "A0" , "X64"], - ["movabs", "w:ax, moff16" , "NONE", "66 A1" , "X64"], - ["movabs", "W:eax, moff32", "NONE", "A1" , "X64"], - ["movabs", "W:rax, moff64", "NONE", "REX.W A1" , "X64"], - ["movabs", "W:moff8, al" , "NONE", "A2" , "X64"], - ["movabs", "W:moff16, ax" , "NONE", "66 A3" , "X64"], - ["movabs", "W:moff32, eax", "NONE", "A3" , "X64"], - ["movabs", "W:moff64, rax", "NONE", "REX.W A3" , "X64"] - ] -}); - -// Remapped instructions contain mapping between instructions that AsmJit expects -// and instructions provided by asmdb. In general, AsmJit uses string instructions -// (like cmps, movs, etc...) without the suffix, so we just remap these and keep -// all others. -const RemappedInsts = { - __proto__: null, - - "cmpsd": { names: ["cmpsd"] , rep: false }, - "movsd": { names: ["movsd"] , rep: false }, - "cmps" : { names: ["cmpsb", "cmpsw", "cmpsd", "cmpsq"], rep: true }, - "movs" : { names: ["movsb", "movsw", "movsd", "movsq"], rep: true }, - "lods" : { names: ["lodsb", "lodsw", "lodsd", "lodsq"], rep: null }, - "scas" : { names: ["scasb", "scasw", "scasd", "scasq"], rep: null }, - "stos" : { names: ["stosb", "stosw", "stosd", "stosq"], rep: null }, - "ins" : { names: ["insb" , "insw" , "insd" ] , rep: null }, - "outs" : { names: ["outsb", "outsw", "outsd"] , rep: null } -}; +const x86isa = new asmdb.x86.ISA(x86data); // ============================================================================ // [tablegen.x86.Filter] @@ -95,7 +52,7 @@ class Filter { for (var i = 0; i < instArray.length; i++) { const inst = instArray[i]; - if (inst.attributes.AltForm) + if (inst.altForm) continue; const s = inst.operands.map((op) => { return op.isImm() ? "imm" : op.toString(); }).join(", "); @@ -113,7 +70,7 @@ class Filter { const result = []; for (var i = 0; i < instArray.length; i++) { const inst = instArray[i]; - if (inst.attributes.AltForm) + if (inst.altForm) continue; result.push(inst); } @@ -167,7 +124,28 @@ class GenUtils { } static cpuFeaturesOf(dbInsts) { - return ArrayUtils.sorted(dbInsts.unionCpuFeatures()); + function cmp(a, b) { + if (a.startsWith("AVX512") && !b.startsWith("AVX512")) + return 1; + if (b.startsWith("AVX512") && !a.startsWith("AVX512")) + return -1; + + if (a.startsWith("AVX") && !b.startsWith("AVX")) + return 1; + if (b.startsWith("AVX") && !a.startsWith("AVX")) + return -1; + + if (a === "FPU" && b !== "FPU") + return 1; + if (b === "FPU" && a !== "FPU") + return -1; + + return a < b ? -1 : a === b ? 0 : 1; + } + + const features = Object.getOwnPropertyNames(dbInsts.unionCpuFeatures()); + features.sort(cmp); + return features; } static assignVexEvexCompatibilityFlags(f, dbInsts) { @@ -261,16 +239,16 @@ class GenUtils { const dbInst = dbInsts[i]; const operands = dbInst.operands; - if (dbInst.attributes.Lock ) f.Lock = true; - if (dbInst.attributes.XAcquire ) f.XAcquire = true; - if (dbInst.attributes.XRelease ) f.XRelease = true; - if (dbInst.attributes.BND ) f.Rep = true; - if (dbInst.attributes.REP ) f.Rep = true; - if (dbInst.attributes.REPNE ) f.Rep = true; - if (dbInst.attributes.RepIgnored ) f.RepIgnored = true; - if (dbInst.attributes.ImplicitZeroing) f.Avx512ImplicitZ = true; + if (dbInst.prefixes.lock ) f.Lock = true; + if (dbInst.prefixes.xacquire ) f.XAcquire = true; + if (dbInst.prefixes.xrelease ) f.XRelease = true; + if (dbInst.prefixes.bnd ) f.Rep = true; + if (dbInst.prefixes.rep ) f.Rep = true; + if (dbInst.prefixes.repne ) f.Rep = true; + if (dbInst.prefixes.repIgnore ) f.RepIgnored = true; + if (dbInst.k === "zeroing" ) f.Avx512ImplicitZ = true; - if (dbInst.fpu) { + if (dbInst.category.FPU) { for (var j = 0; j < operands.length; j++) { const op = operands[j]; if (op.memSize === 16) f.FpuM16 = true; @@ -280,7 +258,7 @@ class GenUtils { } } - if (dbInst.attributes.Tsib) + if (dbInst.tsib) f.Tsib = true; if (dbInst.vsibReg) @@ -289,12 +267,11 @@ class GenUtils { if (dbInst.prefix === "VEX" || dbInst.prefix === "XOP") f.Vex = true; + if (dbInst.encodingPreference === "EVEX") + f.PreferEvex = true; + if (dbInst.prefix === "EVEX") { f.Evex = true; - - if (dbInst.extensions["AVX512_VNNI"]) - f.PreferEvex = true; - if (dbInst.kmask) f.Avx512K = true; if (dbInst.zmask) f.Avx512Z = true; @@ -418,7 +395,7 @@ class GenUtils { } } - static fixedRegOf(reg) { + static fixedRegOfRegName(reg) { switch (reg) { case "es" : return 1; case "cs" : return 2; @@ -447,11 +424,23 @@ class GenUtils { } } + static fixedRegOf(op) { + if (op.isReg()) { + return GenUtils.fixedRegOfRegName(op.reg); + } + else if (op.isMem() && op.memRegOnly) { + return GenUtils.fixedRegOfRegName(op.memRegOnly); + } + else { + return -1; + } + } + static controlFlow(dbInsts) { - if (dbInsts.checkAttribute("Control", "Jump")) return "Jump"; - if (dbInsts.checkAttribute("Control", "Call")) return "Call"; - if (dbInsts.checkAttribute("Control", "Branch")) return "Branch"; - if (dbInsts.checkAttribute("Control", "Return")) return "Return"; + if (dbInsts.checkAttribute("control", "jump")) return "Jump"; + if (dbInsts.checkAttribute("control", "call")) return "Call"; + if (dbInsts.checkAttribute("control", "branch")) return "Branch"; + if (dbInsts.checkAttribute("control", "return")) return "Return"; return "Regular"; } } @@ -473,16 +462,7 @@ class X86TableGen extends core.TableGen { // Get instructions (dbInsts) having the same name as understood by AsmJit. query(name) { - const remapped = RemappedInsts[name]; - if (!remapped) return x86isa.query(name); - - const dbInsts = x86isa.query(remapped.names); - const rep = remapped.rep; - if (rep === null) return dbInsts; - - return dbInsts.filter((inst) => { - return rep === !!(inst.attributes.REP || inst.attributes.REPNE); - }); + return x86isa.query(name); } // -------------------------------------------------------------------------- @@ -500,9 +480,8 @@ class X86TableGen extends core.TableGen { // --- autogenerated fields --- "([^\\)]+)" + "," + // [05] MainOpcodeIndex. "([^\\)]+)" + "," + // [06] AltOpcodeIndex. - "([^\\)]+)" + "," + // [07] NameIndex. - "([^\\)]+)" + "," + // [08] CommonDataIndex. - "([^\\)]+)" + "\\)", // [09] OperationDataIndex. + "([^\\)]+)" + "," + // [07] CommonDataIndex. + "([^\\)]+)" + "\\)", // [08] OperationDataIndex. "g"); var m; @@ -515,7 +494,7 @@ class X86TableGen extends core.TableGen { const dbInsts = this.query(name); if (name && !dbInsts.length) - FAIL(`Instruction '${name}' not found in asmdb`); + FATAL(`Instruction '${name}' not found in asmdb`); const flags = GenUtils.flagsOf(dbInsts); const controlFlow = GenUtils.controlFlow(dbInsts); @@ -548,7 +527,7 @@ class X86TableGen extends core.TableGen { } if (this.insts.length === 0) - FAIL("X86TableGen.parse(): Invalid parsing regexp (no data parsed)"); + FATAL("X86TableGen.parse(): Invalid parsing regexp (no data parsed)"); console.log("Number of Instructions: " + this.insts.length); } @@ -562,7 +541,6 @@ class X86TableGen extends core.TableGen { String(inst.opcode1 ).padEnd(26) + ", " + String(inst.mainOpcodeIndex ).padEnd( 3) + ", " + String(inst.altOpcodeIndex ).padEnd( 3) + ", " + - String(inst.nameIndex ).padEnd( 5) + ", " + String(inst.commonInfoIndex ).padEnd( 3) + ", " + String(inst.additionalInfoIndex).padEnd( 3) + ")"; }) + "\n"; @@ -574,7 +552,7 @@ class X86TableGen extends core.TableGen { // -------------------------------------------------------------------------- printMissing() { - const ignored = MapUtils.arrayToMap([ + const ignored = ArrayUtils.toDict([ "cmpsb", "cmpsw", "cmpsd", "cmpsq", "lodsb", "lodsw", "lodsd", "lodsq", "movsb", "movsw", "movsd", "movsq", @@ -832,7 +810,7 @@ class IdEnum extends core.IdEnum { var text = ""; var features = GenUtils.cpuFeaturesOf(dbInsts); - const priorityFeatures = ["AVX_VNNI"]; + const priorityFeatures = ["AVX_VNNI", "AVX_VNNI_INT8", "AVX_IFMA", "AVX_NE_CONVERT"]; if (features.length) { text += "{"; @@ -970,7 +948,7 @@ class AltOpcodeTable extends core.Task { components[2] = "00"; } else { - FAIL(`Failed to process opcode '${opcode}'`); + FATAL(`Failed to process opcode '${opcode}'`); } const newOpcode = joinOpcodeComponents(components); @@ -1009,8 +987,8 @@ class AltOpcodeTable extends core.Task { // [tablegen.x86.InstSignatureTable] // ============================================================================ -const RegOp = MapUtils.arrayToMap(["al", "ah", "ax", "eax", "rax", "cl", "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd"]); -const MemOp = MapUtils.arrayToMap(["m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"]); +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"]); +const MemOp = ArrayUtils.toDict(["m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"]); const cmpOp = StringUtils.makePriorityCompare([ "RegGpbLo", "RegGpbHi", "RegGpw", "RegGpd", "RegGpq", "RegXmm", "RegYmm", "RegZmm", "RegMm", "RegKReg", "RegSReg", "RegCReg", "RegDReg", "RegSt", "RegBnd", "RegTmm", @@ -1037,7 +1015,7 @@ function StringifyOpArray(a, map) { else if (hasOwn.call(map, op)) mapped = map[op]; else - FAIL(`UNHANDLED OPERAND '${op}'`); + FATAL(`UNHANDLED OPERAND '${op}'`); s += (s ? " | " : "") + mapped; } return s ? s : "0"; @@ -1049,11 +1027,11 @@ class OSignature { } equals(other) { - return MapUtils.equals(this.flags, other.flags); + return ObjectUtils.equals(this.flags, other.flags); } xor(other) { - const result = MapUtils.xor(this.flags, other.flags); + const result = ObjectUtils.xor(this.flags, other.flags); return Object.getOwnPropertyNames(result).length === 0 ? null : result; } @@ -1254,14 +1232,14 @@ class ISignature extends Array { mergeWith(other) { // If both architectures are the same, it's fine to merge. - var ok = this.x86 === other.x86 && this.x64 === other.x64; + const sameArch = this.x86 === other.x86 && this.x64 === other.x64; // If the first arch is [X86|X64] and the second [X64] it's also fine. - if (!ok && this.x86 && this.x64 && !other.x86 && other.x64) - ok = true; + // if (!ok && this.x86 && this.x64 && !other.x86 && other.x64) + // ok = true; // It's not ok if both signatures have different number of implicit operands. - if (!ok || this.implicit !== other.implicit) + if (!sameArch || this.implicit !== other.implicit) return false; // It's not ok if both signatures have different number of operands. @@ -1269,8 +1247,8 @@ class ISignature extends Array { if (len !== other.length) return false; - var xorIndex = -1; - for (var i = 0; i < len; i++) { + let xorIndex = -1; + for (let i = 0; i < len; i++) { const xor = this[i].xor(other[i]); if (xor === null) continue; @@ -1281,12 +1259,9 @@ class ISignature extends Array { } // Bail if mergeWidth at operand-level failed. - if (xorIndex !== -1 && !this[xorIndex].mergeWith(other[xorIndex])) + if (xorIndex === -1 || !this[xorIndex].mergeWith(other[xorIndex])) return false; - this.x86 = this.x86 || other.x86; - this.x64 = this.x64 || other.x64; - return true; } @@ -1297,14 +1272,14 @@ class ISignature extends Array { class SignatureArray extends Array { // Iterate over all signatures and check which operands don't need explicit memory size. - calcImplicitMemSize() { + calcImplicitMemSize(instName) { // Calculates a hash-value (aka key) of all register operands specified by `regOps` in `inst`. function keyOf(inst, regOps) { var s = ""; for (var i = 0; i < inst.length; i++) { const op = inst[i]; if (regOps & (1 << i)) - s += "{" + ArrayUtils.sorted(MapUtils.and(op.flags, RegOp)).join("|") + "}"; + s += "{" + ArrayUtils.sorted(ObjectUtils.and(op.flags, RegOp)).join("|") + "}"; } return s || "?"; } @@ -1323,7 +1298,7 @@ class SignatureArray extends Array { // Check if this instruction signature has a memory operand of explicit size. for (i = 0; i < len; i++) { const aOp = aInst[i]; - const mem = MapUtils.firstOf(aOp.flags, MemOp); + const mem = ObjectUtils.findKey(aOp.flags, MemOp); if (mem) { // Stop if the memory operand has implicit-size or if there is more than one. @@ -1336,7 +1311,7 @@ class SignatureArray extends Array { memPos = i; } } - else if (MapUtils.anyOf(aOp.flags, RegOp)) { + else if (ObjectUtils.hasAny(aOp.flags, RegOp)) { // Doesn't consider 'r/m' as we already checked 'm'. regOps |= (1 << i); } @@ -1361,7 +1336,7 @@ class SignatureArray extends Array { for (i = 0; i < len; i++) { if (i === memPos) continue; - const reg = MapUtils.anyOf(bInst[i].flags, RegOp); + const reg = ObjectUtils.hasAny(bInst[i].flags, RegOp); if (regOps & (1 << i)) hasMatch &= reg; else if (reg) @@ -1372,7 +1347,7 @@ class SignatureArray extends Array { const bOp = bInst[memPos]; if (bOp.flags.mem) continue; - const mem = MapUtils.firstOf(bOp.flags, MemOp); + const mem = ObjectUtils.findKey(bOp.flags, MemOp); if (mem === memOp) { sameSizeSet.push(bInst); } @@ -1412,6 +1387,8 @@ class SignatureArray extends Array { // then keep this implicit as it won't do any harm. These instructions // cannot be mixed and it will make implicit the 32-bit one in cases // where X64 introduced 64-bit ones like `cvtsi2ss`. + if (!/^(bndcl|bndcn|bndcu|ptwrite|(v)?cvtsi2ss|(v)?cvtsi2sd|vcvtusi2ss|vcvtusi2sd)$/.test(instName)) + implicit = false; } else { implicit = false; @@ -1424,8 +1401,9 @@ class SignatureArray extends Array { // Patch all instructions to accept implicit-size memory operand. for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) { const bInst = sameSizeSet[bIndex]; - if (implicit) + if (implicit) { bInst[memPos].flags.mem = true; + } if (!implicit) DEBUG(`${this.name}: Explicit: ${bInst}`); @@ -1594,7 +1572,9 @@ class InstSignatureTable extends core.Task { } makeSignatures(dbInsts) { + const instName = dbInsts.length ? dbInsts[0].name : ""; const signatures = new SignatureArray(); + for (var i = 0; i < dbInsts.length; i++) { const inst = dbInsts[i]; const ops = inst.operands; @@ -1678,29 +1658,9 @@ class InstSignatureTable extends core.Task { op.flags.implicit = true; } - const seg = iop.memSeg; + const seg = iop.memSegment; if (seg) { switch (inst.name) { - case "cmpsb": op.flags.m8 = true; break; - case "cmpsw": op.flags.m16 = true; break; - case "cmpsd": op.flags.m32 = true; break; - case "cmpsq": op.flags.m64 = true; break; - case "lodsb": op.flags.m8 = true; break; - case "lodsw": op.flags.m16 = true; break; - case "lodsd": op.flags.m32 = true; break; - case "lodsq": op.flags.m64 = true; break; - case "movsb": op.flags.m8 = true; break; - case "movsw": op.flags.m16 = true; break; - case "movsd": op.flags.m32 = true; break; - case "movsq": op.flags.m64 = true; break; - case "scasb": op.flags.m8 = true; break; - case "scasw": op.flags.m16 = true; break; - case "scasd": op.flags.m32 = true; break; - case "scasq": op.flags.m64 = true; break; - case "stosb": op.flags.m8 = true; break; - case "stosw": op.flags.m16 = true; break; - case "stosd": op.flags.m32 = true; break; - case "stosq": op.flags.m64 = true; break; case "insb": op.flags.m8 = true; break; case "insw": op.flags.m16 = true; break; case "insd": op.flags.m32 = true; break; @@ -1720,6 +1680,9 @@ class InstSignatureTable extends core.Task { default: console.log(`UNKNOWN MEM IN INSTRUCTION '${inst.name}'`); break; } + if (iop.memRegOnly) + reg = iop.memRegOnly; + if (seg === "ds") op.flags.memDS = true; if (seg === "es") op.flags.memES = true; if (reg === "reg") { op.flags.memBase = true; } @@ -1732,30 +1695,37 @@ class InstSignatureTable extends core.Task { else if (reg) { if (reg == "r8") { op.flags["r8lo"] = true; - op.flags["r8hi"] = true; + + if (!inst.w || inst.w === "W0") + op.flags["r8hi"] = true; } else { op.flags[reg] = true; } } + if (mem) { op.flags[mem] = true; - // HACK: Allow LEA|CL*|PREFETCH* to use any memory size. - if (/^(cldemote|clwb|clflush\w*|lea|prefetch\w*)$/.test(inst.name)) { + // HACK: Allow LEA to use any memory size. + if (/^(lea)$/.test(inst.name)) { op.flags.mem = true; Object.assign(op.flags, MemOp); } // HACK: These instructions specify explicit memory size, but it's just informational. - if (inst.name === "enqcmd" || inst.name === "enqcmds" || inst.name === "movdir64b") + if (/^(call|enqcmd|enqcmds|lcall|ljmp|movdir64b)$/.test(inst.name)) { op.flags.mem = true; - + } } + if (imm) { if (iop.immSign === "any" || iop.immSign === "signed" ) op.flags["i" + imm] = true; if (iop.immSign === "any" || iop.immSign === "unsigned") op.flags["u" + imm] = true; } - if (rel) op.flags["rel" + rel] = true; + + if (rel) { + op.flags["rel" + rel] = true; + } row.push(op); } @@ -1766,8 +1736,8 @@ class InstSignatureTable extends core.Task { } } - if (signatures.length && GenUtils.canUseImplicitMemSize(dbInsts[0].name)) - signatures.calcImplicitMemSize(); + if (signatures.length && GenUtils.canUseImplicitMemSize(instName)) + signatures.calcImplicitMemSize(instName); signatures.compact(); return signatures; @@ -1839,7 +1809,7 @@ class AdditionalInfoTable extends core.Task { break; } - const instFlagsIndex = instFlagsTable.addIndexed("InstRWFlags(" + CxxUtils.flags(instFlags, (f) => { return `FLAG(${f})`; }, "FLAG(None)") + ")"); + const instFlagsIndex = instFlagsTable.addIndexed("InstRWFlags(" + StringUtils.formatCppFlags(instFlags, (f) => { return `FLAG(${f})`; }, "FLAG(None)") + ")"); const rwInfoIndex = rwInfoTable.addIndexed(`{ ${rData}, ${wData} }`); inst.additionalInfoIndex = additionaInfoTable.addIndexed(`{ ${instFlagsIndex}, ${rwInfoIndex}, { ${features} } }`); @@ -1870,7 +1840,7 @@ class AdditionalInfoTable extends core.Task { if (dbInst.name === "mov") continue; - const specialRegs = dbInst.specialRegs; + const regs = dbInst.io; // Mov is a special case, moving to/from control regs makes flags undefined, // which we don't want to have in `X86InstDB::operationData`. This is, thus, @@ -1878,28 +1848,28 @@ class AdditionalInfoTable extends core.Task { if (dbInst.name === "mov") continue; - for (var specialReg in specialRegs) { + for (var reg in regs) { var flag = ""; - switch (specialReg) { - case "FLAGS.CF": flag = "CF"; break; - case "FLAGS.OF": flag = "OF"; break; - case "FLAGS.SF": flag = "SF"; break; - case "FLAGS.ZF": flag = "ZF"; break; - case "FLAGS.AF": flag = "AF"; break; - case "FLAGS.PF": flag = "PF"; break; - case "FLAGS.DF": flag = "DF"; break; - case "FLAGS.IF": flag = "IF"; break; - //case "FLAGS.TF": flag = "TF"; break; - case "FLAGS.AC": flag = "AC"; break; - case "X86SW.C0": flag = "C0"; break; - case "X86SW.C1": flag = "C1"; break; - case "X86SW.C2": flag = "C2"; break; - case "X86SW.C3": flag = "C3"; break; + switch (reg) { + case "CF": flag = "CF"; break; + case "OF": flag = "OF"; break; + case "SF": flag = "SF"; break; + case "ZF": flag = "ZF"; break; + case "AF": flag = "AF"; break; + case "PF": flag = "PF"; break; + case "DF": flag = "DF"; break; + case "IF": flag = "IF"; break; + //case "TF": flag = "TF"; break; + case "AC": flag = "AC"; break; + case "C0": flag = "C0"; break; + case "C1": flag = "C1"; break; + case "C2": flag = "C2"; break; + case "C3": flag = "C3"; break; default: continue; } - switch (specialRegs[specialReg]) { + switch (regs[reg]) { case "R": r[flag] = true; break; @@ -1924,7 +1894,7 @@ class AdditionalInfoTable extends core.Task { // [tablegen.x86.InstRWInfoTable] // ============================================================================ -const NOT_MEM_AMBIGUOUS = MapUtils.arrayToMap([ +const NOT_MEM_AMBIGUOUS = ArrayUtils.toDict([ "call", "movq" ]); @@ -1997,20 +1967,20 @@ class InstRWInfoTable extends core.Task { run() { const insts = this.ctx.insts; - const noRmInfo = CxxUtils.struct( + const noRmInfo = StringUtils.formatCppStruct( "InstDB::RWInfoRm::kCategory" + "None".padEnd(10), StringUtils.decToHex(0, 2), String(0).padEnd(2), - CxxUtils.flags({}), + StringUtils.formatCppFlags({}), "0" ); - const noOpInfo = CxxUtils.struct( + const noOpInfo = StringUtils.formatCppStruct( "0x0000000000000000u", "0x0000000000000000u", "0xFF", "0", - CxxUtils.struct(0), + StringUtils.formatCppStruct(0), "OpRWFlags::kNone" ); @@ -2049,7 +2019,7 @@ class InstRWInfoTable extends core.Task { if (opAcc === "R") flags.Read = true; if (opAcc === "W") flags.Write = true; if (opAcc === "X") flags.RW = true; - Lang.merge(flags, op.flags); + ObjectUtils.merge(flags, op.flags); const rIndex = opAcc === "X" || opAcc === "R" ? op.index : -1; const rWidth = opAcc === "X" || opAcc === "R" ? op.width : -1; @@ -2058,23 +2028,23 @@ class InstRWInfoTable extends core.Task { const consecutiveLeadCount = op.clc; - const opData = CxxUtils.struct( + const opData = StringUtils.formatCppStruct( this.byteMaskFromBitRanges([{ start: rIndex, end: rIndex + rWidth - 1 }]) + "u", this.byteMaskFromBitRanges([{ start: wIndex, end: wIndex + wWidth - 1 }]) + "u", StringUtils.decToHex(op.fixed === -1 ? 0xFF : op.fixed, 2), String(consecutiveLeadCount), - CxxUtils.struct(0), - CxxUtils.flags(flags, function(flag) { return "OpRWFlags::k" + flag; }, "OpRWFlags::kNone") + StringUtils.formatCppStruct(0), + StringUtils.formatCppFlags(flags, function(flag) { return "OpRWFlags::k" + flag; }, "OpRWFlags::kNone") ); rwOpsIndex.push(this.opInfoTable.addIndexed(opData)); } - const rmData = CxxUtils.struct( + const rmData = StringUtils.formatCppStruct( "InstDB::RWInfoRm::kCategory" + rmInfo.category.padEnd(10), StringUtils.decToHex(rmInfo.rmIndexes, 2), String(Math.max(rmInfo.memFixed, 0)).padEnd(2), - CxxUtils.flags({ + StringUtils.formatCppFlags({ "InstDB::RWInfoRm::kFlagAmbiguous": Boolean(rmInfo.memAmbiguous), "InstDB::RWInfoRm::kFlagMovssMovsd": Boolean(inst.name === "movss" || inst.name === "movsd"), "InstDB::RWInfoRm::kFlagPextrw": Boolean(inst.name === "pextrw"), @@ -2083,10 +2053,10 @@ class InstRWInfoTable extends core.Task { rmInfo.memExtension === "None" ? "0" : "uint32_t(CpuFeatures::X86::k" + rmInfo.memExtension + ")" ); - const rwData = CxxUtils.struct( + const rwData = StringUtils.formatCppStruct( "InstDB::RWInfo::kCategory" + rwInfo.category.padEnd(10), String(this.rmInfoTable.addIndexed(rmData)).padEnd(2), - CxxUtils.struct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); }))) + StringUtils.formatCppStruct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); }))) ); if (i == 0) @@ -2134,7 +2104,7 @@ class InstRWInfoTable extends core.Task { for (var j = start; j <= end; j++) { const bytePos = j >> 3; if (bytePos < 0 || bytePos >= arr.length) - FAIL(`Range ${start}:${end} cannot be used to create a byte-mask`); + FATAL(`Range ${start}:${end} cannot be used to create a byte-mask`); arr[bytePos] = 1; } } @@ -2165,7 +2135,7 @@ class InstRWInfoTable extends core.Task { access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?", clc: 0, flags: {}, - fixed: GenUtils.fixedRegOf(op.reg), + fixed: GenUtils.fixedRegOf(op), index: op.rwxIndex, width: op.rwxWidth }; @@ -2195,10 +2165,26 @@ class InstRWInfoTable extends core.Task { if (op.consecutiveLeadCount) d.clc = op.consecutiveLeadCount; - if (op.isReg()) - d.fixed = GenUtils.fixedRegOf(op.reg); - else - d.fixed = GenUtils.fixedRegOf(op.mem); + const instName = dbInst.name; + // NOTE: Avoid push/pop here as PUSH/POP has many variations for segment registers, + // which would set 'd.fixed' field even for GP variation of the instuction. + if (instName !== "push" && instName !== "pop") { + d.fixed = GenUtils.fixedRegOf(op); + } + + switch (instName) { + case "vfcmaddcph": + case "vfmaddcph": + case "vfcmaddcsh": + case "vfmaddcsh": + case "vfcmulcsh": + case "vfmulcsh": + case "vfcmulcph": + case "vfmulcph": + if (j === 0) + d.flags.Unique = true; + break; + } if (op.zext) d.flags.ZExt = true; @@ -2215,7 +2201,7 @@ class InstRWInfoTable extends core.Task { } if (d.fixed !== -1) { - if (op.memSeg) + if (op.memSegment) d.flags.MemPhysId = true; else d.flags.RegPhysId = true; @@ -2225,16 +2211,26 @@ class InstRWInfoTable extends core.Task { rwOps[j] = d; } else { - if (!Lang.deepEqExcept(rwOps[j], d, { "fixed": true, "flags": true })) + if (!ObjectUtils.equalsExcept(rwOps[j], d, { "fixed": true, "flags": true })) return null; if (rwOps[j].fixed === -1) rwOps[j].fixed = d.fixed; - Lang.merge(rwOps[j].flags, d.flags); + ObjectUtils.merge(rwOps[j].flags, d.flags); } } } - return { category: "Generic", rwOps }; + + const name = dbInsts.length ? dbInsts[0].name : ""; + + switch (name) { + case "vpternlogd": + case "vpternlogq": + return { category: "GenericEx", rwOps }; + + default: + return { category: "Generic", rwOps }; + } } function queryRwByData(dbInsts, rwOpsArray) { @@ -2243,12 +2239,13 @@ class InstRWInfoTable extends core.Task { const operands = dbInst.operands; const rwOps = nullOps(); - for (var j = 0; j < operands.length; j++) + for (var j = 0; j < operands.length; j++) { rwOps[j] = makeRwFromOp(operands[j]) + } var match = 0; for (var j = 0; j < rwOpsArray.length; j++) - match |= Lang.deepEq(rwOps, rwOpsArray[j]); + match |= ObjectUtils.equals(rwOps, rwOpsArray[j]); if (!match) return false; @@ -2292,7 +2289,7 @@ class InstRWInfoTable extends core.Task { if (queryRwByData(dbInsts, this.rwCategoryByData[k])) return { category: k, rwOps: nullOps() }; - // FAILURE: Missing data to categorize this instruction. + // FATALURE: Missing data to categorize this instruction. if (name) { const items = dumpRwToData(dbInsts) console.log(`RW: ${dbInsts.length ? dbInsts[0].name : ""}:`); @@ -2305,7 +2302,7 @@ class InstRWInfoTable extends core.Task { } rwOpFlagsForInstruction(instName, opIndex) { - const toMap = MapUtils.arrayToMap; + const toMap = ArrayUtils.toDict; // TODO: We should be able to get this information from asmdb. switch (instName + "@" + opIndex) { @@ -2382,7 +2379,7 @@ class InstRWInfoTable extends core.Task { if (/^(punpcklbw|punpckldq|punpcklwd)$/.test(dbInst.name)) return "None"; - return StringUtils.capitalize(dbInst.name); + return cxx.Utils.capitalize(dbInst.name); } } diff --git a/3rdparty/asmjit/tools/tablegen-x86.sh b/3rdparty/asmjit/tools/tablegen-x86.sh index 40facf326dc..40facf326dc 100644..100755 --- a/3rdparty/asmjit/tools/tablegen-x86.sh +++ b/3rdparty/asmjit/tools/tablegen-x86.sh diff --git a/3rdparty/asmjit/tools/tablegen.js b/3rdparty/asmjit/tools/tablegen.js index fdc65fd1cbd..a151805675b 100644 --- a/3rdparty/asmjit/tools/tablegen.js +++ b/3rdparty/asmjit/tools/tablegen.js @@ -12,618 +12,227 @@ "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; - } - } +const commons = require("./generator-commons.js"); +const cxx = require("./generator-cxx.js"); +const asmdb = require("../db"); - // Try to import global 'asmdb' package as local package is not available. - return require("asmdb"); -})(); exports.asmdb = asmdb; +exports.exp = asmdb.base.exp; -// ============================================================================ -// [Constants] -// ============================================================================ +const hasOwn = Object.prototype.hasOwnProperty; -const kIndent = " "; -const kJustify = 119; -const kAsmJitRoot = ".."; +const FATAL = commons.FATAL; +const StringUtils = commons.StringUtils; -exports.kIndent = kIndent; -exports.kJustify = kJustify; +const kAsmJitRoot = ".."; exports.kAsmJitRoot = kAsmJitRoot; // ============================================================================ -// [Debugging] +// [InstructionNameData] // ============================================================================ -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); +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`); } -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; +class InstructionNameData { + constructor() { + this.names = []; + this.primaryTable = []; + this.stringTable = ""; + this.size = 0; + this.indexComment = []; + this.maxNameLength = 0; } - 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; + 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 { - 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; + // Put the string into a string table. + this.names.push(s); + this.primaryTable.push(-1); + this.indexComment.push(``); } - return true; + if (this.maxNameLength < s.length) + this.maxNameLength = s.length; } - 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 countOf(s, pattern) { - if (!pattern) - FAIL(`Pattern cannot be empty`); - - var n = 0; - var pos = 0; + index() { + const kMaxPrefixSize = 15; + const kMaxSuffixSize = 7; + const names = []; - while ((pos = s.indexOf(pattern, pos)) >= 0) { - n++; - pos += pattern.length; + for (let idx = 0; idx < this.primaryTable.length; idx++) { + if (this.primaryTable[idx] === -1) { + names.push({ name: this.names[idx], index: idx }); + } } - return n; - } + 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; + }); - static capitalize(s) { - s = String(s); - return !s ? s : s[0].toUpperCase() + s.substr(1); - } + for (let z = 0; z < names.length; z++) { + const idx = names[z].index; + const name = names[z].name; - static trimLeft(s) { return s.replace(/^\s+/, ""); } - static trimRight(s) { return s.replace(/\s+$/, ""); } + let done = false; + let longestPrefix = 0; + let longestSuffix = 0; - static upFirst(s) { - if (!s) return ""; - return s[0].toUpperCase() + s.substr(1); - } + let prefix = ""; + let suffix = ""; - 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(); - } + for (let i = Math.min(name.length, kMaxPrefixSize); i > 0; i--) { + prefix = name.substring(0, i); + suffix = name.substring(i); - static format(array, indent, showIndex, mapFn) { - if (!mapFn) - mapFn = StringUtils.asString; + const prefixIndex = this.stringTable.indexOf(prefix); + const suffixIndex = this.stringTable.indexOf(suffix); - var s = ""; - var threshold = 80; + // Matched both parts? + if (prefixIndex !== -1 && suffix === "") { + done = true; + break; + } - if (showIndex === -1) - s += indent; + if (prefixIndex !== -1 && suffixIndex !== -1) { + done = true; + break; + } - for (var i = 0; i < array.length; i++) { - const item = array[i]; - const last = i === array.length - 1; + if (prefixIndex !== -1 && longestPrefix === 0) + longestPrefix = prefix.length; - if (showIndex !== -1) - s += indent; + if (suffixIndex !== -1 && suffix.length > longestSuffix) + longestSuffix = suffix.length; - 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 (suffix.length === kMaxSuffixSize) + break; } - if (showIndex === -1) { - if (s.length >= threshold - 1 && !last) { - s += "\n" + indent; - threshold += 80; + 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 { - if (!last) s += " "; + prefix = name; + suffix = ""; } } - else { - if (!last) s += "\n"; - } - } - return s; - } + if (suffix) { + const prefixIndex = this.addOrReferenceString(prefix); + const suffixIndex = this.addOrReferenceString(suffix); - 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; + this.primaryTable[idx] = prefixIndex | (prefix.length << 12) | (suffixIndex << 16) | (suffix.length << 28); + this.indexComment[idx] = `Large '${prefix}|${suffix}'.`; } - } - - return lines.join("\n"); - } - - static extract(s, start, end) { - var iStart = s.indexOf(start); - var iEnd = s.indexOf(end); - - if (iStart === -1) - FAIL(`StringUtils.extract(): Couldn't locate start mark '${start}'`); - - if (iEnd === -1) - FAIL(`StringUtils.extract(): Couldn't locate end mark '${end}'`); - - return s.substring(iStart + start.length, iEnd).trim(); - } - - static inject(s, start, end, code) { - var iStart = s.indexOf(start); - var iEnd = s.indexOf(end); - - if (iStart === -1) - FAIL(`StringUtils.inject(): Couldn't locate start mark '${start}'`); - - if (iEnd === -1) - FAIL(`StringUtils.inject(): Couldn't locate end mark '${end}'`); - - var nIndent = 0; - while (iStart > 0 && s[iStart-1] === " ") { - iStart--; - nIndent++; - } + else { + const prefixIndex = this.addOrReferenceString(prefix); - if (nIndent) { - const indentation = " ".repeat(nIndent); - code = StringUtils.indent(code, indentation) + indentation; + this.primaryTable[idx] = prefixIndex | (prefix.length << 12); + this.indexComment[idx] = `Large '${prefix}'.`; + } } - - 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; + addOrReferenceString(s) { + let index = this.stringTable.indexOf(s); + if (index === -1) { + index = this.stringTable.length; + this.stringTable += s; } + return index; } -} -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, none) { - if (none == null) - none = "0"; - - if (!fn) - fn = nop; - - var out = ""; - for (var k in obj) { - if (obj[k]) - out += (out ? " | " : "") + fn(k); - } - return out ? out : none; - } - - 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]; + formatIndexTable(tableName) { + if (this.size === -1) + FATAL(`IndexedString.formatIndexTable(): Not indexed yet, call index()`); - offMap[k] = offset; - offset += k.length + 1; + 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"; } - this.size = offset; - // Assign valid offsets to `map`. - for (kp in map) { - k = partialMap[kp]; - map[kp] = offMap[k] + k.length - kp.length; - } + return `const uint32_t ${tableName}[] = {\n${StringUtils.indent(s, " ")}};\n`; } - format(indent, justify) { + formatStringTable(tableName) { 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 = ""; + FATAL(`IndexedString.formatStringTable(): Not indexed yet, call index()`); - 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; - } + 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 s + line; + return `const char ${tableName}[] =\n${StringUtils.indent(s, " ")}\n`; } getSize() { if (this.size === -1) - FAIL(`IndexedString.getSize(): Not indexed yet, call index()`); - return this.size; + FATAL(`IndexedString.getSize(): Not indexed yet, call index()`); + + return this.primaryTable.length * 4 + this.stringTable.length; } getIndex(k) { if (this.size === -1) - FAIL(`IndexedString.getIndex(): Not indexed yet, call index()`); + FATAL(`IndexedString.getIndex(): Not indexed yet, call index()`); if (!hasOwn.call(this.map, k)) - FAIL(`IndexedString.getIndex(): Key '${k}' not found.`); + FATAL(`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; +exports.InstructionNameData = InstructionNameData; // ============================================================================ // [Task] @@ -643,7 +252,7 @@ class Task { } run() { - FAIL("Task.run(): Must be reimplemented"); + FATAL("Task.run(): Must be reimplemented"); } } exports.Task = Task; @@ -652,29 +261,12 @@ 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; +class Injector { + constructor() { 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]; @@ -705,7 +297,7 @@ class TableGen { dataOfFile(file) { const obj = this.files[file]; if (!obj) - FAIL(`TableGen.dataOfFile(): File '${file}' not loaded`); + FATAL(`TableGen.dataOfFile(): File '${file}' not loaded`); return obj.data; } @@ -726,7 +318,7 @@ class TableGen { } if (!done) - FAIL(`TableGen.inject(): Cannot find '${key}'`); + FATAL(`TableGen.inject(): Cannot find '${key}'`); if (size) this.tableSizes[key] = size; @@ -734,20 +326,56 @@ class TableGen { 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) - FAIL(`TableGen.addModule(): Module must have a name`); + FATAL(`TableGen.addModule(): Module must have a name`); if (this.taskMap[task.name]) - FAIL(`TableGen.addModule(): Module '${task.name}' already added`); + FATAL(`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`); + FATAL(`TableGen.addModule(): Dependency '${dependency}' of module '${task.name}' doesn't exist`); }); this.tasks.push(task); @@ -792,7 +420,7 @@ class TableGen { addInst(inst) { if (this.instMap[inst.name]) - FAIL(`TableGen.addInst(): Instruction '${inst.name}' already added`); + FATAL(`TableGen.addInst(): Instruction '${inst.name}' already added`); inst.id = this.insts.length; this.insts.push(inst); @@ -819,25 +447,6 @@ class TableGen { } // -------------------------------------------------------------------------- - // [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] // -------------------------------------------------------------------------- @@ -856,7 +465,7 @@ class IdEnum extends Task { } comment(name) { - FAIL("IdEnum.comment(): Must be reimplemented"); + FATAL("IdEnum.comment(): Must be reimplemented"); } run() { @@ -885,63 +494,75 @@ exports.IdEnum = IdEnum; // [NameTable] // ============================================================================ -class NameTable extends Task { - constructor(name, deps) { - super(name || "NameTable", deps); +class Output { + constructor() { + this.content = Object.create(null); + this.tableSize = Object.create(null); } - run() { - const arch = this.ctx.arch; - const none = "Inst::kIdNone"; + add(id, content, tableSize) { + this.content[id] = content; + this.tableSize[id] = typeof tableSize === "number" ? tableSize : 0; + } +}; +exports.Output = Output; - const insts = this.ctx.insts; - const instNames = new IndexedString(); +function generateNameData(out, instructions) { + const none = "Inst::kIdNone"; - const instFirst = new Array(26); - const instLast = new Array(26); + const instFirst = new Array(26); + const instLast = new Array(26); + const instNameData = new InstructionNameData(); - var maxLength = 0; - for (var i = 0; i < insts.length; i++) { - const inst = insts[i]; - instNames.add(inst.displayName); - maxLength = Math.max(maxLength, inst.displayName.length); - } - instNames.index(); + for (let i = 0; i < instructions.length; i++) + instNameData.add(instructions[i].displayName); + instNameData.index(); - for (var i = 0; i < insts.length; i++) { - const inst = insts[i]; - const name = inst.displayName; - const nameIndex = instNames.getIndex(name); + for (let i = 0; i < instructions.length; i++) { + const inst = instructions[i]; + const displayName = inst.displayName; + const alphaIndex = displayName.charCodeAt(0) - 'a'.charCodeAt(0); - const index = name.charCodeAt(0) - 'a'.charCodeAt(0); - if (index < 0 || index >= 26) - FAIL(`TableGen.generateNameData(): Invalid lookup character '${name[0]}' of '${name}'`); + if (alphaIndex < 0 || alphaIndex >= 26) + FATAL(`generateNameData(): Invalid lookup character '${displayName[0]}' of '${displayName}'`); - inst.nameIndex = nameIndex; - if (instFirst[index] === undefined) - instFirst[index] = `Inst::kId${inst.enum}`; - instLast[index] = `Inst::kId${inst.enum}`; - } + if (instFirst[alphaIndex] === undefined) + instFirst[alphaIndex] = `Inst::kId${inst.enum}`; + instLast[alphaIndex] = `Inst::kId${inst.enum}`; + } - var s = ""; - s += `const char InstDB::_nameData[] =\n${instNames.format(kIndent, kJustify)}\n`; + 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 += `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`; + } + 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; - this.ctx.inject("NameLimits", - StringUtils.disclaimer(`enum : uint32_t { kMaxNameSize = ${maxLength} };\n`)); +class NameTable extends Task { + constructor(name, deps) { + super(name || "NameTable", deps); + } - return this.ctx.inject("NameData", StringUtils.disclaimer(s), instNames.getSize() + 26 * 4); + run() { + const output = new Output(); + generateNameData(output, this.ctx.insts); + this.ctx.inject("NameData", output.content["NameData"], output.tableSize["NameData"]); } } exports.NameTable = NameTable; diff --git a/3rdparty/asmjit/tools/tablegen.sh b/3rdparty/asmjit/tools/tablegen.sh index b2c9cac368c..0ef1e580634 100644..100755 --- a/3rdparty/asmjit/tools/tablegen.sh +++ b/3rdparty/asmjit/tools/tablegen.sh @@ -1,4 +1,5 @@ #!/usr/bin/env sh set -e -node ./tablegen-arm.js $@ +node ./tablegen-a32.js $@ +node ./tablegen-a64.js $@ node ./tablegen-x86.js $@ |