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