Files
asmjit/db/aarch64.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

922 lines
27 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
(function($scope, $as) {
"use strict";
function FAIL(msg) { throw new Error("[AArch64] " + msg); }
// Import
// ======
const base = $scope.base ? $scope.base : require("./base.js");
const exp = $scope.exp ? $scope.exp : require("./exp.js")
const hasOwn = Object.prototype.hasOwnProperty;
const dict = base.dict;
const NONE = base.NONE;
const Parsing = base.Parsing;
const MapUtils = base.MapUtils;
// Export
// ======
const arm = $scope[$as] = dict();
// Database
// ========
arm.dbName = "isa_aarch64.json";
// asmdb.aarch64.Utils
// ===================
// Can be used to assign the number of bits each part of the opcode occupies.
// NOTE: THUMB instructions that use halfword must always specify the width
// of all registers as many instructions accept only LO (r0..r7) registers.
const FieldInfo = {
"P" : { "bits": 1 },
"U" : { "bits": 1 },
"W" : { "bits": 1 },
"S" : { "bits": 1 },
"R" : { "bits": 1 },
"H" : { "bits": 1 },
"F" : { "bits": 1 },
"post" : { "bits": 1 },
"!post" : { "bits": 1 },
"op" : { "bits": 1 }, // TODO: This should be fixed.
"s" : { "bits": 1 },
"sz" : { "bits": 2 },
"msz" : { "bits": 2 },
"sop" : { "bits": 2 },
"cond" : { "bits": 4 },
"nzcv" : { "bits": 4 },
"cmode" : { "bits": 4 },
"CRn" : { "bits": 4 },
"CRm" : { "bits": 4 },
"Rx" : { "bits": 5, "read": true , "write": true },
"Rx2" : { "bits": 5, "read": true , "write": true },
"Rdn" : { "bits": 5, "read": true , "write": true },
"Rd" : { "bits": 5, "read": false, "write": true },
"Rd2" : { "bits": 5, "read": false, "write": true },
"Rs" : { "bits": 5, "read": true , "write": false },
"Rs2" : { "bits": 5, "read": true , "write": false },
"Rn" : { "bits": 5, "read": true , "write": false },
"Rm" : { "bits": 5, "read": true , "write": false },
"Ra" : { "bits": 5, "read": true , "write": false },
"Rt" : { "bits": 5, "read": true , "write": false },
"Rt2" : { "bits": 5, "read": true , "write": false },
"Wx" : { "bits": 5, "read": true , "write": true },
"Wx2" : { "bits": 5, "read": true , "write": true },
"Wdn" : { "bits": 5, "read": true , "write": true },
"Wd" : { "bits": 5, "read": false, "write": true },
"Wd2" : { "bits": 5, "read": false, "write": true },
"Ws" : { "bits": 5, "read": true , "write": false },
"Ws2" : { "bits": 5, "read": true , "write": false },
"Wn" : { "bits": 5, "read": true , "write": false },
"Wm" : { "bits": 5, "read": true , "write": false },
"Wa" : { "bits": 5, "read": true , "write": false },
"Wt" : { "bits": 5, "read": true , "write": false },
"Wt2" : { "bits": 5, "read": true , "write": false },
"Xx" : { "bits": 5, "read": true , "write": true },
"Xx2" : { "bits": 5, "read": true , "write": true },
"Xdn" : { "bits": 5, "read": true , "write": true },
"Xd" : { "bits": 5, "read": false, "write": true },
"Xd2" : { "bits": 5, "read": false, "write": true },
"Xs" : { "bits": 5, "read": true , "write": false },
"Xs2" : { "bits": 5, "read": true , "write": false },
"Xn" : { "bits": 5, "read": true , "write": false },
"Xm" : { "bits": 5, "read": true , "write": false },
"Xa" : { "bits": 5, "read": true , "write": false },
"Xt" : { "bits": 5, "read": true , "write": false },
"Xt2" : { "bits": 5, "read": true , "write": false },
"Bx" : { "bits": 5, "read": true , "write": true },
"Bx2" : { "bits": 5, "read": true , "write": true },
"Bdn" : { "bits": 5, "read": true , "write": true },
"Bd" : { "bits": 5, "read": false, "write": true },
"Bd2" : { "bits": 5, "read": false, "write": true },
"Bs" : { "bits": 5, "read": true , "write": false },
"Bs2" : { "bits": 5, "read": true , "write": false },
"Bn" : { "bits": 5, "read": true , "write": false },
"Bm" : { "bits": 5, "read": true , "write": false },
"Ba" : { "bits": 5, "read": true , "write": false },
"Hx" : { "bits": 5, "read": true , "write": true },
"Hx2" : { "bits": 5, "read": true , "write": true },
"Hdn" : { "bits": 5, "read": true , "write": true },
"Hd" : { "bits": 5, "read": false, "write": true },
"Hd2" : { "bits": 5, "read": false, "write": true },
"Hs" : { "bits": 5, "read": true , "write": false },
"Hs2" : { "bits": 5, "read": true , "write": false },
"Hn" : { "bits": 5, "read": true , "write": false },
"Hm" : { "bits": 5, "read": true , "write": false },
"Ha" : { "bits": 5, "read": true , "write": false },
"Sx" : { "bits": 5, "read": true , "write": true },
"Sx2" : { "bits": 5, "read": true , "write": true },
"Sdn" : { "bits": 5, "read": true , "write": true },
"Sd" : { "bits": 5, "read": false, "write": true },
"Sd2" : { "bits": 5, "read": false, "write": true },
"Ss" : { "bits": 5, "read": true , "write": false },
"Ss2" : { "bits": 5, "read": true , "write": false },
"Sn" : { "bits": 5, "read": true , "write": false },
"Sm" : { "bits": 5, "read": true , "write": false },
"Sa" : { "bits": 5, "read": true , "write": false },
"Dx" : { "bits": 5, "read": true , "write": true },
"Dx2" : { "bits": 5, "read": true , "write": true },
"Ddn" : { "bits": 5, "read": true , "write": true },
"Dd" : { "bits": 5, "read": false, "write": true },
"Dd2" : { "bits": 5, "read": false, "write": true },
"Ds" : { "bits": 5, "read": true , "write": false },
"Ds2" : { "bits": 5, "read": true , "write": false },
"Dn" : { "bits": 5, "read": true , "write": false },
"Dn2" : { "bits": 5, "read": true , "write": false },
"Dm" : { "bits": 5, "read": true , "write": false },
"Da" : { "bits": 5, "read": true , "write": false },
"Qx" : { "bits": 5, "read": true , "write": true },
"Qx2" : { "bits": 5, "read": true , "write": true },
"Qdn" : { "bits": 5, "read": true , "write": true },
"Qd" : { "bits": 5, "read": false, "write": true },
"Qd2" : { "bits": 5, "read": false, "write": true },
"Qs" : { "bits": 5, "read": true , "write": false },
"Qs2" : { "bits": 5, "read": true , "write": false },
"Qn" : { "bits": 5, "read": true , "write": false },
"Qn2" : { "bits": 5, "read": true , "write": false },
"Qm" : { "bits": 5, "read": true , "write": false },
"Qa" : { "bits": 5, "read": true , "write": false },
"Vx" : { "bits": 5, "read": true , "write": true },
"Vx2" : { "bits": 5, "read": true , "write": true },
"Vdn" : { "bits": 5, "read": true , "write": true },
"Vd" : { "bits": 5, "read": false, "write": true },
"Vd2" : { "bits": 5, "read": false, "write": true },
"Vs" : { "bits": 5, "read": true , "write": false },
"Vs2" : { "bits": 5, "read": true , "write": false },
"Vn" : { "bits": 5, "read": true , "write": false },
"Vm" : { "bits": 5, "read": true , "write": false },
"Va" : { "bits": 5, "read": true , "write": false },
"Zx" : { "bits": 5, "read": true , "write": true },
"Zx2" : { "bits": 5, "read": true , "write": true },
"Zda" : { "bits": 5, "read": true , "write": true },
"Zdn" : { "bits": 5, "read": true , "write": true },
"Zdn2" : { "bits": 5, "read": true , "write": true },
"Zd" : { "bits": 5, "read": false, "write": true },
"Zd2" : { "bits": 5, "read": false, "write": true },
"Zs" : { "bits": 5, "read": true , "write": false },
"Zs2" : { "bits": 5, "read": true , "write": false },
"Zn" : { "bits": 5, "read": true , "write": false },
"Zm" : { "bits": 5, "read": true , "write": false },
"Zk" : { "bits": 5, "read": true , "write": false },
"Za" : { "bits": 5, "read": true , "write": false },
"Pdn" : { "bits": 4, "read": true , "write": true },
"Pdm" : { "bits": 4, "read": true , "write": true },
"Pd" : { "bits": 4, "read": false, "write": true },
"Ps" : { "bits": 4, "read": true , "write": false },
"Pn" : { "bits": 4, "read": true , "write": false },
"Pm" : { "bits": 4, "read": true , "write": false },
"Pg" : { "bits": 4, "read": true , "write": false }
};
arm.FieldInfo = FieldInfo;
// AArch64 utilities.
class Utils {
static splitInstructionSignature(s) {
const names = s.match(/^[\w\|]+/)[0];
s = s.substring(names.length);
const opOffset = s.indexOf(" ")
const suffix = s.substring(0, opOffset).trim();
const operands = opOffset === -1 ? "" : s.substring(opOffset + 1).trim();
return {
names: names.split("|").map((base)=>{ return base + suffix}),
operands: operands
}
}
static parseShiftOrExtendOp(s) {
const space = s.indexOf(" ");
if (space === -1)
return "";
const ops = s.substring(0, space).trim();
for (let op of ops.split("|"))
if (!/^(sop|extend|lsl|lsr|asr|uxtw|sxtw|sxtx|mul)$/.test(op))
return "";
return ops;
}
}
arm.Utils = Utils;
function normalizeNumber(n) {
return n < 0 ? 0x100000000 + n : n;
}
function decomposeOperand(s) {
let type = null;
let element = null;
let consecutive = 0;
let maskType = "";
const elementM = s.match(/\[#(\w+)\]$/);
if (elementM) {
element = elementM[1];
s = s.substring(0, s.length - elementM[0].length);
}
const typeM = s.match(/\.(\w+)$/);
if (typeM) {
type = typeM[1];
s = s.substring(0, s.length - typeM[0].length);
}
const maskM = s.match(/\/(M|Z|MZ)$/);
if (maskM) {
maskType = maskM[1];
s = s.substring(0, s.length - maskM[0].length);
}
if (s.endsWith("++")) {
consecutive = 2;
s = s.substring(0, s.length - 2);
}
else if (s.endsWith("+")) {
consecutive = 1;
s = s.substring(0, s.length - 1);
}
let m = s.match(/==|\!=|>=|<=|\*/);
let restrict = false;
if (m) {
restrict = s.substring(m.index);
s = s.substring(0, m.index);
}
return {
data : s,
maskType : maskType,
type : type,
element : element,
restrict : restrict,
consecutive: consecutive
};
}
function splitOpcodeFields(s) {
const arr = s.split("|");
const out = [];
for (let i = 0; i < arr.length; i++) {
const val = arr[i];
if (/^[0-1A-Z]{2,}$/.test(val))
out.push.apply(out, val.match(/([0-1]+)|[A-Z]/g));
else
out.push(val);
}
return out.map((field)=>{return field.trim(); });
}
// asmdb.aarch64.Operand
// =====================
// ARM operand.
class Operand extends base.Operand {
constructor(def) {
super();
this.data = def;
// Register.
this.sp = ""; // GP register stack access: ["", "WSP" or "SP"].
this.mask = ""; // Masking specifier.
}
hasMemModes() {
return Object.keys(this.memModes).length !== 0;
}
get name() {
switch (this.type) {
case "reg": return this.reg;
case "mem": return this.mem;
case "imm": return this.imm;
case "rel": return this.rel;
default : return "";
}
}
get scale() {
if (this.restrict && this.restrict.startsWith("*"))
return parseInt(this.restrict.substring(1), 10);
else
return 0;
}
}
arm.Operand = Operand;
// asmdb.aarch64.Instruction
// =========================
function patternFromOperand(key) { return key; }
// ARM instruction.
class Instruction extends base.Instruction {
constructor(db, data) {
super(db, data);
this.name = data.name;
this.it = dict(); // THUMB's 'it' flags.
this.apsr = dict();
this.fpcsr = dict();
this.calc = dict(); // Calculations required to generate opcode.
this.immCond = []; // Immediate value conditions (array of conditions).
this._assignOperands(data.operands);
this._assignOpcode(data.op);
for (let k in data) {
if (k === "name" || k == "op" || k === "operands")
continue;
this._assignAttribute(k, data[k]);
}
this._updateOperandsInfo();
this._postProcess();
}
_assignAttribute(key, value) {
switch (key) {
case "it":
for (let it of value.split(" "))
this.it[it.trim()] = true;
break;
case "apsr":
case "fpcsr":
this._assignAttributeKeyValue(key, value);
break;
case "imm":
this.imm = exp.parse(value);
break;
case "calc":
for (let calcKey in value)
this.calc[calcKey] = exp.parse(value[calcKey]);
break;
default:
super._assignAttribute(key, value);
}
}
_assignAttributeKeyValue(name, content) {
const attributes = content.trim().split(/[ ]+/);
for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i].trim();
if (!attr)
continue;
const eq = attr.indexOf("=");
let key = eq === -1 ? attr : attr.substring(0, eq);
let val = eq === -1 ? true : attr.substring(eq + 1);
// If the key contains "|" it's a definition of multiple attributes.
if (key.indexOf("|") !== -1) {
const dot = key.indexOf(".");
const base = dot === -1 ? "" : key.substring(0, dot + 1);
const keys = (dot === -1 ? key : key.substring(dot + 1)).split("|");
for (let j = 0; j < keys.length; j++)
this[name][base + keys[j]] = val;
}
else {
this[name][key] = val;
}
}
}
_assignOperands(s) {
if (!s) return;
// Split into individual operands and push them to `operands`.
const arr = base.Parsing.splitOperands(s);
for (let i = 0; i < arr.length; i++) {
let def = arr[i].trim();
const op = new Operand(def);
const sp = def.match(/^(\w+)\|(SP|WSP)$/);
if (sp) {
def = sp[1];
op.sp = sp[2];
}
const consecutive = def.match(/(\d+)x\{(.*)\}([+]?[+]?)/);
if (consecutive)
def = consecutive[2];
op.sign = false;
op.element = null;
op.shiftOp = "";
op.shiftImm = null;
op.shiftCond = "";
// Handle {optional} attribute.
if (Parsing.isOptional(def)) {
op.optional = true;
def = Parsing.clearOptional(def);
}
// Handle commutativity <-> symbol.
if (Parsing.isCommutative(def)) {
op.commutative = true;
def = Parsing.clearCommutative(def);
}
// Handle shift operation.
let shiftOp = Utils.parseShiftOrExtendOp(def);
if (shiftOp) {
op.shiftOp = shiftOp;
def = def.substring(shiftOp.length + 1);
}
if (def.startsWith("[")) {
op.type = "mem";
op.memModes = dict();
op.base = null;
op.index = null;
op.offset = null;
let mem = def;
let didHaveMemMode = false;
for (;;) {
if (mem.endsWith("!")) {
op.memModes.preIndex = true;
mem = mem.substring(0, mem.length - 1);
didHaveMemMode = true;
break;
}
if (mem.endsWith("@")) {
op.memModes.postIndex = true;
mem = mem.substring(0, mem.length - 1);
didHaveMemMode = true;
break;
}
if (mem.endsWith("{!}")) {
op.memModes.offset = true;
op.memModes.preIndex = true;
mem = mem.substring(0, mem.length - 3);
didHaveMemMode = true;
continue;
}
if (mem.endsWith("{@}")) {
op.memModes.offset = true;
op.memModes.postIndex = true;
mem = mem.substring(0, mem.length - 3);
didHaveMemMode = true;
continue;
}
break;
}
if (!mem.endsWith("]"))
FAIL(`Unknown memory operand '${mem}' in '${def}'`);
let parts = mem.substring(1, mem.length - 1).split(",").map(function(s) { return s.trim() });
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const m = part.match(/^\{(([a-z]+)(\|[a-z]+)*)\s+#(\w+)\s*(\*\s*\d+\s*)?\}$/);
if (m) {
op.shiftOp = m[1];
op.shiftImm = m[2];
if (m[3])
op.shiftCond = m[3]
continue;
}
if (i === 0) {
op.base = dict();
op.base.field = part;
op.base.exp = null;
const m = part.match(/^([A-Za-z]\w*(?:\.\w+)?)/);
if (m && m[1].length < part.length) {
op.base.exp = exp.parse(part);
op.base.field = m[1];
}
}
else if (part.startsWith("#")) {
let p = part.substring(1);
let u = "1";
let offExp = null;
let offMul = 1;
if (p.startsWith("+/-")) {
u = "U";
p = p.substring(3);
}
const expMatch = p.match(/^([A-Za-z]\w*)==/);
if (expMatch) {
offExp = exp.parse(p);
p = p.substring(0, expMatch[1].length);
}
const mulMatch = p.match(/\s*\*\s*(\d+)$/);
if (mulMatch) {
offMul = parseInt(mulMatch[1]);
p = p.substring(0, mulMatch.index);
}
op.offset = dict();
op.offset.field = p;
op.offset.u = u;
op.offset.exp = offExp;
op.offset.mul = offMul;
}
else {
let p = part;
let u = "1";
if (p.startsWith("+/-")) {
u = "U";
p = p.substring(3);
}
op.index = dict();
op.index.field = p;
op.index.u = u;
const m = p.match(/^([A-Za-z\|]\w*(?:\.\w+)?)/);
if (m && m[1].length < p.length) {
op.index.exp = exp.parse(p);
op.index.field = m[1];
}
}
}
if (!op.hasMemModes() && (op.offset || op.index))
op.memModes.offset = true;
op.mem = mem;
}
else if (def.startsWith("#")) {
const obj = decomposeOperand(def);
const imm = obj.data;
op.type = "imm";
op.imm = imm.substring(1); // Immediate operand name.
op.immSize = 0; // Immediate size in bits.
op.restrict = obj.restrict; // Immediate condition.
}
else {
// Some instructions use Reg! to specify that the register increments.
if (def.endsWith("!")) {
def = def.substring(0, def.length - 1)
op.regInc = true
}
const obj = decomposeOperand(def);
const reg = obj.data;
const type = reg.substring(0, 1).toLowerCase();
const info = FieldInfo[reg];
if (!info)
FAIL(`Unknown register operand '${reg}' in '${def}'`);
op.type = info.list ? "reg-list" : "reg";
op.reg = reg; // Register name (as specified in manual).
op.regType = type; // Register type.
op.regList = !!info.list; // Register list.
op.maskType = obj.maskType; // Mask type.
op.elementType = obj.type // Element type or t, ta, tb.
op.read = info.read; // Register access (read).
op.write = info.write; // Register access (write).
op.element = obj.element; // Register element[] access.
op.restrict = obj.restrict; // Register condition.
op.consecutive = obj.consecutive;
}
this.operands.push(op);
if (consecutive) {
const count = parseInt(consecutive[1]);
for (let n = 2; n <= count; n++) {
const def = consecutive[3].replace(op.reg, op.reg + n);
const opN = new Operand(def);
opN.type = "reg";
opN.reg = op.reg + n;
opN.regType = op.regType;
opN.read = op.read;
opN.write = op.write;
opN.element = op.element;
opN.consecutive = consecutive[3].length;
opN.artificial = true;
this.operands.push(opN);
}
}
}
}
_assignOpcode(s) {
this.opcodeString = s;
let opcodeIndex = 0;
let opcodeValue = 0;
let patternMap = {};
// Split opcode into its fields.
const arr = splitOpcodeFields(s);
const dup = dict();
const fields = this.fields;
const pattern = [];
const fieldMap = Object.create(null);
for (let field of arr) {
fieldMap[field] = true;
}
for (let i = arr.length - 1; i >= 0; i--) {
let key = arr[i].trim();
let m;
if (/^[0-1]+$/.test(key)) {
// This part of the opcode is RAW bits, they contribute to the `opcodeValue`.
opcodeValue |= parseInt(key, 2) << opcodeIndex;
opcodeIndex += key.length;
pattern.unshift("_".repeat(key.length));
}
else {
pattern.unshift(patternFromOperand(key));
patternMap[patternFromOperand(key)] = true;
let size = 0;
let mask = 0;
let bits = 0;
let from = -1;
let lbit = key.startsWith("'");
let hbit = key.endsWith("'");
if ((m = key.match(/\[\s*(\d+)\s*\:\s*(\d+)\s*\]$/))) {
const a = parseInt(m[1], 10);
const b = parseInt(m[2], 10);
if (a < b)
FAIL(`Invalid bit range '${key}' in opcode '${s}'`);
from = b;
size = a - b + 1;
mask = ((1 << size) - 1) << b;
key = key.substring(0, m.index).trim();
}
else if ((m = key.match(/\[\s*(\d+)\s*\]$/))) {
from = parseInt(m[1], 10);
size = 1;
mask = 1 << from;
key = key.substring(0, m.index).trim();
}
else if ((m = key.match(/\:\s*(\d+)$/))) {
size = parseInt(m[1], 10);
bits = size;
key = key.substring(0, m.index).trim();
}
else {
const key_ = key;
if (lbit || hbit) {
from = 0;
if (lbit && hbit)
FAIL(`Couldn't recognize the format of '${key}' in opcode '${s}'`);
if (lbit) {
key = key.substring(1);
}
if (hbit) {
key = key.substring(0, key.length - 1);
from = 4;
}
size = 1;
}
else if (FieldInfo[key]) {
// Sizes of some standard fields can be assigned automatically.
size = FieldInfo[key].bits;
bits = size;
if (fieldMap["'" + key])
from = 1;
}
else if (key.length === 1) {
// Sizes of one-letter fields (like 'U', 'F', etc...) is 1 if not specified.
size = 1;
bits = 1;
}
else {
FAIL(`Couldn't recognize the size of '${key}' in opcode '${s}'`);
}
if (dup[key_] === true) {
bits = 0;
lbit = 0;
hbit = 0;
}
else {
dup[key_] = true;
}
}
let field = fields[key];
if (!field) {
field = {
index: opcodeIndex,
values: [],
bits: 0,
mask: 0,
lbit: 0,
hbit: 0 // Only 1 if a single quote (') was used.
}
fields[key] = field;
}
if (from === -1)
from = field.bits;
field.mask |= mask;
field.bits += bits;
field.lbit += lbit;
field.hbit += hbit;
field.values.push({
index: opcodeIndex,
from: from,
size: size
});
opcodeIndex += size;
}
}
for (let i = 0; i < pattern.length; i++)
if (pattern[i] === 'U')
pattern[i] = "_";
// Normalize all fields.
for (let key in fields) {
const field = fields[key];
// There should be either number of bits or mask, there shouldn't be both.
if (!field.bits && !field.mask)
FAIL(`Part '${key}' of opcode '${s}' contains neither size nor mask`);
if (field.bits && field.mask)
FAIL(`Part '${key}' of opcode '${s}' contains both size and mask`);
if (field.bits)
field.mask = ((1 << field.bits) - 1);
else if (field.mask)
field.bits = 32 - Math.clz32(field.mask);
// Handle field that used single-quote.
if (field.lbit) {
field.mask = (field.mask << 1) | 0x1;
field.bits++;
}
if (field.hbit) {
field.mask |= 1 << field.bits;
field.bits++;
}
const op = this.operandByName(key);
if (op && op.isImm())
op.immSize = field.bits;
}
// Check if the opcode value has the correct number of bits.
if (opcodeIndex !== 32)
FAIL(`The number of bits '${opcodeIndex}' used by the opcode '${s}' doesn't match 32`);
this.opcodeValue = normalizeNumber(opcodeValue);
}
_assignSpecificAttribute(key, value) {
switch (key) {
case "it": {
const values = String(value).split("|");
for (let i = 0; i < values.length; i++) {
const value = values[i];
switch (value) {
case "in" : this.it.IN = true; break;
case "out" : this.it.OUT = true; break;
case "any" : this.it.IN = true;
this.it.OUT = true; break;
case "last": this.it.LAST = true; break;
case "def" : this.it.DEF = true; break;
default:
this.report(`${this.name}: Unhandled IT value '${value}'`);
}
}
return true;
}
}
return false;
}
_postProcess() {}
operandByName(name) {
const operands = this.operands;
for (let i = 0; i < operands.length; i++) {
const op = operands[i];
if (op.name === name)
return op;
}
return null;
}
}
arm.Instruction = Instruction;
// asmdb.aarch64.ISA
// =================
function mergeGroupData(data, group) {
for (let k in group) {
switch (k) {
case "group":
case "data":
break;
case "ext":
data[k] = (data[k] ? data[k] + " " : "") + group[k];
break;
default:
if (data[k] === undefined)
data[k] = group[k]
break;
}
}
}
class ISA extends base.ISA {
constructor(data) {
super(data);
this.addData(data || NONE);
}
_addInstructions(groups) {
for (let group of groups) {
for (let instructions of group.data) {
const sgn = Utils.splitInstructionSignature(instructions.inst);
const data = MapUtils.cloneExcept(instructions, { "inst": true });
mergeGroupData(data, group)
for (let j = 0; j < sgn.names.length; j++) {
data.name = sgn.names[j];
data.operands = sgn.operands;
if (j > 0)
data.aliasOf = sgn.names[0];
this._addInstruction(new Instruction(this, data));
}
}
}
return this;
}
}
arm.ISA = ISA;
}).apply(this, typeof module === "object" && module && module.exports
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "aarch64"]);