Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/asmjit/asmjit.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorPetr Kobalicek <kobalicek.petr@gmail.com>2019-07-16 02:24:22 +0300
committerGitHub <noreply@github.com>2019-07-16 02:24:22 +0300
commit5d40561d14f93dc45613bfa03155d1dfb4f5825a (patch)
treebe7f7d88b588e79ec0ea7f10060434c7751237da /tools
parent761130b1d8f32b5d3d612d285664fcfef5258149 (diff)
Refactored register allocator asm Compiler. (#249)
Refactored build system macros (ASMJIT_BUILD_STATIC -> ASMJIT_STATIC) Refactored AVX512 broadcast {1toN} - moved to operand from instruction. Refactored naming - renamed getters to not use get prefix. Refactored code structure - move arch-specific stuff into x86 namespace. Refactored some compiler/arch-specific macros, respect rel/abs option in mov REG, [ADDR]. Refactored StringBuilder (Renamed to String, added small string optimization). Refactored LabelId<->LabelEntry mapping, force label offset to 64-bits on all archs. Renamed Runtime to Target (JitRuntime kept for now). Renamed VirtMemManager to JitAllocator. Renamed VirtMem to JitUtils. Renamed FuncSignatureX to FuncSignatureBuilder. Fixed xchg [mem], rex-lo, refactored RelocEntry. Fixed Logger to always show abs|rel when formatting a memory operand Fixed Logger to prefix HEX numbers with 0x prefix Fixed Support::ctzGeneric to always return uint32_t, T doesn't matter. Fixed LightCall to not save MMX and K registers Fixed CpuInfo constructor to propagate NoInit (#243) Added VAES, AVX512_VBMI2, AVX512_VNNI, and AVX512_BITALG cpu-features and instructions. Added emscripten support (asmjit can be now compiled by emscripten). Added asmjit.natvis for better MSVC experience Added x86::ptr_abs|ptr_rel Added support for multibyte nop r/m (#135) Added support for 32-bit to 64-bit zero-extended addresses, improved validation of memory addresses, and removed wrt address type as this will be reworked Added support for multiple sections, reworked address table support (previously known as trampolines) Added the following x86 modifiers to the x86::Emitter - xacquire(), xrelease(), and k(kreg) Added a possibility to use REP prefix with RET instruction Added a possibility to relocate [rel addr] during relocate() Added a variadic function-call support (Compiler), argument duplication (Compiler), better /dev/shm vs /tmp shared memory handling (VirtMem). Removed imm_u imm_ptr helpers, imm() can now accept any integer and pointer. Changed the default behavior of optimizing instructions to disabled with a possibility to enable that feature through kOptionOptimizedForSize Use default copy construction / assignment to prevent new kind of warnings introduced by GCC 9
Diffstat (limited to 'tools')
-rwxr-xr-xtools/configure-mac-xcode.sh9
-rwxr-xr-xtools/configure-makefiles.sh17
-rwxr-xr-xtools/configure-ninja.sh17
-rwxr-xr-xtools/configure-unix-makefiles-dbg.sh9
-rwxr-xr-xtools/configure-unix-makefiles-rel.sh9
-rw-r--r--tools/configure-vs-x64.bat9
-rw-r--r--tools/configure-vs-x86.bat9
-rw-r--r--tools/configure-win-mingw-dbg.bat9
-rw-r--r--tools/configure-win-mingw-rel.bat9
-rw-r--r--tools/configure-win-vs2010-x64.bat9
-rw-r--r--tools/configure-win-vs2010-x86.bat9
-rw-r--r--tools/configure-win-vs2013-x64.bat9
-rw-r--r--tools/configure-win-vs2013-x86.bat9
-rw-r--r--tools/configure-win-vs2015-x64.bat9
-rw-r--r--tools/configure-win-vs2015-x86.bat9
-rwxr-xr-xtools/configure-xcode.sh9
-rw-r--r--tools/generate-arm.js207
-rw-r--r--tools/generate-base.js537
-rw-r--r--tools/generate-x86.js1829
-rw-r--r--tools/tablegen-x86.js2412
-rw-r--r--tools/tablegen.js917
-rwxr-xr-xtools/tablegen.sh3
22 files changed, 3393 insertions, 2672 deletions
diff --git a/tools/configure-mac-xcode.sh b/tools/configure-mac-xcode.sh
deleted file mode 100755
index 45ad3ef..0000000
--- a/tools/configure-mac-xcode.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-ASMJIT_CURRENT_DIR=`pwd`
-ASMJIT_BUILD_DIR="build_xcode"
-
-mkdir ../${ASMJIT_BUILD_DIR}
-cd ../${ASMJIT_BUILD_DIR}
-cmake .. -G"Xcode" -DASMJIT_BUILD_TEST=1
-cd ${ASMJIT_CURRENT_DIR}
diff --git a/tools/configure-makefiles.sh b/tools/configure-makefiles.sh
new file mode 100755
index 0000000..1c2729a
--- /dev/null
+++ b/tools/configure-makefiles.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+CURRENT_DIR=`pwd`
+BUILD_DIR="build"
+BUILD_OPTIONS="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+echo "** Configuring ${BUILD_DIR}_dbg [Debug Build] **"
+mkdir -p ../${BUILD_DIR}_dbg
+cd ../${BUILD_DIR}_dbg
+eval cmake .. -DCMAKE_BUILD_TYPE=Debug ${BUILD_OPTIONS} -DASMJIT_SANITIZE=1
+cd ${CURRENT_DIR}
+
+echo "** Configuring ${BUILD_DIR}_rel [Release Build] **"
+mkdir -p ../${BUILD_DIR}_rel
+cd ../${BUILD_DIR}_rel
+eval cmake .. -DCMAKE_BUILD_TYPE=Release ${BUILD_OPTIONS}
+cd ${CURRENT_DIR}
diff --git a/tools/configure-ninja.sh b/tools/configure-ninja.sh
new file mode 100755
index 0000000..a3da417
--- /dev/null
+++ b/tools/configure-ninja.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+CURRENT_DIR=`pwd`
+BUILD_DIR="build"
+BUILD_OPTIONS="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_TEST=1"
+
+echo "** Configuring ${BUILD_DIR}_dbg [Debug Build] **"
+mkdir -p ../${BUILD_DIR}_dbg
+cd ../${BUILD_DIR}_dbg
+eval cmake .. -G"Ninja" -DCMAKE_BUILD_TYPE=Debug ${BUILD_OPTIONS} -DASMJIT_SANITIZE=1
+cd ${CURRENT_DIR}
+
+echo "** Configuring ${BUILD_DIR}_rel [Release Build] **"
+mkdir -p ../${BUILD_DIR}_rel
+cd ../${BUILD_DIR}_rel
+eval cmake .. -G"Ninja" -DCMAKE_BUILD_TYPE=Release ${BUILD_OPTIONS}
+cd ${CURRENT_DIR}
diff --git a/tools/configure-unix-makefiles-dbg.sh b/tools/configure-unix-makefiles-dbg.sh
deleted file mode 100755
index f127cf2..0000000
--- a/tools/configure-unix-makefiles-dbg.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-ASMJIT_CURRENT_DIR=`pwd`
-ASMJIT_BUILD_DIR="build_makefiles_dbg"
-
-mkdir ../${ASMJIT_BUILD_DIR}
-cd ../${ASMJIT_BUILD_DIR}
-cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DASMJIT_BUILD_TEST=1
-cd ${ASMJIT_CURRENT_DIR}
diff --git a/tools/configure-unix-makefiles-rel.sh b/tools/configure-unix-makefiles-rel.sh
deleted file mode 100755
index 91e556b..0000000
--- a/tools/configure-unix-makefiles-rel.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-ASMJIT_CURRENT_DIR=`pwd`
-ASMJIT_BUILD_DIR="build_makefiles_rel"
-
-mkdir ../${ASMJIT_BUILD_DIR}
-cd ../${ASMJIT_BUILD_DIR}
-cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DASMJIT_BUILD_TEST=1
-cd ${ASMJIT_CURRENT_DIR}
diff --git a/tools/configure-vs-x64.bat b/tools/configure-vs-x64.bat
new file mode 100644
index 0000000..db4012a
--- /dev/null
+++ b/tools/configure-vs-x64.bat
@@ -0,0 +1,9 @@
+@echo off
+
+set CURRENT_DIR=%CD%
+set BUILD_DIR="build_vs_x64"
+
+mkdir ..\%BUILD_DIR%
+cd ..\%BUILD_DIR%
+cmake .. -G"Visual Studio 16" -A x64 -DASMJIT_TEST=1
+cd %CURRENT_DIR%
diff --git a/tools/configure-vs-x86.bat b/tools/configure-vs-x86.bat
new file mode 100644
index 0000000..7bc7b9d
--- /dev/null
+++ b/tools/configure-vs-x86.bat
@@ -0,0 +1,9 @@
+@echo off
+
+set CURRENT_DIR=%CD%
+set BUILD_DIR="build_vs_x86"
+
+mkdir ..\%BUILD_DIR%
+cd ..\%BUILD_DIR%
+cmake .. -G"Visual Studio 16" -A Win32 -DASMJIT_TEST=1
+cd %CURRENT_DIR%
diff --git a/tools/configure-win-mingw-dbg.bat b/tools/configure-win-mingw-dbg.bat
deleted file mode 100644
index 03024e3..0000000
--- a/tools/configure-win-mingw-dbg.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_mingw_dbg"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-mingw-rel.bat b/tools/configure-win-mingw-rel.bat
deleted file mode 100644
index f311383..0000000
--- a/tools/configure-win-mingw-rel.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_mingw_rel"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2010-x64.bat b/tools/configure-win-vs2010-x64.bat
deleted file mode 100644
index 296f38b..0000000
--- a/tools/configure-win-vs2010-x64.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2010_x64"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 10 Win64" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2010-x86.bat b/tools/configure-win-vs2010-x86.bat
deleted file mode 100644
index f99ffe4..0000000
--- a/tools/configure-win-vs2010-x86.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2010_x86"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 10" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2013-x64.bat b/tools/configure-win-vs2013-x64.bat
deleted file mode 100644
index 03b0999..0000000
--- a/tools/configure-win-vs2013-x64.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2013_x64"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 12 Win64" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2013-x86.bat b/tools/configure-win-vs2013-x86.bat
deleted file mode 100644
index a9c553f..0000000
--- a/tools/configure-win-vs2013-x86.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2013_x86"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 12" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2015-x64.bat b/tools/configure-win-vs2015-x64.bat
deleted file mode 100644
index a3f0726..0000000
--- a/tools/configure-win-vs2015-x64.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2015_x64"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 14 Win64" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-win-vs2015-x86.bat b/tools/configure-win-vs2015-x86.bat
deleted file mode 100644
index 6f6544d..0000000
--- a/tools/configure-win-vs2015-x86.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-
-set ASMJIT_CURRENT_DIR=%CD%
-set ASMJIT_BUILD_DIR="build_vs2015_x86"
-
-mkdir ..\%ASMJIT_BUILD_DIR%
-cd ..\%ASMJIT_BUILD_DIR%
-cmake .. -G"Visual Studio 14" -DASMJIT_BUILD_TEST=1
-cd %ASMJIT_CURRENT_DIR%
diff --git a/tools/configure-xcode.sh b/tools/configure-xcode.sh
new file mode 100755
index 0000000..a52c9ef
--- /dev/null
+++ b/tools/configure-xcode.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+BUILD_DIR="build_xcode"
+CURRENT_DIR=`pwd`
+
+mkdir -p ../${BUILD_DIR}
+cd ../${BUILD_DIR}
+cmake .. -G"Xcode" -DASMJIT_TEST=1
+cd ${CURRENT_DIR}
diff --git a/tools/generate-arm.js b/tools/generate-arm.js
deleted file mode 100644
index 39843b0..0000000
--- a/tools/generate-arm.js
+++ /dev/null
@@ -1,207 +0,0 @@
-// [Generate-ARM]
-//
-// NOTE: This script relies on 'asmdb' package. Either install it by using
-// node.js package manager (npm) or by copying/symlinking the whole asmdb
-// directory as [asmjit]/tools/asmdb.
-"use strict";
-
-const base = require("./generate-base.js");
-const hasOwn = Object.prototype.hasOwnProperty;
-const kIndent = base.kIndent;
-const StringUtils = base.StringUtils;
-
-// ----------------------------------------------------------------------------
-// [ArmDB]
-// ----------------------------------------------------------------------------
-
-// Create the ARM database.
-const arm = base.asmdb.arm;
-const isa = new arm.ISA();
-
-console.log(
- isa.query({
- filter: function(inst) {
- return !!inst.extensions.ASIMD;
- }
- })
-);
-
-//console.log(JSON.stringify(isa.instructionMap, null, 2));
-
-// ----------------------------------------------------------------------------
-// [GenUtils]
-// ----------------------------------------------------------------------------
-
-class GenUtils {
- // Get a list of instructions based on `name` and optional `mode`.
- static instsOf(name, mode) {
- const insts = isa.query(name);
- return !mode ? insts : insts.filter(function(inst) { return inst.arch === mode; });
- }
-
- static archOf(group) {
- var t16Arch = false;
- var t32Arch = false;
- var a32Arch = false;
- var a64Arch = false;
-
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- if (inst.encoding === "T16") t16Arch = true;
- if (inst.encoding === "T32") t32Arch = true;
- if (inst.encoding === "A32") a32Arch = true;
- if (inst.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(group) {
- const exts = Object.create(null);
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- for (var k in inst.extensions)
- exts[k] = true;
- }
- const arr = Object.keys(exts);
- arr.sort();
- return arr;
- }
-}
-
-// ----------------------------------------------------------------------------
-// [ArmGenerator]
-// ----------------------------------------------------------------------------
-
-class ArmGenerator extends base.BaseGenerator {
- constructor() {
- super("Arm");
-
- this.load([
- "src/asmjit/arm/arminst.cpp",
- "src/asmjit/arm/arminst.h"
- ]);
- }
-
- // --------------------------------------------------------------------------
- // [Parse]
- // --------------------------------------------------------------------------
-
- parse() {
- this.addInst({
- id: 0,
- name: "",
- enum: "None"
- });
-
- var names = isa.instructionNames;
- for (var i = 0; i < names.length; i++) {
- const name = names[i];
-
- const enum_ = StringUtils.upFirst(name);
- const insts = GenUtils.instsOf(names[i]);
-
- this.addInst({
- id : 0, // Instruction id (numeric value).
- name : name, // Instruction name.
- enum : enum_, // Instruction enum without `kId` prefix.
-
- familyType : "kFamilyNone", // Family type.
- familyIndex : 0, // Index to a family-specific data.
-
- nameIndex : -1, // Instruction name-index.
- commonIndex : -1
- });
- }
-
- console.log("Number of Instructions: " + this.instArray.length);
- }
-
- // --------------------------------------------------------------------------
- // [Generate]
- // --------------------------------------------------------------------------
-
- generate() {
- // Order doesn't matter here.
- this.generateIdData();
- this.generateNameData();
-
- // These must be last, and order matters.
- this.generateCommonData();
- this.generateInstData();
-
- return this;
- }
-
- // --------------------------------------------------------------------------
- // [Generate - CommonData]
- // --------------------------------------------------------------------------
-
- generateCommonData() {
- const table = new base.IndexedArray();
- for (var i = 0; i < this.instArray.length; i++) {
- const inst = this.instArray[i];
-
- const item = "{ " + StringUtils.padLeft("0", 1) + "}";
- inst.commonIndex = table.addIndexed(item);
- }
-
- var s = `const ArmInst::CommonData ArmInstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n`;
- return this.inject("commonData", StringUtils.disclaimer(s), table.length * 12);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - InstData]
- // --------------------------------------------------------------------------
-
- generateInstData() {
- var s = StringUtils.format(this.instArray, "", false, function(inst) {
- return "INST(" +
- StringUtils.padLeft(inst.enum , 16) + ", " +
- StringUtils.padLeft(inst.encoding , 23) + ", " +
- StringUtils.padLeft(inst.opcode0 , 26) + ", " +
- StringUtils.padLeft(inst.nameIndex , 4) + ", " +
- StringUtils.padLeft(inst.commonIndex, 3) + ")";
- }) + "\n";
- return this.inject("instData", s, this.instArray.length * 12);
- }
-
- // --------------------------------------------------------------------------
- // [Reimplement]
- // --------------------------------------------------------------------------
-
- getCommentOf(name) {
- var insts = GenUtils.instsOf(name);
- if (!insts) return "";
-
- var features = GenUtils.featuresOf(insts);
- var comment = GenUtils.archOf(insts);
-
- if (features.length)
- comment += " {" + features.join("|") + "}";
-
- return comment;
- }
-}
-
-// ----------------------------------------------------------------------------
-// [Main]
-// ----------------------------------------------------------------------------
-
-function main() {
- const gen = new ArmGenerator();
-
- gen.parse();
- gen.generate();
- gen.dumpTableSizes();
- gen.save();
-}
-main();
diff --git a/tools/generate-base.js b/tools/generate-base.js
deleted file mode 100644
index d323b59..0000000
--- a/tools/generate-base.js
+++ /dev/null
@@ -1,537 +0,0 @@
-// [Generate-Base]
-"use strict";
-
-const fs = require("fs");
-const hasOwn = Object.prototype.hasOwnProperty;
-
-// ----------------------------------------------------------------------------
-// [asmdb]
-// ----------------------------------------------------------------------------
-
-const asmdb = (function() {
- try {
- // Prefer a local copy of 'asmdb' package if possible.
- return require("./asmdb");
- }
- catch (ex) {
- // Report a possible problem within a local asmdb.
- if (ex.code !== "MODULE_NOT_FOUND")
- throw ex;
-
- // Okay, so global then...
- return require("asmdb");
- }
-})();
-exports.asmdb = asmdb;
-
-// ----------------------------------------------------------------------------
-// [Constants]
-// ----------------------------------------------------------------------------
-
-const kIndent = " ";
-const kJustify = 79;
-const kAsmJitRoot = "..";
-
-exports.kIndent = kIndent;
-exports.kJustify = kJustify;
-exports.kAsmJitRoot = kAsmJitRoot;
-
-// ----------------------------------------------------------------------------
-// [StringUtils]
-// ----------------------------------------------------------------------------
-
-function asString(x) { return String(x); }
-
-class StringUtils {
- static trimLeft(s) {
- return s.replace(/^\s+/, "");
- }
-
- static padLeft(s, n, x) {
- if (!x) x = " ";
-
- s = String(s);
- if (s.length < n)
- s += x.repeat(n - s.length);
-
- return s;
- }
-
- static upFirst(s) {
- if (!s) return "";
- return s[0].toUpperCase() + s.substr(1);
- }
-
- static decToHex(n, nPad) {
- var hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);
- while (nPad > hex.length)
- hex = "0" + hex;
- return "0x" + hex.toUpperCase();
- }
-
- static format(array, indent, showIndex, mapFn) {
- if (!mapFn)
- mapFn = asString;
-
- var s = "";
- const commentSize = showIndex ? String(array.length).length : 0;
-
- for (var i = 0; i < array.length; i++) {
- const last = i === array.length - 1;
- s += `${indent}${mapFn(array[i])}`;
-
- if (commentSize)
- s += `${last ? " " : ","} // #${i}`;
- else if (!last)
- s += ",";
-
- if (!last) s += "\n";
- }
-
- return s;
- }
-
- static makeCxxArray(array, code, indent) {
- if (!indent) indent = kIndent;
- return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;
- }
-
- static makeCxxArrayWithComment(array, code, indent) {
- if (!indent) indent = kIndent;
- var s = "";
- for (var i = 0; i < array.length; i++) {
- const last = i === array.length - 1;
- s += indent + array[i].data +
- (last ? " // " : ", // ") + StringUtils.padLeft(array[i].refs ? "#" + String(i) : "", 5) + array[i].comment + "\n";
- }
- return `${code} = {\n${s}};\n`;
- }
-
- static disclaimer(s) {
- return "// ------------------- Automatically generated, do not edit -------------------\n" +
- s +
- "// ----------------------------------------------------------------------------\n";
- }
-
- static indent(s, indentation) {
- var lines = s.split(/\r?\n/g);
- if (indentation) {
- for (var i = 0; i < lines.length; i++) {
- var line = lines[i];
- if (line) lines[i] = indentation + line;
- }
- }
-
- return lines.join("\n");
- }
-
- static inject(s, start, end, code) {
- var iStart = s.indexOf(start);
- var iEnd = s.indexOf(end);
-
- if (iStart === -1)
- throw new Error(`Utils.inject(): Couldn't locate start mark '${start}'`);
-
- if (iEnd === -1)
- throw new Error(`Utils.inject(): Couldn't locate end mark '${end}'`);
-
- var nIndent = 0;
- while (iStart > 0 && s[iStart-1] === " ") {
- iStart--;
- nIndent++;
- }
-
- if (nIndent) {
- const indentation = " ".repeat(nIndent);
- code = StringUtils.indent(code, indentation) + indentation;
- }
-
- return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);
- }
-}
-exports.StringUtils = StringUtils;
-
-// ----------------------------------------------------------------------------
-// [MapUtils]
-// ----------------------------------------------------------------------------
-
-class MapUtils {
- static arrayToMap(arr, value) {
- if (value === undefined)
- value = true;
-
- const map = Object.create(null);
- for (var i = 0; i < arr.length; i++)
- map[arr[i]] = value;
- return map;
- }
-
- static equals(a, b) {
- for (var k in a) if (!hasOwn.call(b, k)) return false;
- for (var k in b) if (!hasOwn.call(a, k)) return false;
-
- return true;
- }
-
- static firstOf(map, flags) {
- for (var k in flags)
- if (hasOwn.call(map, k))
- return k;
- return undefined;
- }
-
- static anyOf(map, flags) {
- for (var k in flags)
- if (hasOwn.call(map, k))
- return true;
- return false;
- }
-
- static add(a, b) {
- for (var k in b)
- a[k] = b[k];
- return a;
- }
-
- static and(a, b) {
- const out = Object.create(null);
- for (var k in a)
- if (hasOwn.call(b, k))
- out[k] = true;
- return out;
- }
-
- static xor(a, b) {
- const out = Object.create(null);
- for (var k in a) if (!hasOwn.call(b, k)) out[k] = true;
- for (var k in b) if (!hasOwn.call(a, k)) out[k] = true;
- return out;
- }
-};
-exports.MapUtils = MapUtils;
-
-// ----------------------------------------------------------------------------
-// [IndexedArray]
-// ----------------------------------------------------------------------------
-
-class IndexedArray extends Array {
- constructor() {
- super();
- this._index = Object.create(null);
- }
-
- addIndexed(element) {
- const key = typeof element === "string" ? element : JSON.stringify(element);
- var idx = this._index[key];
-
- if (idx !== undefined)
- return idx;
-
- idx = this.length;
- this._index[key] = idx;
- this.push(element);
- return idx;
- }
-}
-exports.IndexedArray = IndexedArray;
-
-// ----------------------------------------------------------------------------
-// [IndexedString]
-// ----------------------------------------------------------------------------
-
-class IndexedString {
- constructor() {
- this.map = Object.create(null);
- this.array = [];
- this.size = -1;
- }
-
- add(s) {
- this.map[s] = -1;
- }
-
- index() {
- const map = this.map;
- const array = this.array;
- const partialMap = Object.create(null);
-
- var k, kp;
- var i, len;
-
- // Create a map that will contain all keys and partial keys.
- for (k in map) {
- if (!k) {
- partialMap[k] = k;
- }
- else {
- for (i = 0, len = k.length; i < len; i++) {
- kp = k.substr(i);
- if (!hasOwn.call(partialMap, kp) || partialMap[kp].length < len)
- partialMap[kp] = k;
- }
- }
- }
-
- // Create an array that will only contain keys that are needed.
- for (k in map)
- if (partialMap[k] === k)
- array.push(k);
- array.sort();
-
- // Create valid offsets to the `array`.
- var offMap = Object.create(null);
- var offset = 0;
-
- for (i = 0, len = array.length; i < len; i++) {
- k = array[i];
-
- offMap[k] = offset;
- offset += k.length + 1;
- }
- this.size = offset;
-
- // Assign valid offsets to `map`.
- for (kp in map) {
- k = partialMap[kp];
- map[kp] = offMap[k] + k.length - kp.length;
- }
- }
-
- format(indent, justify) {
- if (this.size === -1)
- throw new Error(`IndexedString.format(): not indexed yet, call index()`);
-
- const array = this.array;
- if (!justify) justify = 0;
-
- var i;
- var s = "";
- var line = "";
-
- for (i = 0; i < array.length; i++) {
- const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");
- const newl = line + (line ? " " : indent) + item;
-
- if (newl.length <= justify) {
- line = newl;
- continue;
- }
- else {
- s += line + "\n";
- line = indent + item;
- }
- }
-
- return s + line;
- }
-
- getSize() {
- if (this.size === -1)
- throw new Error(`IndexedString.getSize(): Not indexed yet, call index()`);
- return this.size;
- }
-
- getIndex(k) {
- if (this.size === -1)
- throw new Error(`IndexedString.getIndex(): Not indexed yet, call index()`);
-
- if (!hasOwn.call(this.map, k))
- throw new Error(`IndexedString.getIndex(): Key '${k}' not found.`);
-
- return this.map[k];
- }
-}
-exports.IndexedString = IndexedString;
-
-// ----------------------------------------------------------------------------
-// [BaseGenerator]
-// ----------------------------------------------------------------------------
-
-class BaseGenerator {
- constructor(arch) {
- this.arch = arch;
-
- this.instMap = Object.create(null);
- this.instArray = [];
-
- this.files = Object.create(null);
- this.tableSizes = Object.create(null);
- }
-
- // --- File management ---
- load(fileList) {
- for (var i = 0; i < fileList.length; i++) {
- const file = fileList[i];
- const path = kAsmJitRoot + "/" + file;
- const data = fs.readFileSync(path, "utf8").replace(/\r\n/g, "\n");
-
- this.files[file] = {
- prev: data,
- data: data
- };
- }
- return this;
- }
-
- save() {
- for (var file in this.files) {
- const obj = this.files[file];
- if (obj.data !== obj.prev) {
- const path = kAsmJitRoot + "/" + file;
- console.log(`MODIFIED '${file}'`);
-
- fs.writeFileSync(path + ".backup", obj.prev, "utf8");
- fs.writeFileSync(path, obj.data, "utf8");
- }
- }
- }
-
- dataOf(file) {
- const obj = this.files[file];
- if (!obj)
- throw new Error(`BaseGenerator.getData(): File ${file} not loaded`);
- return obj.data;
- }
-
- // --- Instruction management ---
- addInst(inst) {
- inst.id = this.instArray.length;
-
- this.instMap[inst.name] = inst;
- this.instArray.push(inst);
-
- return this;
- }
-
- // --- Code Injection ---
- 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)
- throw new Error(`Generator.inject(): Cannot find '${key}'`);
-
- if (size)
- this.tableSizes[key] = size;
-
- return this;
- }
-
- // --- Independent Generators ---
- generateIdData() {
- const instArray = this.instArray;
-
- var s = "";
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
-
- var line = "kId" + inst.enum + (i ? "" : " = 0") + ",";
- var comment = this.getCommentOf(inst.name);
-
- if (comment)
- line = StringUtils.padLeft(line, 37) + "// " + comment;
-
- s += line + "\n";
- }
- s += "_kIdCount\n";
-
- return this.inject("idData", s);
- }
-
- generateNameData() {
- const arch = this.arch;
- const none = `${arch}Inst::kIdNone`;
-
- const instArray = this.instArray;
- const instNames = new IndexedString();
-
- const instFirst = new Array(26);
- const instLast = new Array(26);
-
- var maxLength = 0;
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- instNames.add(inst.name);
- maxLength = Math.max(maxLength, inst.name.length);
- }
- instNames.index();
-
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- const name = inst.name;
- const nameIndex = instNames.getIndex(name);
-
- const index = name.charCodeAt(0) - 'a'.charCodeAt(0);
- if (index < 0 || index >= 26)
- throw new Error(`BaseGenerator.generateNameData(): Invalid lookup character '${name[0]}' of '${name}'`);
-
- inst.nameIndex = nameIndex;
- if (instFirst[index] === undefined)
- instFirst[index] = `${arch}Inst::kId${inst.enum}`;
- instLast[index] = `${arch}Inst::kId${inst.enum}`;
- }
-
- var s = "";
- s += `const char ${arch}InstDB::nameData[] =\n${instNames.format(kIndent, kJustify)}\n`;
- s += `\n`;
-
- s += `enum {\n`;
- s += ` k${arch}InstMaxLength = ${maxLength}\n`;
- s += `};\n`;
- s += `\n`;
-
- s += `struct InstNameAZ {\n`;
- s += ` uint16_t start;\n`;
- s += ` uint16_t end;\n`;
- s += `};\n`;
- s += `\n`;
-
- s += `static const InstNameAZ ${arch}InstNameAZ[26] = {\n`;
- for (var i = 0; i < instFirst.length; i++) {
- const firstId = instFirst[i] || none;
- const lastId = instLast[i] || none;
-
- s += ` { ${StringUtils.padLeft(firstId, 22)}, ${StringUtils.padLeft(lastId , 22)} + 1 }`;
- if (i !== 26 - 1)
- s += `,`;
- s += `\n`;
- }
- s += `};\n`;
-
- return this.inject("nameData", StringUtils.disclaimer(s), instNames.getSize() + 26 * 4);
- }
-
- // --- Reimplement ---
- getCommentOf(name) {
- throw new Error("BaseGenerator.getCommentOf(): Must be reimplemented");
- }
-
- // --- Miscellaneous ---
- dumpTableSizes() {
- const sizes = this.tableSizes;
-
- var pad = 24;
- var total = 0;
-
- for (var name in sizes) {
- const size = sizes[name];
- total += size;
- console.log(StringUtils.padLeft('Size of ' + name, pad) + ": " + size);
- }
-
- console.log(StringUtils.padLeft('Size of all tables', pad) + ": " + total);
- }
-}
-exports.BaseGenerator = BaseGenerator;
diff --git a/tools/generate-x86.js b/tools/generate-x86.js
deleted file mode 100644
index 976256c..0000000
--- a/tools/generate-x86.js
+++ /dev/null
@@ -1,1829 +0,0 @@
-// [Generate-X86]
-//
-// The purpose of this script is to fetch all instructions' names into a single
-// string and to optimize common patterns that appear in instruction data. It
-// prevents relocation of small strings (instruction names) that has to be done
-// by a linker to make all pointers the binary application/library uses valid.
-// This approach decreases the final size of AsmJit binary and relocation data.
-//
-// NOTE: This script relies on 'asmdb' package. Either install it by using
-// node.js package manager (npm) or by copying/symlinking the whole asmdb
-// directory as [asmjit]/tools/asmdb.
-"use strict";
-
-const base = require("./generate-base.js");
-const hasOwn = Object.prototype.hasOwnProperty;
-const kIndent = base.kIndent;
-const MapUtils = base.MapUtils;
-const StringUtils = base.StringUtils;
-
-const DEBUG = false;
-
-// ----------------------------------------------------------------------------
-// [X86DB]
-// ----------------------------------------------------------------------------
-
-// Create the X86 database and add some special cases recognized by AsmJit.
-const x86 = base.asmdb.x86;
-const isa = new x86.ISA({
- instructions: [
- // Imul in [reg, imm] form is encoded as [reg, reg, imm].
- ["imul" , "r16, ib" , "RM" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul" , "r32, ib" , "RM" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul" , "r64, ib" , "RM" , "REX.W 6B /r ib" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul" , "r16, iw" , "RM" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul" , "r32, id" , "RM" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul" , "r64, id" , "RM" , "REX.W 69 /r id" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"]
- ]
-});
-
-// Remapped instructions contain mapping between instructions that AsmJit expects
-// and instructions provided by asmdb. In general, AsmJit uses `cmps` instructions
-// without the suffix, so we just remap these and keep all others.
-const RemappedInsts = {
- __proto__: null,
-
- "cmpsd": { names: ["cmpsd"] , rep: false },
- "movsd": { names: ["movsd"] , rep: false },
- "cmps" : { names: ["cmpsb", "cmpsw", "cmpsd", "cmpsq"], rep: true },
- "movs" : { names: ["movsb", "movsw", "movsd", "movsq"], rep: true },
- "lods" : { names: ["lodsb", "lodsw", "lodsd", "lodsq"], rep: null },
- "scas" : { names: ["scasb", "scasw", "scasd", "scasq"], rep: null },
- "stos" : { names: ["stosb", "stosw", "stosd", "stosq"], rep: null },
- "ins" : { names: ["insb" , "insw" , "insd" ] , rep: null },
- "outs" : { names: ["outsb", "outsw", "outsd"] , rep: null }
-};
-
-// Map of instructions that can use fixed registers, but are also encodable
-// by using any others. This is to simplify some decisions about instruction
-// flags as we don't want to see `FixedReg` in `adc` instruction, for example.
-const NotFixedInsts = MapUtils.arrayToMap([
- "adc", "add", "and", "cmp", "mov", "or", "sbb", "sub", "test", "xchg", "xor"
-]);
-
-// ----------------------------------------------------------------------------
-// [GenUtils]
-// ----------------------------------------------------------------------------
-
-class GenUtils {
- // Get group of instructions having the same name as understood by AsmJit.
- static groupOf(name) {
- const remapped = RemappedInsts[name];
- if (!remapped) return isa.query(name);
-
- const insts = isa.query(remapped.names);
- const rep = remapped.rep;
- if (rep === null) return insts;
-
- return insts.filter(function(inst) {
- return rep === !!(inst.attributes.REP || inst.attributes.REPNZ);
- });
- }
-
- static hasFixedReg(group) {
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- if (NotFixedInsts[inst.name]) continue;
-
- const operands = inst.operands;
- for (var j = 0; j < operands.length; j++)
- if (operands[j].isFixedReg())
- return true;
- }
-
- return false;
- }
-
- static hasFixedMem(group) {
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- if (NotFixedInsts[inst.name]) continue;
-
- const operands = inst.operands;
- for (var j = 0; j < operands.length; j++)
- if (operands[j].isFixedMem())
- return true;
- }
-
- return false;
- }
-
- static cpuArchOf(group) {
- var anyArch = false;
- var x86Arch = false;
- var x64Arch = false;
-
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- if (inst.arch === "ANY") anyArch = true;
- if (inst.arch === "X86") x86Arch = true;
- if (inst.arch === "X64") x64Arch = true;
- }
-
- return anyArch || (x86Arch && x64Arch) ? "[ANY]" : x86Arch ? "[X86]" : "[X64]";
- }
-
- static cpuFeaturesOf(group) {
- const features = Object.create(null);
-
- for (var i = 0; i < group.length; i++)
- for (var feature in group[i].extensions)
- features[feature] = true;
-
- const result = Object.getOwnPropertyNames(features);
- result.sort();
- return result;
- }
-
- static specialsOf(group) {
- const r = Object.create(null);
- const w = Object.create(null);
-
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- const specialRegs = inst.specialRegs;
-
- // 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 (inst.name === "mov")
- continue;
-
- for (var specialReg in specialRegs) {
- const group = isa.specialRegs[specialReg].group;
- const op = specialRegs[specialReg];
-
- switch (op) {
- case "R":
- r[group] = true;
- break;
- case "X":
- r[group] = true;
- // .. fallthrough ...
- case "W":
- case "U":
- case "0":
- case "1":
- w[group] = true;
- break;
- }
- }
- }
-
- const rArray = Object.getOwnPropertyNames(r);
- const wArray = Object.getOwnPropertyNames(w);
-
- rArray.sort();
- wArray.sort();
-
- return [rArray, wArray];
- }
-
- static flagsOf(group) {
- function getAccess(inst) {
- const operands = inst.operands;
- if (!operands.length) return "";
-
- if (inst.name === "xchg" || inst.name === "xadd")
- return "UseXX";
-
- const op = operands[0];
- if (!op.isRegOrMem())
- return "";
- else if (op.read && op.write)
- return "UseX";
- else
- return op.read ? "UseR" :"UseW";
- }
-
- function replace(map, a, b, c) {
- if (map[a] && map[b]) {
- delete map[a];
- delete map[b];
- map[c] = true;
- }
- }
-
- const f = Object.create(null);
- var i, j;
-
- var mib = group.length > 0 && /^(?:bndldx|bndstx)$/.test(group[0].name);
- var access = "";
- var ambiguous = false;
-
- for (i = 0; i < group.length; i++) {
- const inst = group[i];
- const name = inst.name;
-
- const acc = getAccess(inst);
- if (!access)
- access = acc;
- else if (access !== acc)
- ambiguous = true;
- }
-
- // Default to "RO" if there is no access information nor operands.
- if (!access) access = "UseR";
- if (ambiguous) access = "UseA";
- if (access) {
- if (access === "UseXX")
- f.UseX = true;
- f[access] = true;
- }
-
- if (mib) f.Mib = true;
-
- const fixedReg = GenUtils.hasFixedReg(group);
- const fixedMem = GenUtils.hasFixedMem(group);
-
- if (fixedReg && fixedMem)
- f.FixedRM = true;
- else if (fixedReg)
- f.FixedReg = true;
- else if (fixedMem)
- f.FixedMem = true;
-
- var mmx = false;
- var vec = false;
-
- for (i = 0; i < group.length; i++) {
- const inst = group[i];
- const operands = inst.operands;
-
- if (inst.name === "emms")
- mmx = true;
-
- if (inst.name === "vzeroall" || inst.name === "vzeroupper")
- vec = true;
-
- for (j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (op.reg === "mm")
- mmx = true;
- else if (/^(k|xmm|ymm|zmm)$/.test(op.reg)) {
- vec = true;
- }
- }
- }
-
- if (mmx) f.Mmx = true;
- if (vec) f.Vec = true;
-
- for (i = 0; i < group.length; i++) {
- const inst = group[i];
- const name = inst.name;
- const operands = inst.operands;
-
- if (inst.attributes.LOCK ) f.Lock = true;
- if (inst.attributes.REP ) f.Rep = true;
- if (inst.attributes.REPNZ ) f.Repnz = true;
- if (inst.attributes.XACQUIRE) f.XAcquire = true;
- if (inst.attributes.XRELEASE) f.XRelease = true;
-
- if (inst.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 (inst.vsibReg)
- f.Vsib = true;
-
- if (inst.prefix === "VEX" || inst.prefix === "XOP")
- f.Vex = true;
-
- if (inst.prefix === "EVEX") {
- f.Evex = true;
-
- if (inst.kmask) f.Avx512K = true;
- if (inst.zmask) f.Avx512Z = true;
-
- if (inst.er) f.Avx512ER = true;
- if (inst.sae) f.Avx512SAE = true;
-
- if (inst.broadcast) f["Avx512B" + String(inst.elementSize)] = true;
- if (inst.tupleType === "T1_4X") f.Avx512T4X = true;
- }
- }
-
- replace(f, "Avx512K" , "Avx512Z" , "Avx512KZ");
- replace(f, "Avx512ER" , "Avx512SAE" , "Avx512ER_SAE");
- replace(f, "Avx512KZ" , "Avx512SAE" , "Avx512KZ_SAE");
- replace(f, "Avx512KZ" , "Avx512ER_SAE", "Avx512KZ_ER_SAE");
- replace(f, "Avx512K" , "Avx512B32" , "Avx512K_B32");
- replace(f, "Avx512K" , "Avx512B64" , "Avx512K_B64");
- replace(f, "Avx512KZ" , "Avx512B32" , "Avx512KZ_B32");
- replace(f, "Avx512KZ" , "Avx512B64" , "Avx512KZ_B64");
- replace(f, "Avx512KZ_SAE" , "Avx512B32" , "Avx512KZ_SAE_B32");
- replace(f, "Avx512KZ_SAE" , "Avx512B64" , "Avx512KZ_SAE_B64");
- replace(f, "Avx512KZ_ER_SAE", "Avx512B32" , "Avx512KZ_ER_SAE_B32");
- replace(f, "Avx512KZ_ER_SAE", "Avx512B64" , "Avx512KZ_ER_SAE_B64");
-
- return Object.getOwnPropertyNames(f);
- }
-
- static operationFlagsOf(group) {
- const f = Object.create(null);
-
- for (var i = 0; i < group.length; i++) {
- const inst = group[i];
- const name = inst.name;
-
- const operands = inst.operands;
-
- // Special case: MOV undefines flags if moving between GP and CR|DR registers.
- if (name === "mov")
- f.MovCrDr = true;
-
- // Special case: MOVSS|MOVSD zeroes the remaining part of destination if source operand is memory.
- if ((name === "movss" || name === "movsd") && !inst.attributes.REP)
- f.MovSsSd = true;
-
- // Hardware prefetch.
- if (name.startsWith("prefetch"))
- f.Prefetch = true;
-
- // Memory barrier.
- if (/^[lms]fence$/.test(name))
- f.Barrier = true;
-
- // Instruction is volatile.
- if (inst.attributes.VOLATILE)
- f.Volatile = true;
-
- // Instruction is privileged.
- if (inst.privilege !== "L3")
- f.Privileged = true;
- }
-
- return Object.getOwnPropertyNames(f);
- }
-
- 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++;
- }
- }
-
- 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 "sub" :
- 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 jumpType(name) {
- switch (name) {
- case "jo" :
- case "jno":
- case "jb" : case "jnae":
- case "jae": case "jnb" :
- case "je" : case "jz" :
- case "jne": case "jnz" :
- case "jbe": case "jna" :
- case "js" :
- case "jns":
- case "jp" : case "jpe" :
- case "jnp": case "jpo" :
- case "jl" : case "jnge":
- case "jge": case "jnl" :
- case "jle": case "jng" :
- case "jg" : case "jnle":
- case "jecxz":
- case "loop":
- case "loope":
- case "loopne":
- case "xbegin":
- return "Conditional";
-
- case "jmp" : return "Direct";
- case "call": return "Call";
- case "ret" : return "Return";
-
- default:
- return "None";
- }
- }
-}
-
-// ----------------------------------------------------------------------------
-// [Generate]
-// ----------------------------------------------------------------------------
-
-const RegOp = MapUtils.arrayToMap([
- "al", "ah", "ax", "eax", "rax", "cl",
- "r8lo", "r8hi", "r16", "r32", "r64", "fp", "mm", "k", "xmm", "ymm", "zmm", "bnd", "sreg", "creg", "dreg"
-]);
-
-const MemOp = MapUtils.arrayToMap([
- "m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"
-]);
-
-const OpSortPriority = {
- "read" :-9,
- "write" :-8,
- "rw" :-7,
- "implicit":-6,
-
- "r8lo" : 1,
- "r8hi" : 2,
- "r16" : 3,
- "r32" : 4,
- "r64" : 5,
- "fp" : 6,
- "mm" : 7,
- "k" : 8,
- "xmm" : 9,
- "ymm" : 10,
- "zmm" : 11,
- "sreg" : 12,
- "bnd" : 13,
- "creg" : 14,
- "dreg" : 15,
-
- "mem" : 30,
- "vm" : 31,
- "m8" : 32,
- "m16" : 33,
- "m32" : 34,
- "m48" : 35,
- "m64" : 36,
- "m80" : 37,
- "m128" : 38,
- "m256" : 39,
- "m512" : 40,
- "m1024" : 41,
- "mib" : 42,
- "vm32x" : 43,
- "vm32y" : 44,
- "vm32z" : 45,
- "vm64x" : 46,
- "vm64y" : 47,
- "vm64z" : 48,
- "memBase" : 49,
- "memES" : 50,
- "memDS" : 51,
-
- "i4" : 60,
- "u4" : 61,
- "i8" : 62,
- "u8" : 63,
- "i16" : 64,
- "u16" : 65,
- "i32" : 66,
- "u32" : 67,
- "i64" : 68,
- "u64" : 69,
-
- "rel8" : 70,
- "rel32" : 71
-};
-
-const OpToAsmJitOp = {
- "read" : "FLAG(R)",
- "write" : "FLAG(W)",
- "rw" : "FLAG(X)",
- "implicit": "FLAG(Implicit)",
-
- "r8lo" : "FLAG(GpbLo)",
- "r8hi" : "FLAG(GpbHi)",
- "r16" : "FLAG(Gpw)",
- "r32" : "FLAG(Gpd)",
- "r64" : "FLAG(Gpq)",
- "fp" : "FLAG(Fp)",
- "mm" : "FLAG(Mm)",
- "k" : "FLAG(K)",
- "xmm" : "FLAG(Xmm)",
- "ymm" : "FLAG(Ymm)",
- "zmm" : "FLAG(Zmm)",
- "bnd" : "FLAG(Bnd)",
- "sreg" : "FLAG(Seg)",
- "creg" : "FLAG(Cr)",
- "dreg" : "FLAG(Dr)",
-
- "mem" : "FLAG(Mem)",
- "vm" : "FLAG(Vm)",
-
- "m8" : "MEM(M8)",
- "m16" : "MEM(M16)",
- "m32" : "MEM(M32)",
- "m48" : "MEM(M48)",
- "m64" : "MEM(M64)",
- "m80" : "MEM(M80)",
- "m128" : "MEM(M128)",
- "m256" : "MEM(M256)",
- "m512" : "MEM(M512)",
- "m1024" : "MEM(M1024)",
- "mib" : "MEM(Mib)",
- "mAny" : "MEM(Any)",
- "vm32x" : "MEM(Vm32x)",
- "vm32y" : "MEM(Vm32y)",
- "vm32z" : "MEM(Vm32z)",
- "vm64x" : "MEM(Vm64x)",
- "vm64y" : "MEM(Vm64y)",
- "vm64z" : "MEM(Vm64z)",
-
- "memBase" : "MEM(BaseOnly)",
- "memDS" : "MEM(Ds)",
- "memES" : "MEM(Es)",
-
- "i4" : "FLAG(I4)",
- "u4" : "FLAG(U4)",
- "i8" : "FLAG(I8)",
- "u8" : "FLAG(U8)",
- "i16" : "FLAG(I16)",
- "u16" : "FLAG(U16)",
- "i32" : "FLAG(I32)",
- "u32" : "FLAG(U32)",
- "i64" : "FLAG(I64)",
- "u64" : "FLAG(U64)",
-
- "rel8" : "FLAG(Rel8)",
- "rel32" : "FLAG(Rel32)"
-};
-
-function OpSortFunc(a, b) {
- return (OpSortPriority[a] || 0) - (OpSortPriority[b] || 0);
-}
-
-function SortOpArray(a) {
- a.sort(OpSortFunc);
- return a;
-}
-
-function StringifyArray(a, map) {
- var s = "";
- for (var i = 0; i < a.length; i++) {
- const op = a[i];
- if (!hasOwn.call(map, op))
- throw new Error(`UNHANDLED OPERAND '${op}'`);
- s += (s ? " | " : "") + map[op];
- }
- return s ? s : "0";
-}
-
-class OSignature {
- constructor() {
- this.flags = Object.create(null);
- }
-
- equals(other) {
- return MapUtils.equals(this.flags, other.flags);
- }
-
- xor(other) {
- const result = MapUtils.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 = x86.Utils.regIndexOf(k);
- const kind = x86.Utils.regKindOf(k);
-
- if (kind)
- hasReg = true;
-
- if (index !== null && index !== -1)
- indexKind = kind;
- }
-
- if (hasReg) {
- for (k in bf) {
- const index = x86.Utils.regIndexOf(k);
- if (index !== null && index !== -1) {
- const kind = x86.Utils.regKindOf(k);
- if (indexKind !== kind) return false;
- }
- }
- }
-
- // Can merge...
- for (k in bf) af[k] = true;
- return true;
- }
-
- simplify() {
- const flags = this.flags;
-
- // Implicit register when also any other register can be specified.
- if (flags.al && flags.r8lo) delete flags["al"];
- if (flags.ah && flags.r8hi) delete flags["ah"];
- if (flags.ax && flags.r16 ) delete flags["ax"];
- if (flags.eax && flags.r32 ) delete flags["eax"];
- if (flags.rax && flags.r64 ) delete flags["rax"];
-
- // 32-bit register or 16-bit memory implies also 16-bit reg.
- if (flags.r32 && flags.m16) {
- flags.r16 = true;
- }
-
- // 32-bit register or 8-bit memory implies also 16-bit and 8-bit reg.
- if (flags.r32 && flags.m8) {
- flags.r8lo = true;
- flags.r8hi = true;
- flags.r16 = true;
- }
- }
-
- toString() {
- var s = "";
- var flags = this.flags;
- var prefix = (flags.read && flags.write) ? "X:" : (flags.write) ? "W:" : "R:";
-
- 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 prefix + s;
- }
-
- toAsmJitOpData() {
- var oFlags = this.flags;
-
- var mFlags = Object.create(null);
- var mMemFlags = Object.create(null);
- var mExtFlags = Object.create(null);
- var sRegMask = 0;
-
- if (oFlags.read && oFlags.write)
- mFlags.rw = true;
- else if (oFlags.write)
- mFlags.write = true;
- else
- mFlags.read = true;
-
- for (var k in oFlags) {
- switch (k) {
- case "read" : break;
- case "write" : break;
-
- case "implicit":
- case "r8lo" :
- case "r8hi" :
- case "r16" :
- case "r32" :
- case "r64" :
- case "creg" :
- case "dreg" :
- case "sreg" :
- case "bnd" :
- case "fp" :
- case "k" :
- case "mm" :
- case "xmm" :
- case "ymm" :
- case "zmm" : mFlags[k] = true; break;
-
- case "m8" :
- case "m16" :
- case "m32" :
- case "m48" :
- case "m64" :
- case "m80" :
- case "m128" :
- case "m256" :
- case "m512" :
- case "m1024" : mFlags.mem = true; mMemFlags[k] = true; break;
- case "mib" : mFlags.mem = true; mMemFlags.mib = true; break;
- case "mem" : mFlags.mem = true; mMemFlags.mAny = true; break;
-
- case "memDS" : mFlags.mem = true; mMemFlags.memDS = true; break;
- case "memES" : mFlags.mem = true; mMemFlags.memES = true; break;
- case "memZAX" : mFlags.mem = true; sRegMask |= 1 << 0; mMemFlags.memBase = true; break;
- case "memZSI" : mFlags.mem = true; sRegMask |= 1 << 6; mMemFlags.memBase = true; break;
- case "memZDI" : mFlags.mem = true; sRegMask |= 1 << 7; mMemFlags.memBase = true; break;
-
- case "vm32x" : mFlags.vm = true; mMemFlags.vm32x = true; break;
- case "vm32y" : mFlags.vm = true; mMemFlags.vm32y = true; break;
- case "vm32z" : mFlags.vm = true; mMemFlags.vm32z = true; break;
- case "vm64x" : mFlags.vm = true; mMemFlags.vm64x = true; break;
- case "vm64y" : mFlags.vm = true; mMemFlags.vm64y = true; break;
- case "vm64z" : mFlags.vm = true; mMemFlags.vm64z = true; break;
-
- case "i4" :
- case "u4" :
- case "i8" :
- case "u8" :
- case "i16" :
- case "u16" :
- case "i32" :
- case "u32" :
- case "i64" :
- case "u64" : mFlags[k] = true; break;
-
- case "rel8" :
- case "rel32" :
- mFlags.i32 = true;
- mFlags.i64 = true;
- mFlags[k] = true;
- break;
-
- case "rel16" :
- mFlags.i32 = true;
- mFlags.i64 = true;
- mFlags.rel32 = true;
- break;
-
- default: {
- switch (k) {
- case "es" : mFlags.sreg = true; sRegMask |= 1 << 1; break;
- case "cs" : mFlags.sreg = true; sRegMask |= 1 << 2; break;
- case "ss" : mFlags.sreg = true; sRegMask |= 1 << 3; break;
- case "ds" : mFlags.sreg = true; sRegMask |= 1 << 4; break;
- case "fs" : mFlags.sreg = true; sRegMask |= 1 << 5; break;
- case "gs" : mFlags.sreg = true; sRegMask |= 1 << 6; break;
- case "al" : mFlags.r8lo = true; sRegMask |= 1 << 0; break;
- case "ah" : mFlags.r8hi = true; sRegMask |= 1 << 0; break;
- case "ax" : mFlags.r16 = true; sRegMask |= 1 << 0; break;
- case "eax" : mFlags.r32 = true; sRegMask |= 1 << 0; break;
- case "rax" : mFlags.r64 = true; sRegMask |= 1 << 0; break;
- case "bl" : mFlags.r8lo = true; sRegMask |= 1 << 3; break;
- case "bh" : mFlags.r8hi = true; sRegMask |= 1 << 3; break;
- case "bx" : mFlags.r16 = true; sRegMask |= 1 << 3; break;
- case "ebx" : mFlags.r32 = true; sRegMask |= 1 << 3; break;
- case "rbx" : mFlags.r64 = true; sRegMask |= 1 << 3; break;
- case "cl" : mFlags.r8lo = true; sRegMask |= 1 << 1; break;
- case "ch" : mFlags.r8hi = true; sRegMask |= 1 << 1; break;
- case "cx" : mFlags.r16 = true; sRegMask |= 1 << 1; break;
- case "ecx" : mFlags.r32 = true; sRegMask |= 1 << 1; break;
- case "rcx" : mFlags.r64 = true; sRegMask |= 1 << 1; break;
- case "dl" : mFlags.r8lo = true; sRegMask |= 1 << 2; break;
- case "dh" : mFlags.r8hi = true; sRegMask |= 1 << 2; break;
- case "dx" : mFlags.r16 = true; sRegMask |= 1 << 2; break;
- case "edx" : mFlags.r32 = true; sRegMask |= 1 << 2; break;
- case "rdx" : mFlags.r64 = true; sRegMask |= 1 << 2; break;
- case "si" : mFlags.r16 = true; sRegMask |= 1 << 6; break;
- case "esi" : mFlags.r32 = true; sRegMask |= 1 << 6; break;
- case "rsi" : mFlags.r64 = true; sRegMask |= 1 << 6; break;
- case "di" : mFlags.r16 = true; sRegMask |= 1 << 7; break;
- case "edi" : mFlags.r32 = true; sRegMask |= 1 << 7; break;
- case "rdi" : mFlags.r64 = true; sRegMask |= 1 << 7; break;
- case "fp0" : mFlags.fp = true; sRegMask |= 1 << 0; break;
- case "xmm0" : mFlags.xmm = true; sRegMask |= 1 << 0; break;
- case "ymm0" : mFlags.ymm = true; sRegMask |= 1 << 0; break;
- default:
- console.log(`UNKNOWN OPERAND '${k}'`);
- }
- }
- }
- }
-
- const sFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mFlags )), OpToAsmJitOp);
- const sMemFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mMemFlags)), OpToAsmJitOp);
- const sExtFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mExtFlags)), OpToAsmJitOp);
-
- return `OSIGNATURE(${sFlags || 0}, ${sMemFlags || 0}, ${sExtFlags || 0}, ${StringUtils.decToHex(sRegMask, 2)})`;
- }
-}
-
-class ISignature extends Array {
- constructor(name) {
- super();
- this.name = name;
- this.x86 = false;
- this.x64 = false;
- this.implicit = 0; // Number of implicit operands.
- }
-
- simplify() {
- for (var i = 0; i < this.length; i++)
- this[i].simplify();
- }
-
- 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.
- var ok = 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 (!ok || 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;
-
- var xorIndex = -1;
- for (var 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;
-
- this.x86 = this.x86 || other.x86;
- this.x64 = this.x64 || other.x64;
-
- 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() {
- // 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)) {
- const props = Object.getOwnPropertyNames(MapUtils.and(op.flags, RegOp));
- props.sort();
- s += "{" + props.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 = MapUtils.firstOf(aOp.flags, MemOp);
-
- if (mem) {
- // Stop if the memory operand is implicit or if there is more than one.
- if (aOp.flags.mem || memPos >= 0) {
- memPos = -1;
- break;
- }
- else {
- memOp = mem;
- memPos = i;
- }
- }
- else if (MapUtils.anyOf(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 = MapUtils.anyOf(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 = MapUtils.firstOf(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 is implicit if `diffSizeSet` is empty. That means
- // that the instruction only uses one size for all reg combinations.
- //
- // B) The memory operand is implicit 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(function(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`.
- }
- else {
- implicit = false;
- }
- });
- }
- }
- }
-
- // Patch all instructions to accept implicit memory operand.
- for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
- const bInst = sameSizeSet[bIndex];
- if (implicit) bInst[memPos].flags.mem = true;
-
- if (DEBUG && !implicit)
- console.log(`${this.name}: Explicit: ${bInst}`);
- }
- }
- }
-
- simplify() {
- for (var i = 0; i < this.length; i++)
- this[i].simplify();
- }
-
- compact() {
- 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);
- continue;
- }
- j++;
- }
- }
- }
-
- toString() {
- return `[${this.join(", ")}]`;
- }
-}
-
-// ----------------------------------------------------------------------------
-// [X86Generator]
-// ----------------------------------------------------------------------------
-
-class X86Generator extends base.BaseGenerator {
- constructor() {
- super("X86");
-
- this.opCombinations = Object.create(null);
- this.maxOpRows = 0;
-
- this.opBlackList = {
- "moff8" : true,
- "moff16": true,
- "moff32": true,
- "moff64": true
- };
-
- this.load([
- "src/asmjit/x86/x86inst.cpp",
- "src/asmjit/x86/x86inst.h"
- ]);
- }
-
- signaturesFromInsts(insts) {
- const signatures = new SignatureArray();
-
- for (var i = 0; i < insts.length; i++) {
- const inst = insts[i];
- const ops = inst.operands;
-
- 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;
-
- // Terminate if this operand is something asmjit doesn't support
- // and skip all instructions having implicit `imm` operand of `1`
- // (handled fine by asmjit).
- if (this.opBlackList[mem] === true || iop.immValue !== null) {
- row = null;
- break;
- }
-
- if (reg === "r8") reg = "r8lo";
- if (reg === "seg") reg = "sreg";
- if (reg === "st(i)") reg = "fp";
- if (reg === "st(0)") reg = "fp0";
-
- 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";
-
- const op = new OSignature();
-
- if (iop.read) op.flags.read = true;
- if (iop.write) op.flags.write = true;
-
- if (iop.implicit) {
- row.implicit++;
- op.flags.implicit = true;
- }
-
- if (iop.memSeg) {
- if (iop.memSeg === "ds") op.flags.memDS = true;
- if (iop.memSeg === "es") op.flags.memES = true;
- if (reg === "zax") op.flags.memZAX = true;
- if (reg === "zsi") op.flags.memZSI = true;
- if (reg === "zdi") op.flags.memZDI = true;
- }
- else if (reg) {
- op.flags[reg] = true;
- if (reg === "r8lo") op.flags.r8hi = true;
- }
-
- if (mem) {
- op.flags[mem] = true;
-
- // Exception: Allow LEA to contain any memory size.
- if (inst.name === "lea") MapUtils.add(op.flags, MemOp);
- }
-
- 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);
- }
-
- if (row)
- signatures.push(row);
- }
-
- signatures.calcImplicitMemSize();
- signatures.simplify();
- signatures.compact();
-
- signatures.simplify();
- signatures.compact();
-
- return signatures;
- }
-
- // --------------------------------------------------------------------------
- // [Parse]
- // --------------------------------------------------------------------------
-
- parse() {
- const data = this.dataOf("src/asmjit/x86/x86inst.cpp");
- const re = new RegExp(
- "INST\\(" +
- "([A-Za-z0-9_]+)\\s*" + "," + // [01] Instruction.
- "([^,]+)" + "," + // [02] Encoding.
- "(.{26}[^,]*)" + "," + // [03] Opcode[0].
- "(.{26}[^,]*)" + "," + // [04] Opcode[1].
- "([^,]+)" + "," + // [05] Write-Index.
- "([^,]+)" + "," + // [06] Write-Size.
- // --- autogenerated fields ---
- "([^\\)]+)" + "," + // [07] NameIndex.
- "([^\\)]+)" + "," + // [08] CommonDataIndex.
- "([^\\)]+)" + "\\)",// [09] 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();
- var writeIndex = StringUtils.trimLeft(m[5]);
- var writeSize = StringUtils.trimLeft(m[6]);
-
- const group = GenUtils.groupOf(name);
- if (name && !group.length)
- console.log(`INSTRUCTION '${name}' not found in asmdb`);
-
- const flags = GenUtils.flagsOf(group);
- const signatures = this.signaturesFromInsts(group);
- const singleRegCase = GenUtils.singleRegCase(name);
- const jumpType = GenUtils.jumpType(name);
-
- this.addInst({
- id : 0, // Instruction id (numeric value).
- name : name, // Instruction name.
- enum : enum_, // Instruction enum without `kId` prefix.
- encoding : encoding, // Instruction encoding.
- opcode0 : opcode0, // Primary opcode.
- opcode1 : opcode1, // Secondary opcode.
- flags : flags,
- writeIndex : writeIndex,
- writeSize : writeSize,
- signatures : signatures, // Rows containing instruction signatures.
- singleRegCase : singleRegCase,
- jumpType : jumpType,
-
- nameIndex : -1, // Instruction name-index.
- altOpCodeIndex : -1, // Index to X86InstDB::altOpCodeTable.
- commonDataIndex : -1,
- operationDataIndex: -1,
- sseToAvxDataIndex : -1,
-
- signatureIndex : -1,
- signatureCount : -1
- });
- this.maxOpRows = Math.max(this.maxOpRows, signatures.length);
- }
-
- if (this.instArray.length === 0)
- throw new Error("X86Generator.parse(): Invalid parsing regexp (no data parsed)");
-
- console.log("Number of Instructions: " + this.instArray.length);
- }
-
- // --------------------------------------------------------------------------
- // [Generate]
- // --------------------------------------------------------------------------
-
- generate() {
- // Order doesn't matter here.
- this.generateIdData();
- this.generateNameData();
- this.generateOperationData();
- this.generateSseToAvxData();
- this.generateAltOpCodeData();
- this.generateSignatureData();
-
- // These must be last, and order matters.
- this.generateCommonData();
- this.generateInstData();
-
- return this;
- }
-
- // --------------------------------------------------------------------------
- // [Generate - AltOpCodeData]
- // --------------------------------------------------------------------------
-
- generateAltOpCodeData() {
- const table = new base.IndexedArray();
- for (var i = 0; i < this.instArray.length; i++) {
- const inst = this.instArray[i];
- inst.altOpCodeIndex = table.addIndexed(StringUtils.padLeft(inst.opcode1, 26));
- }
-
- var s = `const uint32_t X86InstDB::altOpCodeData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n`;
- return this.inject("altOpCodeData", StringUtils.disclaimer(s), table.length * 4);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - OperationData]
- // --------------------------------------------------------------------------
-
- generateOperationData() {
- const instArray = this.instArray;
- const table = new base.IndexedArray();
-
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- const group = GenUtils.groupOf(inst.name);
-
- var opFlags = GenUtils.operationFlagsOf(group).map(function(f) { return `OP_FLAG(${f})`; });
- if (!opFlags.length) opFlags.push("0");
-
- var features = GenUtils.cpuFeaturesOf(group).map(function(f) { return `FEATURE(${f})`; });
- if (!features.length) features.push("0");
-
- var [r, w] = GenUtils.specialsOf(group);
- r = r.map(function(item) { return `SPECIAL(${item.replace(".", "_")})`; });
- w = w.map(function(item) { return `SPECIAL(${item.replace(".", "_")})`; });
-
- const opFlagsStr = opFlags.join(" | ");
- const featuresStr = features.join(", ");
- const rStr = r.join(" | ") || "0";
- const wStr = w.join(" | ") || "0";
-
- inst.operationDataIndex = table.addIndexed(`{ ${opFlagsStr}, { ${featuresStr} }, ${rStr}, ${wStr} }`);
- }
-
- var s = `#define OP_FLAG(F) X86Inst::kOperation##F\n` +
- `#define FEATURE(F) CpuInfo::kX86Feature##F\n` +
- `#define SPECIAL(F) x86::kSpecialReg_##F\n` +
- `const X86Inst::OperationData X86InstDB::operationData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
- `#undef SPECIAL\n` +
- `#undef FEATURE\n` +
- `#undef OP_FLAG\n` ;
- return this.inject("operationData", StringUtils.disclaimer(s), table.length * 16);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - SseToAvxData]
- // --------------------------------------------------------------------------
-
- generateSseToAvxData() {
- const instArray = this.instArray;
- const instMap = this.instMap;
- const table = new base.IndexedArray();
-
- function getSseToAvxInsts(insts) {
- const combinations = [];
- for (var x = 0; x < insts.length; x++) {
- const inst = insts[x];
- const ops = inst.operands;
-
- // SSE instruction does never share its name with AVX one.
- if (/^(VEX|XOP|EVEX)$/.test(inst.prefix))
- return null;
-
- var ok = false;
- for (var y = 0; y < ops.length; y++) {
- if (ops[y].reg === "xmm")
- ok = true;
-
- // There is no AVX instruction that works with MMX regs.
- if (ops[y].reg === "mm") {
- ok = false;
- break;
- }
- }
-
- if (ok) combinations.push(inst);
- }
-
- return combinations.length ? combinations : null;
- }
-
- function calcSseToAvxData(insts) {
- const out = {
- mode : "None", // No conversion by default.
- delta: 0 // 0 if no conversion is possible.
- };
-
- const sseInsts = getSseToAvxInsts(insts);
- if (!sseInsts) return out;
-
- const sseName = sseInsts[0].name;
- const avxName = "v" + sseName;
- const avxInsts = GenUtils.groupOf(avxName);
-
- if (!avxInsts.length) {
- if (DEBUG)
- console.log(`SseToAvx: Instruction '${sseName}' has no AVX counterpart`);
- return out;
- }
-
- if (avxName === "vblendvpd" || avxName === "vblendvps" || avxName === "vpblendvb") {
- // Special cases first.
- out.mode = "Blend";
- }
- else {
- // Common case, deduce conversion mode by checking both SSE and AVX instructions.
- const map = Object.create(null);
- for (var sseIndex = 0; sseIndex < sseInsts.length; sseIndex++) {
- const sseInst = sseInsts[sseIndex];
- var match = false;
-
- for (var avxIndex = 0; avxIndex < avxInsts.length; avxIndex++) {
- const avxInst = avxInsts[avxIndex];
-
- // Select only VEX instructions.
- if (avxInst.prefix !== "VEX") continue;
-
- // Check if the AVX version is the same.
- if (GenUtils.eqOps(avxInst.operands, 0, sseInst.operands, 0)) {
- map.raw = true;
- match = true;
- }
- else if (avxInst.operands[0].data === "xmm" && GenUtils.eqOps(avxInst.operands, 1, sseInst.operands, 0)) {
- map.nds = true;
- match = true;
- }
- }
-
- if (!match) {
- const signature = sseInst.operands.map(function(op) { return op.data; }).join(", ");
- console.log(`SseToAvx: Instruction '${sseName}(${signature})' has no AVX counterpart`);
- return out;
- }
- }
-
- out.mode = (map.raw && !map.nds) ? "Move" : (map.raw && map.nds) ? "MoveIfMem" : "Extend";
- }
- out.delta = instMap[avxName].id - instMap[sseName].id;
- return out;
- }
-
- // This will receive a zero index, which means that no translation is possible.
- table.addIndexed("{ " + StringUtils.padLeft(`X86Inst::kSseToAvxNone`, 27) + ", " + StringUtils.padLeft("0", 4) + " }");
-
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
-
- // If it's not `-1` it's an AVX instruction that shares the SseToAvx
- // data. So we won't touch it as it already has `sseToAvxDataIndex`.
- if (inst.sseToAvxDataIndex === -1) {
- const data = calcSseToAvxData(GenUtils.groupOf(inst.name));
- inst.sseToAvxDataIndex = table.addIndexed("{ " + StringUtils.padLeft(`X86Inst::kSseToAvx${data.mode}`, 27) + ", " + StringUtils.padLeft(data.delta, 4) + " }");
- if (data.delta !== 0)
- instMap["v" + inst.name].sseToAvxDataIndex = inst.sseToAvxDataIndex;
- }
- }
-
- var s = `const X86Inst::SseToAvxData X86InstDB::sseToAvxData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n`;
- return this.inject("sseToAvxData", StringUtils.disclaimer(s), table.length * 2);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - SignatureData]
- // --------------------------------------------------------------------------
-
- generateSignatureData() {
- const instArray = this.instArray;
-
- const opMap = Object.create(null);
- const opArr = [];
-
- const signatureMap = Object.create(null);
- const signatureArr = [];
-
- const noOperand = "OSIGNATURE(0, 0, 0, 0xFF)";
- opMap[noOperand] = [0];
- opArr.push(noOperand);
-
- function findSignaturesIndex(rows) {
- const len = rows.length;
- if (!len) return 0;
-
- const indexes = signatureMap[rows[0].data];
- if (indexes === undefined) return -1;
-
- for (var i = 0; i < indexes.length; i++) {
- const index = indexes[i];
- if (index + len > signatureArr.length) continue;
-
- var ok = true;
- for (var j = 0; j < len; j++) {
- if (signatureArr[index + j].data !== rows[j].data) {
- ok = false;
- break;
- }
- }
-
- if (ok)
- return index;
- }
-
- return -1;
- }
-
- function indexSignatures(signatures) {
- const result = signatureArr.length;
-
- for (var i = 0; i < signatures.length; i++) {
- const signature = signatures[i];
- const idx = signatureArr.length;
-
- if (!hasOwn.call(signatureMap, signature.data))
- signatureMap[signature.data] = [];
-
- signatureMap[signature.data].push(idx);
- signatureArr.push(signature);
- }
-
- return result;
- }
-
- for (var len = this.maxOpRows; len >= 0; len--) {
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- const signatures = inst.signatures;
- if (signatures.length !== len) continue;
-
- const signatureEntries = [];
- for (var j = 0; j < len; j++) {
- const signature = signatures[j];
-
- var signatureEntry = `ISIGNATURE(${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(opMap, h)) {
- index = opArr.length;
- opMap[h] = index;
- opArr.push(h);
- }
- else {
- index = opMap[h];
- }
-
- signatureEntry += `, ${StringUtils.padLeft(index, 3)}`;
- x++;
- }
-
- while (x < 6) {
- signatureEntry += `, ${StringUtils.padLeft(0, 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);
-
- signatureArr[index].refs++;
- inst.signatureIndex = index;
- inst.signatureCount = count;
- }
- }
-
- var s = "#define FLAG(flag) X86Inst::kOp##flag\n" +
- "#define MEM(mem) X86Inst::kMemOp##mem\n" +
- "#define OSIGNATURE(flags, memFlags, extFlags, regId) \\\n" +
- " { uint32_t(flags), uint16_t(memFlags), uint8_t(extFlags), uint8_t(regId) }\n" +
- StringUtils.makeCxxArray(opArr, "const X86Inst::OSignature X86InstDB::oSignatureData[]") +
- "#undef OSIGNATURE\n" +
- "#undef MEM\n" +
- "#undef FLAG\n" +
- "\n" +
- "#define ISIGNATURE(count, x86, x64, implicit, o0, o1, o2, o3, o4, o5) \\\n" +
- " { count, (x86 ? uint8_t(X86Inst::kArchMaskX86) : uint8_t(0)) | \\\n" +
- " (x64 ? uint8_t(X86Inst::kArchMaskX64) : uint8_t(0)) , \\\n" +
- " implicit, \\\n" +
- " 0, \\\n" +
- " { o0, o1, o2, o3, o4, o5 } \\\n" +
- " }\n" +
- StringUtils.makeCxxArrayWithComment(signatureArr, "const X86Inst::ISignature X86InstDB::iSignatureData[]") +
- "#undef ISIGNATURE\n";
- return this.inject("signatureData", StringUtils.disclaimer(s), opArr.length * 8 + signatureArr.length * 8);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - CommonData]
- // --------------------------------------------------------------------------
-
- generateCommonData() {
- const table = new base.IndexedArray();
-
- for (var i = 0; i < this.instArray.length; i++) {
- const inst = this.instArray[i];
- const group = GenUtils.groupOf(inst.name);
-
- const flags = inst.flags.map(function(flag) { return `F(${flag})`; }).join("|") || "0";
- const singleRegCase = `SINGLE_REG(${inst.singleRegCase})`;
- const jumpType = `JUMP_TYPE(${inst.jumpType})`;
-
- const item = "{ " + StringUtils.padLeft(flags , 54) + ", " +
- StringUtils.padLeft(inst.writeIndex , 3) + ", " +
- StringUtils.padLeft(inst.writeSize , 3) + ", " +
- StringUtils.padLeft(inst.altOpCodeIndex , 3) + ", " +
- StringUtils.padLeft(inst.signatureIndex , 3) + ", " +
- StringUtils.padLeft(inst.signatureCount , 2) + ", " +
- StringUtils.padLeft(jumpType , 22) + ", " +
- StringUtils.padLeft(singleRegCase , 16) + ", " + "0 }";
- inst.commonDataIndex = table.addIndexed(item);
- }
-
- var s = `#define F(VAL) X86Inst::kFlag##VAL\n` +
- `#define JUMP_TYPE(VAL) Inst::kJumpType##VAL\n` +
- `#define SINGLE_REG(VAL) X86Inst::kSingleReg##VAL\n` +
- `const X86Inst::CommonData X86InstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
- `#undef SINGLE_REG\n` +
- `#undef JUMP_TYPE\n` +
- `#undef F\n`;
- return this.inject("commonData", StringUtils.disclaimer(s), table.length * 12);
- }
-
- // --------------------------------------------------------------------------
- // [Generate - InstData]
- // --------------------------------------------------------------------------
-
- generateInstData() {
- var s = StringUtils.format(this.instArray, "", false, function(inst) {
- return "INST(" +
- StringUtils.padLeft(inst.enum , 16) + ", " +
- StringUtils.padLeft(inst.encoding , 19) + ", " +
- StringUtils.padLeft(inst.opcode0 , 26) + ", " +
- StringUtils.padLeft(inst.opcode1 , 26) + ", " +
- StringUtils.padLeft(inst.writeIndex , 1) + ", " +
- StringUtils.padLeft(inst.writeSize , 1) + ", " +
- StringUtils.padLeft(inst.nameIndex , 4) + ", " +
- StringUtils.padLeft(inst.commonDataIndex , 3) + ", " +
- StringUtils.padLeft(inst.operationDataIndex, 3) + ", " +
- StringUtils.padLeft(inst.sseToAvxDataIndex , 2) + ")";
- }) + "\n";
- return this.inject("instData", s, this.instArray.length * 12);
- }
-
- // --------------------------------------------------------------------------
- // [Reimplement]
- // --------------------------------------------------------------------------
-
- getCommentOf(name) {
- var insts = GenUtils.groupOf(name);
- if (!insts.length) return "";
-
- var features = GenUtils.cpuFeaturesOf(insts);
- var comment = GenUtils.cpuArchOf(insts);
-
- if (features.length) {
- comment += " {";
-
- const vl = features.indexOf("AVX512_VL");
- if (vl !== -1) features.splice(vl, 1);
- comment += features.join("|");
- if (vl !== -1) comment += "+VL";
-
- comment += "}";
- }
-
- return comment;
- }
-
- // --------------------------------------------------------------------------
- // [Print]
- // --------------------------------------------------------------------------
-
- printMissing() {
- const ignored = MapUtils.arrayToMap([
- "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 = "";
- isa.instructionNames.forEach(function(name) {
- var insts = isa.query(name);
- if (!this.instMap[name] && ignored[name] !== true) {
- console.log(`MISSING INSTRUCTION '${name}'`);
- var inst = this.newInstFromInsts(insts);
- if (inst) {
- out += " INST(" +
- StringUtils.padLeft(inst.enum , 16) + ", " +
- StringUtils.padLeft(inst.encoding , 23) + ", " +
- StringUtils.padLeft(inst.opcode0 , 26) + ", " +
- StringUtils.padLeft(inst.opcode1 , 26) + ", " +
- StringUtils.padLeft(inst.writeIndex , 2) + ", " +
- StringUtils.padLeft(inst.writeSize , 2) + ", " +
- StringUtils.padLeft("0", 4) + ", " +
- StringUtils.padLeft("0", 3) + ", " +
- StringUtils.padLeft("0", 3) + ", " +
- StringUtils.padLeft("0", 3) + "),\n";
- }
- }
- }, this);
- console.log(out);
- }
-
- newInstFromInsts(insts) {
- function composeOpCode(obj) {
- return `${obj.type}(${obj.prefix},${obj.opcode},${obj.o},${obj.l},${obj.w},${obj.ew},${obj.en},${obj.tt})`;
- }
-
- function GetAccess(inst) {
- var operands = inst.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";
- }
-
- var inst = insts[0];
-
- var id = this.instArray.length;
- var name = inst.name;
- var enum_ = name[0].toUpperCase() + name.substr(1);
-
- var opcode = inst.opcodeHex;
- var rm = inst.rm;
- var mm = inst.mm;
- var pp = inst.pp;
- var encoding = inst.encoding;
- var prefix = inst.prefix;
-
- var access = GetAccess(inst);
-
- var vexL = undefined;
- var vexW = undefined;
- var evexW = undefined;
-
- for (var i = 1; i < insts.length; i++) {
- inst = insts[i];
-
- if (opcode !== inst.opcode ) return null;
- if (rm !== inst.rm ) return null;
- if (mm !== inst.mm ) return null;
- if (pp !== inst.pp ) return null;
- if (encoding !== inst.encoding ) return null;
- if (prefix !== inst.prefix ) return null;
- if (access !== GetAccess(inst)) return null;
- }
-
- var ppmm = StringUtils.padLeft(pp, 2).replace(/ /g, "0") +
- StringUtils.padLeft(mm, 4).replace(/ /g, "0") ;
-
- var composed = composeOpCode({
- type : prefix === "VEX" || prefix === "EVEX" ? "V" : "O",
- prefix: ppmm,
- opcode: opcode,
- o : rm === "r" ? "_" : (rm ? rm : "_"),
- l : vexL !== undefined ? vexL : "_",
- w : vexW !== undefined ? vexW : "_",
- ew : evexW !== undefined ? vexEW : "_",
- en : "_",
- tt : "_ "
- });
-
- return {
- id : id,
- name : name,
- enum : enum_,
- encoding : encoding,
- opcode0 : composed,
- opcode1 : "0",
- writeIndex : "0",
- writeSize : "0",
- nameIndex : -1,
- commonDataIndex : -1,
- operationDataIndex: -1
- };
- }
-}
-
-// ----------------------------------------------------------------------------
-// [Main]
-// ----------------------------------------------------------------------------
-
-function main() {
- const gen = new X86Generator();
- gen.parse();
- gen.generate();
- gen.printMissing();
- gen.dumpTableSizes();
- gen.save();
-}
-main();
diff --git a/tools/tablegen-x86.js b/tools/tablegen-x86.js
new file mode 100644
index 0000000..22cc53a
--- /dev/null
+++ b/tools/tablegen-x86.js
@@ -0,0 +1,2412 @@
+// [AsmJit]
+// Machine Code Generation for C++.
+//
+// [License]
+// Zlib - See LICENSE.md file in the package.
+
+// ============================================================================
+// tablegen-x86.js
+//
+// The purpose of this script is to fetch all instructions' names into a single
+// string and to optimize common patterns that appear in instruction data. It
+// prevents relocation of small strings (instruction names) that has to be done
+// by a linker to make all pointers the binary application/library uses valid.
+// This approach decreases the final size of AsmJit binary and relocation data.
+//
+// NOTE: This script relies on 'asmdb' package. Either install it by using
+// node.js package manager (npm) or by copying/symlinking the whole asmdb
+// directory as [asmjit]/tools/asmdb.
+// ============================================================================
+
+"use strict";
+
+const core = require("./tablegen.js");
+const asmdb = core.asmdb;
+const kIndent = core.kIndent;
+
+const Lang = core.Lang;
+const CxxUtils = core.CxxUtils;
+const MapUtils = core.MapUtils;
+const ArrayUtils = core.ArrayUtils;
+const StringUtils = core.StringUtils;
+const IndexedArray = core.IndexedArray;
+
+const hasOwn = Object.prototype.hasOwnProperty;
+const disclaimer = StringUtils.disclaimer;
+
+const FAIL = core.FAIL;
+const DEBUG = core.DEBUG;
+
+const decToHex = StringUtils.decToHex;
+
+// ============================================================================
+// [tablegen.x86.x86isa]
+// ============================================================================
+
+// Create the X86 database and add some special cases recognized by AsmJit.
+const x86isa = new asmdb.x86.ISA({
+ instructions: [
+ // Imul in [reg, imm] form is encoded as [reg, reg, imm].
+ ["imul", "r16, ib" , "RMI" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
+ ["imul", "r32, ib" , "RMI" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
+ ["imul", "r64, ib" , "RMI" , "REX.W 6B /r ib", "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"],
+ ["imul", "r16, iw" , "RMI" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
+ ["imul", "r32, id" , "RMI" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
+ ["imul", "r64, id" , "RMI" , "REX.W 69 /r id", "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"]
+ ]
+});
+
+// Remapped instructions contain mapping between instructions that AsmJit expects
+// and instructions provided by asmdb. In general, AsmJit uses string instructions
+// (like cmps, movs, etc...) without the suffix, so we just remap these and keep
+// all others.
+const RemappedInsts = {
+ __proto__: null,
+
+ "cmpsd": { names: ["cmpsd"] , rep: false },
+ "movsd": { names: ["movsd"] , rep: false },
+ "cmps" : { names: ["cmpsb", "cmpsw", "cmpsd", "cmpsq"], rep: true },
+ "movs" : { names: ["movsb", "movsw", "movsd", "movsq"], rep: true },
+ "lods" : { names: ["lodsb", "lodsw", "lodsd", "lodsq"], rep: null },
+ "scas" : { names: ["scasb", "scasw", "scasd", "scasq"], rep: null },
+ "stos" : { names: ["stosb", "stosw", "stosd", "stosq"], rep: null },
+ "ins" : { names: ["insb" , "insw" , "insd" ] , rep: null },
+ "outs" : { names: ["outsb", "outsw", "outsd"] , rep: null }
+};
+
+// ============================================================================
+// [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.attributes.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.attributes.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]
+// ============================================================================
+
+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) {
+ return ArrayUtils.sorted(dbInsts.unionCpuFeatures());
+ }
+
+ static flagsOf(dbInsts) {
+ function replace(map, a, b, c) {
+ if (map[a] && map[b]) {
+ delete map[a];
+ delete map[b];
+ map[c] = true;
+ }
+ }
+
+ 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 (/^(k|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.attributes.Lock ) f.Lock = true;
+ if (dbInst.attributes.XAcquire ) f.XAcquire = true;
+ if (dbInst.attributes.XRelease ) f.XRelease = true;
+ if (dbInst.attributes.REP ) f.Rep = true;
+ if (dbInst.attributes.REPNE ) f.Rep = true;
+ if (dbInst.attributes.RepIgnored) f.RepIgnored = true;
+
+ if (dbInst.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.vsibReg)
+ f.Vsib = true;
+
+ if (dbInst.prefix === "VEX" || dbInst.prefix === "XOP")
+ f.Vex = 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;
+ }
+ }
+
+ replace(f, "Avx512K" , "Avx512Z" , "Avx512KZ");
+ replace(f, "Avx512ER" , "Avx512SAE" , "Avx512ER_SAE");
+ replace(f, "Avx512KZ" , "Avx512SAE" , "Avx512KZ_SAE");
+ replace(f, "Avx512KZ" , "Avx512ER_SAE", "Avx512KZ_ER_SAE");
+ replace(f, "Avx512K" , "Avx512B32" , "Avx512K_B32");
+ replace(f, "Avx512K" , "Avx512B64" , "Avx512K_B64");
+ replace(f, "Avx512KZ" , "Avx512B32" , "Avx512KZ_B32");
+ replace(f, "Avx512KZ" , "Avx512B64" , "Avx512KZ_B64");
+ replace(f, "Avx512KZ_SAE" , "Avx512B32" , "Avx512KZ_SAE_B32");
+ replace(f, "Avx512KZ_SAE" , "Avx512B64" , "Avx512KZ_SAE_B64");
+ replace(f, "Avx512KZ_ER_SAE", "Avx512B32" , "Avx512KZ_ER_SAE_B32");
+ replace(f, "Avx512KZ_ER_SAE", "Avx512B64" , "Avx512KZ_ER_SAE_B64");
+
+ return Object.getOwnPropertyNames(f);
+ }
+
+ 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++;
+ }
+ }
+
+ 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 "sub" :
+ 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 fixedRegOf(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 controlType(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 "None";
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.X86TableGen]
+// ============================================================================
+
+class X86TableGen extends core.TableGen {
+ constructor() {
+ super("X86");
+ }
+
+ // --------------------------------------------------------------------------
+ // [Query]
+ // --------------------------------------------------------------------------
+
+ // Get instructions (dbInsts) having the same name as understood by AsmJit.
+ query(name) {
+ const remapped = RemappedInsts[name];
+ if (!remapped) return x86isa.query(name);
+
+ const dbInsts = x86isa.query(remapped.names);
+ const rep = remapped.rep;
+ if (rep === null) return dbInsts;
+
+ return dbInsts.filter((inst) => {
+ return rep === !!(inst.attributes.REP || inst.attributes.REPNE);
+ });
+ }
+
+ // --------------------------------------------------------------------------
+ // [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] NameIndex.
+ "([^\\)]+)" + "," + // [08] CommonDataIndex.
+ "([^\\)]+)" + "\\)", // [09] 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)
+ FAIL(`Instruction '${name}' not found in asmdb`);
+
+ const flags = GenUtils.flagsOf(dbInsts);
+ const controlType = GenUtils.controlType(dbInsts);
+ const singleRegCase = GenUtils.singleRegCase(name);
+
+ this.addInst({
+ id : 0, // Instruction id (numeric value).
+ name : name, // Instruction name.
+ 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.
+ controlType : controlType,
+ 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.
+ commonInfoIndexA : -1,
+ commomInfoIndexB : -1,
+
+ signatureIndex : -1,
+ signatureCount : -1
+ });
+ }
+
+ if (this.insts.length === 0)
+ FAIL("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.nameIndex ).padEnd( 5) + ", " +
+ String(inst.commonInfoIndexA).padEnd( 3) + ", " +
+ String(inst.commomInfoIndexB).padEnd( 3) + ")";
+ }) + "\n";
+ this.inject("InstInfo", s, this.insts.length * 8);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Other]
+ // --------------------------------------------------------------------------
+
+ printMissing() {
+ const ignored = MapUtils.arrayToMap([
+ "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( 4) + ", " +
+ String("0" ).padEnd( 3) + ", " +
+ String("0" ).padEnd( 3) + "),\n";
+ }
+ }
+ }, this);
+ console.log(out);
+ }
+
+ 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";
+ }
+
+ 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 rm = dbi.rm;
+ var mm = dbi.mm;
+ var pp = dbi.pp;
+ var encoding = dbi.encoding;
+ var isVec = isVecPrefix(dbi.prefix);
+
+ var access = GetAccess(dbi);
+
+ var vexL = undefined;
+ var vexW = undefined;
+ var evexW = undefined;
+
+ 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") {
+ var newEvexW = String(dbi.w === "W0" ? 0 : dbi.w === "W1" ? 1 : "_");
+ if (evexW !== undefined && evexW !== newEvexW)
+ evexW = "x";
+ else
+ evexW = newEvexW;
+ }
+
+ if (opcode !== dbi.opcodeHex ) { console.log(`ISSUE: Opcode ${opcode} != ${dbi.opcodeHex}`); return null; }
+ if (rm !== dbi.rm ) { console.log(`ISSUE: RM ${rm} != ${dbi.rm}`); return null; }
+ if (mm !== dbi.mm ) { console.log(`ISSUE: MM ${mm} != ${dbi.mm}`); return null; }
+ if (pp !== dbi.pp ) { console.log(`ISSUE: PP ${pp} != ${dbi.pp}`); return null; }
+ if (encoding !== dbi.encoding ) { console.log(`ISSUE: Enc ${encoding} != ${dbi.encoding}`); return null; }
+ if (access !== GetAccess(dbi)) { console.log(`ISSUE: Access ${access} != ${GetAccess(dbi)}`); return null; }
+ if (isVec != isVecPrefix(dbi.prefix)) { console.log(`ISSUE: Vex/Non-Vex mismatch`); return null; }
+ }
+
+ var ppmm = pp.padEnd(2).replace(/ /g, "0") +
+ mm.padEnd(4).replace(/ /g, "0") ;
+
+ var composed = composeOpCode({
+ type : isVec ? "V" : "O",
+ prefix: ppmm,
+ opcode: opcode,
+ o : rm === "r" ? "_" : (rm ? rm : "_"),
+ l : vexL !== undefined ? vexL : "_",
+ w : vexW !== undefined ? vexW : "_",
+ ew : evexW !== undefined ? evexW : "_",
+ en : "_",
+ tt : "_ "
+ });
+
+ return {
+ id : id,
+ name : name,
+ enum : enum_,
+ encoding : encoding,
+ opcode0 : composed,
+ opcode1 : "0",
+ nameIndex : -1,
+ commonInfoIndexA : -1,
+ commomInfoIndexB : -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);
+
+ if (features.length) {
+ text += "{";
+ const avxFeatures = filterAVX(features, true);
+ const otherFeatures = filterAVX(features, false);
+
+ 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();
+
+ mainOpcodeTable.addIndexed("O(000000,00,0,0,0,0,0,_ )");
+
+ function indexOpcode(opcode) {
+ if (opcode === "0")
+ return ["00", 0];
+
+ // O_FPU(__,__OP,_)
+ if (opcode.startsWith("O_FPU(")) {
+ var value = opcode.substring(11, 13);
+ var remaining = opcode.substring(0, 11) + "00" + opcode.substring(13);
+
+ return [value, mainOpcodeTable.addIndexed(remaining.padEnd(26))];
+ }
+
+ // X(______,OP,_,_,_,_,_,_ )
+ if (opcode.startsWith("O_FPU(") || opcode.startsWith("O(") || opcode.startsWith("V(")) {
+ var value = opcode.substring(9, 11);
+ var remaining = opcode.substring(0, 9) + "00" + opcode.substring(11);
+
+ remaining = remaining.replace(/,[_xI],/g, ",0,");
+ remaining = remaining.replace(/,[_xI],/g, ",0,");
+ return [value, mainOpcodeTable.addIndexed(remaining.padEnd(26))];
+ }
+
+ FAIL(`Failed to process opcode '${opcode}'`);
+ }
+
+ insts.map((inst) => {
+ const [value, index] = indexOpcode(inst.opcode0);
+ inst.mainOpcodeValue = value;
+ inst.mainOpcodeIndex = index;
+ inst.altOpcodeIndex = altOpcodeTable.addIndexed(inst.opcode1.padEnd(26));
+ });
+ // 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.SseToAvxTable]
+// ============================================================================
+/*
+// Removed from asmjit.
+class InstSseToAvxTable extends core.Task {
+ constructor() {
+ super("InstSseToAvxTable", ["IdEnum"]);
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ const dataTable = new IndexedArray();
+ const indexTable = [];
+
+ function add(data) {
+ return dataTable.addIndexed("{ " + `SseToAvxData::kMode${data.mode}`.padEnd(28) + ", " + String(data.delta).padEnd(4) + " }");
+ }
+
+ // This will receive a zero index, which means that no SseToAvx or AvxToSSe translation is possible.
+ const kInvalidIndex = add({ mode: "None", delta: 0 });
+ insts.forEach((inst) => { indexTable.push(kInvalidIndex); });
+
+ insts.forEach((inst) => {
+ // If it's not `kInvalidIndex` it's an AVX instruction that shares the
+ // SseToAvx data. We won't touch it as it already has the index assigned.
+ if (indexTable[inst.id] === kInvalidIndex) {
+ const data = this.calcSseToAvxData(inst.dbInsts);
+ const index = add(data);
+
+ indexTable[inst.id] = index;
+ if (data.delta !== 0)
+ indexTable[this.ctx.instMap["v" + inst.name].id] = index;
+ }
+ });
+
+ this.inject("SseToAvxIndex",
+ disclaimer(`static const uint8_t sseToAvxIndex[] = {\n${StringUtils.format(indexTable, kIndent, -1)}\n};\n`),
+ indexTable.length * 1);
+
+ this.inject("SseToAvxTable",
+ disclaimer(`static const SseToAvxData sseToAvxData[] = {\n${StringUtils.format(dataTable, kIndent, true)}\n};\n`),
+ dataTable.length * 2);
+ }
+
+ filterSseToAvx(dbInsts) {
+ const filtered = [];
+ for (var x = 0; x < dbInsts.length; x++) {
+ const dbInst = dbInsts[x];
+ const ops = dbInst.operands;
+
+ // SSE instruction does never share its name with AVX one.
+ if (/^(VEX|XOP|EVEX)$/.test(dbInst.prefix))
+ return [];
+
+ var ok = false;
+ for (var y = 0; y < ops.length; y++) {
+ // There is no AVX instruction that works with MMX regs.
+ if (ops[y].reg === "mm") { ok = false; break; }
+ if (ops[y].reg === "xmm") { ok = true; }
+ }
+
+ if (ok)
+ filtered.push(dbInst);
+ }
+
+ return filtered;
+ }
+
+ calcSseToAvxData(dbInsts) {
+ const data = {
+ mode : "None", // No conversion by default.
+ delta: 0 // 0 if no conversion is possible.
+ };
+
+ const dbSseInsts = this.filterSseToAvx(dbInsts);
+ if (!dbSseInsts.length)
+ return data;
+
+ const sseName = dbSseInsts[0].name;
+ const avxName = "v" + sseName;
+
+ const dbAvxInsts = this.ctx.query(avxName);
+ if (!dbAvxInsts.length) {
+ DEBUG(`SseToAvx: Instruction '${sseName}' has no AVX counterpart`);
+ return data;
+ }
+
+ if (avxName === "vblendvpd" || avxName === "vblendvps" || avxName === "vpblendvb") {
+ // Special cases first.
+ data.mode = "Blend";
+ }
+ else {
+ // Common case, deduce conversion mode by checking both SSE and AVX instructions.
+ const map = Object.create(null);
+ for (var sseIndex = 0; sseIndex < dbSseInsts.length; sseIndex++) {
+ const sseInst = dbSseInsts[sseIndex];
+ var match = false;
+
+ for (var avxIndex = 0; avxIndex < dbAvxInsts.length; avxIndex++) {
+ const avxInst = dbAvxInsts[avxIndex];
+
+ // Select only VEX instructions.
+ if (avxInst.prefix !== "VEX") continue;
+
+ // Check if the AVX version is the same.
+ if (GenUtils.eqOps(avxInst.operands, 0, sseInst.operands, 0)) {
+ map.raw = true;
+ match = true;
+ }
+ else if (avxInst.operands[0].data === "xmm" && GenUtils.eqOps(avxInst.operands, 1, sseInst.operands, 0)) {
+ map.nds = true;
+ match = true;
+ }
+ }
+
+ if (!match) {
+ const signature = sseInst.operands.map(function(op) { return op.data; }).join(", ");
+ console.log(`SseToAvx: Instruction '${sseName}(${signature})' has no AVX counterpart`);
+ return data;
+ }
+ }
+
+ data.mode = (map.raw && !map.nds) ? "Move" : (map.raw && map.nds) ? "MoveIfMem" : "Extend";
+ }
+ data.delta = this.ctx.instMap[avxName].id - this.ctx.instMap[sseName].id;
+ return data;
+ }
+}
+*/
+
+// ============================================================================
+// [tablegen.x86.InstSignatureTable]
+// ============================================================================
+
+const RegOp = MapUtils.arrayToMap(["al", "ah", "ax", "eax", "rax", "cl", "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd"]);
+const MemOp = MapUtils.arrayToMap(["m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"]);
+
+const cmpOp = StringUtils.makePriorityCompare([
+ "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd",
+ "mem", "vm", "m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024",
+ "mib",
+ "vm32x", "vm32y", "vm32z", "vm64x", "vm64y", "vm64z",
+ "memBase", "memES", "memDS",
+ "i4", "u4", "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64",
+ "rel8", "rel32",
+ "implicit"
+]);
+
+const OpToAsmJitOp = {
+ "implicit": "F(Implicit)",
+
+ "r8lo" : "F(GpbLo)",
+ "r8hi" : "F(GpbHi)",
+ "r16" : "F(Gpw)",
+ "r32" : "F(Gpd)",
+ "r64" : "F(Gpq)",
+ "xmm" : "F(Xmm)",
+ "ymm" : "F(Ymm)",
+ "zmm" : "F(Zmm)",
+ "mm" : "F(Mm)",
+ "k" : "F(KReg)",
+ "sreg" : "F(SReg)",
+ "creg" : "F(CReg)",
+ "dreg" : "F(DReg)",
+ "st" : "F(St)",
+ "bnd" : "F(Bnd)",
+
+ "mem" : "F(Mem)",
+ "vm" : "F(Vm)",
+
+ "i4" : "F(I4)",
+ "u4" : "F(U4)",
+ "i8" : "F(I8)",
+ "u8" : "F(U8)",
+ "i16" : "F(I16)",
+ "u16" : "F(U16)",
+ "i32" : "F(I32)",
+ "u32" : "F(U32)",
+ "i64" : "F(I64)",
+ "u64" : "F(U64)",
+
+ "rel8" : "F(Rel8)",
+ "rel32" : "F(Rel32)",
+
+ "m8" : "M(M8)",
+ "m16" : "M(M16)",
+ "m32" : "M(M32)",
+ "m48" : "M(M48)",
+ "m64" : "M(M64)",
+ "m80" : "M(M80)",
+ "m128" : "M(M128)",
+ "m256" : "M(M256)",
+ "m512" : "M(M512)",
+ "m1024" : "M(M1024)",
+ "mib" : "M(Mib)",
+ "mAny" : "M(Any)",
+ "vm32x" : "M(Vm32x)",
+ "vm32y" : "M(Vm32y)",
+ "vm32z" : "M(Vm32z)",
+ "vm64x" : "M(Vm64x)",
+ "vm64y" : "M(Vm64y)",
+ "vm64z" : "M(Vm64z)",
+
+ "memBase" : "M(BaseOnly)",
+ "memDS" : "M(Ds)",
+ "memES" : "M(Es)"
+};
+
+function StringifyArray(a, map) {
+ var s = "";
+ for (var i = 0; i < a.length; i++) {
+ const op = a[i];
+ if (!hasOwn.call(map, op))
+ FAIL(`UNHANDLED OPERAND '${op}'`);
+ s += (s ? " | " : "") + map[op];
+ }
+ return s ? s : "0";
+}
+
+class OSignature {
+ constructor() {
+ this.flags = Object.create(null);
+ }
+
+ equals(other) {
+ return MapUtils.equals(this.flags, other.flags);
+ }
+
+ xor(other) {
+ const result = MapUtils.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;
+ }
+
+ simplify() {
+ const flags = this.flags;
+
+ // 32-bit register or 16-bit memory implies also 16-bit reg.
+ if (flags.r32 && flags.m16) {
+ flags.r16 = true;
+ }
+
+ // 32-bit register or 8-bit memory implies also 16-bit and 8-bit reg.
+ if (flags.r32 && flags.m8) {
+ flags.r8lo = true;
+ flags.r8hi = true;
+ flags.r16 = 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 oFlags = this.flags;
+
+ var mFlags = Object.create(null);
+ var mMemFlags = Object.create(null);
+ var mExtFlags = Object.create(null);
+ var sRegMask = 0;
+
+ for (var k in oFlags) {
+ switch (k) {
+ case "implicit":
+ case "r8lo" :
+ case "r8hi" :
+ case "r16" :
+ case "r32" :
+ case "r64" :
+ case "creg" :
+ case "dreg" :
+ case "sreg" :
+ case "bnd" :
+ case "st" :
+ case "k" :
+ case "mm" :
+ case "xmm" :
+ case "ymm" :
+ case "zmm" : mFlags[k] = true; break;
+
+ case "m8" :
+ case "m16" :
+ case "m32" :
+ case "m48" :
+ case "m64" :
+ case "m80" :
+ case "m128" :
+ case "m256" :
+ case "m512" :
+ case "m1024" : mFlags.mem = true; mMemFlags[k] = true; break;
+ case "mib" : mFlags.mem = true; mMemFlags.mib = true; break;
+ case "mem" : mFlags.mem = true; mMemFlags.mAny = true; break;
+
+ case "memBase" : mFlags.mem = true; mMemFlags.memBase = true; break;
+ case "memDS" : mFlags.mem = true; mMemFlags.memDS = true; break;
+ case "memES" : mFlags.mem = true; mMemFlags.memES = true; break;
+ case "memZAX" : mFlags.mem = true; sRegMask |= 1 << 0; break;
+ case "memZSI" : mFlags.mem = true; sRegMask |= 1 << 6; break;
+ case "memZDI" : mFlags.mem = true; sRegMask |= 1 << 7; break;
+
+ case "vm32x" : mFlags.vm = true; mMemFlags.vm32x = true; break;
+ case "vm32y" : mFlags.vm = true; mMemFlags.vm32y = true; break;
+ case "vm32z" : mFlags.vm = true; mMemFlags.vm32z = true; break;
+ case "vm64x" : mFlags.vm = true; mMemFlags.vm64x = true; break;
+ case "vm64y" : mFlags.vm = true; mMemFlags.vm64y = true; break;
+ case "vm64z" : mFlags.vm = true; mMemFlags.vm64z = true; break;
+
+ case "i4" :
+ case "u4" :
+ case "i8" :
+ case "u8" :
+ case "i16" :
+ case "u16" :
+ case "i32" :
+ case "u32" :
+ case "i64" :
+ case "u64" : mFlags[k] = true; break;
+
+ case "rel8" :
+ case "rel32" :
+ mFlags.i32 = true;
+ mFlags.i64 = true;
+ mFlags[k] = true;
+ break;
+
+ case "rel16" :
+ mFlags.i32 = true;
+ mFlags.i64 = true;
+ mFlags.rel32 = true;
+ break;
+
+ default: {
+ switch (k) {
+ case "es" : mFlags.sreg = true; sRegMask |= 1 << 1; break;
+ case "cs" : mFlags.sreg = true; sRegMask |= 1 << 2; break;
+ case "ss" : mFlags.sreg = true; sRegMask |= 1 << 3; break;
+ case "ds" : mFlags.sreg = true; sRegMask |= 1 << 4; break;
+ case "fs" : mFlags.sreg = true; sRegMask |= 1 << 5; break;
+ case "gs" : mFlags.sreg = true; sRegMask |= 1 << 6; break;
+ case "al" : mFlags.r8lo = true; sRegMask |= 1 << 0; break;
+ case "ah" : mFlags.r8hi = true; sRegMask |= 1 << 0; break;
+ case "ax" : mFlags.r16 = true; sRegMask |= 1 << 0; break;
+ case "eax" : mFlags.r32 = true; sRegMask |= 1 << 0; break;
+ case "rax" : mFlags.r64 = true; sRegMask |= 1 << 0; break;
+ case "cl" : mFlags.r8lo = true; sRegMask |= 1 << 1; break;
+ case "ch" : mFlags.r8hi = true; sRegMask |= 1 << 1; break;
+ case "cx" : mFlags.r16 = true; sRegMask |= 1 << 1; break;
+ case "ecx" : mFlags.r32 = true; sRegMask |= 1 << 1; break;
+ case "rcx" : mFlags.r64 = true; sRegMask |= 1 << 1; break;
+ case "dl" : mFlags.r8lo = true; sRegMask |= 1 << 2; break;
+ case "dh" : mFlags.r8hi = true; sRegMask |= 1 << 2; break;
+ case "dx" : mFlags.r16 = true; sRegMask |= 1 << 2; break;
+ case "edx" : mFlags.r32 = true; sRegMask |= 1 << 2; break;
+ case "rdx" : mFlags.r64 = true; sRegMask |= 1 << 2; break;
+ case "bl" : mFlags.r8lo = true; sRegMask |= 1 << 3; break;
+ case "bh" : mFlags.r8hi = true; sRegMask |= 1 << 3; break;
+ case "bx" : mFlags.r16 = true; sRegMask |= 1 << 3; break;
+ case "ebx" : mFlags.r32 = true; sRegMask |= 1 << 3; break;
+ case "rbx" : mFlags.r64 = true; sRegMask |= 1 << 3; break;
+ case "si" : mFlags.r16 = true; sRegMask |= 1 << 6; break;
+ case "esi" : mFlags.r32 = true; sRegMask |= 1 << 6; break;
+ case "rsi" : mFlags.r64 = true; sRegMask |= 1 << 6; break;
+ case "di" : mFlags.r16 = true; sRegMask |= 1 << 7; break;
+ case "edi" : mFlags.r32 = true; sRegMask |= 1 << 7; break;
+ case "rdi" : mFlags.r64 = true; sRegMask |= 1 << 7; break;
+ case "st0" : mFlags.st = true; sRegMask |= 1 << 0; break;
+ case "xmm0" : mFlags.xmm = true; sRegMask |= 1 << 0; break;
+ case "ymm0" : mFlags.ymm = true; sRegMask |= 1 << 0; break;
+ default:
+ console.log(`UNKNOWN OPERAND '${k}'`);
+ }
+ }
+ }
+ }
+
+ const sFlags = StringifyArray(ArrayUtils.sorted(mFlags , cmpOp), OpToAsmJitOp);
+ const sMemFlags = StringifyArray(ArrayUtils.sorted(mMemFlags, cmpOp), OpToAsmJitOp);
+ const sExtFlags = StringifyArray(ArrayUtils.sorted(mExtFlags, cmpOp), OpToAsmJitOp);
+
+ return `ROW(${sFlags || 0}, ${sMemFlags || 0}, ${sExtFlags || 0}, ${decToHex(sRegMask, 2)})`;
+ }
+}
+
+class ISignature extends Array {
+ constructor(name) {
+ super();
+ this.name = name;
+ this.x86 = false;
+ this.x64 = false;
+ this.implicit = 0; // Number of implicit operands.
+ }
+
+ simplify() {
+ for (var i = 0; i < this.length; i++)
+ this[i].simplify();
+ }
+
+ 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.
+ var ok = 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 (!ok || 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;
+
+ var xorIndex = -1;
+ for (var 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;
+
+ this.x86 = this.x86 || other.x86;
+ this.x64 = this.x64 || other.x64;
+
+ 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() {
+ // Calculates a hash-value (aka key) of all register operands specified by `regOps` in `inst`.
+ function keyOf(inst, regOps) {
+ var s = "";
+ for (var i = 0; i < inst.length; i++) {
+ const op = inst[i];
+ if (regOps & (1 << i))
+ s += "{" + ArrayUtils.sorted(MapUtils.and(op.flags, RegOp)).join("|") + "}";
+ }
+ 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 = MapUtils.firstOf(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 (MapUtils.anyOf(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 = MapUtils.anyOf(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 = MapUtils.firstOf(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`.
+ }
+ 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}`);
+ }
+ }
+ }
+
+ simplify() {
+ for (var i = 0; i < this.length; i++)
+ this[i].simplify();
+ }
+
+ compact() {
+ 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);
+ continue;
+ }
+ j++;
+ }
+ }
+ }
+
+ toString() {
+ return `[${this.join(", ")}]`;
+ }
+}
+
+class InstSignatureTable extends core.Task {
+ constructor() {
+ super("InstSignatureTable");
+
+ this.maxOpRows = 0;
+ this.opBlackList = {
+ "moff8" : true,
+ "moff16": true,
+ "moff32": true,
+ "moff64": true
+ };
+ }
+
+ 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, 0, 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, (x86 ? uint8_t(InstDB::kModeX86) : uint8_t(0)) | \\\n` +
+ ` (x64 ? uint8_t(InstDB::kModeX64) : 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(flags, mFlags, extFlags, regId) { uint32_t(flags), uint16_t(mFlags), uint8_t(extFlags), uint8_t(regId) }\n` +
+ `#define F(VAL) InstDB::kOp##VAL\n` +
+ `#define M(VAL) InstDB::kMemOp##VAL\n` +
+ StringUtils.makeCxxArray(oSignatureArr, "const InstDB::OpSignature InstDB::_opSignatureTable[]") +
+ `#undef M\n` +
+ `#undef F\n` +
+ `#undef ROW\n`;
+ this.inject("InstSignatureTable", disclaimer(s), oSignatureArr.length * 8 + iSignatureArr.length * 8);
+ }
+
+ makeSignatures(dbInsts) {
+ 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;
+
+ // Terminate if this operand is something asmjit doesn't support
+ // and skip all instructions having implicit `imm` operand of `1`,
+ // which are handled fine by asmjit.
+ if (this.opBlackList[mem] === true || iop.immValue !== null)
+ break;
+
+ if (reg === "r8") reg = "r8lo";
+ if (reg === "seg") reg = "sreg";
+ if (reg === "st(i)") reg = "st";
+ if (reg === "st(0)") reg = "st0";
+
+ 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.memSeg;
+ if (seg) {
+ 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) {
+ op.flags[reg] = true;
+ if (reg === "r8lo") op.flags.r8hi = true;
+ }
+ if (mem) {
+ op.flags[mem] = true;
+ // Exception: Allow LEA to use any memory size.
+ if (inst.name === "lea") MapUtils.add(op.flags, MemOp);
+ }
+ 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);
+ }
+ }
+
+ signatures.calcImplicitMemSize();
+ signatures.simplify();
+ signatures.compact();
+
+ signatures.simplify();
+ signatures.compact();
+
+ return signatures;
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.InstCommonInfoTableB]
+// ============================================================================
+
+class InstCommonInfoTableB extends core.Task {
+ constructor() {
+ super("InstCommonInfoTableB");
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+ const commonTableB = new IndexedArray();
+ const rwInfoTable = 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 rwDataIndex = rwInfoTable.addIndexed(`{ ${rData}, ${wData} }`);
+
+ inst.commomInfoIndexB = commonTableB.addIndexed(`{ { ${features} }, ${rwDataIndex}, 0 }`);
+ });
+
+ var s = `#define EXT(VAL) uint32_t(Features::k##VAL)\n` +
+ `const InstDB::CommonInfoTableB InstDB::_commonInfoTableB[] = {\n${StringUtils.format(commonTableB, kIndent, true)}\n};\n` +
+ `#undef EXT\n` +
+ `\n` +
+ `#define FLAG(VAL) uint32_t(Status::k##VAL)\n` +
+ `const InstDB::RWFlagsInfoTable InstDB::_rwFlagsInfoTable[] = {\n${StringUtils.format(rwInfoTable, kIndent, true)}\n};\n` +
+ `#undef FLAG\n`;
+ this.inject("InstCommonInfoTableB", disclaimer(s), commonTableB.length * 8 + rwInfoTable.length * 8);
+ }
+
+ 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 specialRegs = dbInst.specialRegs;
+
+ // 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 specialReg in specialRegs) {
+ var flag = "";
+ switch (specialReg) {
+ case "FLAGS.CF": flag = "CF"; break;
+ case "FLAGS.OF": flag = "OF"; break;
+ case "FLAGS.SF": flag = "SF"; break;
+ case "FLAGS.ZF": flag = "ZF"; break;
+ case "FLAGS.AF": flag = "AF"; break;
+ case "FLAGS.PF": flag = "PF"; break;
+ case "FLAGS.DF": flag = "DF"; break;
+ case "FLAGS.IF": flag = "IF"; break;
+ //case "FLAGS.TF": flag = "TF"; break;
+ case "FLAGS.AC": flag = "AC"; break;
+ case "X86SW.C0": flag = "C0"; break;
+ case "X86SW.C1": flag = "C1"; break;
+ case "X86SW.C2": flag = "C2"; break;
+ case "X86SW.C3": flag = "C3"; break;
+ default:
+ continue;
+ }
+
+ switch (specialRegs[specialReg]) {
+ 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 = MapUtils.arrayToMap([
+ "call", "movq"
+]);
+
+class InstRWInfoTable extends core.Task {
+ constructor() {
+ super("InstRWInfoTable");
+
+ this.rwInfoIndex = [];
+ this.rwInfoTable = new IndexedArray();
+ this.rmInfoTable = new IndexedArray();
+ this.opInfoTable = new IndexedArray();
+
+ const _ = null;
+ this.rwCategoryByName = {
+ "imul" : "Imul",
+ "mov" : "Mov",
+ "movhpd" : "Movh64",
+ "movhps" : "Movh64",
+ "vmaskmovpd": "Vmaskmov",
+ "vmaskmovps": "Vmaskmov",
+ "vmovddup" : "Vmovddup",
+ "vmovmskpd" : "Vmovmskpd",
+ "vmovmskps" : "Vmovmskps",
+ "vpmaskmovd": "Vmaskmov",
+ "vpmaskmovq": "Vmaskmov"
+ };
+ this.rwCategoryByData = {
+ Vmov1_8: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 8}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 16}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov1_4: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width:128}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov1_2: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width:128}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width:256}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
+ ],
+ Vmov2_1: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_]
+ ],
+ Vmov4_1: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_]
+ ],
+ Vmov8_1: [
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 16},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
+ [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_]
+ ]
+ };
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ const noRmInfo = CxxUtils.struct(
+ "InstDB::RWInfoRm::kCategory" + "None".padEnd(10),
+ StringUtils.decToHex(0, 2),
+ String(0).padEnd(2),
+ CxxUtils.flags({}),
+ "0"
+ );
+
+ const noOpInfo = CxxUtils.struct(
+ "0x0000000000000000u",
+ "0x0000000000000000u",
+ "0xFF",
+ CxxUtils.struct(0),
+ "0"
+ );
+
+ 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(o2Insts), this.rwInfo(oxInsts)];
+ const rmInfoArray = [this.rmInfo(o2Insts), this.rmInfo(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;
+ Lang.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 opData = CxxUtils.struct(
+ 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),
+ CxxUtils.struct(0),
+ CxxUtils.flags(flags, function(flag) { return "OpRWInfo::k" + flag; })
+ );
+
+ rwOpsIndex.push(this.opInfoTable.addIndexed(opData));
+ }
+
+ const rmData = CxxUtils.struct(
+ "InstDB::RWInfoRm::kCategory" + rmInfo.category.padEnd(10),
+ StringUtils.decToHex(rmInfo.rmIndexes, 2),
+ String(Math.max(rmInfo.memFixed, 0)).padEnd(2),
+ CxxUtils.flags({ "InstDB::RWInfoRm::kFlagAmbiguous": Boolean(rmInfo.memAmbiguous) }),
+ rmInfo.memExtension === "None" ? "0" : "Features::k" + rmInfo.memExtension
+ );
+
+ const rwData = CxxUtils.struct(
+ "InstDB::RWInfo::kCategory" + rwInfo.category.padEnd(10),
+ String(this.rmInfoTable.addIndexed(rmData)).padEnd(2),
+ CxxUtils.struct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); })))
+ );
+
+ this.rwInfoIndex.push(this.rwInfoTable.addIndexed(rwData));
+ }
+ });
+
+ var s = "";
+ s += "const uint8_t InstDB::rwInfoIndex[Inst::_kIdCount * 2] = {\n" + StringUtils.format(this.rwInfoIndex, kIndent, -1) + "\n};\n";
+ s += "\n";
+ s += "const InstDB::RWInfo InstDB::rwInfo[] = {\n" + StringUtils.format(this.rwInfoTable, 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.rwInfoIndex.length +
+ this.rwInfoTable.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)
+ FAIL(`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(dbInsts) {
+ 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" : "?",
+ flags: {},
+ fixed: GenUtils.fixedRegOf(op.reg),
+ 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" : "?",
+ flags: {},
+ fixed: -1,
+ index: -1,
+ width: -1
+ };
+
+ if (op.isReg())
+ d.fixed = GenUtils.fixedRegOf(op.reg);
+ else
+ d.fixed = GenUtils.fixedRegOf(op.mem);
+
+ if (op.zext)
+ d.flags.ZExt = 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.memSeg)
+ d.flags.MemPhysId = true;
+ else
+ d.flags.RegPhysId = true;
+ }
+
+ if (rwOps[j] === null) {
+ rwOps[j] = d;
+ }
+ else {
+ if (!Lang.deepEqExcept(rwOps[j], d, { "fixed": true, "flags": true }))
+ return null;
+
+ if (rwOps[j].fixed === -1)
+ rwOps[j].fixed = d.fixed;
+ Lang.merge(rwOps[j].flags, d.flags);
+ }
+ }
+ }
+ 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 |= Lang.deepEq(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() };
+
+ // FAILURE: 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;
+ }
+
+ // Reg/Mem Info
+ // ------------
+
+ rmInfo(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)
+ };
+
+ 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) {
+ if (dbInst.name === "mov" || dbInst.name === "vmovddup")
+ return "None"; // Special case
+ return StringUtils.capitalize(dbInst.name); // Special case.
+ }
+ }
+
+ 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 "vpslldq":
+ case "vpsrldq":
+ return "AVX512_BW";
+
+ default:
+ return "None";
+ }
+ }
+}
+
+// ============================================================================
+// [tablegen.x86.InstCommonTable]
+// ============================================================================
+
+class InstCommonTable extends core.Task {
+ constructor() {
+ super("InstCommonTable", [
+ "IdEnum",
+ "NameTable",
+ "InstSignatureTable",
+ "InstCommonInfoTableB",
+ "InstRWInfoTable"
+ ]);
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+ const table = new IndexedArray();
+
+ insts.forEach((inst) => {
+ const flags = inst.flags.map(function(flag) { return `F(${flag})`; }).join("|") || "0";
+ const singleRegCase = `SINGLE_REG(${inst.singleRegCase})`;
+ const controlType = `CONTROL(${inst.controlType})`;
+
+ const row = "{ " +
+ String(flags ).padEnd(54) + ", " +
+ String(inst.signatureIndex).padEnd( 3) + ", " +
+ String(inst.signatureCount).padEnd( 2) + ", " +
+ String(controlType ).padEnd(16) + ", " +
+ String(singleRegCase ).padEnd(16) + ", " + "0 }";
+ inst.commonInfoIndexA = table.addIndexed(row);
+ });
+
+ var s = `#define F(VAL) InstDB::kFlag##VAL\n` +
+ `#define CONTROL(VAL) Inst::kControl##VAL\n` +
+ `#define SINGLE_REG(VAL) InstDB::kSingleReg##VAL\n` +
+ `const InstDB::CommonInfo InstDB::_commonInfoTable[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
+ `#undef SINGLE_REG\n` +
+ `#undef CONTROL\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 InstCommonInfoTableB())
+ .addTask(new InstRWInfoTable())
+ .addTask(new InstCommonTable())
+ .run();
diff --git a/tools/tablegen.js b/tools/tablegen.js
new file mode 100644
index 0000000..7d7a20c
--- /dev/null
+++ b/tools/tablegen.js
@@ -0,0 +1,917 @@
+// [AsmJit]
+// Machine Code Generation for C++.
+//
+// [License]
+// ZLIB - See LICENSE.md file in the package.
+
+// ============================================================================
+// tablegen.js
+//
+// Provides core foundation for generating tables that AsmJit requires. This
+// file should provide everything table generators need in general.
+// ============================================================================
+
+"use strict";
+
+const VERBOSE = false;
+
+// ============================================================================
+// [Imports]
+// ============================================================================
+
+const fs = require("fs");
+const hasOwn = Object.prototype.hasOwnProperty;
+
+const asmdb = (function() {
+ // Try to import local 'asmdb' package, if available.
+ try {
+ return require("./asmdb");
+ }
+ catch (ex) {
+ if (ex.code !== "MODULE_NOT_FOUND") {
+ console.log(`FATAL ERROR: ${ex.message}`);
+ throw ex;
+ }
+ }
+
+ // Try to import global 'asmdb' package as local package is not available.
+ return require("asmdb");
+})();
+exports.asmdb = asmdb;
+
+// ============================================================================
+// [Constants]
+// ============================================================================
+
+const kIndent = " ";
+const kJustify = 119;
+const kAsmJitRoot = "..";
+
+exports.kIndent = kIndent;
+exports.kJustify = kJustify;
+exports.kAsmJitRoot = kAsmJitRoot;
+
+// ============================================================================
+// [Debugging]
+// ============================================================================
+
+function DEBUG(msg) {
+ if (VERBOSE)
+ console.log(msg);
+}
+exports.DEBUG = DEBUG;
+
+function WARN(msg) {
+ console.log(msg);
+}
+exports.WARN = WARN;
+
+function FAIL(msg) {
+ console.log(`FATAL ERROR: ${msg}`);
+ throw new Error(msg);
+}
+exports.FAIL = FAIL;
+
+// ============================================================================
+// [Lang]
+// ============================================================================
+
+function nop(x) { return x; }
+
+class Lang {
+ static merge(a, b) {
+ if (a === b)
+ return a;
+
+ for (var k in b) {
+ var av = a[k];
+ var bv = b[k];
+
+ if (typeof av === "object" && typeof bv === "object")
+ Lang.merge(av, bv);
+ else
+ a[k] = bv;
+ }
+
+ return a;
+ }
+
+ static deepEq(a, b) {
+ if (a === b)
+ return true;
+
+ if (typeof a !== typeof b)
+ return false;
+
+ if (typeof a !== "object")
+ return a === b;
+
+ if (Array.isArray(a) || Array.isArray(b)) {
+ if (Array.isArray(a) !== Array.isArray(b))
+ return false;
+
+ const len = a.length;
+ if (b.length !== len)
+ return false;
+
+ for (var i = 0; i < len; i++)
+ if (!Lang.deepEq(a[i], b[i]))
+ return false;
+ }
+ else {
+ if (a === null || b === null)
+ return a === b;
+
+ for (var k in a)
+ if (!hasOwn.call(b, k) || !Lang.deepEq(a[k], b[k]))
+ return false;
+
+ for (var k in b)
+ if (!hasOwn.call(a, k))
+ return false;
+ }
+
+ return true;
+ }
+
+ static deepEqExcept(a, b, except) {
+ if (a === b)
+ return true;
+
+ if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b))
+ return Lang.deepEq(a, b);
+
+ for (var k in a)
+ if (!hasOwn.call(except, k) && (!hasOwn.call(b, k) || !Lang.deepEq(a[k], b[k])))
+ return false;
+
+ for (var k in b)
+ if (!hasOwn.call(except, k) && !hasOwn.call(a, k))
+ return false;
+
+ return true;
+ }
+}
+exports.Lang = Lang;
+
+// ============================================================================
+// [StringUtils]
+// ============================================================================
+
+class StringUtils {
+ static asString(x) { return String(x); }
+
+ static capitalize(s) {
+ s = String(s);
+ return !s ? s : s[0].toUpperCase() + s.substr(1);
+ }
+
+ static trimLeft(s) { return s.replace(/^\s+/, ""); }
+ static trimRight(s) { return s.replace(/\s+$/, ""); }
+
+ static upFirst(s) {
+ if (!s) return "";
+ return s[0].toUpperCase() + s.substr(1);
+ }
+
+ static decToHex(n, nPad) {
+ var hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);
+ while (nPad > hex.length)
+ hex = "0" + hex;
+ return "0x" + hex.toUpperCase();
+ }
+
+ static format(array, indent, showIndex, mapFn) {
+ if (!mapFn)
+ mapFn = StringUtils.asString;
+
+ var s = "";
+ var threshold = 80;
+
+ if (showIndex === -1)
+ s += indent;
+
+ for (var i = 0; i < array.length; i++) {
+ const item = array[i];
+ const last = i === array.length - 1;
+
+ if (showIndex !== -1)
+ s += indent;
+
+ s += mapFn(item);
+ if (showIndex > 0) {
+ s += `${last ? " " : ","} // #${i}`;
+ if (typeof array.refCountOf === "function")
+ s += ` [ref=${array.refCountOf(item)}x]`;
+ }
+ else if (!last) {
+ s += ",";
+ }
+
+ if (showIndex === -1) {
+ if (s.length >= threshold - 1 && !last) {
+ s += "\n" + indent;
+ threshold += 80;
+ }
+ else {
+ if (!last) s += " ";
+ }
+ }
+ else {
+ if (!last) s += "\n";
+ }
+ }
+
+ return s;
+ }
+
+ static makeCxxArray(array, code, indent) {
+ if (!indent) indent = kIndent;
+ return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;
+ }
+
+ static makeCxxArrayWithComment(array, code, indent) {
+ if (!indent) indent = kIndent;
+ var s = "";
+ for (var i = 0; i < array.length; i++) {
+ const last = i === array.length - 1;
+ s += indent + array[i].data +
+ (last ? " // " : ", // ") + (array[i].refs ? "#" + String(i) : "").padEnd(5) + array[i].comment + "\n";
+ }
+ return `${code} = {\n${s}};\n`;
+ }
+
+ static disclaimer(s) {
+ return "// ------------------- Automatically generated, do not edit -------------------\n" +
+ s +
+ "// ----------------------------------------------------------------------------\n";
+ }
+
+ static indent(s, indentation) {
+ var lines = s.split(/\r?\n/g);
+ if (indentation) {
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line) lines[i] = indentation + line;
+ }
+ }
+
+ return lines.join("\n");
+ }
+
+ static inject(s, start, end, code) {
+ var iStart = s.indexOf(start);
+ var iEnd = s.indexOf(end);
+
+ if (iStart === -1)
+ FAIL(`Utils.inject(): Couldn't locate start mark '${start}'`);
+
+ if (iEnd === -1)
+ FAIL(`Utils.inject(): Couldn't locate end mark '${end}'`);
+
+ var nIndent = 0;
+ while (iStart > 0 && s[iStart-1] === " ") {
+ iStart--;
+ nIndent++;
+ }
+
+ if (nIndent) {
+ const indentation = " ".repeat(nIndent);
+ code = StringUtils.indent(code, indentation) + indentation;
+ }
+
+ return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);
+ }
+
+ static makePriorityCompare(priorityArray) {
+ const map = Object.create(null);
+ priorityArray.forEach((str, index) => { map[str] = index; });
+
+ return function(a, b) {
+ const ax = hasOwn.call(map, a) ? map[a] : Infinity;
+ const bx = hasOwn.call(map, b) ? map[b] : Infinity;
+ return ax != bx ? ax - bx : a < b ? -1 : a > b ? 1 : 0;
+ }
+ }
+}
+exports.StringUtils = StringUtils;
+
+// ============================================================================
+// [ArrayUtils]
+// ============================================================================
+
+class ArrayUtils {
+ static min(arr, fn) {
+ if (!arr.length)
+ return null;
+
+ if (!fn)
+ fn = nop;
+
+ var v = fn(arr[0]);
+ for (var i = 1; i < arr.length; i++)
+ v = Math.min(v, fn(arr[i]));
+ return v;
+ }
+
+ static max(arr, fn) {
+ if (!arr.length)
+ return null;
+
+ if (!fn)
+ fn = nop;
+
+ var v = fn(arr[0]);
+ for (var i = 1; i < arr.length; i++)
+ v = Math.max(v, fn(arr[i]));
+ return v;
+ }
+
+ static sorted(obj, cmp) {
+ const out = Array.isArray(obj) ? obj.slice() : Object.getOwnPropertyNames(obj);
+ out.sort(cmp);
+ return out;
+ }
+
+ static deepIndexOf(arr, what) {
+ for (var i = 0; i < arr.length; i++)
+ if (Lang.deepEq(arr[i], what))
+ return i;
+ return -1;
+ }
+}
+exports.ArrayUtils = ArrayUtils;
+
+// ============================================================================
+// [MapUtils]
+// ============================================================================
+
+class MapUtils {
+ static clone(map) {
+ return Object.assign(Object.create(null), map);
+ }
+
+ static arrayToMap(arr, value) {
+ if (value === undefined)
+ value = true;
+
+ const out = Object.create(null);
+ for (var i = 0; i < arr.length; i++)
+ out[arr[i]] = value;
+ return out;
+ }
+
+ static equals(a, b) {
+ for (var k in a) if (!hasOwn.call(b, k)) return false;
+ for (var k in b) if (!hasOwn.call(a, k)) return false;
+ return true;
+ }
+
+ static firstOf(map, flags) {
+ for (var k in flags)
+ if (hasOwn.call(map, k))
+ return k;
+ return undefined;
+ }
+
+ static anyOf(map, flags) {
+ for (var k in flags)
+ if (hasOwn.call(map, k))
+ return true;
+ return false;
+ }
+
+ static add(a, b) {
+ for (var k in b)
+ a[k] = b[k];
+ return a;
+ }
+
+ static and(a, b) {
+ const out = Object.create(null);
+ for (var k in a)
+ if (hasOwn.call(b, k))
+ out[k] = true;
+ return out;
+ }
+
+ static xor(a, b) {
+ const out = Object.create(null);
+ for (var k in a) if (!hasOwn.call(b, k)) out[k] = true;
+ for (var k in b) if (!hasOwn.call(a, k)) out[k] = true;
+ return out;
+ }
+};
+exports.MapUtils = MapUtils;
+
+// ============================================================================
+// [CxxUtils]
+// ============================================================================
+
+class CxxUtils {
+ static flags(obj, fn) {
+ if (!fn)
+ fn = nop;
+
+ var out = "";
+ for (var k in obj) {
+ if (obj[k])
+ out += (out ? " | " : "") + fn(k);
+ }
+ return out ? out : "0";
+ }
+
+ static struct(...args) {
+ return "{ " + args.join(", ") + " }";
+ }
+};
+exports.CxxUtils = CxxUtils;
+
+// ============================================================================
+// [IndexedString]
+// ============================================================================
+
+// IndexedString is mostly used to merge all instruction names into a single
+// string with external index. It's designed mostly for generating C++ tables.
+//
+// Consider the following cases in C++:
+//
+// a) static const char* const* instNames = { "add", "mov", "vpunpcklbw" };
+//
+// b) static const char instNames[] = { "add\0" "mov\0" "vpunpcklbw\0" };
+// static const uint16_t instNameIndex[] = { 0, 4, 8 };
+//
+// The latter (b) has an advantage that it doesn't have to be relocated by the
+// linker, which saves a lot of space in the resulting binary and a lot of CPU
+// cycles (and memory) when the linker loads it. AsmJit supports thousands of
+// instructions so each optimization like this makes it smaller and faster to
+// load.
+class IndexedString {
+ constructor() {
+ this.map = Object.create(null);
+ this.array = [];
+ this.size = -1;
+ }
+
+ add(s) {
+ this.map[s] = -1;
+ }
+
+ index() {
+ const map = this.map;
+ const array = this.array;
+ const partialMap = Object.create(null);
+
+ var k, kp;
+ var i, len;
+
+ // Create a map that will contain all keys and partial keys.
+ for (k in map) {
+ if (!k) {
+ partialMap[k] = k;
+ }
+ else {
+ for (i = 0, len = k.length; i < len; i++) {
+ kp = k.substr(i);
+ if (!hasOwn.call(partialMap, kp) || partialMap[kp].length < len)
+ partialMap[kp] = k;
+ }
+ }
+ }
+
+ // Create an array that will only contain keys that are needed.
+ for (k in map)
+ if (partialMap[k] === k)
+ array.push(k);
+ array.sort();
+
+ // Create valid offsets to the `array`.
+ var offMap = Object.create(null);
+ var offset = 0;
+
+ for (i = 0, len = array.length; i < len; i++) {
+ k = array[i];
+
+ offMap[k] = offset;
+ offset += k.length + 1;
+ }
+ this.size = offset;
+
+ // Assign valid offsets to `map`.
+ for (kp in map) {
+ k = partialMap[kp];
+ map[kp] = offMap[k] + k.length - kp.length;
+ }
+ }
+
+ format(indent, justify) {
+ if (this.size === -1)
+ FAIL(`IndexedString.format(): not indexed yet, call index()`);
+
+ const array = this.array;
+ if (!justify) justify = 0;
+
+ var i;
+ var s = "";
+ var line = "";
+
+ for (i = 0; i < array.length; i++) {
+ const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");
+ const newl = line + (line ? " " : indent) + item;
+
+ if (newl.length <= justify) {
+ line = newl;
+ continue;
+ }
+ else {
+ s += line + "\n";
+ line = indent + item;
+ }
+ }
+
+ return s + line;
+ }
+
+ getSize() {
+ if (this.size === -1)
+ FAIL(`IndexedString.getSize(): Not indexed yet, call index()`);
+ return this.size;
+ }
+
+ getIndex(k) {
+ if (this.size === -1)
+ FAIL(`IndexedString.getIndex(): Not indexed yet, call index()`);
+
+ if (!hasOwn.call(this.map, k))
+ FAIL(`IndexedString.getIndex(): Key '${k}' not found.`);
+
+ return this.map[k];
+ }
+}
+exports.IndexedString = IndexedString;
+
+// ============================================================================
+// [IndexedArray]
+// ============================================================================
+
+// IndexedArray is an Array replacement that allows to index each item inserted
+// to it. Its main purpose is to avoid data duplication, if an item passed to
+// `addIndexed()` is already within the Array then it's not inserted and the
+// existing index is returned instead.
+function IndexedArray_keyOf(item) {
+ return typeof item === "string" ? item : JSON.stringify(item);
+}
+
+class IndexedArray extends Array {
+ constructor() {
+ super();
+ this._index = Object.create(null);
+ }
+
+ refCountOf(item) {
+ const key = IndexedArray_keyOf(item);
+ const idx = this._index[key];
+
+ return idx !== undefined ? idx.refCount : 0;
+ }
+
+ addIndexed(item) {
+ const key = IndexedArray_keyOf(item);
+ var idx = this._index[key];
+
+ if (idx !== undefined) {
+ idx.refCount++;
+ return idx.data;
+ }
+
+ idx = this.length;
+ this._index[key] = {
+ data: idx,
+ refCount: 1
+ };
+ this.push(item);
+ return idx;
+ }
+}
+exports.IndexedArray = IndexedArray;
+
+// ============================================================================
+// [Task]
+// ============================================================================
+
+// A base runnable task that can access the TableGen through `this.ctx`.
+class Task {
+ constructor(name, deps) {
+ this.ctx = null;
+ this.name = name || "";
+ this.deps = deps || [];
+ }
+
+ inject(key, str, size) {
+ this.ctx.inject(key, str, size);
+ return this;
+ }
+
+ run() {
+ FAIL("Task.run(): Must be reimplemented");
+ }
+}
+exports.Task = Task;
+
+// ============================================================================
+// [TableGen]
+// ============================================================================
+
+// Main context used to load, generate, and store instruction tables. The idea
+// is to be extensible, so it stores 'Task's to be executed with minimal deps
+// management.
+class TableGen {
+ constructor(arch) {
+ this.arch = arch;
+ this.files = Object.create(null);
+ this.tableSizes = Object.create(null);
+
+ this.tasks = [];
+ this.taskMap = Object.create(null);
+
+ this.insts = [];
+ this.instMap = Object.create(null);
+
+ this.aliases = [];
+ this.aliasMem = Object.create(null);
+ }
+
+ // --------------------------------------------------------------------------
+ // [File Management]
+ // --------------------------------------------------------------------------
+
+ load(fileList) {
+ for (var i = 0; i < fileList.length; i++) {
+ const file = fileList[i];
+ const path = kAsmJitRoot + "/" + file;
+ const data = fs.readFileSync(path, "utf8").replace(/\r\n/g, "\n");
+
+ this.files[file] = {
+ prev: data,
+ data: data
+ };
+ }
+ return this;
+ }
+
+ save() {
+ for (var file in this.files) {
+ const obj = this.files[file];
+ if (obj.data !== obj.prev) {
+ const path = kAsmJitRoot + "/" + file;
+ console.log(`MODIFIED '${file}'`);
+
+ fs.writeFileSync(path + ".backup", obj.prev, "utf8");
+ fs.writeFileSync(path, obj.data, "utf8");
+ }
+ }
+ }
+
+ dataOfFile(file) {
+ const obj = this.files[file];
+ if (!obj)
+ FAIL(`TableGen.dataOfFile(): File '${file}' not loaded`);
+ return obj.data;
+ }
+
+ inject(key, str, size) {
+ const begin = "// ${" + key + ":Begin}\n";
+ const end = "// ${" + key + ":End}\n";
+
+ var done = false;
+ for (var file in this.files) {
+ const obj = this.files[file];
+ const data = obj.data;
+
+ if (data.indexOf(begin) !== -1) {
+ obj.data = StringUtils.inject(data, begin, end, str);
+ done = true;
+ break;
+ }
+ }
+
+ if (!done)
+ FAIL(`TableGen.inject(): Cannot find '${key}'`);
+
+ if (size)
+ this.tableSizes[key] = size;
+
+ return this;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Task Management]
+ // --------------------------------------------------------------------------
+
+ addTask(task) {
+ if (!task.name)
+ FAIL(`TableGen.addModule(): Module must have a name`);
+
+ if (this.taskMap[task.name])
+ FAIL(`TableGen.addModule(): Module '${task.name}' already added`);
+
+ task.deps.forEach((dependency) => {
+ if (!this.taskMap[dependency])
+ FAIL(`TableGen.addModule(): Dependency '${dependency}' of module '${task.name}' doesn't exist`);
+ });
+
+ this.tasks.push(task);
+ this.taskMap[task.name] = task;
+
+ task.ctx = this;
+ return this;
+ }
+
+ runTasks() {
+ const tasks = this.tasks;
+ const tasksDone = Object.create(null);
+
+ var pending = tasks.length;
+ while (pending) {
+ const oldPending = pending;
+ const arrPending = [];
+
+ for (var i = 0; i < tasks.length; i++) {
+ const task = tasks[i];
+ if (tasksDone[task.name])
+ continue;
+
+ if (task.deps.every((dependency) => { return tasksDone[dependency] === true; })) {
+ task.run();
+ tasksDone[task.name] = true;
+ pending--;
+ }
+ else {
+ arrPending.push(task.name);
+ }
+ }
+
+ if (oldPending === pending)
+ throw Error(`TableGen.runModules(): Modules '${arrPending.join("|")}' stuck (cyclic dependency?)`);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // [Instruction Management]
+ // --------------------------------------------------------------------------
+
+ addInst(inst) {
+ if (this.instMap[inst.name])
+ FAIL(`TableGen.addInst(): Instruction '${inst.name}' already added`);
+
+ inst.id = this.insts.length;
+ this.insts.push(inst);
+ this.instMap[inst.name] = inst;
+
+ return this;
+ }
+
+ addAlias(alias, name) {
+ this.aliases.push(alias);
+ this.aliasMap[alias] = name;
+
+ return this;
+ }
+
+ // --------------------------------------------------------------------------
+ // [Run]
+ // --------------------------------------------------------------------------
+
+ run() {
+ this.onBeforeRun();
+ this.runTasks();
+ this.onAfterRun();
+ }
+
+ // --------------------------------------------------------------------------
+ // [Other]
+ // --------------------------------------------------------------------------
+
+ dumpTableSizes() {
+ const sizes = this.tableSizes;
+
+ var pad = 26;
+ var total = 0;
+
+ for (var name in sizes) {
+ const size = sizes[name];
+ total += size;
+ console.log(("Size of " + name).padEnd(pad) + ": " + size);
+ }
+
+ console.log("Size of all tables".padEnd(pad) + ": " + total);
+ }
+
+ // --------------------------------------------------------------------------
+ // [Hooks]
+ // --------------------------------------------------------------------------
+
+ onBeforeRun() {}
+ onAfterRun() {}
+}
+exports.TableGen = TableGen;
+
+// ============================================================================
+// [IdEnum]
+// ============================================================================
+
+class IdEnum extends Task {
+ constructor(name, deps) {
+ super(name || "IdEnum", deps);
+ }
+
+ comment(name) {
+ FAIL("IdEnum.comment(): Must be reimplemented");
+ }
+
+ run() {
+ const insts = this.ctx.insts;
+
+ var s = "";
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+
+ var line = "kId" + inst.enum + (i ? "" : " = 0") + ",";
+ var text = this.comment(inst);
+
+ if (text)
+ line = line.padEnd(37) + "//!< " + text;
+
+ s += line + "\n";
+ }
+ s += "_kIdCount\n";
+
+ return this.ctx.inject("InstId", s);
+ }
+}
+exports.IdEnum = IdEnum;
+
+// ============================================================================
+// [NameTable]
+// ============================================================================
+
+class NameTable extends Task {
+ constructor(name, deps) {
+ super(name || "NameTable", deps);
+ }
+
+ run() {
+ const arch = this.ctx.arch;
+ const none = "Inst::kIdNone";
+
+ const insts = this.ctx.insts;
+ const instNames = new IndexedString();
+
+ const instFirst = new Array(26);
+ const instLast = new Array(26);
+
+ var maxLength = 0;
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+ instNames.add(inst.name);
+ maxLength = Math.max(maxLength, inst.name.length);
+ }
+ instNames.index();
+
+ for (var i = 0; i < insts.length; i++) {
+ const inst = insts[i];
+ const name = inst.name;
+ const nameIndex = instNames.getIndex(name);
+
+ const index = name.charCodeAt(0) - 'a'.charCodeAt(0);
+ if (index < 0 || index >= 26)
+ FAIL(`TableGen.generateNameData(): Invalid lookup character '${name[0]}' of '${name}'`);
+
+ inst.nameIndex = nameIndex;
+ if (instFirst[index] === undefined)
+ instFirst[index] = `Inst::kId${inst.enum}`;
+ instLast[index] = `Inst::kId${inst.enum}`;
+ }
+
+ var s = "";
+ s += `const char InstDB::_nameData[] =\n${instNames.format(kIndent, kJustify)}\n`;
+ s += `\n`;
+ s += `const InstDB::InstNameIndex InstDB::instNameIndex[26] = {\n`;
+ for (var i = 0; i < instFirst.length; i++) {
+ const firstId = instFirst[i] || none;
+ const lastId = instLast[i] || none;
+
+ s += ` { ${String(firstId).padEnd(22)}, ${String(lastId).padEnd(22)} + 1 }`;
+ if (i !== 26 - 1)
+ s += `,`;
+ s += `\n`;
+ }
+ s += `};\n`;
+
+ this.ctx.inject("NameLimits",
+ StringUtils.disclaimer(`enum : uint32_t { kMaxNameSize = ${maxLength} };\n`));
+
+ return this.ctx.inject("NameData", StringUtils.disclaimer(s), instNames.getSize() + 26 * 4);
+ }
+}
+exports.NameTable = NameTable;
diff --git a/tools/tablegen.sh b/tools/tablegen.sh
new file mode 100755
index 0000000..dbb37f1
--- /dev/null
+++ b/tools/tablegen.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+node ./tablegen-x86.js