Files
asmjit/tools/generator-commons.js
kobalicek 6c9a6b2454 [abi] Reorganized instruction DB, removed deprecated instructions
* Removed AVX512_ER, AVX512_PF, AVX512_4FMAPS, and AVX512_4VNNIW
    extensions and corresponding instructions (these were never
    advertised by any x86 CPU and were only used by Xeon Phi acc.,
    which AsmJit never supported)
  * Removed CPU extensions HLE, MPX, and TSX
  * Kept extension RTM, which is only for backward compatibility to
    recognize instructions, but it's no longer checked by CpuInfo as
    it's been deprecated together with HLE and MPX
  * The xtest instruction now reports it requires RTM
  * Reorganized x86 extensions a bit - they are now reordered to group
    them by category, preparing for the future where extension IDs will
    be always added after existing records for ABI compatibility
  * Instruction vcvtneps2bf16 no longer accepts form without an explicit
    memory operand size
  * Removed aliased instructions in CMOVcc, Jcc, And SETcc categories,
    now there is only a single instruction id for all aliased instructions.
  * Added a new feature to always show instruction aliases in Logger, which
    includes formatting instructio nodes (Builder, Compiler)

Instruction DB-only updates (not applied to C++ yet):

  * AsmJit DB from now uses the same license as AsmJit (Zlib) and
    no longer applies dual licensing (Zlib and Public Domain)
  * Added support for aggregated instruction definitions in
    x86 instruction database, which should simplify the maintenance
    and reduce bugs (also the syntax is comparable to descriptions
    used by Intel APX instruction manuals)
  * Added support for APX instructions and new features
  * Added support for AVX10.1 and AVX10.2 instructions (both new
    instructions and new encodings of existing instructions)
  * Added support for MOVRS instructions
  * Added support for KL instructions (loadiwkey)
  * Added support for AESKLE instructions
  * Added support for AESKLEWIDE_KL instructions
  * Added support for AMX_[AVX512|MOVRS|FP8|TF32|TRANSPOSE]
  * NOTE: None of the instruction additions is currently used by
    Asmjit, it's a pure database update that needs more work to
    make all the instructions available in future AsmJit
2025-05-10 15:04:11 +02:00

596 lines
13 KiB
JavaScript

// 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 makeEnumName(name) {
return name ? name.charAt(0).toUpperCase() + name.substring(1) : "";
}
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;