[ABI] Updated instruction DB, operands, and minor API changes

This changeset contains an updated instruction database that brings
ARM32 instructions for the first time. It also updates instruction
database tooling especially for ARM64, which will also be used by
ARM32 generator.

Additionally, new operan has been added, which represents a register
list as used by ARM32 instruction set.

Other minor changes are related to ARM - some stuff had to be moved
to a64 namespace from arm namespace as it's incompatible between
32-bit and 64-bit ISA.
This commit is contained in:
kobalicek
2023-12-26 23:28:40 +01:00
parent 13bd440022
commit b25df5554d
75 changed files with 5007 additions and 3934 deletions

592
tools/generator-commons.js Normal file
View File

@@ -0,0 +1,592 @@
// This file is part of AsmJit project <https://asmjit.com>
//
// See asmjit.h or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
const hasOwn = Object.prototype.hasOwnProperty;
function nop(x) { return x; }
// Generator - Constants
// ---------------------
const kIndent = " ";
exports.kIndent = kIndent;
const kLineWidth = 120;
// Generator - Logging
// -------------------
let VERBOSE = false;
function setDebugVerbosity(value) {
VERBOSE = value;
}
exports.setDebugVerbosity = setDebugVerbosity;
function DEBUG(msg) {
if (VERBOSE)
console.log(msg);
}
exports.DEBUG = DEBUG;
function WARN(msg) {
console.log(msg);
}
exports.WARN = WARN;
function FATAL(msg) {
console.log(`FATAL: ${msg}`);
throw new Error(msg);
}
exports.FATAL = FATAL;
// Generator - Object Utilities
// ----------------------------
class ObjectUtils {
static clone(map) {
return Object.assign(Object.create(null), map);
}
static merge(a, b) {
if (a === b)
return a;
for (let k in b) {
let av = a[k];
let bv = b[k];
if (typeof av === "object" && typeof bv === "object")
ObjectUtils.merge(av, bv);
else
a[k] = bv;
}
return a;
}
static equals(a, b) {
if (a === b)
return true;
if (typeof a !== typeof b)
return false;
if (typeof a !== "object")
return a === b;
if (Array.isArray(a) || Array.isArray(b)) {
if (Array.isArray(a) !== Array.isArray(b))
return false;
const len = a.length;
if (b.length !== len)
return false;
for (let i = 0; i < len; i++)
if (!ObjectUtils.equals(a[i], b[i]))
return false;
}
else {
if (a === null || b === null)
return a === b;
for (let k in a)
if (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k]))
return false;
for (let k in b)
if (!hasOwn.call(a, k))
return false;
}
return true;
}
static equalsExcept(a, b, except) {
if (a === b)
return true;
if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b))
return ObjectUtils.equals(a, b);
for (let k in a)
if (!hasOwn.call(except, k) && (!hasOwn.call(b, k) || !ObjectUtils.equals(a[k], b[k])))
return false;
for (let k in b)
if (!hasOwn.call(except, k) && !hasOwn.call(a, k))
return false;
return true;
}
static findKey(map, keys) {
for (let key in keys)
if (hasOwn.call(map, key))
return key;
return undefined;
}
static hasAny(map, keys) {
for (let key in keys)
if (hasOwn.call(map, key))
return true;
return false;
}
static and(a, b) {
const out = Object.create(null);
for (let k in a)
if (hasOwn.call(b, k))
out[k] = true;
return out;
}
static xor(a, b) {
const out = Object.create(null);
for (let k in a) if (!hasOwn.call(b, k)) out[k] = true;
for (let k in b) if (!hasOwn.call(a, k)) out[k] = true;
return out;
}
}
exports.ObjectUtils = ObjectUtils;
// Generator - Array Utilities
// ---------------------------
class ArrayUtils {
static min(arr, fn) {
if (!arr.length)
return null;
if (!fn)
fn = nop;
let v = fn(arr[0]);
for (let i = 1; i < arr.length; i++)
v = Math.min(v, fn(arr[i]));
return v;
}
static max(arr, fn) {
if (!arr.length)
return null;
if (!fn)
fn = nop;
let v = fn(arr[0]);
for (let i = 1; i < arr.length; i++)
v = Math.max(v, fn(arr[i]));
return v;
}
static sorted(obj, cmp) {
const out = Array.isArray(obj) ? obj.slice() : Object.getOwnPropertyNames(obj);
out.sort(cmp);
return out;
}
static deepIndexOf(arr, what) {
for (let i = 0; i < arr.length; i++)
if (ObjectUtils.equals(arr[i], what))
return i;
return -1;
}
static toDict(arr, value) {
if (value === undefined)
value = true;
const out = Object.create(null);
for (let i = 0; i < arr.length; i++)
out[arr[i]] = value;
return out;
}
}
exports.ArrayUtils = ArrayUtils;
// Generator - String Utilities
// ----------------------------
class StringUtils {
static asString(x) { return String(x); }
static countOf(s, pattern) {
if (!pattern)
FATAL(`Pattern cannot be empty`);
let n = 0;
let pos = 0;
while ((pos = s.indexOf(pattern, pos)) >= 0) {
n++;
pos += pattern.length;
}
return n;
}
static trimLeft(s) { return s.replace(/^\s+/, ""); }
static trimRight(s) { return s.replace(/\s+$/, ""); }
static upFirst(s) {
if (!s) return "";
return s[0].toUpperCase() + s.substr(1);
}
static decToHex(n, nPad) {
let hex = Number(n < 0 ? 0x100000000 + n : n).toString(16);
while (nPad > hex.length)
hex = "0" + hex;
return "0x" + hex.toUpperCase();
}
static format(array, indent, showIndex, mapFn) {
if (!mapFn)
mapFn = StringUtils.asString;
let s = "";
let threshold = 80;
if (showIndex === -1)
s += indent;
for (let i = 0; i < array.length; i++) {
const item = array[i];
const last = i === array.length - 1;
if (showIndex !== -1)
s += indent;
s += mapFn(item);
if (showIndex > 0) {
s += `${last ? " " : ","} // #${i}`;
if (typeof array.refCountOf === "function")
s += ` [ref=${array.refCountOf(item)}x]`;
}
else if (!last) {
s += ",";
}
if (showIndex === -1) {
if (s.length >= threshold - 1 && !last) {
s += "\n" + indent;
threshold += 80;
}
else {
if (!last) s += " ";
}
}
else {
if (!last) s += "\n";
}
}
return s;
}
static makeCxxArray(array, code, indent) {
if (typeof indent !== "string")
indent = kIndent;
return `${code} = {\n${indent}` + array.join(`,\n${indent}`) + `\n};\n`;
}
static makeCxxArrayWithComment(array, code, indent) {
if (typeof indent !== "string")
indent = kIndent;
let s = "";
for (let i = 0; i < array.length; i++) {
const last = i === array.length - 1;
s += indent + array[i].data +
(last ? " // " : ", // ") + (array[i].refs ? "#" + String(i) : "").padEnd(5) + array[i].comment + "\n";
}
return `${code} = {\n${s}};\n`;
}
static formatCppStruct(...args) {
return "{ " + args.join(", ") + " }";
}
static formatCppFlags(obj, fn, none) {
if (none == null)
none = "0";
if (!fn)
fn = nop;
let out = "";
for (let k in obj) {
if (obj[k])
out += (out ? " | " : "") + fn(k);
}
return out ? out : none;
}
static formatRecords(array, indent, fn) {
if (typeof indent !== "string")
indent = kIndent;
if (!fn)
fn = nop;
let s = "";
let line = "";
for (let i = 0; i < array.length; i++) {
const item = fn(array[i]);
const combined = line ? line + ", " + item : item;
if (combined.length >= kLineWidth) {
s = s ? s + ",\n" + line : line;
line = item;
}
else {
line = combined;
}
}
if (line) {
s = s ? s + ",\n" + line : line;
}
return StringUtils.indent(s, indent);
}
static disclaimer(s) {
return "// ------------------- Automatically generated, do not edit -------------------\n" +
s +
"// ----------------------------------------------------------------------------\n";
}
static indent(s, indentation) {
if (typeof indentation === "number")
indentation = " ".repeat(indentation);
let lines = s.split(/\r?\n/g);
if (indentation) {
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (line)
lines[i] = indentation + line;
}
}
return lines.join("\n");
}
static extract(s, start, end) {
const iStart = s.indexOf(start);
const iEnd = s.indexOf(end);
if (iStart === -1)
FATAL(`StringUtils.extract(): Couldn't locate start mark '${start}'`);
if (iEnd === -1)
FATAL(`StringUtils.extract(): Couldn't locate end mark '${end}'`);
return s.substring(iStart + start.length, iEnd).trim();
}
static inject(s, start, end, code) {
let iStart = s.indexOf(start);
let iEnd = s.indexOf(end);
if (iStart === -1)
FATAL(`StringUtils.inject(): Couldn't locate start mark '${start}'`);
if (iEnd === -1)
FATAL(`StringUtils.inject(): Couldn't locate end mark '${end}'`);
let nIndent = 0;
while (iStart > 0 && s[iStart-1] === " ") {
iStart--;
nIndent++;
}
if (nIndent) {
const indentation = " ".repeat(nIndent);
code = StringUtils.indent(code, indentation) + indentation;
}
return s.substr(0, iStart + start.length + nIndent) + code + s.substr(iEnd);
}
static makePriorityCompare(priorityArray) {
const map = Object.create(null);
priorityArray.forEach((str, index) => { map[str] = index; });
return function(a, b) {
const ax = hasOwn.call(map, a) ? map[a] : Infinity;
const bx = hasOwn.call(map, b) ? map[b] : Infinity;
return ax != bx ? ax - bx : a < b ? -1 : a > b ? 1 : 0;
}
}
}
exports.StringUtils = StringUtils;
// Generator - Indexed Array
// =========================
// IndexedArray is an Array replacement that allows to index each item inserted to it. Its main purpose
// is to avoid data duplication, if an item passed to `addIndexed()` is already within the Array then
// it's not inserted and the existing index is returned instead.
function IndexedArray_keyOf(item) {
return typeof item === "string" ? item : JSON.stringify(item);
}
class IndexedArray extends Array {
constructor() {
super();
this._index = Object.create(null);
}
refCountOf(item) {
const key = IndexedArray_keyOf(item);
const idx = this._index[key];
return idx !== undefined ? idx.refCount : 0;
}
addIndexed(item) {
const key = IndexedArray_keyOf(item);
let idx = this._index[key];
if (idx !== undefined) {
idx.refCount++;
return idx.data;
}
idx = this.length;
this._index[key] = {
data: idx,
refCount: 1
};
this.push(item);
return idx;
}
}
exports.IndexedArray = IndexedArray;
// Generator - Indexed String
// ==========================
// IndexedString is mostly used to merge all instruction names into a single string with external
// index. It's designed mostly for generating C++ tables. Consider the following cases in C++:
//
// a) static const char* const* instNames = { "add", "mov", "vpunpcklbw" };
//
// b) static const char instNames[] = { "add\0" "mov\0" "vpunpcklbw\0" };
// static const uint16_t instNameIndex[] = { 0, 4, 8 };
//
// The latter (b) has an advantage that it doesn't have to be relocated by the linker, which saves
// a lot of space in the resulting binary and a lot of CPU cycles (and memory) when the linker loads
// it. AsmJit supports thousands of instructions so each optimization like this makes it smaller and
// faster to load.
class IndexedString {
constructor() {
this.map = Object.create(null);
this.array = [];
this.size = -1;
}
add(s) {
this.map[s] = -1;
}
index() {
const map = this.map;
const array = this.array;
const partialMap = Object.create(null);
let k, kp;
let i, len;
// Create a map that will contain all keys and partial keys.
for (k in map) {
if (!k) {
partialMap[k] = k;
}
else {
for (i = 0, len = k.length; i < len; i++) {
kp = k.substr(i);
if (!hasOwn.call(partialMap, kp) || partialMap[kp].length < len)
partialMap[kp] = k;
}
}
}
// Create an array that will only contain keys that are needed.
for (k in map)
if (partialMap[k] === k)
array.push(k);
array.sort();
// Create valid offsets to the `array`.
let offMap = Object.create(null);
let offset = 0;
for (i = 0, len = array.length; i < len; i++) {
k = array[i];
offMap[k] = offset;
offset += k.length + 1;
}
this.size = offset;
// Assign valid offsets to `map`.
for (kp in map) {
k = partialMap[kp];
map[kp] = offMap[k] + k.length - kp.length;
}
}
format(indent, justify) {
if (this.size === -1)
FATAL(`IndexedString.format(): not indexed yet, call index()`);
const array = this.array;
if (!justify) justify = 0;
let i;
let s = "";
let line = "";
for (i = 0; i < array.length; i++) {
const item = "\"" + array[i] + ((i !== array.length - 1) ? "\\0\"" : "\";");
const newl = line + (line ? " " : indent) + item;
if (newl.length <= justify) {
line = newl;
continue;
}
else {
s += line + "\n";
line = indent + item;
}
}
return s + line;
}
getSize() {
if (this.size === -1)
FATAL(`IndexedString.getSize(): Not indexed yet, call index()`);
return this.size;
}
getIndex(k) {
if (this.size === -1)
FATAL(`IndexedString.getIndex(): Not indexed yet, call index()`);
if (!hasOwn.call(this.map, k))
FATAL(`IndexedString.getIndex(): Key '${k}' not found.`);
return this.map[k];
}
}
exports.IndexedString = IndexedString;