summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asmjit/tools
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asmjit/tools')
-rwxr-xr-x3rdparty/asmjit/tools/configure-makefiles.sh13
-rwxr-xr-x3rdparty/asmjit/tools/configure-ninja.sh13
-rwxr-xr-x3rdparty/asmjit/tools/configure-sanitizers.sh17
-rw-r--r--3rdparty/asmjit/tools/configure-vs2019-x64.bat2
-rw-r--r--3rdparty/asmjit/tools/configure-vs2019-x86.bat2
-rw-r--r--3rdparty/asmjit/tools/configure-vs2022-x64.bat2
-rw-r--r--3rdparty/asmjit/tools/configure-vs2022-x86.bat2
-rwxr-xr-x3rdparty/asmjit/tools/configure-xcode.sh8
-rw-r--r--3rdparty/asmjit/tools/enumgen.js417
-rwxr-xr-x3rdparty/asmjit/tools/enumgen.sh3
-rw-r--r--3rdparty/asmjit/tools/generator-commons.js592
-rw-r--r--3rdparty/asmjit/tools/generator-cxx.js270
-rw-r--r--3rdparty/asmjit/tools/tablegen-a64.js352
-rwxr-xr-x3rdparty/asmjit/tools/tablegen-a64.sh3
-rw-r--r--3rdparty/asmjit/tools/tablegen-x86.js2635
-rwxr-xr-x3rdparty/asmjit/tools/tablegen-x86.sh3
-rw-r--r--3rdparty/asmjit/tools/tablegen.js568
-rwxr-xr-x3rdparty/asmjit/tools/tablegen.sh5
18 files changed, 4907 insertions, 0 deletions
diff --git a/3rdparty/asmjit/tools/configure-makefiles.sh b/3rdparty/asmjit/tools/configure-makefiles.sh
new file mode 100755
index 00000000000..69503dc28ef
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-makefiles.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+CURRENT_DIR="`pwd`"
+BUILD_DIR="${CURRENT_DIR}/../build"
+BUILD_OPTIONS="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+echo "== [Configuring Build - Debug] =="
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Debug" -DCMAKE_BUILD_TYPE=Debug ${BUILD_OPTIONS}
+echo ""
+
+echo "== [Configuring Build - Release] =="
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Release" -DCMAKE_BUILD_TYPE=Release ${BUILD_OPTIONS}
+echo ""
diff --git a/3rdparty/asmjit/tools/configure-ninja.sh b/3rdparty/asmjit/tools/configure-ninja.sh
new file mode 100755
index 00000000000..84808d24363
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-ninja.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+CURRENT_DIR="`pwd`"
+BUILD_DIR="${CURRENT_DIR}/../build"
+BUILD_OPTIONS="-G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+echo "== [Configuring Build - Debug] =="
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Debug" -DCMAKE_BUILD_TYPE=Debug ${BUILD_OPTIONS}
+echo ""
+
+echo "== [Configuring Build - Release] =="
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Release" -DCMAKE_BUILD_TYPE=Release ${BUILD_OPTIONS}
+echo ""
diff --git a/3rdparty/asmjit/tools/configure-sanitizers.sh b/3rdparty/asmjit/tools/configure-sanitizers.sh
new file mode 100755
index 00000000000..d35b9a4cad3
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-sanitizers.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+CURRENT_DIR="`pwd`"
+BUILD_DIR="${CURRENT_DIR}/../build"
+BUILD_OPTIONS="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+echo "== [Configuring Build - Release_ASAN] =="
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}/Release_ASAN" ${BUILD_OPTIONS} -DCMAKE_BUILD_TYPE=Release -DASMJIT_SANITIZE=address
+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-vs2019-x64.bat b/3rdparty/asmjit/tools/configure-vs2019-x64.bat
new file mode 100644
index 00000000000..05bc31e4fc4
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-vs2019-x64.bat
@@ -0,0 +1,2 @@
+@echo off
+cmake .. -B "..\build_vs2019_x64" -G"Visual Studio 16" -A x64 -DASMJIT_TEST=1
diff --git a/3rdparty/asmjit/tools/configure-vs2019-x86.bat b/3rdparty/asmjit/tools/configure-vs2019-x86.bat
new file mode 100644
index 00000000000..a0e266395a4
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-vs2019-x86.bat
@@ -0,0 +1,2 @@
+@echo off
+cmake .. -B "..\build_vs2019_x86" -G"Visual Studio 16" -A Win32 -DASMJIT_TEST=1
diff --git a/3rdparty/asmjit/tools/configure-vs2022-x64.bat b/3rdparty/asmjit/tools/configure-vs2022-x64.bat
new file mode 100644
index 00000000000..b33f5411de5
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-vs2022-x64.bat
@@ -0,0 +1,2 @@
+@echo off
+cmake .. -B "..\build_vs2022_x64" -G"Visual Studio 17" -A x64 -DASMJIT_TEST=1
diff --git a/3rdparty/asmjit/tools/configure-vs2022-x86.bat b/3rdparty/asmjit/tools/configure-vs2022-x86.bat
new file mode 100644
index 00000000000..0ba350531d9
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-vs2022-x86.bat
@@ -0,0 +1,2 @@
+@echo off
+cmake .. -B "..\build_vs2022_x86" -G"Visual Studio 17" -A Win32 -DASMJIT_TEST=1
diff --git a/3rdparty/asmjit/tools/configure-xcode.sh b/3rdparty/asmjit/tools/configure-xcode.sh
new file mode 100755
index 00000000000..d9c7d983d75
--- /dev/null
+++ b/3rdparty/asmjit/tools/configure-xcode.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+CURRENT_DIR="`pwd`"
+BUILD_DIR="${CURRENT_DIR}/../build"
+BUILD_OPTIONS="-G Xcode -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+mkdir -p "${BUILD_DIR}"
+eval cmake "${CURRENT_DIR}/.." -B "${BUILD_DIR}" ${BUILD_OPTIONS}
diff --git a/3rdparty/asmjit/tools/enumgen.js b/3rdparty/asmjit/tools/enumgen.js
new file mode 100644
index 00000000000..b9d57e3dac9
--- /dev/null
+++ b/3rdparty/asmjit/tools/enumgen.js
@@ -0,0 +1,417 @@
+"use strict";
+
+const fs = require("fs");
+const path = require("path");
+
+const hasOwn = Object.prototype.hasOwnProperty;
+
+// ============================================================================
+// [Tokenizer]
+// ============================================================================
+
+// The list of "token types" which our lexer understands:
+const tokenizerPatterns = [
+ { type: "space" , re: /^\s+/ },
+ { type: "comment" , re: /^(\/\/.*(\n|$)|\/\*.*\*\/)/ },
+ { type: "symbol" , re: /^[a-zA-Z_]\w*/ },
+ { type: "integer" , re: /^(-?\d+|0[x|X][0-9A-Fa-f]+)(l)?(l)?(u)?\b/ },
+ { type: "comma" , re: /^,/ },
+ { type: "operator", re: /(\+|\+\+|-|--|\/|\*|<<|>>|=|==|<|<=|>|>=|&|&&|\||\|\||\^|~|!)/ },
+ { type: "paren" , re: /^[\(\)\{\}\[\]]/ }
+];
+
+function nextToken(input, from, patterns) {
+ if (from >= input.length) {
+ return {
+ type: "end",
+ begin: from,
+ end: from,
+ content: ""
+ }
+ }
+
+ const s = input.slice(from);
+ for (var i = 0; i < patterns.length; i++) {
+ const pattern = patterns[i];
+ const result = s.match(pattern.re);
+
+ if (result !== null) {
+ const content = result[0];
+ return {
+ type: pattern.type,
+ begin: from,
+ end: from + content.length,
+ content: content
+ };
+ }
+ }
+
+ return {
+ type: "invalid",
+ begin: from,
+ end: from + 1,
+ content: input[from]
+ };
+}
+
+class Tokenizer {
+ constructor(input, patterns) {
+ this.input = input;
+ this.index = 0;
+ this.patterns = patterns;
+ }
+
+ next() {
+ for (;;) {
+ const token = nextToken(this.input, this.index, this.patterns);
+ this.index = token.end;
+ if (token.type === "space" || token.type === "comment")
+ continue;
+ return token;
+ }
+ }
+
+ revert(token) {
+ this.index = token.begin;
+ }
+}
+
+// ============================================================================
+// [Parser]
+// ============================================================================
+
+function parseEnum(input) {
+ const map = Object.create(null);
+ const hasOwn = Object.prototype.hasOwnProperty;
+ const tokenizer = new Tokenizer(input, tokenizerPatterns);
+
+ var value = -1;
+
+ for (;;) {
+ var token = tokenizer.next();
+ if (token.type === "end")
+ break;
+
+ if (token.type === "symbol") {
+ const symbol = token.content;
+ token = tokenizer.next();
+ if (token.content === "=") {
+ token = tokenizer.next();
+ if (token.type !== "integer")
+ throw Error(`Expected an integer after symbol '${symbol} = '`);
+ value = parseInt(token.content);
+ }
+ else {
+ value++;
+ }
+
+ if (!hasOwn.call(map, symbol))
+ map[symbol] = value;
+ else
+ console.log(`${symbol} already defined, skipping...`);
+
+ token = tokenizer.next();
+ if (token.type !== "comma")
+ tokenizer.revert(token);
+ continue;
+ }
+
+ throw Error(`Unexpected token ${token.type} (${token.content})`);
+ }
+
+ return map;
+}
+
+// ============================================================================
+// [Stringify]
+// ============================================================================
+
+function compare(a, b) {
+ return a < b ? -1 : a == b ? 0 : 1;
+}
+
+function compactedSize(table) {
+ var size = 0;
+ for (var i = 0; i < table.length; i++)
+ size += table[i].name.length + 1;
+ return size;
+}
+
+function indexTypeFromSize(size) {
+ if (size <= 256)
+ return 'uint8_t';
+ else if (size <= 65536)
+ return 'uint16_t';
+ else
+ return 'uint32_t';
+}
+
+function indent(s, indentation) {
+ var lines = s.split(/\r?\n/g);
+ if (indentation) {
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line) lines[i] = indentation + line;
+ }
+ }
+
+ return lines.join("\n");
+}
+
+function stringifyEnum(map, options) {
+ var output = "";
+
+ const stripPrefix = options.strip;
+ const outputPrefix = options.output;
+
+ var max = -1;
+ var table = [];
+
+ for (var k in map) {
+ var name = k;
+ if (stripPrefix) {
+ if (name.startsWith(stripPrefix))
+ name = name.substring(stripPrefix.length);
+ else
+ throw Error(`Cannot strip prefix '${stripPrefix}' in '${key}'`);
+ }
+
+ table.push({ name: name, value: map[k] });
+ max = Math.max(max, map[k]);
+ }
+
+ table.sort(function(a, b) { return compare(a.value, b.value); });
+
+ const unknownIndex = compactedSize(table);
+ table.push({ name: "<Unknown>", value: max + 1 });
+
+ const indexType = indexTypeFromSize(compactedSize(table));
+
+ function buildStringData() {
+ var s = "";
+ for (var i = 0; i < table.length; i++) {
+ s += ` "${table[i].name}\\0"`;
+ if (i == table.length - 1)
+ s += `;`;
+ s += `\n`;
+ }
+ return s;
+ }
+
+ function buildIndexData() {
+ var index = 0;
+ var indexArray = [];
+
+ for (var i = 0; i < table.length; i++) {
+ while (indexArray.length < table[i].value)
+ indexArray.push(unknownIndex);
+
+ indexArray.push(index);
+ index += table[i].name.length + 1;
+ }
+
+ var s = "";
+ var line = "";
+ var pos = 0;
+
+ for (var i = 0; i < indexArray.length; i++) {
+ if (line)
+ line += " ";
+
+ line += `${indexArray[i]}`;
+ if (i != indexArray.length - 1)
+ line += `,`;
+
+ if (i == indexArray.length - 1 || line.length >= 72) {
+ s += ` ${line}\n`;
+ line = "";
+ }
+ }
+
+ return s;
+ }
+
+ output += `static const char ${outputPrefix}String[] =\n` + buildStringData() + `\n`;
+ output += `static const ${indexType} ${outputPrefix}Index[] = {\n` + buildIndexData() + `};\n`;
+
+ return output;
+}
+
+// ============================================================================
+// [FileSystem]
+// ============================================================================
+
+function walkDir(baseDir) {
+ function walk(baseDir, nestedPath, out) {
+ fs.readdirSync(baseDir).forEach((file) => {
+ const stat = fs.statSync(path.join(baseDir, file));
+ if (stat.isDirectory()) {
+ if (!stat.isSymbolicLink())
+ walk(path.join(baseDir, file), path.join(nestedPath, file), out)
+ }
+ else {
+ out.push(path.join(nestedPath, file));
+ }
+ });
+ return out;
+ }
+
+ return walk(baseDir, "", []);
+}
+
+// ============================================================================
+// [Generator]
+// ============================================================================
+
+class Generator {
+ constructor(options) {
+ this.enumMap = Object.create(null);
+ this.outputs = [];
+
+ this.verify = options.verify;
+ this.baseDir = options.baseDir;
+ this.noBackup = options.noBackup;
+ }
+
+ readEnums() {
+ console.log(`Scanning: ${this.baseDir}`);
+ walkDir(this.baseDir).forEach((fileName) => {
+ if (/\.(cc|cpp|h|hpp)$/.test(fileName)) {
+ const content = fs.readFileSync(path.join(this.baseDir, fileName), "utf8");
+ this.addEnumsFromSource(fileName, content);
+
+ if (/@EnumStringBegin(\{.*\})@/.test(content))
+ this.outputs.push(fileName);
+ }
+ });
+ }
+
+ writeEnums() {
+ this.outputs.forEach((fileName) => {
+ console.log(`Output: ${fileName}`);
+
+ const oldContent = fs.readFileSync(path.join(this.baseDir, fileName), "utf8");
+ const newContent = this.injectEnumsToSource(oldContent);
+
+ if (oldContent != newContent) {
+ if (this.verify) {
+ console.log(` FAILED: File is not up to date.`);
+ process.exit(1);
+ }
+ else {
+ if (!this.noBackup) {
+ fs.writeFileSync(path.join(this.baseDir, fileName + ".backup"), oldContent, "utf8");
+ console.log(` Created ${fileName}.backup`);
+ }
+ fs.writeFileSync(path.join(this.baseDir, fileName), newContent, "utf8");
+ console.log(` Updated ${fileName}`);
+ }
+ }
+ else {
+ console.log(` File is up to date.`);
+ }
+ });
+ }
+
+ addEnumsFromSource(fileName, src) {
+ var found = false;
+ const matches = [...src.matchAll(/(?:@EnumValuesBegin(\{.*\})@|@EnumValuesEnd@)/g)];
+
+ for (var i = 0; i < matches.length; i += 2) {
+ const def = matches[i];
+ const end = matches[i + 1];
+
+ if (!def[0].startsWith("@EnumValuesBegin"))
+ throw new Error(`Cannot start with '${def[0]}'`);
+
+ if (!end)
+ throw new Error(`Missing @EnumValuesEnd for '${def[0]}'`);
+
+ if (!end[0].startsWith("@EnumValuesEnd@"))
+ throw new Error(`Expected @EnumValuesEnd@ for '${def[0]}' and not '${end[0]}'`);
+
+ const options = JSON.parse(def[1]);
+ const enumName = options.enum;
+
+ if (!enumName)
+ throw Error(`Missing 'enum' in '${def[0]}`);
+
+ if (hasOwn.call(this.enumMap, enumName))
+ throw new Error(`Enumeration '${enumName}' is already defined`);
+
+ const startIndex = src.lastIndexOf("\n", def.index) + 1;
+ const endIndex = end.index + end[0].length;
+
+ if (startIndex === -1 || startIndex > endIndex)
+ throw new Error(`Internal Error, indexes have unexpected values: startIndex=${startIndex} endIndex=${endIndex}`);
+
+ if (!found) {
+ found = true;
+ console.log(`Found: ${fileName}`);
+ }
+
+ console.log(` Parsing Enum: ${enumName}`);
+ this.enumMap[enumName] = parseEnum(src.substring(startIndex, endIndex));
+ }
+ }
+
+ injectEnumsToSource(src) {
+ const matches = [...src.matchAll(/(?:@EnumStringBegin(\{.*\})@|@EnumStringEnd@)/g)];
+ var delta = 0;
+
+ for (var i = 0; i < matches.length; i += 2) {
+ const def = matches[i];
+ const end = matches[i + 1];
+
+ if (!def[0].startsWith("@EnumStringBegin"))
+ throw new Error(`Cannot start with '${def[0]}'`);
+
+ if (!end)
+ throw new Error(`Missing @EnumStringEnd@ for '${def[0]}'`);
+
+ if (!end[0].startsWith("@EnumStringEnd@"))
+ throw new Error(`Expected @EnumStringEnd@ for '${def[0]}' and not '${end[0]}'`);
+
+ const options = JSON.parse(def[1]);
+ const enumName = options.enum;
+
+ if (!enumName)
+ throwError(`Missing 'name' in '${def[0]}`);
+
+ if (!hasOwn.call(this.enumMap, enumName))
+ throw new Error(`Enumeration '${enumName}' not found`);
+
+ console.log(` Injecting Enum: ${enumName}`);
+
+ const startIndex = src.indexOf("\n", def.index + delta) + 1;
+ const endIndex = src.lastIndexOf("\n", end.index + delta) + 1;
+
+ if (startIndex === -1 || endIndex === -1 || startIndex > endIndex)
+ throw new Error(`Internal Error, indexes have unexpected values: startIndex=${startIndex} endIndex=${endIndex}`);
+
+ // Calculate the indentation.
+ const indentation = (function() {
+ const begin = src.lastIndexOf("\n", def.index + delta) + 1;
+ const end = src.indexOf("/", begin);
+ return src.substring(begin, end);
+ })();
+
+ const newContent = indent(stringifyEnum(this.enumMap[enumName], options), indentation);
+ src = src.substring(0, startIndex) + newContent + src.substring(endIndex);
+
+ delta -= endIndex - startIndex;
+ delta += newContent.length;
+ }
+
+ return src;
+ }
+}
+
+const generator = new Generator({
+ baseDir : path.resolve(__dirname, "../src"),
+ verify : process.argv.indexOf("--verify") !== -1,
+ noBackup: process.argv.indexOf("--no-backup") !== -1
+});
+
+generator.readEnums();
+generator.writeEnums();
diff --git a/3rdparty/asmjit/tools/enumgen.sh b/3rdparty/asmjit/tools/enumgen.sh
new file mode 100755
index 00000000000..4783db2449d
--- /dev/null
+++ b/3rdparty/asmjit/tools/enumgen.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+set -e
+node ./enumgen.js $@
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-a64.js b/3rdparty/asmjit/tools/tablegen-a64.js
new file mode 100644
index 00000000000..1466fc63b01
--- /dev/null
+++ b/3rdparty/asmjit/tools/tablegen-a64.js
@@ -0,0 +1,352 @@
+// 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
+
+"use strict";
+
+const core = require("./tablegen.js");
+const commons = require("./generator-commons.js");
+const hasOwn = Object.prototype.hasOwnProperty;
+
+const asmdb = core.asmdb;
+const kIndent = commons.kIndent;
+const IndexedArray = commons.IndexedArray;
+const StringUtils = commons.StringUtils;
+
+const FATAL = commons.FATAL;
+
+// ============================================================================
+// [ArmDB]
+// ============================================================================
+
+// 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) {
+ const insts = isa.query(name);
+ return !mode ? insts : insts.filter(function(inst) { return inst.arch === mode; });
+ }
+
+ static archOf(records) {
+ var t16Arch = false;
+ var t32Arch = false;
+ var a32Arch = false;
+ var a64Arch = false;
+
+ for (var i = 0; i < records.length; i++) {
+ const record = records[i];
+ if (record.encoding === "T16") t16Arch = true;
+ if (record.encoding === "T32") t32Arch = true;
+ if (record.encoding === "A32") a32Arch = true;
+ if (record.encoding === "A64") a64Arch = true;
+ }
+
+ var s = (t16Arch && !t32Arch) ? "T16" :
+ (t32Arch && !t16Arch) ? "T32" :
+ (t16Arch && t32Arch) ? "Txx" : "---";
+ s += " ";
+ s += (a32Arch) ? "A32" : "---";
+ s += " ";
+ s += (a64Arch) ? "A64" : "---";
+
+ return `[${s}]`;
+ }
+
+ static featuresOf(records) {
+ const exts = Object.create(null);
+ for (var i = 0; i < records.length; i++) {
+ const record = records[i];
+ for (var k in record.extensions)
+ exts[k] = true;
+ }
+ const arr = Object.keys(exts);
+ arr.sort();
+ return arr;
+ }
+}
+*/
+
+// ============================================================================
+// [tablegen.arm.ArmTableGen]
+// ============================================================================
+
+class ArmTableGen extends core.TableGen {
+ constructor() {
+ super("A64");
+ }
+
+ // --------------------------------------------------------------------------
+ // [Parse / Merge]
+ // --------------------------------------------------------------------------
+
+ parse() {
+ const rawData = this.dataOfFile("src/asmjit/arm/a64instdb.cpp");
+ const stringData = StringUtils.extract(rawData, "// ${InstInfo:Begin}", "// ${InstInfo:End");
+
+ const re = new RegExp(
+ "INST\\(\\s*" +
+ // [01] Instruction.
+ "(" +
+ "[A-Za-z0-9_]+" +
+ ")\\s*,\\s*" +
+
+ // [02] Encoding.
+ "(" +
+ "[^,]+" +
+ ")\\s*,\\s*" +
+
+ // [03] OpcodeData.
+ "(" +
+ "\\([^\\)]+\\)" +
+ ")\\s*,\\s*" +
+
+ // [04] RWInfo.
+ "(" +
+ "[^,]+" +
+ ")\\s*,\\s*" +
+
+ // [05] InstructionFlags.
+ "(\\s*" +
+ "(?:" +
+ "(?:" +
+ "[\\d]+" +
+ "|" +
+ "F\\([^\\)]*\\)" +
+ ")" +
+ "\\s*" +
+ "[|]?\\s*" +
+ ")+" +
+ ")\\s*,\\s*" +
+
+ // --- autogenerated fields ---
+
+ // [06] OpcodeDataIndex.
+ "([^\\)]+)" +
+ "\\s*\\)"
+
+ , "g");
+
+ var m;
+ while ((m = re.exec(stringData)) !== null) {
+ var enum_ = m[1];
+ var name = enum_ === "None" ? "" : enum_.toLowerCase();
+ var encoding = m[2].trim();
+ var opcodeData = m[3].trim();
+ var rwInfo = m[4].trim();
+ var instFlags = m[5].trim();
+
+ var displayName = name;
+ if (name.endsWith("_v"))
+ displayName = name.substring(0, name.length - 2);
+
+ // We have just matched #define INST()
+ if (name == "id" &&
+ encoding === "encoding" &&
+ encodingDataIndex === "encodingDataIndex")
+ continue;
+
+ this.addInst({
+ id : 0, // Instruction id (numeric value).
+ name : name, // Instruction name.
+ displayName : displayName, // Instruction name to display.
+ enum : enum_, // Instruction enum without `kId` prefix.
+ encoding : encoding, // Opcode encoding.
+ opcodeData : opcodeData, // Opcode data.
+ opcodeDataIndex : -1, // Opcode data index.
+ rwInfo : rwInfo, // RW info.
+ flags : instFlags // Instruction flags.
+ });
+ }
+
+ if (this.insts.length === 0 || this.insts.length !== StringUtils.countOf(stringData, "INST("))
+ FATAL("ARMTableGen.parse(): Invalid parsing regexp (no data parsed)");
+
+ console.log("Number of Instructions: " + this.insts.length);
+ }
+
+ merge() {
+ var s = StringUtils.format(this.insts, "", true, function(inst) {
+ return "INST(" +
+ String(inst.enum ).padEnd(17) + ", " +
+ String(inst.encoding ).padEnd(19) + ", " +
+ String(inst.opcodeData ).padEnd(86) + ", " +
+ String(inst.rwInfo ).padEnd(10) + ", " +
+ String(inst.flags ).padEnd(26) + ", " +
+ String(inst.opcodeDataIndex ).padEnd( 3) + ")" ;
+ }) + "\n";
+ return this.inject("InstInfo", s, this.insts.length * 4);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Hooks]
+ // --------------------------------------------------------------------------
+
+ onBeforeRun() {
+ this.load([
+ "src/asmjit/arm/a64emitter.h",
+ "src/asmjit/arm/a64globals.h",
+ "src/asmjit/arm/a64instdb.cpp",
+ "src/asmjit/arm/a64instdb.h",
+ "src/asmjit/arm/a64instdb_p.h"
+ ]);
+ this.parse();
+ }
+
+ onAfterRun() {
+ this.merge();
+ this.save();
+ this.dumpTableSizes();
+ }
+}
+
+// ============================================================================
+// [tablegen.arm.IdEnum]
+// ============================================================================
+
+class IdEnum extends core.IdEnum {
+ constructor() {
+ super("IdEnum");
+ }
+
+ comment(inst) {
+ let name = inst.name;
+ let ext = [];
+
+ if (name.endsWith("_v")) {
+ name = name.substr(0, name.length - 2);
+ ext.push("ASIMD");
+ }
+
+ let exts = "";
+ if (ext.length)
+ exts = " {" + ext.join("&") + "}";
+
+ return `Instruction '${name}'${exts}.`;
+ }
+}
+
+// ============================================================================
+// [tablegen.arm.NameTable]
+// ============================================================================
+
+class NameTable extends core.NameTable {
+ constructor() {
+ super("NameTable");
+ }
+}
+
+// ============================================================================
+// [tablegen.arm.EncodingTable]
+// ============================================================================
+
+class EncodingTable extends core.Task {
+ constructor() {
+ super("EncodingTable");
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+ const map = {};
+
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+
+ const encoding = inst.encoding;
+ const opcodeData = inst.opcodeData.replace(/\(/g, "{ ").replace(/\)/g, " }");
+
+ if (!hasOwn.call(map, encoding))
+ map[encoding] = [];
+
+ if (inst.opcodeData === "(_)") {
+ inst.opcodeDataIndex = 0;
+ continue;
+ }
+
+ const opcodeTable = map[encoding];
+ const opcodeDataIndex = opcodeTable.length;
+
+ opcodeTable.push({ name: inst.name, data: opcodeData });
+ inst.opcodeDataIndex = opcodeDataIndex;
+ }
+
+ const keys = Object.keys(map);
+ keys.sort();
+
+ var tableSource = "";
+ var tableHeader = "";
+ var encodingIds = "";
+
+ encodingIds += "enum EncodingId : uint32_t {\n"
+ encodingIds += " kEncodingNone = 0";
+
+ keys.forEach((dataClass) => {
+ const dataName = dataClass[0].toLowerCase() + dataClass.substr(1);
+ const opcodeTable = map[dataClass];
+ const count = opcodeTable.length;
+
+ if (dataClass !== "None") {
+ encodingIds += ",\n"
+ encodingIds += " kEncoding" + dataClass;
+ }
+
+ if (count) {
+ tableHeader += `extern const ${dataClass} ${dataName}[${count}];\n`;
+
+ if (tableSource)
+ tableSource += "\n";
+
+ tableSource += `const ${dataClass} ${dataName}[${count}] = {\n`;
+ for (var i = 0; i < count; i++) {
+ tableSource += ` ${opcodeTable[i].data}` + (i == count - 1 ? " " : ",") + " // " + opcodeTable[i].name + "\n";
+ }
+ tableSource += `};\n`;
+ }
+ });
+
+ encodingIds += "\n};\n";
+
+ return this.ctx.inject("EncodingId" , StringUtils.disclaimer(encodingIds), 0) +
+ this.ctx.inject("EncodingDataForward", StringUtils.disclaimer(tableHeader), 0) +
+ this.ctx.inject("EncodingData" , StringUtils.disclaimer(tableSource), 0);
+ }
+}
+// ============================================================================
+// [tablegen.arm.CommonTable]
+// ============================================================================
+
+class CommonTable extends core.Task {
+ constructor() {
+ super("CommonTable", [
+ "IdEnum",
+ "NameTable"
+ ]);
+ }
+
+ run() {
+ //const table = new IndexedArray();
+
+ //for (var i = 0; i < insts.length; i++) {
+ // const inst = insts[i];
+ // const item = "{ " + "0" + "}";
+ // inst.commonIndex = table.addIndexed(item);
+ //}
+
+ // return this.ctx.inject("InstInfo", StringUtils.disclaimer(s), 0);
+ return 0;
+ }
+}
+
+// ============================================================================
+// [Main]
+// ============================================================================
+
+new ArmTableGen()
+ .addTask(new IdEnum())
+ .addTask(new NameTable())
+ .addTask(new EncodingTable())
+ .addTask(new CommonTable())
+ .run();
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-x86.js b/3rdparty/asmjit/tools/tablegen-x86.js
new file mode 100644
index 00000000000..377a71045e7
--- /dev/null
+++ b/3rdparty/asmjit/tools/tablegen-x86.js
@@ -0,0 +1,2635 @@
+// 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
+
+"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 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 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(x86data);
+
+// ============================================================================
+// [tablegen.x86.Filter]
+// ============================================================================
+
+class Filter {
+ static unique(instArray) {
+ const result = [];
+ const known = {};
+
+ for (var i = 0; i < instArray.length; i++) {
+ const inst = instArray[i];
+ if (inst.altForm)
+ continue;
+
+ const s = inst.operands.map((op) => { return op.isImm() ? "imm" : op.toString(); }).join(", ");
+ if (known[s] === true)
+ continue;
+
+ known[s] = true;
+ result.push(inst);
+ }
+
+ return result;
+ }
+
+ static noAltForm(instArray) {
+ const result = [];
+ for (var i = 0; i < instArray.length; i++) {
+ const inst = instArray[i];
+ if (inst.altForm)
+ continue;
+ result.push(inst);
+ }
+ return result;
+ }
+
+ static byArch(instArray, arch) {
+ return instArray.filter(function(inst) {
+ return inst.arch === "ANY" || inst.arch === arch;
+ });
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.GenUtils]
+// ============================================================================
+
+const VexToEvexMap = {
+ "vbroadcastf128": "vbroadcastf32x4",
+ "vbroadcasti128": "vbroadcasti32x4",
+ "vextractf128": "vextractf32x4",
+ "vextracti128": "vextracti32x4",
+ "vinsertf128": "vinsertf32x4",
+ "vinserti128": "vinserti32x4",
+ "vmovdqa": "vmovdqa32",
+ "vmovdqu": "vmovdqu32",
+ "vpand": "vpandd",
+ "vpandn": "vpandnd",
+ "vpor": "vpord",
+ "vpxor": "vpxord",
+ "vroundpd": "vrndscalepd",
+ "vroundps": "vrndscaleps",
+ "vroundsd": "vrndscalesd",
+ "vroundss": "vrndscaless"
+};
+
+class GenUtils {
+ static cpuArchOf(dbInsts) {
+ var anyArch = false;
+ var x86Arch = false;
+ var x64Arch = false;
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ if (dbInst.arch === "ANY") anyArch = true;
+ if (dbInst.arch === "X86") x86Arch = true;
+ if (dbInst.arch === "X64") x64Arch = true;
+ }
+
+ return anyArch || (x86Arch && x64Arch) ? "" : x86Arch ? "(X86)" : "(X64)";
+ }
+
+ static cpuFeaturesOf(dbInsts) {
+ 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) {
+ const vexInsts = dbInsts.filter((inst) => { return inst.prefix === "VEX"; });
+ const evexInsts = dbInsts.filter((inst) => { return inst.prefix === "EVEX"; });
+
+ function isCompatible(vexInst, evexInst) {
+ if (vexInst.operands.length !== evexInst.operands.length)
+ return false;
+
+ for (let i = 0; i < vexInst.operands.length; i++) {
+ const vexOp = vexInst.operands[i];
+ const evexOp = evexInst.operands[i];
+
+ if (vexOp.data === evexOp.data)
+ continue;
+
+ if (vexOp.reg && vexOp.reg === evexOp.reg)
+ continue;
+ if (vexOp.mem && vexOp.mem === evexOp.mem)
+ continue;
+
+ return false;
+ }
+ return true;
+ }
+
+ let compatible = 0;
+ for (const vexInst of vexInsts) {
+ for (const evexInst of evexInsts) {
+ if (isCompatible(vexInst, evexInst)) {
+ compatible++;
+ break;
+ }
+ }
+ }
+
+ if (compatible == vexInsts.length) {
+ f.EvexCompat = true;
+ return true;
+ }
+
+ if (evexInsts[0].operands[0].reg === "k") {
+ f.EvexKReg = true;
+ return true;
+ }
+
+ if (evexInsts[0].operands.length == 2 && vexInsts[0].operands.length === 3) {
+ f.EvexTwoOp = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ static flagsOf(dbInsts) {
+ const f = Object.create(null);
+ var i, j;
+
+ var mib = dbInsts.length > 0 && /^(?:bndldx|bndstx)$/.test(dbInsts[0].name);
+ if (mib)
+ f.Mib = true;
+
+ var mmx = false;
+ var vec = false;
+
+ for (i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+
+ if (dbInst.name === "emms")
+ mmx = true;
+
+ if (dbInst.name === "vzeroall" || dbInst.name === "vzeroupper")
+ vec = true;
+
+ for (j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (op.reg === "mm")
+ mmx = true;
+ else if (/^(xmm|ymm|zmm)$/.test(op.reg)) {
+ vec = true;
+ }
+ }
+ }
+
+ if (mmx) f.Mmx = true;
+ if (vec) f.Vec = true;
+
+ for (i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+
+ 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.category.FPU) {
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (op.memSize === 16) f.FpuM16 = true;
+ if (op.memSize === 32) f.FpuM32 = true;
+ if (op.memSize === 64) f.FpuM64 = true;
+ if (op.memSize === 80) f.FpuM80 = true;
+ }
+ }
+
+ if (dbInst.tsib)
+ f.Tsib = true;
+
+ if (dbInst.vsibReg)
+ f.Vsib = true;
+
+ 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.kmask) f.Avx512K = true;
+ if (dbInst.zmask) f.Avx512Z = true;
+
+ if (dbInst.er) f.Avx512ER = true;
+ if (dbInst.sae) f.Avx512SAE = true;
+
+ if (dbInst.broadcast) f["Avx512B" + String(dbInst.elementSize)] = true;
+ if (dbInst.tupleType === "T1_4X") f.Avx512T4X = true;
+ }
+
+ if (VexToEvexMap[dbInst.name])
+ f.EvexTransformable = true;
+ }
+
+ if (f.Vex && f.Evex) {
+ GenUtils.assignVexEvexCompatibilityFlags(f, dbInsts)
+ }
+
+ const result = Object.getOwnPropertyNames(f);
+ result.sort();
+ return result;
+ }
+
+ static eqOps(aOps, aFrom, bOps, bFrom) {
+ var x = 0;
+ for (;;) {
+ const aIndex = x + aFrom;
+ const bIndex = x + bFrom;
+
+ const aOut = aIndex >= aOps.length;
+ const bOut = bIndex >= bOps.length;
+
+ if (aOut || bOut)
+ return !!(aOut && bOut);
+
+ const aOp = aOps[aIndex];
+ const bOp = bOps[bIndex];
+
+ if (aOp.data !== bOp.data)
+ return false;
+
+ x++;
+ }
+ }
+
+ // Prevent some instructions from having implicit memory size if that would
+ // make them ambiguous. There are some instructions where the ambiguity is
+ // okay, but some like 'push' and 'pop' where it isn't.
+ static canUseImplicitMemSize(name) {
+ switch (name) {
+ case "pop":
+ case "push":
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
+ static singleRegCase(name) {
+ switch (name) {
+ case "xchg" :
+
+ case "and" :
+ case "pand" : case "vpand" : case "vpandd" : case "vpandq" :
+ case "andpd" : case "vandpd" :
+ case "andps" : case "vandps" :
+
+ case "or" :
+ case "por" : case "vpor" : case "vpord" : case "vporq" :
+ case "orpd" : case "vorpd" :
+ case "orps" : case "vorps" :
+
+ case "pminsb" : case "vpminsb": case "pmaxsb" : case "vpmaxsb" :
+ case "pminsw" : case "vpminsw": case "pmaxsw" : case "vpmaxsw" :
+ case "pminsd" : case "vpminsd": case "pmaxsd" : case "vpmaxsd" :
+ case "pminub" : case "vpminub": case "pmaxub" : case "vpmaxub" :
+ case "pminuw" : case "vpminuw": case "pmaxuw" : case "vpmaxuw" :
+ case "pminud" : case "vpminud": case "pmaxud" : case "vpmaxud" :
+ return "RO";
+
+ case "pandn" : case "vpandn" : case "vpandnd" : case "vpandnq" :
+
+ case "xor" :
+ case "pxor" : case "vpxor" : case "vpxord" : case "vpxorq" :
+ case "xorpd" : case "vxorpd" :
+ case "xorps" : case "vxorps" :
+
+ case "kxnorb":
+ case "kxnord":
+ case "kxnorw":
+ case "kxnorq":
+
+ case "kxorb":
+ case "kxord":
+ case "kxorw":
+ case "kxorq":
+
+ case "sub" :
+ case "sbb" :
+ case "psubb" : case "vpsubb" :
+ case "psubw" : case "vpsubw" :
+ case "psubd" : case "vpsubd" :
+ case "psubq" : case "vpsubq" :
+ case "psubsb" : case "vpsubsb": case "psubusb" : case "vpsubusb" :
+ case "psubsw" : case "vpsubsw": case "psubusw" : case "vpsubusw" :
+
+ case "vpcmpeqb": case "pcmpeqb": case "vpcmpgtb": case "pcmpgtb" :
+ case "vpcmpeqw": case "pcmpeqw": case "vpcmpgtw": case "pcmpgtw" :
+ case "vpcmpeqd": case "pcmpeqd": case "vpcmpgtd": case "pcmpgtd" :
+ case "vpcmpeqq": case "pcmpeqq": case "vpcmpgtq": case "pcmpgtq" :
+
+ case "vpcmpb" : case "vpcmpub":
+ case "vpcmpd" : case "vpcmpud":
+ case "vpcmpw" : case "vpcmpuw":
+ case "vpcmpq" : case "vpcmpuq":
+ return "WO";
+
+ default:
+ return "None";
+ }
+ }
+
+ static fixedRegOfRegName(reg) {
+ switch (reg) {
+ case "es" : return 1;
+ case "cs" : return 2;
+ case "ss" : return 3;
+ case "ds" : return 4;
+ case "fs" : return 5;
+ case "gs" : return 6;
+ case "ah" : return 0;
+ case "ch" : return 1;
+ case "dh" : return 2;
+ case "bh" : return 3;
+ case "al" : case "ax": case "eax": case "rax": case "zax": return 0;
+ case "cl" : case "cx": case "ecx": case "rcx": case "zcx": return 1;
+ case "dl" : case "dx": case "edx": case "rdx": case "zdx": return 2;
+ case "bl" : case "bx": case "ebx": case "rbx": case "zbx": return 3;
+ case "spl" : case "sp": case "esp": case "rsp": case "zsp": return 4;
+ case "bpl" : case "bp": case "ebp": case "rbp": case "zbp": return 5;
+ case "sil" : case "si": case "esi": case "rsi": case "zsi": return 6;
+ case "dil" : case "di": case "edi": case "rdi": case "zdi": return 7;
+ case "st0" : return 0;
+ case "xmm0": return 0;
+ case "ymm0": return 0;
+ case "zmm0": return 0;
+ default:
+ return -1;
+ }
+ }
+
+ 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";
+ return "Regular";
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.X86TableGen]
+// ============================================================================
+
+class X86TableGen extends core.TableGen {
+ constructor() {
+ super("X86");
+
+ this.emitMissingString = "";
+ }
+
+ // --------------------------------------------------------------------------
+ // [Query]
+ // --------------------------------------------------------------------------
+
+ // Get instructions (dbInsts) having the same name as understood by AsmJit.
+ query(name) {
+ return x86isa.query(name);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Parse / Merge]
+ // --------------------------------------------------------------------------
+
+ parse() {
+ const data = this.dataOfFile("src/asmjit/x86/x86instdb.cpp");
+ const re = new RegExp(
+ "INST\\(" +
+ "([A-Za-z0-9_]+)\\s*" + "," + // [01] Instruction.
+ "([^,]+)" + "," + // [02] Encoding.
+ "(.{26}[^,]*)" + "," + // [03] Opcode[0].
+ "(.{26}[^,]*)" + "," + // [04] Opcode[1].
+ // --- autogenerated fields ---
+ "([^\\)]+)" + "," + // [05] MainOpcodeIndex.
+ "([^\\)]+)" + "," + // [06] AltOpcodeIndex.
+ "([^\\)]+)" + "," + // [07] CommonDataIndex.
+ "([^\\)]+)" + "\\)", // [08] OperationDataIndex.
+ "g");
+
+ var m;
+ while ((m = re.exec(data)) !== null) {
+ var enum_ = m[1];
+ var name = enum_ === "None" ? "" : enum_.toLowerCase();
+ var encoding = m[2].trim();
+ var opcode0 = m[3].trim();
+ var opcode1 = m[4].trim();
+
+ const dbInsts = this.query(name);
+ if (name && !dbInsts.length)
+ FATAL(`Instruction '${name}' not found in asmdb`);
+
+ const flags = GenUtils.flagsOf(dbInsts);
+ const controlFlow = GenUtils.controlFlow(dbInsts);
+ const singleRegCase = GenUtils.singleRegCase(name);
+
+ this.addInst({
+ id : 0, // Instruction id (numeric value).
+ name : name, // Instruction name.
+ displayName : name, // Instruction name to display.
+ enum : enum_, // Instruction enum without `kId` prefix.
+ dbInsts : dbInsts, // All dbInsts returned from asmdb query.
+ encoding : encoding, // Instruction encoding.
+ opcode0 : opcode0, // Primary opcode.
+ opcode1 : opcode1, // Secondary opcode.
+ flags : flags,
+ signatures : null, // Instruction signatures.
+ controlFlow : controlFlow,
+ singleRegCase : singleRegCase,
+
+ mainOpcodeValue : -1, // Main opcode value (0.255 hex).
+ mainOpcodeIndex : -1, // Index to InstDB::_mainOpcodeTable.
+ altOpcodeIndex : -1, // Index to InstDB::_altOpcodeTable.
+ nameIndex : -1, // Index to InstDB::_nameData.
+ commonInfoIndex : -1,
+ additionalInfoIndex: -1,
+
+ signatureIndex : -1,
+ signatureCount : -1
+ });
+ }
+
+ if (this.insts.length === 0)
+ FATAL("X86TableGen.parse(): Invalid parsing regexp (no data parsed)");
+
+ console.log("Number of Instructions: " + this.insts.length);
+ }
+
+ merge() {
+ var s = StringUtils.format(this.insts, "", true, function(inst) {
+ return "INST(" +
+ String(inst.enum ).padEnd(17) + ", " +
+ String(inst.encoding ).padEnd(19) + ", " +
+ String(inst.opcode0 ).padEnd(26) + ", " +
+ String(inst.opcode1 ).padEnd(26) + ", " +
+ String(inst.mainOpcodeIndex ).padEnd( 3) + ", " +
+ String(inst.altOpcodeIndex ).padEnd( 3) + ", " +
+ String(inst.commonInfoIndex ).padEnd( 3) + ", " +
+ String(inst.additionalInfoIndex).padEnd( 3) + ")";
+ }) + "\n";
+ this.inject("InstInfo", s, this.insts.length * 8);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Other]
+ // --------------------------------------------------------------------------
+
+ printMissing() {
+ const ignored = ArrayUtils.toDict([
+ "cmpsb", "cmpsw", "cmpsd", "cmpsq",
+ "lodsb", "lodsw", "lodsd", "lodsq",
+ "movsb", "movsw", "movsd", "movsq",
+ "scasb", "scasw", "scasd", "scasq",
+ "stosb", "stosw", "stosd", "stosq",
+ "insb" , "insw" , "insd" ,
+ "outsb", "outsw", "outsd",
+ "wait" // Maps to `fwait`, which AsmJit uses instead.
+ ]);
+
+ var out = "";
+ x86isa.instructionNames.forEach(function(name) {
+ var dbInsts = x86isa.query(name);
+ if (!this.instMap[name] && ignored[name] !== true) {
+ console.log(`MISSING INSTRUCTION '${name}'`);
+ var inst = this.newInstFromGroup(dbInsts);
+ if (inst) {
+ out += " INST(" +
+ String(inst.enum ).padEnd(17) + ", " +
+ String(inst.encoding ).padEnd(19) + ", " +
+ String(inst.opcode0 ).padEnd(26) + ", " +
+ String(inst.opcode1 ).padEnd(26) + ", " +
+ String("0" ).padEnd( 3) + ", " +
+ String("0" ).padEnd( 3) + ", " +
+ String("0" ).padEnd( 5) + ", " +
+ String("0" ).padEnd( 3) + ", " +
+ String("0" ).padEnd( 3) + "),\n";
+ }
+ }
+ }, this);
+ console.log(out);
+ console.log(this.emitMissingString);
+ }
+
+ newInstFromGroup(dbInsts) {
+ function composeOpCode(obj) {
+ return `${obj.type}(${obj.prefix},${obj.opcode},${obj.o},${obj.l},${obj.w},${obj.ew},${obj.en},${obj.tt})`;
+ }
+
+ function GetAccess(dbInst) {
+ var operands = dbInst.operands;
+ if (!operands.length) return "";
+
+ var op = operands[0];
+ if (op.read && op.write)
+ return "RW";
+ else if (op.read)
+ return "RO";
+ else
+ return "WO";
+ }
+
+ function isVecPrefix(s) {
+ return s === "VEX" || s === "EVEX" || s === "XOP";
+ }
+
+ function formatEmit(dbi) {
+ const results = [];
+ const nameUp = dbi.name[0].toUpperCase() + dbi.name.substr(1);
+
+ for (let choice = 0; choice < 2; choice++) {
+ let s = `ASMJIT_INST_${dbi.operands.length}x(${dbi.name}, ${nameUp}`;
+ for (let j = 0; j < dbi.operands.length; j++) {
+ s += ", ";
+ const op = dbi.operands[j];
+ var reg = op.reg;
+ var mem = op.mem;
+
+ if (op.isReg() && op.isMem()) {
+ if (choice == 0) mem = null;
+ if (choice == 1) reg = null;
+ }
+
+ if (reg) {
+ if (reg === "xmm" || reg === "ymm" || reg === "zmm")
+ s += "Vec";
+ else if (reg === "k")
+ s += "KReg";
+ else if (reg === "r32" || reg === "r64" || reg === "r16" || reg === "r8")
+ s += "Gp";
+ else
+ s += reg;
+ }
+ else if (mem) {
+ s += "Mem";
+ }
+ else if (op.isImm()) {
+ s += "Imm";
+ }
+ else {
+ s += "Unknown";
+ }
+ }
+ s += `)`;
+ results.push(s);
+ }
+
+ return results;
+ }
+
+ var dbi = dbInsts[0];
+
+ var id = this.insts.length;
+ var name = dbi.name;
+ var enum_ = name[0].toUpperCase() + name.substr(1);
+
+ var opcode = dbi.opcodeHex;
+ var modR = dbi.modR;
+ var mm = dbi.mm;
+ var pp = dbi.pp;
+ var encoding = dbi.encoding;
+ var isVec = isVecPrefix(dbi.prefix);
+ var evexCount = 0;
+
+ var access = GetAccess(dbi);
+
+ var vexL = undefined;
+ var vexW = undefined;
+ var evexW = undefined;
+ var cdshl = "_";
+ var tupleType = "_";
+
+ const tupleTypeToCDSHL = {
+ "FVM": "4",
+ "FV": "4",
+ "HVM": "3",
+ "HV": "3",
+ "QVM": "2",
+ "QV": "2",
+ "T1S": "?"
+ }
+
+ const emitMap = {};
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ dbi = dbInsts[i];
+
+ if (dbi.prefix === "VEX" || dbi.prefix === "XOP") {
+ var newVexL = String(dbi.l === "128" ? 0 : dbi.l === "256" ? 1 : dbi.l === "512" ? 2 : "_");
+ var newVexW = String(dbi.w === "W0" ? 0 : dbi.w === "W1" ? 1 : "_");
+
+ if (vexL !== undefined && vexL !== newVexL)
+ vexL = "x";
+ else
+ vexL = newVexL;
+ if (vexW !== undefined && vexW !== newVexW)
+ vexW = "x";
+ else
+ vexW = newVexW;
+ }
+
+ if (dbi.prefix === "EVEX") {
+ evexCount++;
+ var newEvexW = String(dbi.w === "W0" ? 0 : dbi.w === "W1" ? 1 : "_");
+ if (evexW !== undefined && evexW !== newEvexW)
+ evexW = "x";
+ else
+ evexW = newEvexW;
+
+ if (dbi.tupleType) {
+ if (tupleType !== "_" && tupleType !== dbi.tupleType) {
+ console.log(`${dbi.name}: WARNING: TupleType ${tupleType} != ${dbi.tupleType}`);
+ }
+
+ tupleType = dbi.tupleType;
+ }
+ }
+
+ if (opcode !== dbi.opcodeHex ) { console.log(`${dbi.name}: ISSUE: Opcode ${opcode} != ${dbi.opcodeHex}`); return null; }
+ if (modR !== dbi.modR ) { console.log(`${dbi.name}: ISSUE: ModR ${modR} != ${dbi.modR}`); return null; }
+ if (mm !== dbi.mm ) { console.log(`${dbi.name}: ISSUE: MM ${mm} != ${dbi.mm}`); return null; }
+ if (pp !== dbi.pp ) { console.log(`${dbi.name}: ISSUE: PP ${pp} != ${dbi.pp}`); return null; }
+ if (encoding !== dbi.encoding ) { console.log(`${dbi.name}: ISSUE: Enc ${encoding} != ${dbi.encoding}`); return null; }
+ if (access !== GetAccess(dbi)) { console.log(`${dbi.name}: ISSUE: Access ${access} != ${GetAccess(dbi)}`); return null; }
+ if (isVec != isVecPrefix(dbi.prefix)) { console.log(`${dbi.name}: ISSUE: Vex/Non-Vex mismatch`); return null; }
+
+ formatEmit(dbi).forEach((emit) => {
+ if (!emitMap[emit]) {
+ emitMap[emit] = true;
+ this.emitMissingString += emit + "\n";
+ }
+ });
+ }
+
+ if (tupleType !== "_")
+ cdshl = tupleTypeToCDSHL[tupleType] || "?";
+
+ var ppmm = pp.padEnd(2).replace(/ /g, "0") +
+ mm.padEnd(4).replace(/ /g, "0") ;
+
+ var composed = composeOpCode({
+ type : evexCount == dbInsts.length ? "E" : isVec ? "V" : "O",
+ prefix: ppmm,
+ opcode: opcode,
+ o : modR === "r" ? "_" : (modR ? modR : "_"),
+ l : vexL !== undefined ? vexL : "_",
+ w : vexW !== undefined ? vexW : "_",
+ ew : evexW !== undefined ? evexW : "_",
+ en : cdshl,
+ tt : dbi.modRM ? dbi.modRM + " " : tupleType.padEnd(3)
+ });
+
+ return {
+ id : id,
+ name : name,
+ enum : enum_,
+ encoding : encoding,
+ opcode0 : composed,
+ opcode1 : "0",
+ nameIndex : -1,
+ commonInfoIndex : -1,
+ additionalInfoIndex: -1
+ };
+ }
+
+ // --------------------------------------------------------------------------
+ // [Hooks]
+ // --------------------------------------------------------------------------
+
+ onBeforeRun() {
+ this.load([
+ "src/asmjit/x86/x86globals.h",
+ "src/asmjit/x86/x86instdb.cpp",
+ "src/asmjit/x86/x86instdb.h",
+ "src/asmjit/x86/x86instdb_p.h"
+ ]);
+ this.parse();
+ }
+
+ onAfterRun() {
+ this.merge();
+ this.save();
+ this.dumpTableSizes();
+ this.printMissing();
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.IdEnum]
+// ============================================================================
+
+class IdEnum extends core.IdEnum {
+ constructor() {
+ super("IdEnum");
+ }
+
+ comment(inst) {
+ function filterAVX(features, avx) {
+ return features.filter(function(item) { return /^(AVX|FMA)/.test(item) === avx; });
+ }
+
+ var dbInsts = inst.dbInsts;
+ if (!dbInsts.length) return "Invalid instruction id.";
+
+ var text = "";
+ var features = GenUtils.cpuFeaturesOf(dbInsts);
+
+ const priorityFeatures = ["AVX_VNNI", "AVX_VNNI_INT8", "AVX_IFMA", "AVX_NE_CONVERT"];
+
+ if (features.length) {
+ text += "{";
+ const avxFeatures = filterAVX(features, true);
+ const otherFeatures = filterAVX(features, false);
+
+ for (const pf of priorityFeatures) {
+ const index = avxFeatures.indexOf(pf);
+ if (index != -1) {
+ avxFeatures.splice(index, 1);
+ avxFeatures.unshift(pf);
+ }
+ }
+
+ const vl = avxFeatures.indexOf("AVX512_VL");
+ if (vl !== -1) avxFeatures.splice(vl, 1);
+
+ const fma = avxFeatures.indexOf("FMA");
+ if (fma !== -1) { avxFeatures.splice(fma, 1); avxFeatures.splice(0, 0, "FMA"); }
+
+ text += avxFeatures.join("|");
+ if (vl !== -1) text += "+VL";
+
+ if (otherFeatures.length)
+ text += (avxFeatures.length ? " & " : "") + otherFeatures.join("|");
+
+ text += "}";
+ }
+
+ var arch = GenUtils.cpuArchOf(dbInsts);
+ if (arch)
+ text += (text ? " " : "") + arch;
+
+ return `Instruction '${inst.name}'${(text ? " " + text : "")}.`;
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.NameTable]
+// ============================================================================
+
+class NameTable extends core.NameTable {
+ constructor() {
+ super("NameTable");
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.AltOpcodeTable]
+// ============================================================================
+
+class AltOpcodeTable extends core.Task {
+ constructor() {
+ super("AltOpcodeTable");
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ const mainOpcodeTable = new IndexedArray();
+ const altOpcodeTable = new IndexedArray();
+
+ const cdttSimplification = {
+ "0" : "None",
+ "_" : "None",
+ "FV" : "ByLL",
+ "HV" : "ByLL",
+ "QV" : "ByLL",
+ "FVM" : "ByLL",
+ "T1S" : "None",
+ "T1F" : "None",
+ "T1_4X": "None",
+ "T2" : "None",
+ "T4" : "None",
+ "T8" : "None",
+ "HVM" : "ByLL",
+ "QVM" : "ByLL",
+ "OVM" : "ByLL",
+ "128" : "None",
+ "T4X" : "None"
+ }
+
+ const noOp = "O(000000,00,0,0,0,0,0,0 )";
+
+ mainOpcodeTable.addIndexed(noOp);
+
+ function splitOpcodeToComponents(opcode) {
+ const i = opcode.indexOf("(");
+ const prefix = opcode.substr(0, i);
+ return [prefix].concat(opcode.substring(i + 1, opcode.length - 1).split(","));
+ }
+
+ function normalizeOpcodeComponents(components) {
+ for (let i = 1; i < components.length; i++) {
+ components[i] = components[i].trim();
+ // These all are zeros that only have some contextual meaning in the table, but the assembler doesn't care.
+ if (components[i] === "_" || components[i] === "I" || components[i] === "x")
+ components[i] = "0";
+ }
+
+ // Simplify CDTT (compressed displacement TupleType).
+ if (components.length >= 9) {
+ if (components[0] === "V" || components[0] === "E") {
+ const cdtt = components[8];
+ if (cdttSimplification[cdtt] !== undefined)
+ components[8] = cdttSimplification[cdtt];
+ }
+ }
+ return components;
+ }
+
+ function joinOpcodeComponents(components) {
+ const prefix = components[0];
+ const values = components.slice(1);
+ if (values.length >= 8)
+ values[7] = values[7].padEnd(4);
+ return prefix + "(" + values.join(",") + ")";
+ }
+
+ function indexMainOpcode(opcode) {
+ if (opcode === "0")
+ return ["00", 0];
+
+ var opcodeByte = "";
+ const components = normalizeOpcodeComponents(splitOpcodeToComponents(opcode));
+
+ if (components[0] === "O_FPU") {
+ // Reset opcode byte, this is stored in the instruction data itself.
+ opcodeByte = components[2].substr(2, 2);
+ components[2] = components[2].substr(0, 2) + "00";
+ }
+ else if (components[0] === "O" || components[0] === "V" || components[0] === "E") {
+ // Reset opcode byte, this is stored in the instruction data itself.
+ opcodeByte = components[2];
+ components[2] = "00";
+ }
+ else {
+ FATAL(`Failed to process opcode '${opcode}'`);
+ }
+
+ const newOpcode = joinOpcodeComponents(components);
+ return [opcodeByte, mainOpcodeTable.addIndexed(newOpcode.padEnd(27))];
+ }
+
+ function indexAltOpcode(opcode) {
+ if (opcode === "0")
+ opcode = noOp;
+ else
+ opcode = joinOpcodeComponents(normalizeOpcodeComponents(splitOpcodeToComponents(opcode)));
+ return altOpcodeTable.addIndexed(opcode.padEnd(27));
+ }
+
+ insts.map((inst) => {
+ const [value, index] = indexMainOpcode(inst.opcode0);
+ inst.mainOpcodeValue = value;
+ inst.mainOpcodeIndex = index;
+ inst.altOpcodeIndex = indexAltOpcode(inst.opcode1);
+ });
+
+ // console.log(mainOpcodeTable.length);
+ // console.log(StringUtils.format(mainOpcodeTable, kIndent, true));
+
+ this.inject("MainOpcodeTable",
+ disclaimer(`const uint32_t InstDB::_mainOpcodeTable[] = {\n${StringUtils.format(mainOpcodeTable, kIndent, true)}\n};\n`),
+ mainOpcodeTable.length * 4);
+
+ this.inject("AltOpcodeTable",
+ disclaimer(`const uint32_t InstDB::_altOpcodeTable[] = {\n${StringUtils.format(altOpcodeTable, kIndent, true)}\n};\n`),
+ altOpcodeTable.length * 4);
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.InstSignatureTable]
+// ============================================================================
+
+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",
+ "MemUnspecified", "Mem8", "Mem16", "Mem32", "Mem48", "Mem64", "Mem80", "Mem128", "Mem256", "Mem512", "Mem1024",
+ "Vm32x", "Vm32y", "Vm32z", "Vm64x", "Vm64y", "Vm64z",
+ "ImmI4", "ImmU4", "ImmI8", "ImmU8", "ImmI16", "ImmU16", "ImmI32", "ImmU32", "ImmI64", "ImmU64",
+ "Rel8", "Rel32",
+ "FlagMemBase",
+ "FlagMemDs",
+ "FlagMemEs",
+ "FlagMib",
+ "FlagTMem",
+ "FlagConsecutive",
+ "FlagImplicit"
+]);
+
+function StringifyOpArray(a, map) {
+ var s = "";
+ for (var i = 0; i < a.length; i++) {
+ const op = a[i];
+ var mapped = null;
+ if (typeof map === "function")
+ mapped = map(op);
+ else if (hasOwn.call(map, op))
+ mapped = map[op];
+ else
+ FATAL(`UNHANDLED OPERAND '${op}'`);
+ s += (s ? " | " : "") + mapped;
+ }
+ return s ? s : "0";
+}
+
+class OSignature {
+ constructor() {
+ this.flags = Object.create(null);
+ }
+
+ equals(other) {
+ return ObjectUtils.equals(this.flags, other.flags);
+ }
+
+ xor(other) {
+ const result = ObjectUtils.xor(this.flags, other.flags);
+ return Object.getOwnPropertyNames(result).length === 0 ? null : result;
+ }
+
+ mergeWith(other) {
+ const af = this.flags;
+ const bf = other.flags;
+
+ var k;
+ var indexKind = "";
+ var hasReg = false;
+
+ for (k in af) {
+ const index = asmdb.x86.Utils.regIndexOf(k);
+ const kind = asmdb.x86.Utils.regKindOf(k);
+
+ if (kind)
+ hasReg = true;
+
+ if (index !== null && index !== -1)
+ indexKind = kind;
+ }
+
+ if (hasReg) {
+ for (k in bf) {
+ const index = asmdb.x86.Utils.regIndexOf(k);
+ if (index !== null && index !== -1) {
+ const kind = asmdb.x86.Utils.regKindOf(k);
+ if (indexKind !== kind)
+ return false;
+ }
+ }
+ }
+
+ // Can merge...
+ for (k in bf)
+ af[k] = true;
+ return true;
+ }
+
+ toString() {
+ var s = "";
+ var flags = this.flags;
+
+ for (var k in flags) {
+ if (k === "read" || k === "write" || k === "implicit" || k === "memDS" || k === "memES")
+ continue;
+
+ var x = k;
+ if (x === "memZAX") x = "zax";
+ if (x === "memZDI") x = "zdi";
+ if (x === "memZSI") x = "zsi";
+ s += (s ? "|" : "") + x;
+ }
+
+ if (flags.memDS) s = "ds:[" + s + "]";
+ if (flags.memES) s = "es:[" + s + "]";
+
+ if (flags.implicit)
+ s = "<" + s + ">";
+
+ return s;
+ }
+
+ toAsmJitOpData() {
+ var opFlags = Object.create(null);
+ var regMask = 0;
+
+ for (var k in this.flags) {
+ switch (k) {
+ case "r8lo" : opFlags.RegGpbLo = true; break;
+ case "r8hi" : opFlags.RegGpbHi = true; break;
+ case "r16" : opFlags.RegGpw = true; break;
+ case "r32" : opFlags.RegGpd = true; break;
+ case "r64" : opFlags.RegGpq = true; break;
+ case "creg" : opFlags.RegCReg = true; break;
+ case "dreg" : opFlags.RegDReg = true; break;
+ case "sreg" : opFlags.RegSReg = true; break;
+ case "bnd" : opFlags.RegBnd = true; break;
+ case "st" : opFlags.RegSt = true; break;
+ case "k" : opFlags.RegKReg = true; break;
+ case "mm" : opFlags.RegMm = true; break;
+ case "xmm" : opFlags.RegXmm = true; break;
+ case "ymm" : opFlags.RegYmm = true; break;
+ case "zmm" : opFlags.RegZmm = true; break;
+ case "tmm" : opFlags.RegTmm = true; break;
+
+ case "m8" : opFlags.Mem8 = true; break;
+ case "m16" : opFlags.Mem16 = true; break;
+ case "m32" : opFlags.Mem32 = true; break;
+ case "m48" : opFlags.Mem48 = true; break;
+ case "m64" : opFlags.Mem64 = true; break;
+ case "m80" : opFlags.Mem80 = true; break;
+ case "m128" : opFlags.Mem128 = true; break;
+ case "m256" : opFlags.Mem256 = true; break;
+ case "m512" : opFlags.Mem512 = true; break;
+ case "m1024" : opFlags.Mem1024 = true; break;
+
+ case "mem" : opFlags.MemUnspecified = true; break;
+ case "mib" : opFlags.MemUnspecified = true; opFlags.FlagMib = true; break;
+ case "tmem" : opFlags.MemUnspecified = true; opFlags.FlagTMem = true; break;
+
+ case "memBase" : opFlags.FlagMemBase = true; break;
+ case "memDS" : opFlags.FlagMemDs = true; break;
+ case "memES" : opFlags.FlagMemEs = true; break;
+ case "memZAX" : regMask |= 1 << 0; break;
+ case "memZSI" : regMask |= 1 << 6; break;
+ case "memZDI" : regMask |= 1 << 7; break;
+
+ case "vm32x" : opFlags.Vm32x = true; break;
+ case "vm32y" : opFlags.Vm32y = true; break;
+ case "vm32z" : opFlags.Vm32z = true; break;
+ case "vm64x" : opFlags.Vm64x = true; break;
+ case "vm64y" : opFlags.Vm64y = true; break;
+ case "vm64z" : opFlags.Vm64z = true; break;
+
+ case "i4" : opFlags.ImmI4 = true; break;
+ case "u4" : opFlags.ImmU4 = true; break;
+ case "i8" : opFlags.ImmI8 = true; break;
+ case "u8" : opFlags.ImmU8 = true; break;
+ case "i16" : opFlags.ImmI16 = true; break;
+ case "u16" : opFlags.ImmU16 = true; break;
+ case "i32" : opFlags.ImmI32 = true; break;
+ case "u32" : opFlags.ImmU32 = true; break;
+ case "i64" : opFlags.ImmI64 = true; break;
+ case "u64" : opFlags.ImmU64 = true; break;
+
+ case "rel8" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel8 = true; break;
+ case "rel16" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel32 = true; break;
+ case "rel32" : opFlags.ImmI32 = true; opFlags.ImmI64 = true; opFlags.Rel32 = true; break;
+
+ case "es" : opFlags.RegSReg = true; regMask |= 1 << 1; break;
+ case "cs" : opFlags.RegSReg = true; regMask |= 1 << 2; break;
+ case "ss" : opFlags.RegSReg = true; regMask |= 1 << 3; break;
+ case "ds" : opFlags.RegSReg = true; regMask |= 1 << 4; break;
+ case "fs" : opFlags.RegSReg = true; regMask |= 1 << 5; break;
+ case "gs" : opFlags.RegSReg = true; regMask |= 1 << 6; break;
+ case "al" : opFlags.RegGpbLo = true; regMask |= 1 << 0; break;
+ case "ah" : opFlags.RegGpbHi = true; regMask |= 1 << 0; break;
+ case "ax" : opFlags.RegGpw = true; regMask |= 1 << 0; break;
+ case "eax" : opFlags.RegGpd = true; regMask |= 1 << 0; break;
+ case "rax" : opFlags.RegGpq = true; regMask |= 1 << 0; break;
+ case "cl" : opFlags.RegGpbLo = true; regMask |= 1 << 1; break;
+ case "ch" : opFlags.RegGpbHi = true; regMask |= 1 << 1; break;
+ case "cx" : opFlags.RegGpw = true; regMask |= 1 << 1; break;
+ case "ecx" : opFlags.RegGpd = true; regMask |= 1 << 1; break;
+ case "rcx" : opFlags.RegGpq = true; regMask |= 1 << 1; break;
+ case "dl" : opFlags.RegGpbLo = true; regMask |= 1 << 2; break;
+ case "dh" : opFlags.RegGpbHi = true; regMask |= 1 << 2; break;
+ case "dx" : opFlags.RegGpw = true; regMask |= 1 << 2; break;
+ case "edx" : opFlags.RegGpd = true; regMask |= 1 << 2; break;
+ case "rdx" : opFlags.RegGpq = true; regMask |= 1 << 2; break;
+ case "bl" : opFlags.RegGpbLo = true; regMask |= 1 << 3; break;
+ case "bh" : opFlags.RegGpbHi = true; regMask |= 1 << 3; break;
+ case "bx" : opFlags.RegGpw = true; regMask |= 1 << 3; break;
+ case "ebx" : opFlags.RegGpd = true; regMask |= 1 << 3; break;
+ case "rbx" : opFlags.RegGpq = true; regMask |= 1 << 3; break;
+ case "si" : opFlags.RegGpw = true; regMask |= 1 << 6; break;
+ case "esi" : opFlags.RegGpd = true; regMask |= 1 << 6; break;
+ case "rsi" : opFlags.RegGpq = true; regMask |= 1 << 6; break;
+ case "di" : opFlags.RegGpw = true; regMask |= 1 << 7; break;
+ case "edi" : opFlags.RegGpd = true; regMask |= 1 << 7; break;
+ case "rdi" : opFlags.RegGpq = true; regMask |= 1 << 7; break;
+ case "st0" : opFlags.RegSt = true; regMask |= 1 << 0; break;
+ case "xmm0" : opFlags.RegXmm = true; regMask |= 1 << 0; break;
+ case "ymm0" : opFlags.RegYmm = true; regMask |= 1 << 0; break;
+
+ case "implicit": opFlags.FlagImplicit = true; break;
+
+ default:
+ console.log(`UNKNOWN OPERAND '${k}'`);
+ }
+ }
+
+ const outputFlags = StringifyOpArray(ArrayUtils.sorted(opFlags, cmpOp), function(k) { return `F(${k})`; });
+ return `ROW(${outputFlags || 0}, ${decToHex(regMask, 2)})`;
+ }
+}
+
+class ISignature extends Array {
+ constructor(name) {
+ super();
+ this.name = name;
+ this.x86 = false;
+ this.x64 = false;
+ this.implicit = 0; // Number of implicit operands.
+ }
+
+ opEquals(other) {
+ const len = this.length;
+ if (len !== other.length) return false;
+
+ for (var i = 0; i < len; i++)
+ if (!this[i].equals(other[i]))
+ return false;
+
+ return true;
+ }
+
+ mergeWith(other) {
+ // If both architectures are the same, it's fine to merge.
+ 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;
+
+ // It's not ok if both signatures have different number of implicit operands.
+ if (!sameArch || this.implicit !== other.implicit)
+ return false;
+
+ // It's not ok if both signatures have different number of operands.
+ const len = this.length;
+ if (len !== other.length)
+ return false;
+
+ let xorIndex = -1;
+ for (let i = 0; i < len; i++) {
+ const xor = this[i].xor(other[i]);
+ if (xor === null) continue;
+
+ if (xorIndex === -1)
+ xorIndex = i;
+ else
+ return false;
+ }
+
+ // Bail if mergeWidth at operand-level failed.
+ if (xorIndex === -1 || !this[xorIndex].mergeWith(other[xorIndex]))
+ return false;
+
+ return true;
+ }
+
+ toString() {
+ return "{" + this.join(", ") + "}";
+ }
+}
+
+class SignatureArray extends Array {
+ // Iterate over all signatures and check which operands don't need explicit memory size.
+ 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(ObjectUtils.and(op.flags, RegOp)).join("|") + "}";
+ }
+ return s || "?";
+ }
+
+ var i;
+ var aIndex, bIndex;
+
+ for (aIndex = 0; aIndex < this.length; aIndex++) {
+ const aInst = this[aIndex];
+ const len = aInst.length;
+
+ var memOp = "";
+ var memPos = -1;
+ var regOps = 0;
+
+ // Check if this instruction signature has a memory operand of explicit size.
+ for (i = 0; i < len; i++) {
+ const aOp = aInst[i];
+ const mem = ObjectUtils.findKey(aOp.flags, MemOp);
+
+ if (mem) {
+ // Stop if the memory operand has implicit-size or if there is more than one.
+ if (aOp.flags.mem || memPos >= 0) {
+ memPos = -1;
+ break;
+ }
+ else {
+ memOp = mem;
+ memPos = i;
+ }
+ }
+ else if (ObjectUtils.hasAny(aOp.flags, RegOp)) {
+ // Doesn't consider 'r/m' as we already checked 'm'.
+ regOps |= (1 << i);
+ }
+ }
+
+ if (memPos < 0)
+ continue;
+
+ // Create a `sameSizeSet` set of all instructions having the exact
+ // explicit memory operand at the same position and registers at
+ // positions matching `regOps` bits and `diffSizeSet` having memory
+ // operand of different size, but registers at the same positions.
+ const sameSizeSet = [aInst];
+ const diffSizeSet = [];
+ const diffSizeHash = Object.create(null);
+
+ for (bIndex = 0; bIndex < this.length; bIndex++) {
+ const bInst = this[bIndex];
+ if (aIndex === bIndex || len !== bInst.length) continue;
+
+ var hasMatch = 1;
+ for (i = 0; i < len; i++) {
+ if (i === memPos) continue;
+
+ const reg = ObjectUtils.hasAny(bInst[i].flags, RegOp);
+ if (regOps & (1 << i))
+ hasMatch &= reg;
+ else if (reg)
+ hasMatch = 0;
+ }
+
+ if (hasMatch) {
+ const bOp = bInst[memPos];
+ if (bOp.flags.mem) continue;
+
+ const mem = ObjectUtils.findKey(bOp.flags, MemOp);
+ if (mem === memOp) {
+ sameSizeSet.push(bInst);
+ }
+ else if (mem) {
+ const key = keyOf(bInst, regOps);
+ diffSizeSet.push(bInst);
+ if (!diffSizeHash[key])
+ diffSizeHash[key] = [bInst];
+ else
+ diffSizeHash[key].push(bInst);
+ }
+ }
+ }
+
+ // Two cases.
+ // A) The memory operand has implicit-size if `diffSizeSet` is empty. That
+ // means that the instruction only uses one size for all reg combinations.
+ //
+ // B) The memory operand has implicit-size if `diffSizeSet` contains different
+ // register signatures than `sameSizeSet`.
+ var implicit = true;
+
+ if (!diffSizeSet.length) {
+ // Case A:
+ }
+ else {
+ // Case B: Find collisions in `sameSizeSet` and `diffSizeSet`.
+ for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
+ const bInst = sameSizeSet[bIndex];
+ const key = keyOf(bInst, regOps);
+
+ const diff = diffSizeHash[key];
+ if (diff) {
+ diff.forEach((diffInst) => {
+ if ((bInst.x86 && !diffInst.x86) || (!bInst.x86 && diffInst.x86)) {
+ // If this is X86|ANY instruction and the other is X64, or vice-versa,
+ // 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;
+ }
+ });
+ }
+ }
+ }
+
+ // Patch all instructions to accept implicit-size memory operand.
+ for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
+ const bInst = sameSizeSet[bIndex];
+ if (implicit) {
+ bInst[memPos].flags.mem = true;
+ }
+
+ if (!implicit)
+ DEBUG(`${this.name}: Explicit: ${bInst}`);
+ }
+ }
+ }
+
+ compact() {
+ var didSomething = true;
+ while (didSomething) {
+ didSomething = false;
+ for (var i = 0; i < this.length; i++) {
+ var row = this[i];
+ var j = i + 1;
+ while (j < this.length) {
+ if (row.mergeWith(this[j])) {
+ this.splice(j, 1);
+ didSomething = true;
+ continue;
+ }
+ j++;
+ }
+ }
+ }
+ }
+
+ toString() {
+ return `[${this.join(", ")}]`;
+ }
+}
+
+class InstSignatureTable extends core.Task {
+ constructor() {
+ super("InstSignatureTable");
+ this.maxOpRows = 0;
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ insts.forEach((inst) => {
+ inst.signatures = this.makeSignatures(Filter.noAltForm(inst.dbInsts));
+ this.maxOpRows = Math.max(this.maxOpRows, inst.signatures.length);
+ });
+
+ const iSignatureMap = Object.create(null);
+ const iSignatureArr = [];
+
+ const oSignatureMap = Object.create(null);
+ const oSignatureArr = [];
+
+ // Must be first to be assigned to zero.
+ const oSignatureNone = "ROW(0, 0xFF)";
+ oSignatureMap[oSignatureNone] = [0];
+ oSignatureArr.push(oSignatureNone);
+
+ function findSignaturesIndex(rows) {
+ const len = rows.length;
+ if (!len) return 0;
+
+ const indexes = iSignatureMap[rows[0].data];
+ if (indexes === undefined) return -1;
+
+ for (var i = 0; i < indexes.length; i++) {
+ const index = indexes[i];
+ if (index + len > iSignatureArr.length) continue;
+
+ var ok = true;
+ for (var j = 0; j < len; j++) {
+ if (iSignatureArr[index + j].data !== rows[j].data) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok)
+ return index;
+ }
+
+ return -1;
+ }
+
+ function indexSignatures(signatures) {
+ const result = iSignatureArr.length;
+
+ for (var i = 0; i < signatures.length; i++) {
+ const signature = signatures[i];
+ const idx = iSignatureArr.length;
+
+ if (!hasOwn.call(iSignatureMap, signature.data))
+ iSignatureMap[signature.data] = [];
+
+ iSignatureMap[signature.data].push(idx);
+ iSignatureArr.push(signature);
+ }
+
+ return result;
+ }
+
+ for (var len = this.maxOpRows; len >= 0; len--) {
+ insts.forEach((inst) => {
+ const signatures = inst.signatures;
+ if (signatures.length === len) {
+ const signatureEntries = [];
+ for (var j = 0; j < len; j++) {
+ const signature = signatures[j];
+
+ var signatureEntry = `ROW(${signature.length}, ${signature.x86 ? 1 : 0}, ${signature.x64 ? 1 : 0}, ${signature.implicit}`;
+ var signatureComment = signature.toString();
+
+ var x = 0;
+ while (x < signature.length) {
+ const h = signature[x].toAsmJitOpData();
+ var index = -1;
+ if (!hasOwn.call(oSignatureMap, h)) {
+ index = oSignatureArr.length;
+ oSignatureMap[h] = index;
+ oSignatureArr.push(h);
+ }
+ else {
+ index = oSignatureMap[h];
+ }
+
+ signatureEntry += `, ${String(index).padEnd(3)}`;
+ x++;
+ }
+
+ while (x < 6) {
+ signatureEntry += `, ${String(0).padEnd(3)}`;
+ x++;
+ }
+
+ signatureEntry += `)`;
+ signatureEntries.push({ data: signatureEntry, comment: signatureComment, refs: 0 });
+ }
+
+ var count = signatureEntries.length;
+ var index = findSignaturesIndex(signatureEntries);
+
+ if (index === -1)
+ index = indexSignatures(signatureEntries);
+
+ iSignatureArr[index].refs++;
+ inst.signatureIndex = index;
+ inst.signatureCount = count;
+ }
+ });
+ }
+
+ var s = `#define ROW(count, x86, x64, implicit, o0, o1, o2, o3, o4, o5) \\\n` +
+ ` { count, uint8_t(x86 ? uint8_t(InstDB::Mode::kX86) : uint8_t(0)) | \\\n` +
+ ` (x64 ? uint8_t(InstDB::Mode::kX64) : uint8_t(0)) , \\\n` +
+ ` implicit, \\\n` +
+ ` 0, \\\n` +
+ ` { o0, o1, o2, o3, o4, o5 } \\\n` +
+ ` }\n` +
+ StringUtils.makeCxxArrayWithComment(iSignatureArr, "const InstDB::InstSignature InstDB::_instSignatureTable[]") +
+ `#undef ROW\n` +
+ `\n` +
+ `#define ROW(opFlags, regId) { opFlags, uint8_t(regId) }\n` +
+ `#define F(VAL) uint64_t(InstDB::OpFlags::k##VAL)\n` +
+ StringUtils.makeCxxArray(oSignatureArr, "const InstDB::OpSignature InstDB::_opSignatureTable[]") +
+ `#undef F\n` +
+ `#undef ROW\n`;
+ this.inject("InstSignatureTable", disclaimer(s), oSignatureArr.length * 8 + iSignatureArr.length * 8);
+ }
+
+ 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;
+
+ // NOTE: This changed from having reg|mem merged into creating two signatures
+ // instead. Imagine two instructions in one `dbInsts` array:
+ //
+ // 1. mov reg, reg/mem
+ // 2. mov reg/mem, reg
+ //
+ // If we merge them and then unmerge, we will have 4 signatures, when iterated:
+ //
+ // 1a. mov reg, reg
+ // 1b. mov reg, mem
+ // 2a. mov reg, reg
+ // 2b. mov mem, reg
+ //
+ // So, instead of merging them here, we insert separated signatures and let
+ // the tool merge them in a way that can be easily unmerged at runtime into:
+ //
+ // 1a. mov reg, reg
+ // 1b. mov reg, mem
+ // 2b. mov mem, reg
+ var modrmCount = 1;
+ for (var modrm = 0; modrm < modrmCount; modrm++) {
+ var row = new ISignature(inst.name);
+ row.x86 = (inst.arch === "ANY" || inst.arch === "X86");
+ row.x64 = (inst.arch === "ANY" || inst.arch === "X64");
+
+ for (var j = 0; j < ops.length; j++) {
+ var iop = ops[j];
+
+ var reg = iop.reg;
+ var mem = iop.mem;
+ var imm = iop.imm;
+ var rel = iop.rel;
+
+ // Skip all instructions having implicit `imm` operand of `1`.
+ if (iop.immValue !== null)
+ break;
+
+ // Shorten the number of signatures of 'mov' instruction.
+ if (inst.name === "mov" && mem.startsWith("moff"))
+ break;
+
+ if (reg === "seg") reg = "sreg";
+ if (reg === "st(i)") reg = "st";
+ if (reg === "st(0)") reg = "st0";
+
+ if (mem === "moff8") mem = "m8";
+ if (mem === "moff16") mem = "m16";
+ if (mem === "moff32") mem = "m32";
+ if (mem === "moff64") mem = "m64";
+
+ if (mem === "m32fp") mem = "m32";
+ if (mem === "m64fp") mem = "m64";
+ if (mem === "m80fp") mem = "m80";
+ if (mem === "m80bcd") mem = "m80";
+ if (mem === "m80dec") mem = "m80";
+ if (mem === "m16int") mem = "m16";
+ if (mem === "m32int") mem = "m32";
+ if (mem === "m64int") mem = "m64";
+
+ if (mem === "m16_16") mem = "m32";
+ if (mem === "m16_32") mem = "m48";
+ if (mem === "m16_64") mem = "m80";
+
+ if (reg && mem) {
+ if (modrmCount === 1) {
+ mem = null;
+ modrmCount++;
+ }
+ else {
+ reg = null;
+ }
+ }
+
+ const op = new OSignature();
+ if (iop.implicit) {
+ row.implicit++;
+ op.flags.implicit = true;
+ }
+
+ const seg = iop.memSegment;
+ if (seg) {
+ switch (inst.name) {
+ case "insb": op.flags.m8 = true; break;
+ case "insw": op.flags.m16 = true; break;
+ case "insd": op.flags.m32 = true; break;
+ case "outsb": op.flags.m8 = true; break;
+ case "outsw": op.flags.m16 = true; break;
+ case "outsd": op.flags.m32 = true; break;
+ case "clzero": op.flags.mem = true; op.flags.m512 = true; break;
+ case "enqcmd": op.flags.mem = true; op.flags.m512 = true; break;
+ case "enqcmds": op.flags.mem = true; op.flags.m512 = true; break;
+ case "movdir64b": op.flags.mem = true; op.flags.m512 = true; break;
+ case "maskmovq": op.flags.mem = true; op.flags.m64 = true; break;
+ case "maskmovdqu": op.flags.mem = true; op.flags.m128 = true; break;
+ case "vmaskmovdqu": op.flags.mem = true; op.flags.m128 = true; break;
+ case "monitor": op.flags.mem = true; break;
+ case "monitorx": op.flags.mem = true; break;
+ case "umonitor": op.flags.mem = true; break;
+ 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; }
+ if (reg === "r32") { op.flags.memBase = true; }
+ if (reg === "r64") { op.flags.memBase = true; }
+ if (reg === "zax") { op.flags.memBase = true; op.flags.memZAX = true; }
+ if (reg === "zsi") { op.flags.memBase = true; op.flags.memZSI = true; }
+ if (reg === "zdi") { op.flags.memBase = true; op.flags.memZDI = true; }
+ }
+ else if (reg) {
+ if (reg == "r8") {
+ op.flags["r8lo"] = 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 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 (/^(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;
+ }
+
+ row.push(op);
+ }
+
+ // Not equal if we terminated the loop.
+ if (j === ops.length)
+ signatures.push(row);
+ }
+ }
+
+ if (signatures.length && GenUtils.canUseImplicitMemSize(instName))
+ signatures.calcImplicitMemSize(instName);
+
+ signatures.compact();
+ return signatures;
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.AdditionalInfoTable]
+// ============================================================================
+
+class AdditionalInfoTable extends core.Task {
+ constructor() {
+ super("AdditionalInfoTable");
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+ const rwInfoTable = new IndexedArray();
+ const instFlagsTable = new IndexedArray();
+ const additionaInfoTable = new IndexedArray();
+
+ // If the instruction doesn't read any flags it should point to the first index.
+ rwInfoTable.addIndexed(`{ 0, 0 }`);
+
+ insts.forEach((inst) => {
+ const dbInsts = inst.dbInsts;
+
+ var features = GenUtils.cpuFeaturesOf(dbInsts).map(function(f) { return `EXT(${f})`; }).join(", ");
+ if (!features) features = "0";
+
+ var [r, w] = this.rwFlagsOf(dbInsts);
+ const rData = r.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0";
+ const wData = w.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0";
+ const instFlags = Object.create(null);
+
+ switch (inst.name) {
+ case "kmovb":
+ case "kmovd":
+ case "kmovq":
+ case "kmovw":
+ case "mov":
+ case "movq":
+ case "movsd":
+ case "movss":
+ case "movapd":
+ case "movaps":
+ case "movdqa":
+ case "movdqu":
+ case "movupd":
+ case "movups":
+ case "vmovapd":
+ case "vmovaps":
+ case "vmovdqa":
+ case "vmovdqa8":
+ case "vmovdqa16":
+ case "vmovdqa32":
+ case "vmovdqa64":
+ case "vmovdqu":
+ case "vmovdqu8":
+ case "vmovdqu16":
+ case "vmovdqu32":
+ case "vmovdqu64":
+ case "vmovq":
+ case "vmovsd":
+ case "vmovss":
+ case "vmovupd":
+ case "vmovups":
+ instFlags["MovOp"] = true;
+ break;
+ }
+
+ 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} } }`);
+ });
+
+ var s = `#define EXT(VAL) uint32_t(CpuFeatures::X86::k##VAL)\n` +
+ `const InstDB::AdditionalInfo InstDB::_additionalInfoTable[] = {\n${StringUtils.format(additionaInfoTable, kIndent, true)}\n};\n` +
+ `#undef EXT\n` +
+ `\n` +
+ `#define FLAG(VAL) uint32_t(CpuRWFlags::kX86_##VAL)\n` +
+ `const InstDB::RWFlagsInfoTable InstDB::_rwFlagsInfoTable[] = {\n${StringUtils.format(rwInfoTable, kIndent, true)}\n};\n` +
+ `#undef FLAG\n` +
+ `\n` +
+ `#define FLAG(VAL) uint32_t(InstRWFlags::k##VAL)\n` +
+ `const InstRWFlags InstDB::_instFlagsTable[] = {\n${StringUtils.format(instFlagsTable, kIndent, true)}\n};\n` +
+ `#undef FLAG\n`;
+ this.inject("AdditionalInfoTable", disclaimer(s), additionaInfoTable.length * 8 + rwInfoTable.length * 8 + instFlagsTable.length * 4);
+ }
+
+ rwFlagsOf(dbInsts) {
+ const r = Object.create(null);
+ const w = Object.create(null);
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+
+ // Omit special cases, this is handled well in C++ code.
+ if (dbInst.name === "mov")
+ continue;
+
+ 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,
+ // a special case instruction analyzer must deal with.
+ if (dbInst.name === "mov")
+ continue;
+
+ for (var reg in regs) {
+ var flag = "";
+ 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 (regs[reg]) {
+ case "R":
+ r[flag] = true;
+ break;
+ case "X":
+ r[flag] = true;
+ // ... fallthrough ...
+ case "W":
+ case "U":
+ case "0":
+ case "1":
+ w[flag] = true;
+ break;
+ }
+ }
+ }
+
+ return [ArrayUtils.sorted(r), ArrayUtils.sorted(w)];
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.InstRWInfoTable]
+// ============================================================================
+
+const NOT_MEM_AMBIGUOUS = ArrayUtils.toDict([
+ "call", "movq"
+]);
+
+class InstRWInfoTable extends core.Task {
+ constructor() {
+ super("InstRWInfoTable");
+
+ this.rwInfoIndexA = [];
+ this.rwInfoIndexB = [];
+ this.rwInfoTableA = new IndexedArray();
+ this.rwInfoTableB = new IndexedArray();
+
+ this.rmInfoTable = new IndexedArray();
+ this.opInfoTable = new IndexedArray();
+
+ this.rwCategoryByName = {
+ "imul" : "Imul",
+ "mov" : "Mov",
+ "movabs" : "Movabs",
+ "movhpd" : "Movh64",
+ "movhps" : "Movh64",
+ "punpcklbw" : "Punpcklxx",
+ "punpckldq" : "Punpcklxx",
+ "punpcklwd" : "Punpcklxx",
+ "vmaskmovpd": "Vmaskmov",
+ "vmaskmovps": "Vmaskmov",
+ "vmovddup" : "Vmovddup",
+ "vmovmskpd" : "Vmovmskpd",
+ "vmovmskps" : "Vmovmskps",
+ "vpmaskmovd": "Vmaskmov",
+ "vpmaskmovq": "Vmaskmov"
+ };
+
+ const _ = null;
+ this.rwCategoryByData = {
+ Vmov1_8: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 8}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 16}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov1_4: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov1_2: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width:256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov2_1: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:256},_,_,_,_]
+ ],
+ Vmov4_1: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width:128},_,_,_,_]
+ ],
+ Vmov8_1: [
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 16},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
+ [{access: "W", clc: 0, flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", clc: 0, flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_]
+ ]
+ };
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ const noRmInfo = StringUtils.formatCppStruct(
+ "InstDB::RWInfoRm::kCategory" + "None".padEnd(10),
+ StringUtils.decToHex(0, 2),
+ String(0).padEnd(2),
+ StringUtils.formatCppFlags({}),
+ "0"
+ );
+
+ const noOpInfo = StringUtils.formatCppStruct(
+ "0x0000000000000000u",
+ "0x0000000000000000u",
+ "0xFF",
+ "0",
+ StringUtils.formatCppStruct(0),
+ "OpRWFlags::kNone"
+ );
+
+ this.rmInfoTable.addIndexed(noRmInfo);
+ this.opInfoTable.addIndexed(noOpInfo);
+
+ insts.forEach((inst) => {
+ // Alternate forms would only mess this up, so filter them out.
+ const dbInsts = Filter.noAltForm(inst.dbInsts);
+
+ // The best we can do is to divide instructions that have 2 operands and others.
+ // This gives us the highest chance of preventing special cases (which were not
+ // entirely avoided).
+ const o2Insts = dbInsts.filter((inst) => { return inst.operands.length === 2; });
+ const oxInsts = dbInsts.filter((inst) => { return inst.operands.length !== 2; });
+
+ const rwInfoArray = [this.rwInfo(inst, o2Insts), this.rwInfo(inst, oxInsts)];
+ const rmInfoArray = [this.rmInfo(inst, o2Insts), this.rmInfo(inst, oxInsts)];
+
+ for (var i = 0; i < 2; i++) {
+ const rwInfo = rwInfoArray[i];
+ const rmInfo = rmInfoArray[i];
+
+ const rwOps = rwInfo.rwOps;
+ const rwOpsIndex = [];
+ for (var j = 0; j < rwOps.length; j++) {
+ const op = rwOps[j];
+ if (!op) {
+ rwOpsIndex.push(this.opInfoTable.addIndexed(noOpInfo));
+ continue;
+ }
+
+ const flags = {};
+ const opAcc = op.access;
+
+ if (opAcc === "R") flags.Read = true;
+ if (opAcc === "W") flags.Write = true;
+ if (opAcc === "X") flags.RW = true;
+ ObjectUtils.merge(flags, op.flags);
+
+ const rIndex = opAcc === "X" || opAcc === "R" ? op.index : -1;
+ const rWidth = opAcc === "X" || opAcc === "R" ? op.width : -1;
+ const wIndex = opAcc === "X" || opAcc === "W" ? op.index : -1;
+ const wWidth = opAcc === "X" || opAcc === "W" ? op.width : -1;
+
+ const consecutiveLeadCount = op.clc;
+
+ 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),
+ StringUtils.formatCppStruct(0),
+ StringUtils.formatCppFlags(flags, function(flag) { return "OpRWFlags::k" + flag; }, "OpRWFlags::kNone")
+ );
+
+ rwOpsIndex.push(this.opInfoTable.addIndexed(opData));
+ }
+
+ const rmData = StringUtils.formatCppStruct(
+ "InstDB::RWInfoRm::kCategory" + rmInfo.category.padEnd(10),
+ StringUtils.decToHex(rmInfo.rmIndexes, 2),
+ String(Math.max(rmInfo.memFixed, 0)).padEnd(2),
+ 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"),
+ "InstDB::RWInfoRm::kFlagFeatureIfRMI": Boolean(rmInfo.memExtensionIfRMI)
+ }),
+ rmInfo.memExtension === "None" ? "0" : "uint32_t(CpuFeatures::X86::k" + rmInfo.memExtension + ")"
+ );
+
+ const rwData = StringUtils.formatCppStruct(
+ "InstDB::RWInfo::kCategory" + rwInfo.category.padEnd(10),
+ String(this.rmInfoTable.addIndexed(rmData)).padEnd(2),
+ StringUtils.formatCppStruct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); })))
+ );
+
+ if (i == 0)
+ this.rwInfoIndexA.push(this.rwInfoTableA.addIndexed(rwData));
+ else
+ this.rwInfoIndexB.push(this.rwInfoTableB.addIndexed(rwData));
+ }
+ });
+
+ var s = "";
+ s += "const uint8_t InstDB::rwInfoIndexA[Inst::_kIdCount] = {\n" + StringUtils.format(this.rwInfoIndexA, kIndent, -1) + "\n};\n";
+ s += "\n";
+ s += "const uint8_t InstDB::rwInfoIndexB[Inst::_kIdCount] = {\n" + StringUtils.format(this.rwInfoIndexB, kIndent, -1) + "\n};\n";
+ s += "\n";
+ s += "const InstDB::RWInfo InstDB::rwInfoA[] = {\n" + StringUtils.format(this.rwInfoTableA, kIndent, true) + "\n};\n";
+ s += "\n";
+ s += "const InstDB::RWInfo InstDB::rwInfoB[] = {\n" + StringUtils.format(this.rwInfoTableB, kIndent, true) + "\n};\n";
+ s += "\n";
+ s += "const InstDB::RWInfoOp InstDB::rwInfoOp[] = {\n" + StringUtils.format(this.opInfoTable, kIndent, true) + "\n};\n";
+ s += "\n";
+ s += "const InstDB::RWInfoRm InstDB::rwInfoRm[] = {\n" + StringUtils.format(this.rmInfoTable, kIndent, true) + "\n};\n";
+
+ const size = this.rwInfoIndexA.length +
+ this.rwInfoIndexB.length +
+ this.rwInfoTableA.length * 8 +
+ this.rwInfoTableB.length * 8 +
+ this.rmInfoTable.length * 4 +
+ this.opInfoTable.length * 24;
+
+ this.inject("InstRWInfoTable", disclaimer(s), size);
+ }
+
+ byteMaskFromBitRanges(ranges) {
+ const arr = [];
+ for (var i = 0; i < 64; i++)
+ arr.push(0);
+
+ for (var i = 0; i < ranges.length; i++) {
+ const start = ranges[i].start;
+ const end = ranges[i].end;
+
+ if (start < 0)
+ continue;
+
+ for (var j = start; j <= end; j++) {
+ const bytePos = j >> 3;
+ if (bytePos < 0 || bytePos >= arr.length)
+ FATAL(`Range ${start}:${end} cannot be used to create a byte-mask`);
+ arr[bytePos] = 1;
+ }
+ }
+
+ var s = "0x";
+ for (var i = arr.length - 4; i >= 0; i -= 4) {
+ const value = (arr[i + 3] << 3) | (arr[i + 2] << 2) | (arr[i + 1] << 1) | arr[i];
+ s += value.toString(16).toUpperCase();
+ }
+ return s;
+ }
+
+ // Read/Write Info
+ // ---------------
+
+ rwInfo(asmInst, dbInsts) {
+ const self = this;
+
+ function nullOps() {
+ return [null, null, null, null, null, null];
+ }
+
+ function makeRwFromOp(op) {
+ if (!op.isRegOrMem())
+ return null;
+
+ return {
+ access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?",
+ clc: 0,
+ flags: {},
+ fixed: GenUtils.fixedRegOf(op),
+ index: op.rwxIndex,
+ width: op.rwxWidth
+ };
+ }
+
+ function queryRwGeneric(dbInsts, step) {
+ var rwOps = nullOps();
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (!op.isRegOrMem())
+ continue;
+
+ const opSize = op.isReg() ? op.regSize : op.memSize;
+ var d = {
+ access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?",
+ clc: 0,
+ flags: {},
+ fixed: -1,
+ index: -1,
+ width: -1
+ };
+
+ if (op.consecutiveLeadCount)
+ d.clc = op.consecutiveLeadCount;
+
+ 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;
+
+ if (op.regIndexRel)
+ d.flags.Consecutive = true;
+
+ for (var k in self.rwOpFlagsForInstruction(asmInst.name, j))
+ d.flags[k] = true;
+
+ if ((step === -1 || step === j) || op.rwxIndex !== 0 || op.rwxWidth !== opSize) {
+ d.index = op.rwxIndex;
+ d.width = op.rwxWidth;
+ }
+
+ if (d.fixed !== -1) {
+ if (op.memSegment)
+ d.flags.MemPhysId = true;
+ else
+ d.flags.RegPhysId = true;
+ }
+
+ if (rwOps[j] === null) {
+ rwOps[j] = d;
+ }
+ else {
+ if (!ObjectUtils.equalsExcept(rwOps[j], d, { "fixed": true, "flags": true }))
+ return null;
+
+ if (rwOps[j].fixed === -1)
+ rwOps[j].fixed = d.fixed;
+ ObjectUtils.merge(rwOps[j].flags, d.flags);
+ }
+ }
+ }
+
+ 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) {
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+ const rwOps = nullOps();
+
+ 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 |= ObjectUtils.equals(rwOps, rwOpsArray[j]);
+
+ if (!match)
+ return false;
+ }
+
+ return true;
+ }
+
+ function dumpRwToData(dbInsts) {
+ const out = [];
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+ const rwOps = nullOps();
+
+ for (var j = 0; j < operands.length; j++)
+ rwOps[j] = makeRwFromOp(operands[j])
+
+ if (ArrayUtils.deepIndexOf(out, rwOps) !== -1)
+ continue;
+
+ out.push(rwOps);
+ }
+ return out;
+ }
+
+ // Some instructions are just special...
+ const name = dbInsts.length ? dbInsts[0].name : "";
+ if (name in this.rwCategoryByName)
+ return { category: this.rwCategoryByName[name], rwOps: nullOps() };
+
+ // Generic rules.
+ for (var i = -1; i <= 6; i++) {
+ const rwInfo = queryRwGeneric(dbInsts, i);
+ if (rwInfo)
+ return rwInfo;
+ }
+
+ // Specific rules.
+ for (var k in this.rwCategoryByData)
+ if (queryRwByData(dbInsts, this.rwCategoryByData[k]))
+ return { category: k, rwOps: nullOps() };
+
+ // FATALURE: Missing data to categorize this instruction.
+ if (name) {
+ const items = dumpRwToData(dbInsts)
+ console.log(`RW: ${dbInsts.length ? dbInsts[0].name : ""}:`);
+ items.forEach((item) => {
+ console.log(" " + JSON.stringify(item));
+ });
+ }
+
+ return null;
+ }
+
+ rwOpFlagsForInstruction(instName, opIndex) {
+ const toMap = ArrayUtils.toDict;
+
+ // TODO: We should be able to get this information from asmdb.
+ switch (instName + "@" + opIndex) {
+ case "cmps@0": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "cmps@1": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "movs@0": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "movs@1": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "lods@1": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "stos@0": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "scas@1": return toMap(['MemBaseRW', 'MemBasePostModify']);
+ case "bndstx@0": return toMap(['MemBaseWrite', 'MemIndexWrite']);
+
+ default:
+ return {};
+ }
+ }
+
+ // Reg/Mem Info
+ // ------------
+
+ rmInfo(asmInst, dbInsts) {
+ const info = {
+ category: "None",
+ rmIndexes: this.rmReplaceableIndexes(dbInsts),
+ memFixed: this.rmFixedSize(dbInsts),
+ memAmbiguous: this.rmIsAmbiguous(dbInsts),
+ memConsistent: this.rmIsConsistent(dbInsts),
+ memExtension: this.rmExtension(dbInsts),
+ memExtensionIfRMI: this.rmExtensionIfRMI(dbInsts)
+ };
+
+ if (info.memFixed !== -1)
+ info.category = "Fixed";
+ else if (info.memConsistent)
+ info.category = "Consistent";
+ else if (info.rmIndexes)
+ info.category = this.rmReplaceableCategory(dbInsts);
+
+ return info;
+ }
+
+ rmReplaceableCategory(dbInsts) {
+ var category = null;
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+
+ var rs = -1;
+ var ms = -1;
+
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (op.isMem())
+ ms = op.memSize;
+ else if (op.isReg())
+ rs = Math.max(rs, op.regSize);
+ }
+
+ var c = (rs === -1 ) ? "None" :
+ (ms === -1 ) ? "None" :
+ (ms === rs ) ? "Fixed" :
+ (ms === rs / 2) ? "Half" :
+ (ms === rs / 4) ? "Quarter" :
+ (ms === rs / 8) ? "Eighth" : "Unknown";
+
+ if (category === null)
+ category = c;
+ else if (category !== c) {
+ // Special cases.
+ if (dbInst.name === "mov" || dbInst.name === "vmovddup")
+ return "None";
+
+ if (/^(punpcklbw|punpckldq|punpcklwd)$/.test(dbInst.name))
+ return "None";
+
+ return cxx.Utils.capitalize(dbInst.name);
+ }
+ }
+
+ if (category === "Unknown")
+ console.log(`Instruction '${dbInsts[0].name}' has no RMInfo category.`);
+
+ return category || "Unknown";
+ }
+
+ rmReplaceableIndexes(dbInsts) {
+ function maskOf(inst, fn) {
+ var m = 0;
+ var operands = inst.operands;
+ for (var i = 0; i < operands.length; i++)
+ if (fn(operands[i]))
+ m |= (1 << i);
+ return m;
+ }
+
+ function getRegIndexes(inst) { return maskOf(inst, function(op) { return op.isReg(); }); };
+ function getMemIndexes(inst) { return maskOf(inst, function(op) { return op.isMem(); }); };
+
+ var mask = 0;
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+
+ var mi = getMemIndexes(dbInst);
+ var ri = getRegIndexes(dbInst) & ~mi;
+
+ if (!mi)
+ continue;
+
+ const match = dbInsts.some((inst) => {
+ var ti = getRegIndexes(inst);
+ return ((ri & ti) === ri && (mi & ti) === mi);
+ });
+
+ if (!match)
+ return 0;
+ mask |= mi;
+ }
+
+ return mask;
+ }
+
+ rmFixedSize(insts) {
+ var savedOp = null;
+
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+ const operands = inst.operands;
+
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (op.mem) {
+ if (savedOp && savedOp.mem !== op.mem)
+ return -1;
+ savedOp = op;
+ }
+ }
+ }
+
+ return savedOp ? Math.max(savedOp.memSize, 0) / 8 : -1;
+ }
+
+ rmIsConsistent(insts) {
+ var hasMem = 0;
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+ const operands = inst.operands;
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (op.mem) {
+ hasMem = 1;
+ if (!op.reg)
+ return 0;
+ if (asmdb.x86.Utils.regSize(op.reg) !== op.memSize)
+ return 0;
+ }
+ }
+ }
+ return hasMem;
+ }
+
+ rmIsAmbiguous(dbInsts) {
+ function isAmbiguous(dbInsts) {
+ const memMap = {};
+ const immMap = {};
+
+ for (var i = 0; i < dbInsts.length; i++) {
+ const dbInst = dbInsts[i];
+ const operands = dbInst.operands;
+
+ var memStr = "";
+ var immStr = "";
+ var hasMem = false;
+ var hasImm = false;
+
+ for (var j = 0; j < operands.length; j++) {
+ const op = operands[j];
+ if (j) {
+ memStr += ", ";
+ immStr += ", ";
+ }
+
+ if (op.isImm()) {
+ immStr += "imm";
+ hasImm = true;
+ }
+ else {
+ immStr += op.toString();
+ }
+
+ if (op.mem) {
+ memStr += "m";
+ hasMem = true;
+ }
+ else {
+ memStr += op.isImm() ? "imm" : op.toString();
+ }
+ }
+
+ if (hasImm) {
+ if (immMap[immStr] === true)
+ continue;
+ immMap[immStr] = true;
+ }
+
+ if (hasMem) {
+ if (memMap[memStr] === true)
+ return 1;
+ memMap[memStr] = true;
+ }
+ }
+ return 0;
+ }
+
+ const uniqueInsts = Filter.unique(dbInsts);
+
+ // Special cases.
+ if (!dbInsts.length)
+ return 0;
+
+ if (NOT_MEM_AMBIGUOUS[dbInsts[0].name])
+ return 0;
+
+ return (isAmbiguous(Filter.byArch(uniqueInsts, "X86")) << 0) |
+ (isAmbiguous(Filter.byArch(uniqueInsts, "X64")) << 1) ;
+ }
+
+ rmExtension(dbInsts) {
+ if (!dbInsts.length)
+ return "None";
+
+ const name = dbInsts[0].name;
+ switch (name) {
+ case "pextrw":
+ return "SSE4_1";
+
+ case "vpslld":
+ case "vpsllq":
+ case "vpsrad":
+ case "vpsrld":
+ case "vpsrlq":
+ return "AVX512_F";
+
+ case "vpslldq":
+ case "vpsllw":
+ case "vpsraw":
+ case "vpsrldq":
+ case "vpsrlw":
+ return "AVX512_BW";
+
+ default:
+ return "None";
+ }
+ }
+
+ rmExtensionIfRMI(dbInsts) {
+ if (!dbInsts.length)
+ return 0;
+
+ const name = dbInsts[0].name;
+ return /^(vpslld|vpsllq|vpsrad|vpsrld|vpsrlq|vpslldq|vpsllw|vpsraw|vpsrldq|vpsrlw)$/.test(name);
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.InstCommonTable]
+// ============================================================================
+
+class InstCommonTable extends core.Task {
+ constructor() {
+ super("InstCommonTable", [
+ "IdEnum",
+ "NameTable",
+ "InstSignatureTable",
+ "AdditionalInfoTable",
+ "InstRWInfoTable"
+ ]);
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+ const table = new IndexedArray();
+
+ insts.forEach((inst) => {
+ const commonFlagsArray = inst.flags.filter((flag) => { return !flag.startsWith("Avx512"); });
+ const avx512FlagsArray = inst.flags.filter((flag) => { return flag.startsWith("Avx512"); });
+
+ const commonFlags = commonFlagsArray.map(function(flag) { return `F(${flag })`; }).join("|") || "0";
+ const avx512Flags = avx512FlagsArray.map(function(flag) { return `X(${flag.substr(6)})`; }).join("|") || "0";
+
+ const controlFlow = `CONTROL_FLOW(${inst.controlFlow})`;
+ const singleRegCase = `SAME_REG_HINT(${inst.singleRegCase})`;
+
+ const row = "{ " +
+ String(commonFlags ).padEnd(50) + ", " +
+ String(avx512Flags ).padEnd(30) + ", " +
+ String(inst.signatureIndex).padEnd( 3) + ", " +
+ String(inst.signatureCount).padEnd( 2) + ", " +
+ String(controlFlow ).padEnd(16) + ", " +
+ String(singleRegCase ).padEnd(16) + "}";
+ inst.commonInfoIndex = table.addIndexed(row);
+ });
+
+ var s = `#define F(VAL) uint32_t(InstDB::InstFlags::k##VAL)\n` +
+ `#define X(VAL) uint32_t(InstDB::Avx512Flags::k##VAL)\n` +
+ `#define CONTROL_FLOW(VAL) uint8_t(InstControlFlow::k##VAL)\n` +
+ `#define SAME_REG_HINT(VAL) uint8_t(InstSameRegHint::k##VAL)\n` +
+ `const InstDB::CommonInfo InstDB::_commonInfoTable[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
+ `#undef SAME_REG_HINT\n` +
+ `#undef CONTROL_FLOW\n` +
+ `#undef X\n` +
+ `#undef F\n`;
+ this.inject("InstCommonTable", disclaimer(s), table.length * 8);
+ }
+}
+
+// ============================================================================
+// [Main]
+// ============================================================================
+
+new X86TableGen()
+ .addTask(new IdEnum())
+ .addTask(new NameTable())
+ .addTask(new AltOpcodeTable())
+ .addTask(new InstSignatureTable())
+ .addTask(new AdditionalInfoTable())
+ .addTask(new InstRWInfoTable())
+ .addTask(new InstCommonTable())
+ .run();
diff --git a/3rdparty/asmjit/tools/tablegen-x86.sh b/3rdparty/asmjit/tools/tablegen-x86.sh
new file mode 100755
index 00000000000..40facf326dc
--- /dev/null
+++ b/3rdparty/asmjit/tools/tablegen-x86.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+set -e
+node ./tablegen-x86.js $@
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;
diff --git a/3rdparty/asmjit/tools/tablegen.sh b/3rdparty/asmjit/tools/tablegen.sh
new file mode 100755
index 00000000000..0ef1e580634
--- /dev/null
+++ b/3rdparty/asmjit/tools/tablegen.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+set -e
+node ./tablegen-a32.js $@
+node ./tablegen-a64.js $@
+node ./tablegen-x86.js $@