Files
asmjit/tools/generator-cxx.js
kobalicek 2ff454d415 [abi] AsmJit v1.17 - cumulative & breaking changes
* Reworked register operands - all vector registers are now
    platform::Vec deriving from UniVec (universal vector operand),
    additionally, there is no platform::Reg, instead asmjit::Reg
    provides all necessary features to make it a base register for
    each target architecture
  * Reworked casting between registers - now architecture agnostic
    names are preferred - use Gp32 instead of Gpd or GpW, Gp64
    instead of Gpq and GpX, etc...
  * Reworked vector registers and their names - architecture
    agnostic naming is now preferred Vec32, Vec64, Vec128, etc...
  * Reworked naming conventions used across AsmJit - for clarity
    Identifiers are now prefixed with the type, like sectionId(),
    labelId(), etc...
  * Reworked how Zone and ZoneAllocator are used across AsmJit,
    prefering Zone in most cases and ZoneAllocator only for
    containers - this change alone achieves around 5% better
    performance of Builder and Compiler
  * Reworked LabelEntry - decreased the size of the base entry
    to 16 bytes for anonymous and unnamed labels. Avoided an
    indirection when using labelEntries() - LabelEntry is now
    a value and not a pointer
  * Renamed LabelLink to Fixup
  * Added a new header <asmjit/host.h> which would include
    <asmjit/core.h> + target tools for the host architecture,
    if enabled and supported
  * Added new AArch64 instructions (BTI, CSSC, CHKFEAT)
  * Added a mvn_ alternative of mvn instruction (fix for Windows
    ARM64 SDK)
  * Added more AArch64 CPU features to CpuInfo
  * Added better support for Apple CPU detection (Apple M3, M4)
  * Added a new benchmarking tool asmjit_bench_overhead, which
    benchmarks the overhead of CodeHolder::init()/reset() and
    creating/attaching emitters to it. Thanks to the benchmark the
    most common code-paths were optimized
  * Added a new benchmarking tool asmjit_bench_regalloc, which
    aims to benchmark the cost and complexity of register allocation.
  * Renamed asmjit_test_perf to asmjit_bench_codegen to make it
    clear what is a test and what is a benchmark
2025-06-15 16:45:37 +02:00

275 lines
5.4 KiB
JavaScript

// This file is part of AsmJit project <https://asmjit.com>
//
// See <asmjit/core.h> or LICENSE.md for license and copyright information
// SPDX-License-Identifier: Zlib
// C++ code generation helpers.
const commons = require("./generator-commons.js");
const FATAL = commons.FATAL;
// Utilities to convert primitives to C++ code.
class Utils {
static toHexRaw(val, pad) {
if (val < 0)
val = 0xFFFFFFFF + val + 1;
let s = val.toString(16);
if (pad != null && s.length < pad)
s = "0".repeat(pad - s.length) + s;
return s.toUpperCase();
}
static toHex(val, pad) {
return "0x" + Utils.toHexRaw(val, pad);
}
static capitalize(s) {
s = String(s);
return !s ? s : s[0].toUpperCase() + s.substr(1);
}
static camelCase(s) {
if (s == null || s === "")
return s;
s = String(s);
if (/^[A-Z]+$/.test(s))
return s.toLowerCase();
else
return s[0].toLowerCase() + s.substr(1);
}
static normalizeSymbolName(s) {
switch (s) {
case "and":
case "or":
case "xor":
return s + "_";
default:
return s;
}
}
static indent(s, indentation) {
if (typeof indentation === "number")
indentation = " ".repeat(indentation);
var lines = s.split(/\r?\n/g);
if (indentation) {
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line)
lines[i] = indentation + line;
}
}
return lines.join("\n");
}
}
exports.Utils = Utils;
// A node that represents a C++ construct.
class Node {
constructor(kind) {
this.kind = kind;
}
};
exports.Node = Node;
// A single line of C++ code that declares a variable with optional initialization.
class Var extends Node {
constructor(type, name, init) {
super("var");
this.type = type;
this.name = name;
this.init = init || "";
}
toString() {
let s = this.type + " " + this.name;
if (this.init)
s += " = " + this.init;
return s + ";\n";
}
};
exports.Var = Var;
// A single line of C++ code, which should not contain any branch or a variable declaration.
class Line extends Node {
constructor(code) {
super("line");
this.code = code;
}
toString() {
return String(this.code) + "\n";
}
};
exports.Line = Line;
// A block containing an array of `Node` items (may contain nested blocks, etc...).
class Block extends Node {
constructor(nodes) {
super("block");
this.nodes = nodes || [];
}
isEmpty() {
return this.nodes.length === 0;
}
appendNode(node) {
if (!(node instanceof Node))
FATAL("Block.appendNode(): Node must be an instance of Node");
this.nodes.push(node);
return this;
}
prependNode(node) {
if (!(node instanceof Node))
FATAL("Block.prependNode(): Node must be an instance of Node");
this.nodes.unshift(node);
return this;
}
insertNode(index, node) {
if (!(node instanceof Node))
FATAL("Block.insertNode(): Node must be an instance of Node");
if (index >= this.nodes.length)
this.nodes.push(node);
else
this.nodes.splice(index, 0, node);
return this;
}
addVarDecl(type, name, init) {
let node = type;
if (!(node instanceof Var))
node = new Var(type, name, init);
let i = 0;
while (i < this.nodes.length) {
const n = this.nodes[i];
if (n.kind === "var" && n.name === node.name && n.init === node.init)
return this;
if (n.kind !== "var")
break;
i++;
}
this.insertNode(i, node);
return this;
}
addLine(code) {
if (typeof code !== "string")
FATAL("Block.addLine(): Line must be string");
this.nodes.push(new Line(code));
return this;
}
prependEmptyLine() {
if (!this.isEmpty())
this.nodes.splice(0, 0, new Line(""));
return this;
}
addEmptyLine() {
if (!this.isEmpty())
this.nodes.push(new Line(""));
return this;
}
toString() {
let s = "";
for (let node of this.nodes)
s += String(node);
return s;
}
}
exports.Block = Block;
// A C++ 'condition' (if statement) and its 'body' if it's taken.
class If extends Node {
constructor(cond, body) {
super("if");
if (body == null)
body = new Block();
if (!(body instanceof Block))
FATAL("If() - body must be a Block");
this.cond = cond;
this.body = body;
}
toString() {
const cond = String(this.cond);
const body = String(this.body);
return `if (${cond}) {\n` + Utils.indent(body, 2) + `}\n`;
}
}
exports.If = If;
//! A C++ switch statement.
class Case extends Node {
constructor(cond, body) {
super("case");
this.cond = cond;
this.body = body || new Block();
}
toString() {
let s = "";
for (let node of this.body.nodes)
s += String(node)
if (this.cond !== "default")
return `case ${this.cond}: {\n` + Utils.indent(s, 2) + `}\n`;
else
return `default: {\n` + Utils.indent(s, 2) + `}\n`;
}
};
exports.Case = Case;
class Switch extends Node {
constructor(expression, cases) {
super("switch");
this.expression = expression;
this.cases = cases || [];
}
addCase(cond, body) {
this.cases.push(new Case(cond, body));
return this;
}
toString() {
let s = "";
for (let c of this.cases) {
if (s)
s += "\n";
s += String(c);
}
return `switch (${this.expression}) {\n` + Utils.indent(s, 2) + `}\n`;
}
}
exports.Switch = Switch;