mirror of
https://github.com/asmjit/asmjit.git
synced 2025-12-18 04:54:36 +03:00
[ABI] Completely reworked instruction DB and generators
* Instruction database is now part of asmjit to keep it in sync * X86/X64 ISA data has been reworked, now in a proper JSON format * ARM32 ISA data has been added (currently only DB, support later) * ARM64 ISA data has been added * ARM features detection has been updated
This commit is contained in:
15
db/README.md
Normal file
15
db/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
AsmJit Instruction Database
|
||||
---------------------------
|
||||
|
||||
This is a database of instructions that is used by AsmJit to generate its internal database and also assembler implementations. This project started initially as AsmDB, but was merged to AsmJit later to make the maintenance easier. The database was created in a way so that each instruction definition would only need a single line in JSON data. The data is then processed by architecture specific data readers that make the data canonical and ready for processing.
|
||||
|
||||
AsmJit database provides the following ISAs:
|
||||
|
||||
* `isa_x86.json` - provides X86 instruction data (both 32-bit and 64-bit)
|
||||
* `isa_arm.json` - provides AArch32 instruction data (both ARM32 and THUMB)
|
||||
* `isa_a64.json` - provides AArch64 instruction data
|
||||
|
||||
To Be Documented
|
||||
----------------
|
||||
|
||||
This project will be refactored and documented in the future.
|
||||
921
db/aarch64.js
Normal file
921
db/aarch64.js
Normal file
@@ -0,0 +1,921 @@
|
||||
// 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 instructictions 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(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 inst of group.data) {
|
||||
const sgn = Utils.splitInstructionSignature(inst.inst);
|
||||
const data = MapUtils.cloneExcept(inst, { "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"]);
|
||||
941
db/arm.js
Normal file
941
db/arm.js
Normal file
@@ -0,0 +1,941 @@
|
||||
// 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("[AArch32] " + 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_arm.json";
|
||||
|
||||
// asmdb.arm.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 instructictions 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 },
|
||||
"isFp32": { "bits": 1 },
|
||||
"F" : { "bits": 1 },
|
||||
"align" : { "bits": 2 },
|
||||
"ja" : { "bits": 1 },
|
||||
"jb" : { "bits": 1 },
|
||||
"op" : { "bits": 1 }, // TODO: This should be fixed.
|
||||
"sz" : { "bits": 2 },
|
||||
"sop" : { "bits": 2 },
|
||||
"cond" : { "bits": 4 },
|
||||
"cmode" : { "bits": 4 },
|
||||
"Cn" : { "bits": 4 },
|
||||
"Cm" : { "bits": 4 },
|
||||
"Rd" : { "bits": 4, "read": false, "write": true },
|
||||
"Rd2" : { "bits": 4, "read": false, "write": true },
|
||||
"RdLo" : { "bits": 4, "read": false, "write": true },
|
||||
"RdHi" : { "bits": 4, "read": false, "write": true },
|
||||
"RdList": { "bits": 4, "read": false, "write": true , "list": true },
|
||||
"Rx" : { "bits": 4, "read": true , "write": true },
|
||||
"RxLo" : { "bits": 4, "read": true , "write": true },
|
||||
"RxHi" : { "bits": 4, "read": true , "write": true },
|
||||
"Rn" : { "bits": 4, "read": true , "write": false },
|
||||
"Rm" : { "bits": 4, "read": true , "write": false },
|
||||
"Ra" : { "bits": 4, "read": true , "write": false },
|
||||
"Rs" : { "bits": 4, "read": true , "write": false },
|
||||
"Rs2" : { "bits": 4, "read": true , "write": false },
|
||||
"RsList": { "bits": 4, "read": true , "write": false , "list": true },
|
||||
"Sd" : { "bits": 4, "read": false, "write": true },
|
||||
"Sd2" : { "bits": 4, "read": false, "write": true },
|
||||
"Sx" : { "bits": 4, "read": true , "write": true },
|
||||
"Sn" : { "bits": 4, "read": true , "write": false },
|
||||
"Sm" : { "bits": 4, "read": true , "write": false },
|
||||
"Ss" : { "bits": 4, "read": true , "write": false },
|
||||
"Ss2" : { "bits": 4, "read": true , "write": false },
|
||||
"Dd" : { "bits": 4, "read": false, "write": true },
|
||||
"Dd2" : { "bits": 4, "read": false, "write": true },
|
||||
"Dd3" : { "bits": 4, "read": false, "write": true },
|
||||
"Dd4" : { "bits": 4, "read": false, "write": true },
|
||||
"Dx" : { "bits": 4, "read": true , "write": true },
|
||||
"Dx2" : { "bits": 4, "read": true , "write": true },
|
||||
"Dn" : { "bits": 4, "read": true , "write": false },
|
||||
"Dn2" : { "bits": 4, "read": true , "write": false },
|
||||
"Dn3" : { "bits": 4, "read": true , "write": false },
|
||||
"Dn4" : { "bits": 4, "read": true , "write": false },
|
||||
"Dm" : { "bits": 4, "read": true , "write": false },
|
||||
"Ds" : { "bits": 4, "read": true , "write": false },
|
||||
"Ds2" : { "bits": 4, "read": true , "write": false },
|
||||
"Ds3" : { "bits": 4, "read": true , "write": false },
|
||||
"Ds4" : { "bits": 4, "read": true , "write": false },
|
||||
"Vd" : { "bits": 4, "read": false, "write": true },
|
||||
"Vd2" : { "bits": 4, "read": false, "write": true },
|
||||
"Vd3" : { "bits": 4, "read": false, "write": true },
|
||||
"Vd4" : { "bits": 4, "read": false, "write": true },
|
||||
"VdList": { "bits": 4, "read": false, "write": true , "list": true },
|
||||
"Vx" : { "bits": 4, "read": true , "write": true },
|
||||
"Vx2" : { "bits": 4, "read": true , "write": true },
|
||||
"Vn" : { "bits": 4, "read": true , "write": false },
|
||||
"Vm" : { "bits": 4, "read": true , "write": false },
|
||||
"Vs" : { "bits": 4, "read": true , "write": false },
|
||||
"Vs2" : { "bits": 4, "read": true , "write": false },
|
||||
"VsList": { "bits": 4, "read": true , "write": false , "list": true }
|
||||
};
|
||||
|
||||
arm.FieldInfo = FieldInfo;
|
||||
|
||||
// ARM 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 parseShiftOp(s) {
|
||||
const m = s.match(/^(sop|lsl_or_asr|lsl|lsr|asr|ror|rrx) /);
|
||||
return m ? m[1] : "";
|
||||
}
|
||||
|
||||
static parseDtArray(s) {
|
||||
const out = [];
|
||||
if (!s) return out;
|
||||
|
||||
const arr = s.split("|");
|
||||
let i;
|
||||
|
||||
// First expand anything between X-Y, for example s8-32 would be expanded to [s8, s16, s32].
|
||||
for (i = 0; i < arr.length; i++) {
|
||||
const v = arr[i];
|
||||
|
||||
if (v.indexOf("-") !== -1) {
|
||||
const m = /^([A-Za-z]+)?(\d+)-(\d+)$/.exec(v);
|
||||
if (!m)
|
||||
FAIL(`Couldn't parse '${s}' data-type`);
|
||||
|
||||
let type = m[1] || "";
|
||||
let size = parseInt(m[2], 10);
|
||||
let last = parseInt(m[3], 10);
|
||||
|
||||
if (!Utils.checkDtSize(size) || !Utils.checkDtSize(last))
|
||||
FAIL(`Invalid dt width in '${s}'`);
|
||||
|
||||
do {
|
||||
out.push(type + String(size));
|
||||
size <<= 1;
|
||||
} while (size <= last);
|
||||
}
|
||||
else {
|
||||
out.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Now expand 'x' to 's' and 'u'.
|
||||
i = 0;
|
||||
while (i < out.length) {
|
||||
const v = out[i];
|
||||
if (v.startsWith("x")) {
|
||||
out.splice(i, 1, "s" + v.substr(1), "u" + v.substr(1));
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static checkDtSize(x) {
|
||||
return x === 8 || x === 16 || x === 32 || x === 64;
|
||||
}
|
||||
}
|
||||
arm.Utils = Utils;
|
||||
|
||||
function normalizeNumber(n) {
|
||||
return n < 0 ? 0x100000000 + n : n;
|
||||
}
|
||||
|
||||
function decomposeOperand(s) {
|
||||
const elementSuffix = "[#i]";
|
||||
let element = null;
|
||||
let consecutive = 0;
|
||||
|
||||
if (s.endsWith(elementSuffix)) {
|
||||
element = "#i";
|
||||
s = s.substr(0, s.length - elementSuffix.length);
|
||||
}
|
||||
|
||||
if (s.endsWith("++")) {
|
||||
consecutive = 2;
|
||||
s = s.substr(0, s.length - 2);
|
||||
}
|
||||
else if (s.endsWith("+")) {
|
||||
consecutive = 1;
|
||||
s = s.substr(0, s.length - 1);
|
||||
}
|
||||
|
||||
let m = s.match(/==|\!=|>=|<=|\*/);
|
||||
let restrict = false;
|
||||
|
||||
if (m) {
|
||||
restrict = s.substr(m.index);
|
||||
s = s.substr(0, m.index);
|
||||
}
|
||||
|
||||
return {
|
||||
data : s,
|
||||
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.arm.Operand
|
||||
// =================
|
||||
|
||||
// ARM operand.
|
||||
class Operand extends base.Operand {
|
||||
constructor(def) {
|
||||
super(def);
|
||||
}
|
||||
|
||||
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.substr(1), 10);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
arm.Operand = Operand;
|
||||
|
||||
// asmdb.arm.Instruction
|
||||
// =====================
|
||||
|
||||
function patternFromOperand(key) {
|
||||
return key;
|
||||
// return key.replace(/\b(?:[RVDS](?:d|s|n|m|x|x2))\b/, "R");
|
||||
}
|
||||
|
||||
// ARM instruction.
|
||||
class Instruction extends base.Instruction {
|
||||
constructor(db, data) {
|
||||
super(db, data);
|
||||
// name, operands, encoding, opcode, metadata
|
||||
|
||||
const encoding = hasOwn.call(data, "a32") ? "a32" :
|
||||
hasOwn.call(data, "t32") ? "t32" :
|
||||
hasOwn.call(data, "t16") ? "t16" : "";
|
||||
|
||||
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.s = null; // Instruction S flag (null, true, or false).
|
||||
this.dt = []; // Instruction <dt> field (first data-type).
|
||||
this.dt2 = []; // Instruction <dt2> field (second data-type).
|
||||
|
||||
this.availableFrom = ""; // Instruction supported by from ARMv???.
|
||||
this.availableUntil = ""; // Instruction supported by until ARMv???.
|
||||
|
||||
this._assignOperands(data.operands);
|
||||
this._assignEncoding(encoding.toUpperCase());
|
||||
this._assignOpcode(data[encoding]);
|
||||
|
||||
for (let k in data) {
|
||||
if (k === "name" || k == encoding || 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.substr(0, eq);
|
||||
let val = eq === -1 ? true : attr.substr(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.substr(0, dot + 1);
|
||||
const keys = (dot === -1 ? key : key.substr(dot + 1)).split("|");
|
||||
|
||||
for (let j = 0; j < keys.length; j++)
|
||||
this[name][base + keys[j]] = val;
|
||||
}
|
||||
else {
|
||||
this[name][key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_assignEncoding(s) {
|
||||
this.arch = s === "T16" || s === "T32" ? "THUMB" : "ARM";
|
||||
this.encoding = s;
|
||||
}
|
||||
|
||||
_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];
|
||||
const op = new Operand(def);
|
||||
|
||||
const consecutive = def.match(/(\d+)x\{(.*)\}([+][+]?)/);
|
||||
if (consecutive)
|
||||
def = consecutive[2];
|
||||
|
||||
op.sign = false;
|
||||
op.element = null;
|
||||
op.shiftOp = "";
|
||||
op.shiftImm = null;
|
||||
|
||||
// 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.parseShiftOp(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(/^\{(lsl|sop)\s+#(\w+)\}$/);
|
||||
if (m) {
|
||||
op.shiftOp = m[1];
|
||||
op.shiftImm = m[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
op.base = dict();
|
||||
op.base.field = part;
|
||||
op.base.exp = null;
|
||||
|
||||
const m = part.match(/^([A-Za-z]\w*)/);
|
||||
if (m.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.substr(0, expMatch[1].length);
|
||||
}
|
||||
|
||||
const mulMatch = p.match(/\s*\*\s*(\d+)$/);
|
||||
if (mulMatch) {
|
||||
offMul = parseInt(mulMatch[1]);
|
||||
p = p.substr(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*)/);
|
||||
if (m.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 {
|
||||
const obj = decomposeOperand(def);
|
||||
const reg = obj.data;
|
||||
|
||||
const type = reg.substr(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.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;
|
||||
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];
|
||||
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.substr(0, m.index).trim();
|
||||
}
|
||||
else if ((m = key.match(/\[\s*(\d+)\s*\]$/))) {
|
||||
from = parseInt(m[1], 10);
|
||||
size = 1;
|
||||
mask = 1 << from;
|
||||
key = key.substr(0, m.index).trim();
|
||||
}
|
||||
else if ((m = key.match(/\:\s*(\d+)$/))) {
|
||||
size = parseInt(m[1], 10);
|
||||
bits = size;
|
||||
key = key.substr(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 (either 16 or 32).
|
||||
if (opcodeIndex !== 16 && opcodeIndex !== 32)
|
||||
FAIL(`The number of bits '${opcodeIndex}' used by the opcode '${s}' doesn't match 16 or 32`);
|
||||
this.opcodeValue = normalizeNumber(opcodeValue);
|
||||
}
|
||||
|
||||
_assignSpecificAttribute(key, value) {
|
||||
// Support ARMv?+ and ARMv?- attributes.
|
||||
if (/^ARM\w+[+-]$/.test(key)) {
|
||||
const armv = key.substr(0, key.length - 1);
|
||||
const sign = key.substr(key.length - 1);
|
||||
|
||||
if (sign === "+")
|
||||
this.availableFrom = armv;
|
||||
else
|
||||
this.availableUntil = armv;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// ARM instruction name could consist of name and optional type information
|
||||
// specified as <dt> and <dt2> in ARM manuals. We parse this information and
|
||||
// store it to `dt` and `dt2` fields. In addition, we also recognize the `S`
|
||||
// suffix (uppercase) of the instruction and mark it as `S` instruction. After
|
||||
// that the name is normalized to be lowercased.
|
||||
//
|
||||
// This functionality requires all the instruction data to be already set-up.
|
||||
_postProcess() {
|
||||
let s = this.name;
|
||||
|
||||
// Parse <dt> and <dt2> fields.
|
||||
if (s.indexOf(".") !== -1) {
|
||||
const parts = s.split(".");
|
||||
this.name = parts[0];
|
||||
|
||||
if (parts.length > 3)
|
||||
FAIL(`Couldn't recognize name attributes of '${s}'`);
|
||||
|
||||
for (let i = 1; i < parts.length; i++) {
|
||||
const dt = Utils.parseDtArray(parts[i]);
|
||||
if (i === 1)
|
||||
this.dt = dt;
|
||||
else
|
||||
this.dt2 = dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Recognize "S" suffix.
|
||||
if (this.name.endsWith("S")) {
|
||||
this.name = this.name.substr(0, this.name.length - 1) + "s";
|
||||
this.s = true;
|
||||
}
|
||||
|
||||
this.dt.sort();
|
||||
}
|
||||
|
||||
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.arm.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 inst of group.data) {
|
||||
const sgn = Utils.splitInstructionSignature(inst.inst);
|
||||
const data = MapUtils.cloneExcept(inst, { "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;
|
||||
}
|
||||
/*
|
||||
_addInstructions(instructions) {
|
||||
for (let i = 0; i < instructions.length; i++) {
|
||||
const obj = instructions[i];
|
||||
const sgn = obj.inst;
|
||||
const sep = sgn.indexOf(" ");
|
||||
|
||||
const names = (sep !== -1 ? sgn.substring(0, sep) : sgn).trim().split("/");
|
||||
const operands = sep !== -1 ? sgn.substring(sep + 1) : "";
|
||||
|
||||
const encoding = hasOwn.call(obj, "a32") ? "a32" :
|
||||
hasOwn.call(obj, "t32") ? "t32" :
|
||||
hasOwn.call(obj, "t16") ? "t16" : "";
|
||||
|
||||
if (!encoding)
|
||||
FAIL(`Instrution ${names.join("/")} doesn't encoding, it must provide either a32, t32, or t16 field`);
|
||||
|
||||
for (let j = 0; j < names.length; j++) {
|
||||
const inst = new Instruction(this, names[j], operands, encoding.toUpperCase(), obj[encoding], obj);
|
||||
if (j > 0)
|
||||
inst.aliasOf = names[0];
|
||||
this._addInstruction(inst);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
*/
|
||||
}
|
||||
arm.ISA = ISA;
|
||||
|
||||
}).apply(this, typeof module === "object" && module && module.exports
|
||||
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "arm"]);
|
||||
645
db/base.js
Normal file
645
db/base.js
Normal file
@@ -0,0 +1,645 @@
|
||||
// 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("[BASE] " + msg); }
|
||||
|
||||
// Import.
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
const exp = $scope.exp ? $scope.exp : require("./exp.js");
|
||||
|
||||
|
||||
// Export.
|
||||
const base = $scope[$as] = Object.create(null);
|
||||
|
||||
base.exp = exp;
|
||||
|
||||
function dict(src) {
|
||||
const dst = Object.create(null);
|
||||
if (src)
|
||||
Object.assign(dst, src);
|
||||
return dst;
|
||||
}
|
||||
base.dict = dict;
|
||||
const NONE = base.NONE = Object.freeze(dict());
|
||||
|
||||
// asmdb.base.Symbols
|
||||
// ==================
|
||||
|
||||
const Symbols = Object.freeze({
|
||||
Commutative: '~'
|
||||
});
|
||||
base.Symbols = Symbols;
|
||||
|
||||
// asmdb.base.Parsing
|
||||
// ==================
|
||||
|
||||
// Namespace that provides functions related to text parsing.
|
||||
const Parsing = {
|
||||
// Get whether the string `s` representing an operand is <implicit>.
|
||||
isImplicit: function(s) { return s.startsWith("<") && s.endsWith(">"); },
|
||||
|
||||
// Clear <implicit> attribute from the given operand string `s`.
|
||||
clearImplicit: function(s) { return s.substring(1, s.length - 1); },
|
||||
|
||||
// Get whether the string `s` representing an operand is {optional}.
|
||||
isOptional: function(s) { return s.startsWith("{") && s.endsWith("}"); },
|
||||
|
||||
// Clear {optional} attribute from the given operand string `s`.
|
||||
clearOptional: function(s) { return s.substring(1, s.length - 1); },
|
||||
|
||||
// Get whether the string `s` representing an operand specifies commutativity.
|
||||
isCommutative: function(s) { return s.length > 0 && s.charAt(0) === Symbols.Commutative; },
|
||||
|
||||
// Clear commutative attribute from the given operand string `s`.
|
||||
clearCommutative: function(s) { return s.substring(1); },
|
||||
|
||||
// Matches a closing bracket in string `s` starting `from` the given index.
|
||||
// It behaves like `s.indexOf()`, but uses a counter and skips all nested
|
||||
// matches.
|
||||
matchClosingChar: function(s, from) {
|
||||
const len = s.length;
|
||||
const opening = s.charCodeAt(from);
|
||||
const closing = opening === 40 ? 31 : // ().
|
||||
opening === 60 ? 62 : // <>.
|
||||
opening === 91 ? 93 : // [].
|
||||
opening === 123 ? 125 : 0; // {}.
|
||||
|
||||
let i = from;
|
||||
let pending = 1;
|
||||
do {
|
||||
if (++i >= len)
|
||||
break;
|
||||
|
||||
const c = s.charCodeAt(i);
|
||||
pending += Number(c === opening);
|
||||
pending -= Number(c === closing);
|
||||
} while (pending);
|
||||
|
||||
return i;
|
||||
},
|
||||
|
||||
// Split instruction operands into an array containing each operand as a
|
||||
// trimmed string. This function is similar to `s.split(",")`, however,
|
||||
// it matches brackets inside the operands and won't just blindly split
|
||||
// the string based on "," token. If operand contains metadata or it's
|
||||
// an address it would still be split correctly.
|
||||
splitOperands: function(s) {
|
||||
const result = [];
|
||||
|
||||
s = s.trim();
|
||||
if (!s)
|
||||
return result;
|
||||
|
||||
let start = 0;
|
||||
let i = 0;
|
||||
let c = "";
|
||||
|
||||
for (;;) {
|
||||
if (i === s.length || (c = s[i]) === ",") {
|
||||
const op = s.substring(start, i).trim();
|
||||
if (!op)
|
||||
FAIL(`Found empty operand in '${s}'`);
|
||||
|
||||
result.push(op);
|
||||
if (i === s.length)
|
||||
return result;
|
||||
|
||||
start = ++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c === "<" || c === ">") && i != start) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === "[" || c === "{" || c === "(" || c === "<")
|
||||
i = base.Parsing.matchClosingChar(s, i);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
base.Parsing = Parsing;
|
||||
|
||||
// asmdb.base.MapUtils
|
||||
// ===================
|
||||
|
||||
const MapUtils = {
|
||||
cloneExcept(map, except) {
|
||||
const out = Object.create(null);
|
||||
for (let k in map) {
|
||||
if (k in except)
|
||||
continue
|
||||
out[k] = map[k];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
base.MapUtils = MapUtils;
|
||||
|
||||
// asmdb.base.Operand
|
||||
// ==================
|
||||
|
||||
const OperandFlags = Object.freeze({
|
||||
Optional : 0x00000001,
|
||||
Implicit : 0x00000002,
|
||||
Commutative: 0x00000004,
|
||||
ZExt : 0x00000008,
|
||||
ReadAccess : 0x00000010,
|
||||
WriteAccess: 0x00000020
|
||||
});
|
||||
base.OperandFlags = OperandFlags;
|
||||
|
||||
class Operand {
|
||||
constructor(data) {
|
||||
this.type = ""; // Type of the operand ("reg", "reg-list", "mem", "reg/mem", "imm", "rel").
|
||||
this.data = data; // The operand's data (possibly processed).
|
||||
this.flags = 0;
|
||||
|
||||
this.reg = ""; // Register operand's definition.
|
||||
this.mem = ""; // Memory operand's definition.
|
||||
this.imm = 0; // Immediate operand's size.
|
||||
this.rel = 0; // Relative displacement operand's size.
|
||||
|
||||
this.restrict = ""; // Operand is restricted (specific register or immediate value).
|
||||
this.read = false; // True if the operand is a read-op from reg/mem.
|
||||
this.write = false; // True if the operand is a write-op to reg/mem.
|
||||
|
||||
this.regType = ""; // Register operand's type.
|
||||
this.regIndexRel = 0; // Register index is relative to the previous register operand index (0 if not).
|
||||
this.memSize = -1; // Memory operand's size.
|
||||
this.immSign = ""; // Immediate sign (any / signed / unsigned).
|
||||
this.immValue = null; // Immediate value - `null` or `1` (only used by shift/rotate instructions).
|
||||
|
||||
this.rwxIndex = -1; // Read/Write (RWX) index.
|
||||
this.rwxWidth = -1; // Read/Write (RWX) width.
|
||||
}
|
||||
|
||||
_getFlag(flag) {
|
||||
return (this.flags & flag) != 0;
|
||||
}
|
||||
|
||||
_setFlag(flag, value) {
|
||||
this.flags = (this.flags & ~flag) | (value ? flag : 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
get optional() { return this._getFlag(OperandFlags.Optional); }
|
||||
set optional(value) { this._setFlag(OperandFlags.Optional, value); }
|
||||
|
||||
get implicit() { return this._getFlag(OperandFlags.Implicit); }
|
||||
set implicit(value) { this._setFlag(OperandFlags.Implicit, value); }
|
||||
|
||||
get commutative() { return this._getFlag(OperandFlags.Commutative); }
|
||||
set commutative(value) { this._setFlag(OperandFlags.Commutative, value); }
|
||||
|
||||
get zext() { return this._getFlag(OperandFlags.ZExt); }
|
||||
set zext(value) { this._setFlag(OperandFlags.ZExt, value); }
|
||||
|
||||
toString() { return this.data; }
|
||||
|
||||
isReg() { return !!this.reg; }
|
||||
isMem() { return !!this.mem; }
|
||||
isImm() { return !!this.imm; }
|
||||
isRel() { return !!this.rel; }
|
||||
|
||||
isRegMem() { return this.reg && this.mem; }
|
||||
isRegOrMem() { return !!this.reg || !!this.mem; }
|
||||
|
||||
isRegList() { return this.type === "reg-list" }
|
||||
isPartialOp() { return false; }
|
||||
}
|
||||
base.Operand = Operand;
|
||||
|
||||
// asmdb.base.Instruction
|
||||
// ======================
|
||||
|
||||
// Defines interface and properties that each architecture dependent instruction
|
||||
// must provide even if that particular architecture doesn't use that feature(s).
|
||||
class Instruction {
|
||||
constructor(db) {
|
||||
Object.defineProperty(this, "db", { value: db });
|
||||
|
||||
this.name = ""; // Instruction name.
|
||||
this.arch = "ANY"; // Architecture.
|
||||
this.encoding = ""; // Encoding type.
|
||||
this.operands = []; // Instruction operands.
|
||||
|
||||
this.implicit = 0; // Indexes of all implicit operands (registers / memory).
|
||||
this.commutative = 0; // Indexes of all commutative operands.
|
||||
|
||||
this.opcodeString = ""; // Instruction opcode as specified in manual.
|
||||
this.opcodeValue = 0; // Instruction opcode as number (arch dependent).
|
||||
this.fields = dict(); // Information about each opcode field (arch dependent).
|
||||
this.operations = dict(); // Operations the instruction performs.
|
||||
|
||||
this.io = dict(); // Instruction input / output (CPU flags, states, and other registers).
|
||||
this.ext = dict(); // ISA extensions required by the instruction.
|
||||
this.category = dict(); // Instruction categories.
|
||||
|
||||
this.specialRegs = dict(); // Information about read/write to special registers.
|
||||
|
||||
this.altForm = false; // This is an alternative form, not needed to create a signature.
|
||||
this.volatile = false; // Instruction is volatile and should not be reordered.
|
||||
this.control = "none"; // Control flow type (none by default).
|
||||
this.privilege = ""; // Privilege-level required to execute the instruction.
|
||||
this.aliasOf = ""; // Instruction is an alias of another instruction
|
||||
}
|
||||
|
||||
get extArray() {
|
||||
const out = Object.keys(this.ext);
|
||||
out.sort();
|
||||
return out;
|
||||
}
|
||||
|
||||
_assignAttribute(key, value) {
|
||||
switch (key) {
|
||||
case "ext":
|
||||
case "io":
|
||||
case "category":
|
||||
return this._combineAttribute(key, value);
|
||||
|
||||
default:
|
||||
if (typeof this[key] === undefined)
|
||||
FAIL(`Cannot assign ${key}=${value}`);
|
||||
this[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_combineAttribute(key, value) {
|
||||
if (typeof value === "string")
|
||||
value = value.split(" ");
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
for (let v of value) {
|
||||
let pKeys = v;
|
||||
let pValue = true;
|
||||
|
||||
const i = v.indexOf("=");
|
||||
if (i !== -1) {
|
||||
pValue = v.substring(i + 1);
|
||||
pKeys = v.substring(0, i).trim();
|
||||
}
|
||||
|
||||
for (let pk of pKeys.trim().split("|").map(function(s) { return s.trim(); })) {
|
||||
this[key][pk] = pValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let k in value)
|
||||
this[key][k] = value[k];
|
||||
}
|
||||
}
|
||||
|
||||
_updateOperandsInfo() {
|
||||
this.implicit = 0;
|
||||
this.commutative = 0;
|
||||
|
||||
for (let i = 0; i < this.operands.length; i++) {
|
||||
const op = this.operands[i];
|
||||
|
||||
if (op.implicit) this.implicit |= (1 << i);
|
||||
if (op.commutative) this.commutative |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
isAlias() { return !!this.aliasOf; }
|
||||
isCommutative() { return this.commutative !== 0; }
|
||||
|
||||
hasImplicit() { return this.implicit !== 0; }
|
||||
|
||||
hasAttribute(name, matchValue) {
|
||||
const value = this[name];
|
||||
if (value === undefined)
|
||||
return false;
|
||||
|
||||
if (matchValue === undefined)
|
||||
return true;
|
||||
|
||||
return value === matchValue;
|
||||
}
|
||||
|
||||
report(msg) {
|
||||
console.log(`${this}: ${msg}`);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.name} ${this.operands.join(", ")}`;
|
||||
}
|
||||
}
|
||||
base.Instruction = Instruction;
|
||||
|
||||
// asmdb.base.InstructionGroup
|
||||
// ===========================
|
||||
|
||||
// Instruction group is simply array of function that has some additional
|
||||
// functionality.
|
||||
class InstructionGroup extends Array {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (arguments.length === 1) {
|
||||
const a = arguments[0];
|
||||
if (Array.isArray(a)) {
|
||||
for (let i = 0; i < a.length; i++)
|
||||
this.push(a[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unionCpuFeatures(name) {
|
||||
const result = dict();
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
const inst = this[i];
|
||||
const features = inst.ext;
|
||||
for (let k in features)
|
||||
result[k] = features[k];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
checkAttribute(key, value) {
|
||||
let n = 0;
|
||||
for (let i = 0; i < this.length; i++)
|
||||
n += Number(this[i][key] === value);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
base.InstructionGroup = InstructionGroup;
|
||||
|
||||
const EmptyInstructionGroup = Object.freeze(new InstructionGroup());
|
||||
|
||||
// asmdb.base.ISA
|
||||
// ==============
|
||||
|
||||
class ISA {
|
||||
constructor() {
|
||||
this._instructions = null; // Instruction array (contains all instructions).
|
||||
this._instructionNames = null; // Instruction names (sorted), regenerated when needed.
|
||||
this._instructionMap = dict(); // Instruction name to `Instruction[]` mapping.
|
||||
this._aliases = dict(); // Instruction aliases.
|
||||
this._cpuLevels = dict(); // Architecture versions.
|
||||
this._extensions = dict(); // Architecture extensions.
|
||||
this._attributes = dict(); // Instruction attributes.
|
||||
this._specialRegs = dict(); // Special registers.
|
||||
this._shortcuts = dict(); // Shortcuts used by instructions metadata.
|
||||
this.stats = {
|
||||
insts : 0, // Number of all instructions.
|
||||
groups: 0 // Number of grouped instructions (having unique name).
|
||||
};
|
||||
}
|
||||
|
||||
get instructions() {
|
||||
let array = this._instructions;
|
||||
if (array === null) {
|
||||
array = [];
|
||||
const map = this.instructionMap;
|
||||
const names = this.instructionNames;
|
||||
for (let i = 0; i < names.length; i++)
|
||||
array.push.apply(array, map[names[i]]);
|
||||
this._instructions = array;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
get instructionNames() {
|
||||
let names = this._instructionNames;
|
||||
if (names === null) {
|
||||
names = Object.keys(this._instructionMap);
|
||||
names.sort();
|
||||
this._instructionNames = names;
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
get instructionMap() { return this._instructionMap; }
|
||||
get aliases() { return this._aliases; }
|
||||
get cpuLevels() { return this._cpuLevels; }
|
||||
get extensions() { return this._extensions; }
|
||||
get attributes() { return this._attributes; }
|
||||
get specialRegs() { return this._specialRegs; }
|
||||
get shortcuts() { return this._shortcuts; }
|
||||
|
||||
query(args, copy) {
|
||||
if (typeof args !== "object" || !args || Array.isArray(args))
|
||||
return this._queryByName(args, copy);
|
||||
|
||||
const filter = args.filter;
|
||||
if (filter)
|
||||
copy = false;
|
||||
|
||||
let result = this._queryByName(args.name, copy);
|
||||
if (filter)
|
||||
result = result.filter(filter, args.filterThis);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_queryByName(name, copy) {
|
||||
let result = EmptyInstructionGroup;
|
||||
const map = this._instructionMap;
|
||||
|
||||
if (typeof name === "string") {
|
||||
const insts = map[name];
|
||||
if (insts) result = insts;
|
||||
return copy ? result.slice() : result;
|
||||
}
|
||||
|
||||
if (Array.isArray(name)) {
|
||||
const names = name;
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const insts = map[names[i]];
|
||||
if (!insts) continue;
|
||||
|
||||
if (result === EmptyInstructionGroup)
|
||||
result = new InstructionGroup();
|
||||
|
||||
for (let j = 0; j < insts.length; j++)
|
||||
result.push(insts[j]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
result = this.instructions;
|
||||
return copy ? result.slice() : result;
|
||||
}
|
||||
|
||||
forEachGroup(cb, thisArg) {
|
||||
const map = this._instructionMap;
|
||||
const names = this.instructionNames;
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
cb.call(thisArg, name, map[name]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
addData(data) {
|
||||
if (typeof data !== "object" || !data)
|
||||
FAIL("ISA.addData(): data argument must be object");
|
||||
|
||||
if (data.cpuLevels) this._addCpuLevels(data.cpuLevels);
|
||||
if (data.specialRegs) this._addSpecialRegs(data.specialRegs);
|
||||
if (data.shortcuts) this._addShortcuts(data.shortcuts);
|
||||
if (data.instructions) this._addInstructions(data.instructions);
|
||||
if (data.postproc) this._postProc(data.postproc);
|
||||
}
|
||||
|
||||
_postProc(groups) {
|
||||
for (let group of groups) {
|
||||
for (let iRule of group.instructions) {
|
||||
const names = iRule.inst.split(" ");
|
||||
for (let name of names) {
|
||||
const insts = this._instructionMap[name];
|
||||
if (!insts)
|
||||
FAIL(`Instruction ${name} referenced by '${group.group}' group doesn't exist`);
|
||||
|
||||
for (let k in iRule) {
|
||||
if (k === "inst" || k === "data")
|
||||
continue;
|
||||
for (let inst of insts) {
|
||||
inst._assignAttribute(k, iRule[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_addCpuLevels(items) {
|
||||
if (!Array.isArray(items))
|
||||
FAIL("Property 'cpuLevels' must be array");
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const name = item.name;
|
||||
|
||||
const obj = {
|
||||
name: name
|
||||
};
|
||||
|
||||
this._cpuLevels[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
_addExtensions(items) {
|
||||
if (!Array.isArray(items))
|
||||
FAIL("Property 'extensions' must be array");
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const name = item.name;
|
||||
|
||||
const obj = {
|
||||
name: name,
|
||||
from: item.from || ""
|
||||
};
|
||||
|
||||
this._extensions[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
_addAttributes(items) {
|
||||
if (!Array.isArray(items))
|
||||
FAIL("Property 'attributes' must be array");
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const name = item.name;
|
||||
const type = item.type;
|
||||
|
||||
if (!/^(?:flag|string|string\[\])$/.test(type))
|
||||
FAIL(`Unknown attribute type '${type}'`);
|
||||
|
||||
const obj = {
|
||||
name: name,
|
||||
type: type,
|
||||
doc : item.doc || ""
|
||||
};
|
||||
|
||||
this._attributes[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
_addSpecialRegs(items) {
|
||||
if (!Array.isArray(items))
|
||||
FAIL("Property 'specialRegs' must be array");
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const name = item.name;
|
||||
|
||||
const obj = {
|
||||
name : name,
|
||||
group: item.group || name,
|
||||
doc : item.doc || ""
|
||||
};
|
||||
|
||||
this._specialRegs[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
_addShortcuts(items) {
|
||||
if (!Array.isArray(items))
|
||||
FAIL("Property 'shortcuts' must be array");
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const name = item.name;
|
||||
const expand = item.expand;
|
||||
|
||||
if (!name || !expand)
|
||||
FAIL("Shortcut must contain 'name' and 'expand' properties");
|
||||
|
||||
const obj = {
|
||||
name : name,
|
||||
expand: expand,
|
||||
doc : item.doc || ""
|
||||
};
|
||||
|
||||
this._shortcuts[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
_addInstructions(instructions) {
|
||||
FAIL("ISA._addInstructions() must be reimplemented");
|
||||
}
|
||||
|
||||
_addInstruction(inst) {
|
||||
let group;
|
||||
|
||||
if (hasOwn.call(this._instructionMap, inst.name)) {
|
||||
group = this._instructionMap[inst.name];
|
||||
}
|
||||
else {
|
||||
group = new InstructionGroup();
|
||||
this._instructionNames = null;
|
||||
this._instructionMap[inst.name] = group;
|
||||
this.stats.groups++;
|
||||
}
|
||||
|
||||
if (inst.aliasOf)
|
||||
this._aliases[inst.name] = inst.aliasOf;
|
||||
|
||||
group.push(inst);
|
||||
this.stats.insts++;
|
||||
this._instructions = null;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
base.ISA = ISA;
|
||||
|
||||
}).apply(this, typeof module === "object" && module && module.exports
|
||||
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "base"]);
|
||||
635
db/exp.js
Normal file
635
db/exp.js
Normal file
@@ -0,0 +1,635 @@
|
||||
// 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";
|
||||
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
// Supported Operators
|
||||
// -------------------
|
||||
|
||||
const kUnaryOperators = {
|
||||
"-": {prec: 3, rtl : 1, emit: "-@1" },
|
||||
"~": {prec: 3, rtl : 1, emit: "~@1" },
|
||||
"!": {prec: 3, rtl : 1, emit: "!@1" }
|
||||
};
|
||||
|
||||
const kBinaryOperators = {
|
||||
"*" : { prec: 5, rtl : 0, emit: "@1 * @2" },
|
||||
"/" : { prec: 5, rtl : 0, emit: "@1 / @2" },
|
||||
"%" : { prec: 5, rtl : 0, emit: "@1 % @2" },
|
||||
"+" : { prec: 6, rtl : 0, emit: "@1 + @2" },
|
||||
"-" : { prec: 6, rtl : 0, emit: "@1 - @2" },
|
||||
">>": { prec: 7, rtl : 0, emit: "@1 >> @2" },
|
||||
"<<": { prec: 7, rtl : 0, emit: "@1 << @2" },
|
||||
"<" : { prec: 9, rtl : 0, emit: "@1 < @2" },
|
||||
">" : { prec: 9, rtl : 0, emit: "@1 > @2" },
|
||||
"<=": { prec: 9, rtl : 0, emit: "@1 <= @2" },
|
||||
">=": { prec: 9, rtl : 0, emit: "@1 >= @2" },
|
||||
"==": { prec:10, rtl : 0, emit: "@1 == @2" },
|
||||
"!=": { prec:10, rtl : 0, emit: "@1 != @2" },
|
||||
"&" : { prec:11, rtl : 0, emit: "@1 & @2" },
|
||||
"^" : { prec:12, rtl : 0, emit: "@1 ^ @2" },
|
||||
"|" : { prec:13, rtl : 0, emit: "@1 | @2" },
|
||||
"&&": { prec:14, rtl : 0, emit: "@1 && @2" },
|
||||
"||": { prec:15, rtl : 0, emit: "@1 || @2" },
|
||||
"?" : { prec:16, rtl : 0, emit: "@1 ? @2" },
|
||||
":" : { prec:16, rtl : 0, emit: "@1 : @2" }
|
||||
};
|
||||
|
||||
const kMaxOperatorLen = 4;
|
||||
|
||||
function rightAssociate(info, bPrec) {
|
||||
return info.prec > bPrec || (info.prec === bPrec && info.rtl);
|
||||
}
|
||||
|
||||
// Expression Error
|
||||
// ----------------
|
||||
|
||||
// Contains `message` and `position` members. If the `position` is not `-1` then it is
|
||||
// a zero-based index, which points to a first character of the token near the error.
|
||||
class ExpressionError extends Error {
|
||||
constructor(message, position) {
|
||||
super(message);
|
||||
this.name = "ExpressionError";
|
||||
this.message = message;
|
||||
this.position = position != null ? position : -1;
|
||||
}
|
||||
}
|
||||
|
||||
function throwTokenizerError(token) {
|
||||
throw new ExpressionError(`Unexpected token '${token.data}'`, token.position);
|
||||
}
|
||||
|
||||
function throwExpressionError(message, position) {
|
||||
throw new ExpressionError(message, position);
|
||||
}
|
||||
|
||||
// Expression Tree
|
||||
// ---------------
|
||||
|
||||
function mustEnclose(node) {
|
||||
return node.isUnary() ? node.child.isOperator() : node.isBinary() ? true : false;
|
||||
}
|
||||
|
||||
class ExpNode {
|
||||
constructor(type) { this.type = type; }
|
||||
|
||||
isImm() { return this.type === "imm"; }
|
||||
isVar() { return this.type === "var"; }
|
||||
isCall() { return this.type === "call"; }
|
||||
isUnary() { return this.type === "unary"; }
|
||||
isBinary() { return this.type === "binary"; }
|
||||
isOperator() { return this.type === "unary" || this.type === "binary"; }
|
||||
|
||||
info() { return null; }
|
||||
clone() { throw new Error("ExpNode.clone() must be overridden"); }
|
||||
toString(ctx) { throw new Error("ExpNode.toString() must be overridden"); }
|
||||
}
|
||||
|
||||
class ImmNode extends ExpNode {
|
||||
constructor(imm) {
|
||||
super("imm");
|
||||
this.imm = imm || 0;
|
||||
}
|
||||
|
||||
clone() { return new ImmNode(this.imm); }
|
||||
toString(ctx) { return ctx ? ctx.stringifyImmediate(this.imm) : String(this.imm); }
|
||||
}
|
||||
|
||||
class VarNode extends ExpNode {
|
||||
constructor(name) {
|
||||
super("var");
|
||||
this.name = name || "";
|
||||
}
|
||||
|
||||
clone() { return new VarNode(this.var); }
|
||||
toString(ctx) { return ctx ? ctx.stringifyVariable(this.name) : String(this.name); }
|
||||
}
|
||||
|
||||
class CallNode extends ExpNode {
|
||||
constructor(name, args) {
|
||||
super("call");
|
||||
this.name = name || "";
|
||||
this.args = args || [];
|
||||
}
|
||||
|
||||
clone() { return new CallNode(this.name, this.args.map(function(arg) { return arg.clone(); })); }
|
||||
|
||||
toString(ctx) {
|
||||
if (this.name === "$bit") {
|
||||
return `((${this.args[0]} >> ${this.args[1]}) & 1)`;
|
||||
}
|
||||
else {
|
||||
let argsCode = this.args.map(function(arg) { return arg.toString(ctx); }).join(", ");
|
||||
if (ctx)
|
||||
return `${ctx.stringifyFunction(this.name)}(${argsCode})`;
|
||||
else
|
||||
return `${this.name}(${argsCode})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UnaryNode extends ExpNode {
|
||||
constructor(op, child) {
|
||||
if (!hasOwn.call(kUnaryOperators, op))
|
||||
throw new Error(`Invalid unary operator '${op}`);
|
||||
|
||||
super("unary");
|
||||
this.op = op;
|
||||
this.child = child || null;
|
||||
}
|
||||
|
||||
info() { return kUnaryOperators[this.op]; }
|
||||
clone() { return new UnaryNode(this.op, this.left ? this.left.clone() : null); }
|
||||
|
||||
toString(ctx) {
|
||||
return this.info().emit.replace(/@1/g, () => {
|
||||
const node = this.child;
|
||||
const code = node.toString(ctx);
|
||||
return mustEnclose(node) ? `(${code})` : code;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryNode extends ExpNode {
|
||||
constructor(op, left, right) {
|
||||
if (!hasOwn.call(kBinaryOperators, op))
|
||||
throw new Error(`Invalid binary operator '${op}`);
|
||||
|
||||
super("binary");
|
||||
this.op = op || "";
|
||||
this.left = left || null;
|
||||
this.right = right || null;
|
||||
}
|
||||
|
||||
info() { return kBinaryOperators[this.op]; }
|
||||
clone() { return new BinaryNode(this.op, this.left ? this.left.clone() : null, this.right ? this.right.clone() : null); }
|
||||
|
||||
toString(ctx) {
|
||||
return this.info().emit.replace(/@[1-2]/g, (p) => {
|
||||
const node = p === "@1" ? this.left : this.right;
|
||||
const code = node.toString(ctx);
|
||||
return mustEnclose(node) ? `(${code})` : code;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function Imm(imm) { return new ImmNode(imm); }
|
||||
function Var(name) { return new VarNode(name); }
|
||||
function Call(name, args) { return new CallNode(name, args); }
|
||||
function Unary(op, child) { return new UnaryNode(op, child); }
|
||||
function Binary(op, left, right) { return new BinaryNode(op, left, right); }
|
||||
|
||||
/*
|
||||
// TODO: Unused, remove?
|
||||
function Negate(child) { return Unary("-", child); }
|
||||
function BitNot(child) { return Unary("~", child); }
|
||||
|
||||
function Add(left, right) { return Binary("+", left, right); }
|
||||
function Sub(left, right) { return Binary("-", left, right); }
|
||||
function Mul(left, right) { return Binary("*", left, right); }
|
||||
function Div(left, right) { return Binary("/", left, right); }
|
||||
function Mod(left, right) { return Binary("%", left, right); }
|
||||
function Shl(left, right) { return Binary("<<", left, right); }
|
||||
function Shr(left, right) { return Binary(">>", left, right); }
|
||||
function BitAnd(left, right) { return Binary("&", left, right); }
|
||||
function BitOr(left, right) { return Binary("|", left, right); }
|
||||
function BitXor(left, right) { return Binary("^", left, right); }
|
||||
function Eq(left, right) { return Binary("==", left, right); }
|
||||
function Ne(left, right) { return Binary("!=", left, right); }
|
||||
function Lt(left, right) { return Binary("<", left, right); }
|
||||
function Le(left, right) { return Binary("<=", left, right); }
|
||||
function Gt(left, right) { return Binary(">", left, right); }
|
||||
function Ge(left, right) { return Binary(">=", left, right); }
|
||||
function And(left, right) { return Binary("&&", left, right); }
|
||||
function Or(left, right) { return Binary("||", left, right); }
|
||||
*/
|
||||
|
||||
// Expression Tokenizer
|
||||
// --------------------
|
||||
|
||||
const kCharNone = 0; // '_' - Character category - Invalid or <end>.
|
||||
const kCharSpace = 1; // 'S' - Character category - Space.
|
||||
const kCharAlpha = 2; // 'A' - Character category - Alpha [A-Za-z_].
|
||||
const kCharDigit = 3; // 'D' - Character category - Digit [0-9].
|
||||
const kCharPunct = 4; // '$' - Character category - Punctuation.
|
||||
|
||||
const Category = (function(_, S, A, D, $) {
|
||||
const Table = [
|
||||
_,_,_,_,_,_,_,_,_,S,S,S,S,S,_,_, // 000-015 |......... ..|
|
||||
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, // 016-031 |................|
|
||||
S,$,$,$,$,$,$,$,$,$,$,$,$,$,$,$, // 032-047 | !"#$%&'()*+,-./|
|
||||
D,D,D,D,D,D,D,D,D,D,$,$,$,$,$,$, // 048-063 |0123456789:;<=>?|
|
||||
$,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A, // 064-079 |@ABCDEFGHIJKLMNO|
|
||||
A,A,A,A,A,A,A,A,A,A,A,$,$,$,$,A, // 080-095 |PQRSTUVWXYZ[\]^_|
|
||||
$,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A, // 096-111 |`abcdefghijklmno|
|
||||
A,A,A,A,A,A,A,A,A,A,A,$,$,$,$,_, // 112-127 |pqrstuvwxyz{|}~ |
|
||||
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, // 128-143 |................|
|
||||
_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_ // 144-159 |................|
|
||||
];
|
||||
const kTableLength = Table.length;
|
||||
|
||||
return function(c) {
|
||||
if (c < kTableLength)
|
||||
return Table[c];
|
||||
return kCharNone;
|
||||
};
|
||||
})(kCharNone, kCharSpace, kCharAlpha, kCharDigit, kCharPunct);
|
||||
|
||||
const kTokenNone = 0;
|
||||
const kTokenPunct = 1;
|
||||
const kTokenIdent = 2;
|
||||
const kTokenValue = 3;
|
||||
|
||||
function newToken(type, position, data, value) {
|
||||
return {
|
||||
type : type, // Token type, see `kToken...`.
|
||||
position: position, // Token position in expression's source.
|
||||
data : data, // Token data (content) as string.
|
||||
value : value // Token value (only if the token is a value).
|
||||
};
|
||||
}
|
||||
const NoToken = newToken(kTokenNone, -1, "<end>", null);
|
||||
|
||||
// Must be reset before it can be used, use `RegExp.lastIndex`.
|
||||
const reValue = /(?:(?:\d*\.\d+|\d+)(?:[E|e][+|-]?\d+)?)/g;
|
||||
|
||||
function tokenize(source) {
|
||||
const len = source.length;
|
||||
const tokens = [];
|
||||
|
||||
let i = 0, j = 0; // Current index in `source` and temporary.
|
||||
let start = 0; // Current token start position.
|
||||
let data = ""; // Current token data (content) as string.
|
||||
let c, cat; // Current character code and category.
|
||||
|
||||
while (i < len) {
|
||||
cat = Category(c = source.charCodeAt(i));
|
||||
|
||||
if (cat === kCharSpace) {
|
||||
i++;
|
||||
}
|
||||
else if (cat === kCharDigit) {
|
||||
const n = tokens.length - 1;
|
||||
if (n >= 0 && tokens[n].data === "." && source[i - 1] === ".") {
|
||||
tokens.length = n;
|
||||
i--;
|
||||
}
|
||||
reValue.lastIndex = i;
|
||||
data = reValue.exec(source)[0];
|
||||
|
||||
tokens.push(newToken(kTokenValue, i, data, parseFloat(data)));
|
||||
i += data.length;
|
||||
}
|
||||
else if (cat === kCharAlpha) {
|
||||
start = i;
|
||||
while (++i < len && ((cat = Category(source.charCodeAt(i))) === kCharAlpha || cat === kCharDigit))
|
||||
continue;
|
||||
|
||||
data = source.substring(start, i);
|
||||
tokens.push(newToken(kTokenIdent, start, data, null));
|
||||
}
|
||||
else if (cat === kCharPunct) {
|
||||
start = i;
|
||||
while (++i < len && Category(source.charCodeAt(i)) === kCharPunct)
|
||||
continue;
|
||||
|
||||
data = source.substring(start, i);
|
||||
do {
|
||||
for (j = Math.min(i - start, kMaxOperatorLen); j > 0; j--) {
|
||||
const part = source.substr(start, j);
|
||||
if (hasOwn.call(kUnaryOperators, part) || hasOwn.call(kBinaryOperators, part) || j === 1) {
|
||||
tokens.push(newToken(kTokenPunct, start, part, null));
|
||||
start += j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (start < i);
|
||||
}
|
||||
else {
|
||||
throwExpressionError(`Unrecognized character '0x${c.toString(16)}'`, i);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Expression Parser
|
||||
// -----------------
|
||||
|
||||
class Parser {
|
||||
constructor(tokens) {
|
||||
this.tokens = tokens;
|
||||
this.tIndex = 0;
|
||||
}
|
||||
|
||||
peek() { return this.tIndex < this.tokens.length ? this.tokens[this.tIndex ] : NoToken; }
|
||||
next() { return this.tIndex < this.tokens.length ? this.tokens[this.tIndex++] : NoToken; }
|
||||
skip() { this.tIndex++; return this; }
|
||||
back(token) { this.tIndex -= +(token !== NoToken); return this; }
|
||||
|
||||
parse() {
|
||||
// The root expression cannot be empty.
|
||||
let token = this.peek();
|
||||
if (token === NoToken)
|
||||
throwExpressionError("Expression cannot be empty", 0);
|
||||
|
||||
const exp = this.parseExpression();
|
||||
|
||||
// The root expression must reach the end of the input.
|
||||
token = this.peek();
|
||||
if (token !== NoToken)
|
||||
throwTokenizerError(token);
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
parseExpression() {
|
||||
const stack = [];
|
||||
let value = null;
|
||||
let token = null;
|
||||
|
||||
for (;;) {
|
||||
// The only case of value not being `null` is after ternary-if. In that
|
||||
// case the value was already parsed so we want to skip this section.
|
||||
if (value === null) {
|
||||
let unaryFirst = null;
|
||||
let unaryLast = null;
|
||||
|
||||
token = this.next();
|
||||
|
||||
// Parse a possible unary operator(s).
|
||||
if (token.type === kTokenPunct) {
|
||||
do {
|
||||
const opName = token.data;
|
||||
const opInfo = kUnaryOperators[opName];
|
||||
|
||||
if (!opInfo)
|
||||
break;
|
||||
|
||||
const node = Unary(opName);
|
||||
if (unaryLast)
|
||||
unaryLast.child = node;
|
||||
else
|
||||
unaryFirst = node;
|
||||
|
||||
unaryLast = node;
|
||||
token = this.next();
|
||||
} while (token.type === kTokenPunct);
|
||||
}
|
||||
|
||||
// Parse a value, variable, function call, or nested expression.
|
||||
if (token.type === kTokenValue) {
|
||||
value = Imm(token.value);
|
||||
}
|
||||
else if (token.type === kTokenIdent) {
|
||||
const name = token.data;
|
||||
const after = this.peek();
|
||||
|
||||
if (after.data === "(")
|
||||
value = this.parseCall(token.data);
|
||||
else if (after.data === "[")
|
||||
value = this.parseBitAccess(token.data);
|
||||
else
|
||||
value = Var(name);
|
||||
}
|
||||
else if (token.data === "(") {
|
||||
value = this.parseExpression();
|
||||
token = this.next();
|
||||
|
||||
if (token.data !== ")")
|
||||
throwTokenizerError(token);
|
||||
}
|
||||
else {
|
||||
throwTokenizerError(token);
|
||||
}
|
||||
|
||||
// Replace the value with the top-level unary operator, if parsed.
|
||||
if (unaryFirst) {
|
||||
unaryLast.child = value;
|
||||
value = unaryFirst;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a possible binary operator - the loop must repeat, if present.
|
||||
token = this.peek();
|
||||
if (token.type === kTokenPunct && hasOwn.call(kBinaryOperators, token.data)) {
|
||||
const opName = token.data;
|
||||
if (opName === ":")
|
||||
break;
|
||||
|
||||
// Consume the token.
|
||||
this.skip();
|
||||
|
||||
const bNode = Binary(opName, null, null);
|
||||
|
||||
if (!stack.length) {
|
||||
bNode.left = value;
|
||||
stack.push(bNode);
|
||||
}
|
||||
else {
|
||||
let aNode = stack.pop();
|
||||
let aPrec = aNode.info().prec;
|
||||
let bPrec = bNode.info().prec;
|
||||
|
||||
if (aPrec > bPrec) {
|
||||
aNode.right = bNode;
|
||||
bNode.left = value;
|
||||
stack.push(aNode, bNode);
|
||||
}
|
||||
else {
|
||||
aNode.right = value;
|
||||
|
||||
// Advance to the top-most op that has less/equal precedence than `bPrec`.
|
||||
while (stack.length) {
|
||||
if (rightAssociate(aNode.info(), bPrec))
|
||||
break;
|
||||
aNode = stack.pop();
|
||||
}
|
||||
|
||||
if (!stack.length && !rightAssociate(aNode.info(), bPrec)) {
|
||||
bNode.left = aNode;
|
||||
stack.push(bNode);
|
||||
}
|
||||
else {
|
||||
const tmp = aNode.right;
|
||||
aNode.right = bNode;
|
||||
bNode.left = tmp;
|
||||
stack.push(aNode, bNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse "<cond> {ternary-if} <taken> {ternary-else} <not-taken>".
|
||||
if (opName === "?") {
|
||||
const ternLeft = this.parseExpression();
|
||||
const ternTok = this.next();
|
||||
|
||||
if (ternTok.data !== ":")
|
||||
throwExpressionError(`Unterminated ternary if '${token.data}'`, token.position);
|
||||
|
||||
const ternRight = this.parseExpression();
|
||||
value = Binary(opName, info, ternLeft, ternRight);
|
||||
}
|
||||
else {
|
||||
value = null;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (value === null)
|
||||
throwExpressionError("Invalid expression");
|
||||
|
||||
if (stack.length !== 0) {
|
||||
stack[stack.length - 1].right = value;
|
||||
value = stack[0];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
parseCall(name) {
|
||||
const args = [];
|
||||
|
||||
let token = this.next();
|
||||
if (token.data !== "(")
|
||||
throwTokenizerError(token);
|
||||
|
||||
for (;;) {
|
||||
token = this.peek();
|
||||
if (token.data === ")")
|
||||
break;
|
||||
|
||||
if (args.length !== 0) {
|
||||
if (token.data !== ",")
|
||||
throwTokenizerError(token);
|
||||
this.skip();
|
||||
}
|
||||
|
||||
args.push(this.parseExpression());
|
||||
}
|
||||
|
||||
this.skip();
|
||||
return Call(name, args);
|
||||
}
|
||||
|
||||
parseBitAccess(name) {
|
||||
let token = this.next();
|
||||
if (token.data !== "[")
|
||||
throwTokenizerError(token);
|
||||
|
||||
token = this.next();
|
||||
if (token.type != kTokenValue)
|
||||
throwTokenizerError(token);
|
||||
|
||||
const index = token.value;
|
||||
|
||||
token = this.next();
|
||||
if (token.data !== "]")
|
||||
throwTokenizerError(token);
|
||||
|
||||
return Call("$bit", [Var(name), index]);
|
||||
}
|
||||
}
|
||||
|
||||
function parse(source) {
|
||||
const tokens = tokenize(source);
|
||||
return new Parser(tokens).parse();
|
||||
}
|
||||
|
||||
// Expression Visitors
|
||||
// -------------------
|
||||
|
||||
class Visitor {
|
||||
visit(node) {
|
||||
switch (node.type) {
|
||||
case "imm":
|
||||
case "var": {
|
||||
break;
|
||||
}
|
||||
|
||||
case "call": {
|
||||
for (let arg of node.args)
|
||||
this.visit(arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case "unary": {
|
||||
if (node.child)
|
||||
this.visit(node.child);
|
||||
break;
|
||||
}
|
||||
|
||||
case "binary": {
|
||||
if (node.left)
|
||||
this.visit(node.left);
|
||||
if (node.right)
|
||||
this.visit(node.right);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Visitor.visit(): Unknown node type '${node.type}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Collector extends Visitor {
|
||||
constructor(nodeType, dst) {
|
||||
super();
|
||||
this.dict = dst || Object.create(null);
|
||||
this.nodeType = nodeType;
|
||||
}
|
||||
|
||||
visit(node) {
|
||||
if (node.type === this.nodeType) {
|
||||
if (hasOwn.call(this.dict, node.name))
|
||||
this.dict[node.name]++;
|
||||
else
|
||||
this.dict[node.name] = 1;
|
||||
}
|
||||
|
||||
super.visit(node);
|
||||
}
|
||||
}
|
||||
|
||||
function collectVars(node, dst) {
|
||||
const collector = new Collector("var", dst);
|
||||
collector.visit(node)
|
||||
return collector.dict;
|
||||
}
|
||||
|
||||
function collectCalls(node, dst) {
|
||||
const collector = new Collector("call", dst);
|
||||
collector.visit(node)
|
||||
return collector.dict;
|
||||
}
|
||||
|
||||
// Exports
|
||||
// -------
|
||||
|
||||
$scope[$as] = {
|
||||
Imm: Imm,
|
||||
Var: Var,
|
||||
Call: Call,
|
||||
Unary: Unary,
|
||||
Binary: Binary,
|
||||
Visitor: Visitor,
|
||||
ExpressionError: ExpressionError,
|
||||
|
||||
parse: parse,
|
||||
collectVars: collectVars,
|
||||
collectCalls: collectCalls
|
||||
};
|
||||
|
||||
}).apply(this, typeof module === "object" && module && module.exports
|
||||
? [module, "exports"] : [this.asmdb || (this.asmdb = {}), "exp"]);
|
||||
11
db/index.js
Normal file
11
db/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// 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
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.base = require("./base.js");
|
||||
exports.arm = require("./arm.js");
|
||||
exports.aarch64 = require("./aarch64.js");
|
||||
exports.x86 = require("./x86.js");
|
||||
3743
db/isa_aarch64.json
Normal file
3743
db/isa_aarch64.json
Normal file
File diff suppressed because it is too large
Load Diff
15
db/isa_aarch64_sme.json
Normal file
15
db/isa_aarch64_sme.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"instructions": [
|
||||
{"category": "SME", "ext": "SME", "data": [
|
||||
{"inst": "smstart {#sme_mode=1}" , "op": "11010101|00|000011|0100|0|op:2|1|011|11111" , "imm": "ImmSMEMode(sme_mode)"},
|
||||
{"inst": "smstop {#sme_mode=1}" , "op": "11010101|00|000011|0100|0|op:2|0|011|11111" , "imm": "ImmSMEMode(sme_mode)"},
|
||||
|
||||
{"inst": "eorbt Zd.t, Zn.t, Zm.t" , "op": "01000101|sz|0|Zm|100100|Zn|Zda"},
|
||||
{"inst": "eortb Zd.t, Zn.t, Zm.t" , "op": "01000101|sz|0|Zm|100101|Zn|Zda"},
|
||||
{"inst": "TODO_psel Pd, Pn, Pm.t[Wv, #imm]" , "op": "00100101|sz|0110011100010|Pg|0|Pdn"},
|
||||
{"inst": "revd Zd.Q, Pg/M, Zn.Q" , "op": "00000101|00|101110100|Pg:3|Zn|Zd"},
|
||||
{"inst": "sclamp Zd.t, Zn.t, Zm.t" , "op": "01000100|sz|0|Zm|110000|Zn|Zda"},
|
||||
{"inst": "uclamp Zd.t, Zn.t, Zm.t" , "op": "01000100|sz|0|Zm|110001|Zn|Zda"}
|
||||
]}
|
||||
]
|
||||
}
|
||||
2454
db/isa_arm.json
Normal file
2454
db/isa_arm.json
Normal file
File diff suppressed because it is too large
Load Diff
4600
db/isa_x86.json
Normal file
4600
db/isa_x86.json
Normal file
File diff suppressed because it is too large
Load Diff
29
db/package.json
Normal file
29
db/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "asmdb",
|
||||
"version": "0.1.0",
|
||||
"license": "Unlicense",
|
||||
"engines": { "node": ">=8" },
|
||||
|
||||
"description": "Instructions database and utilities for X86/X64 and ARM (THUMB/A32/A64) architectures.",
|
||||
"keywords": [
|
||||
"asm", "assembler", "database", "instructions",
|
||||
"arm", "thumb", "thumb2", "a32", "a64", "aarch32", "aarch64",
|
||||
"x86", "x86_64", "x64", "amd64"
|
||||
],
|
||||
|
||||
"homepage": "https://github.com/asmjit/asmjit/db",
|
||||
"bugs": {
|
||||
"url": "https://github.com/asmjit/asmjit/issues"
|
||||
},
|
||||
|
||||
"contributors": [
|
||||
"Petr Kobalicek <kobalicek.petr@gmail.com> (kobalicek.com)"
|
||||
],
|
||||
|
||||
"main": "index.js",
|
||||
|
||||
"repository" : {
|
||||
"type": "git",
|
||||
"url": "https://github.com/asmjit/asmjit.git"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user