Files
asmjit/tools/generate-x86.js

1830 lines
57 KiB
JavaScript

// [Generate-X86]
//
// The purpose of this script is to fetch all instructions' names into a single
// string and to optimize common patterns that appear in instruction data. It
// prevents relocation of small strings (instruction names) that has to be done
// by a linker to make all pointers the binary application/library uses valid.
// This approach decreases the final size of AsmJit binary and relocation data.
//
// NOTE: This script relies on 'asmdb' package. Either install it by using
// node.js package manager (npm) or by copying/symlinking the whole asmdb
// directory as [asmjit]/tools/asmdb.
"use strict";
const base = require("./generate-base.js");
const hasOwn = Object.prototype.hasOwnProperty;
const kIndent = base.kIndent;
const MapUtils = base.MapUtils;
const StringUtils = base.StringUtils;
const DEBUG = false;
// ----------------------------------------------------------------------------
// [X86DB]
// ----------------------------------------------------------------------------
// Create the X86 database and add some special cases recognized by AsmJit.
const x86 = base.asmdb.x86;
const isa = new x86.ISA({
instructions: [
// Imul in [reg, imm] form is encoded as [reg, reg, imm].
["imul" , "r16, ib" , "RM" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
["imul" , "r32, ib" , "RM" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
["imul" , "r64, ib" , "RM" , "REX.W 6B /r ib" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"],
["imul" , "r16, iw" , "RM" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
["imul" , "r32, id" , "RM" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
["imul" , "r64, id" , "RM" , "REX.W 69 /r id" , "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"]
]
});
// Remapped instructions contain mapping between instructions that AsmJit expects
// and instructions provided by asmdb. In general, AsmJit uses `cmps` instructions
// without the suffix, so we just remap these and keep all others.
const RemappedInsts = {
__proto__: null,
"cmpsd": { names: ["cmpsd"] , rep: false },
"movsd": { names: ["movsd"] , rep: false },
"cmps" : { names: ["cmpsb", "cmpsw", "cmpsd", "cmpsq"], rep: true },
"movs" : { names: ["movsb", "movsw", "movsd", "movsq"], rep: true },
"lods" : { names: ["lodsb", "lodsw", "lodsd", "lodsq"], rep: null },
"scas" : { names: ["scasb", "scasw", "scasd", "scasq"], rep: null },
"stos" : { names: ["stosb", "stosw", "stosd", "stosq"], rep: null },
"ins" : { names: ["insb" , "insw" , "insd" ] , rep: null },
"outs" : { names: ["outsb", "outsw", "outsd"] , rep: null }
};
// Map of instructions that can use fixed registers, but are also encodable
// by using any others. This is to simplify some decisions about instruction
// flags as we don't want to see `FixedReg` in `adc` instruction, for example.
const NotFixedInsts = MapUtils.arrayToMap([
"adc", "add", "and", "cmp", "mov", "or", "sbb", "sub", "test", "xchg", "xor"
]);
// ----------------------------------------------------------------------------
// [GenUtils]
// ----------------------------------------------------------------------------
class GenUtils {
// Get group of instructions having the same name as understood by AsmJit.
static groupOf(name) {
const remapped = RemappedInsts[name];
if (!remapped) return isa.query(name);
const insts = isa.query(remapped.names);
const rep = remapped.rep;
if (rep === null) return insts;
return insts.filter(function(inst) {
return rep === !!(inst.attributes.REP || inst.attributes.REPNZ);
});
}
static hasFixedReg(group) {
for (var i = 0; i < group.length; i++) {
const inst = group[i];
if (NotFixedInsts[inst.name]) continue;
const operands = inst.operands;
for (var j = 0; j < operands.length; j++)
if (operands[j].isFixedReg())
return true;
}
return false;
}
static hasFixedMem(group) {
for (var i = 0; i < group.length; i++) {
const inst = group[i];
if (NotFixedInsts[inst.name]) continue;
const operands = inst.operands;
for (var j = 0; j < operands.length; j++)
if (operands[j].isFixedMem())
return true;
}
return false;
}
static cpuArchOf(group) {
var anyArch = false;
var x86Arch = false;
var x64Arch = false;
for (var i = 0; i < group.length; i++) {
const inst = group[i];
if (inst.arch === "ANY") anyArch = true;
if (inst.arch === "X86") x86Arch = true;
if (inst.arch === "X64") x64Arch = true;
}
return anyArch || (x86Arch && x64Arch) ? "[ANY]" : x86Arch ? "[X86]" : "[X64]";
}
static cpuFeaturesOf(group) {
const features = Object.create(null);
for (var i = 0; i < group.length; i++)
for (var feature in group[i].extensions)
features[feature] = true;
const result = Object.getOwnPropertyNames(features);
result.sort();
return result;
}
static specialsOf(group) {
const r = Object.create(null);
const w = Object.create(null);
for (var i = 0; i < group.length; i++) {
const inst = group[i];
const specialRegs = inst.specialRegs;
// Mov is a special case, moving to/from control regs makes flags undefined,
// which we don't want to have in `X86InstDB::operationData`. This is, thus,
// a special case instruction analyzer must deal with.
if (inst.name === "mov")
continue;
for (var specialReg in specialRegs) {
const group = isa.specialRegs[specialReg].group;
const op = specialRegs[specialReg];
switch (op) {
case "R":
r[group] = true;
break;
case "X":
r[group] = true;
// .. fallthrough ...
case "W":
case "U":
case "0":
case "1":
w[group] = true;
break;
}
}
}
const rArray = Object.getOwnPropertyNames(r);
const wArray = Object.getOwnPropertyNames(w);
rArray.sort();
wArray.sort();
return [rArray, wArray];
}
static flagsOf(group) {
function getAccess(inst) {
const operands = inst.operands;
if (!operands.length) return "";
if (inst.name === "xchg" || inst.name === "xadd")
return "UseXX";
const op = operands[0];
if (!op.isRegOrMem())
return "";
else if (op.read && op.write)
return "UseX";
else
return op.read ? "UseR" :"UseW";
}
function replace(map, a, b, c) {
if (map[a] && map[b]) {
delete map[a];
delete map[b];
map[c] = true;
}
}
const f = Object.create(null);
var i, j;
var mib = group.length > 0 && /^(?:bndldx|bndstx)$/.test(group[0].name);
var access = "";
var ambiguous = false;
for (i = 0; i < group.length; i++) {
const inst = group[i];
const name = inst.name;
const acc = getAccess(inst);
if (!access)
access = acc;
else if (access !== acc)
ambiguous = true;
}
// Default to "RO" if there is no access information nor operands.
if (!access) access = "UseR";
if (ambiguous) access = "UseA";
if (access) {
if (access === "UseXX")
f.UseX = true;
f[access] = true;
}
if (mib) f.Mib = true;
const fixedReg = GenUtils.hasFixedReg(group);
const fixedMem = GenUtils.hasFixedMem(group);
if (fixedReg && fixedMem)
f.FixedRM = true;
else if (fixedReg)
f.FixedReg = true;
else if (fixedMem)
f.FixedMem = true;
var mmx = false;
var vec = false;
for (i = 0; i < group.length; i++) {
const inst = group[i];
const operands = inst.operands;
if (inst.name === "emms")
mmx = true;
if (inst.name === "vzeroall" || inst.name === "vzeroupper")
vec = true;
for (j = 0; j < operands.length; j++) {
const op = operands[j];
if (op.reg === "mm")
mmx = true;
else if (/^(k|xmm|ymm|zmm)$/.test(op.reg)) {
vec = true;
}
}
}
if (mmx) f.Mmx = true;
if (vec) f.Vec = true;
for (i = 0; i < group.length; i++) {
const inst = group[i];
const name = inst.name;
const operands = inst.operands;
if (inst.attributes.LOCK ) f.Lock = true;
if (inst.attributes.REP ) f.Rep = true;
if (inst.attributes.REPNZ ) f.Repnz = true;
if (inst.attributes.XACQUIRE) f.XAcquire = true;
if (inst.attributes.XRELEASE) f.XRelease = true;
if (inst.fpu) {
for (var j = 0; j < operands.length; j++) {
const op = operands[j];
if (op.memSize === 16) f.FpuM16 = true;
if (op.memSize === 32) f.FpuM32 = true;
if (op.memSize === 64) f.FpuM64 = true;
if (op.memSize === 80) f.FpuM80 = true;
}
}
if (inst.vsibReg)
f.Vsib = true;
if (inst.prefix === "VEX" || inst.prefix === "XOP")
f.Vex = true;
if (inst.prefix === "EVEX") {
f.Evex = true;
if (inst.kmask) f.Avx512K = true;
if (inst.zmask) f.Avx512Z = true;
if (inst.er) f.Avx512ER = true;
if (inst.sae) f.Avx512SAE = true;
if (inst.broadcast) f["Avx512B" + String(inst.elementSize)] = true;
if (inst.tupleType === "T1_4X") f.Avx512T4X = true;
}
}
replace(f, "Avx512K" , "Avx512Z" , "Avx512KZ");
replace(f, "Avx512ER" , "Avx512SAE" , "Avx512ER_SAE");
replace(f, "Avx512KZ" , "Avx512SAE" , "Avx512KZ_SAE");
replace(f, "Avx512KZ" , "Avx512ER_SAE", "Avx512KZ_ER_SAE");
replace(f, "Avx512K" , "Avx512B32" , "Avx512K_B32");
replace(f, "Avx512K" , "Avx512B64" , "Avx512K_B64");
replace(f, "Avx512KZ" , "Avx512B32" , "Avx512KZ_B32");
replace(f, "Avx512KZ" , "Avx512B64" , "Avx512KZ_B64");
replace(f, "Avx512KZ_SAE" , "Avx512B32" , "Avx512KZ_SAE_B32");
replace(f, "Avx512KZ_SAE" , "Avx512B64" , "Avx512KZ_SAE_B64");
replace(f, "Avx512KZ_ER_SAE", "Avx512B32" , "Avx512KZ_ER_SAE_B32");
replace(f, "Avx512KZ_ER_SAE", "Avx512B64" , "Avx512KZ_ER_SAE_B64");
return Object.getOwnPropertyNames(f);
}
static operationFlagsOf(group) {
const f = Object.create(null);
for (var i = 0; i < group.length; i++) {
const inst = group[i];
const name = inst.name;
const operands = inst.operands;
// Special case: MOV undefines flags if moving between GP and CR|DR registers.
if (name === "mov")
f.MovCrDr = true;
// Special case: MOVSS|MOVSD zeroes the remaining part of destination if source operand is memory.
if ((name === "movss" || name === "movsd") && !inst.attributes.REP)
f.MovSsSd = true;
// Hardware prefetch.
if (name.startsWith("prefetch"))
f.Prefetch = true;
// Memory barrier.
if (/^[lms]fence$/.test(name))
f.Barrier = true;
// Instruction is volatile.
if (inst.attributes.VOLATILE)
f.Volatile = true;
// Instruction is privileged.
if (inst.privilege !== "L3")
f.Privileged = true;
}
return Object.getOwnPropertyNames(f);
}
static eqOps(aOps, aFrom, bOps, bFrom) {
var x = 0;
for (;;) {
const aIndex = x + aFrom;
const bIndex = x + bFrom;
const aOut = aIndex >= aOps.length;
const bOut = bIndex >= bOps.length;
if (aOut || bOut)
return !!(aOut && bOut);
const aOp = aOps[aIndex];
const bOp = bOps[bIndex];
if (aOp.data !== bOp.data)
return false;
x++;
}
}
static singleRegCase(name) {
switch (name) {
case "xchg" :
case "and" :
case "pand" : case "vpand" : case "vpandd" : case "vpandq" :
case "andpd" : case "vandpd" :
case "andps" : case "vandps" :
case "or" :
case "por" : case "vpor" : case "vpord" : case "vporq" :
case "orpd" : case "vorpd" :
case "orps" : case "vorps" :
case "pminsb" : case "vpminsb" : case "pmaxsb" : case "vpmaxsb" :
case "pminsw" : case "vpminsw" : case "pmaxsw" : case "vpmaxsw" :
case "pminsd" : case "vpminsd" : case "pmaxsd" : case "vpmaxsd" :
case "pminub" : case "vpminub" : case "pmaxub" : case "vpmaxub" :
case "pminuw" : case "vpminuw" : case "pmaxuw" : case "vpmaxuw" :
case "pminud" : case "vpminud" : case "pmaxud" : case "vpmaxud" :
return "RO";
case "pandn" : case "vpandn" : case "vpandnd" : case "vpandnq" :
case "xor" :
case "pxor" : case "vpxor" : case "vpxord" : case "vpxorq" :
case "xorpd" : case "vxorpd" :
case "xorps" : case "vxorps" :
case "sub" :
case "psubb" : case "vpsubb" :
case "psubw" : case "vpsubw" :
case "psubd" : case "vpsubd" :
case "psubq" : case "vpsubq" :
case "psubsb" : case "vpsubsb" : case "psubusb" : case "vpsubusb" :
case "psubsw" : case "vpsubsw" : case "psubusw" : case "vpsubusw" :
case "vpcmpeqb": case "pcmpeqb" : case "vpcmpgtb": case "pcmpgtb" :
case "vpcmpeqw": case "pcmpeqw" : case "vpcmpgtw": case "pcmpgtw" :
case "vpcmpeqd": case "pcmpeqd" : case "vpcmpgtd": case "pcmpgtd" :
case "vpcmpeqq": case "pcmpeqq" : case "vpcmpgtq": case "pcmpgtq" :
case "vpcmpb" : case "vpcmpub" :
case "vpcmpd" : case "vpcmpud" :
case "vpcmpw" : case "vpcmpuw" :
case "vpcmpq" : case "vpcmpuq" :
return "WO";
default:
return "None";
}
}
static jumpType(name) {
switch (name) {
case "jo" :
case "jno":
case "jb" : case "jnae":
case "jae": case "jnb" :
case "je" : case "jz" :
case "jne": case "jnz" :
case "jbe": case "jna" :
case "js" :
case "jns":
case "jp" : case "jpe" :
case "jnp": case "jpo" :
case "jl" : case "jnge":
case "jge": case "jnl" :
case "jle": case "jng" :
case "jg" : case "jnle":
case "jecxz":
case "loop":
case "loope":
case "loopne":
case "xbegin":
return "Conditional";
case "jmp" : return "Direct";
case "call": return "Call";
case "ret" : return "Return";
default:
return "None";
}
}
}
// ----------------------------------------------------------------------------
// [Generate]
// ----------------------------------------------------------------------------
const RegOp = MapUtils.arrayToMap([
"al", "ah", "ax", "eax", "rax", "cl",
"r8lo", "r8hi", "r16", "r32", "r64", "fp", "mm", "k", "xmm", "ymm", "zmm", "bnd", "sreg", "creg", "dreg"
]);
const MemOp = MapUtils.arrayToMap([
"m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"
]);
const OpSortPriority = {
"read" :-9,
"write" :-8,
"rw" :-7,
"implicit":-6,
"r8lo" : 1,
"r8hi" : 2,
"r16" : 3,
"r32" : 4,
"r64" : 5,
"fp" : 6,
"mm" : 7,
"k" : 8,
"xmm" : 9,
"ymm" : 10,
"zmm" : 11,
"sreg" : 12,
"bnd" : 13,
"creg" : 14,
"dreg" : 15,
"mem" : 30,
"vm" : 31,
"m8" : 32,
"m16" : 33,
"m32" : 34,
"m48" : 35,
"m64" : 36,
"m80" : 37,
"m128" : 38,
"m256" : 39,
"m512" : 40,
"m1024" : 41,
"mib" : 42,
"vm32x" : 43,
"vm32y" : 44,
"vm32z" : 45,
"vm64x" : 46,
"vm64y" : 47,
"vm64z" : 48,
"memBase" : 49,
"memES" : 50,
"memDS" : 51,
"i4" : 60,
"u4" : 61,
"i8" : 62,
"u8" : 63,
"i16" : 64,
"u16" : 65,
"i32" : 66,
"u32" : 67,
"i64" : 68,
"u64" : 69,
"rel8" : 70,
"rel32" : 71
};
const OpToAsmJitOp = {
"read" : "FLAG(R)",
"write" : "FLAG(W)",
"rw" : "FLAG(X)",
"implicit": "FLAG(Implicit)",
"r8lo" : "FLAG(GpbLo)",
"r8hi" : "FLAG(GpbHi)",
"r16" : "FLAG(Gpw)",
"r32" : "FLAG(Gpd)",
"r64" : "FLAG(Gpq)",
"fp" : "FLAG(Fp)",
"mm" : "FLAG(Mm)",
"k" : "FLAG(K)",
"xmm" : "FLAG(Xmm)",
"ymm" : "FLAG(Ymm)",
"zmm" : "FLAG(Zmm)",
"bnd" : "FLAG(Bnd)",
"sreg" : "FLAG(Seg)",
"creg" : "FLAG(Cr)",
"dreg" : "FLAG(Dr)",
"mem" : "FLAG(Mem)",
"vm" : "FLAG(Vm)",
"m8" : "MEM(M8)",
"m16" : "MEM(M16)",
"m32" : "MEM(M32)",
"m48" : "MEM(M48)",
"m64" : "MEM(M64)",
"m80" : "MEM(M80)",
"m128" : "MEM(M128)",
"m256" : "MEM(M256)",
"m512" : "MEM(M512)",
"m1024" : "MEM(M1024)",
"mib" : "MEM(Mib)",
"mAny" : "MEM(Any)",
"vm32x" : "MEM(Vm32x)",
"vm32y" : "MEM(Vm32y)",
"vm32z" : "MEM(Vm32z)",
"vm64x" : "MEM(Vm64x)",
"vm64y" : "MEM(Vm64y)",
"vm64z" : "MEM(Vm64z)",
"memBase" : "MEM(BaseOnly)",
"memDS" : "MEM(Ds)",
"memES" : "MEM(Es)",
"i4" : "FLAG(I4)",
"u4" : "FLAG(U4)",
"i8" : "FLAG(I8)",
"u8" : "FLAG(U8)",
"i16" : "FLAG(I16)",
"u16" : "FLAG(U16)",
"i32" : "FLAG(I32)",
"u32" : "FLAG(U32)",
"i64" : "FLAG(I64)",
"u64" : "FLAG(U64)",
"rel8" : "FLAG(Rel8)",
"rel32" : "FLAG(Rel32)"
};
function OpSortFunc(a, b) {
return (OpSortPriority[a] || 0) - (OpSortPriority[b] || 0);
}
function SortOpArray(a) {
a.sort(OpSortFunc);
return a;
}
function StringifyArray(a, map) {
var s = "";
for (var i = 0; i < a.length; i++) {
const op = a[i];
if (!hasOwn.call(map, op))
throw new Error(`UNHANDLED OPERAND '${op}'`);
s += (s ? " | " : "") + map[op];
}
return s ? s : "0";
}
class OSignature {
constructor() {
this.flags = Object.create(null);
}
equals(other) {
return MapUtils.equals(this.flags, other.flags);
}
xor(other) {
const result = MapUtils.xor(this.flags, other.flags);
return Object.getOwnPropertyNames(result).length === 0 ? null : result;
}
mergeWith(other) {
const af = this.flags;
const bf = other.flags;
var k;
var indexKind = "";
var hasReg = false;
for (k in af) {
const index = x86.Utils.regIndexOf(k);
const kind = x86.Utils.regKindOf(k);
if (kind)
hasReg = true;
if (index !== null && index !== -1)
indexKind = kind;
}
if (hasReg) {
for (k in bf) {
const index = x86.Utils.regIndexOf(k);
if (index !== null && index !== -1) {
const kind = x86.Utils.regKindOf(k);
if (indexKind !== kind) return false;
}
}
}
// Can merge...
for (k in bf) af[k] = true;
return true;
}
simplify() {
const flags = this.flags;
// Implicit register when also any other register can be specified.
if (flags.al && flags.r8lo) delete flags["al"];
if (flags.ah && flags.r8hi) delete flags["ah"];
if (flags.ax && flags.r16 ) delete flags["ax"];
if (flags.eax && flags.r32 ) delete flags["eax"];
if (flags.rax && flags.r64 ) delete flags["rax"];
// 32-bit register or 16-bit memory implies also 16-bit reg.
if (flags.r32 && flags.m16) {
flags.r16 = true;
}
// 32-bit register or 8-bit memory implies also 16-bit and 8-bit reg.
if (flags.r32 && flags.m8) {
flags.r8lo = true;
flags.r8hi = true;
flags.r16 = true;
}
}
toString() {
var s = "";
var flags = this.flags;
var prefix = (flags.read && flags.write) ? "X:" : (flags.write) ? "W:" : "R:";
for (var k in flags) {
if (k === "read" || k === "write" || k === "implicit" || k === "memDS" || k === "memES")
continue;
var x = k;
if (x === "memZAX") x = "zax";
if (x === "memZDI") x = "zdi";
if (x === "memZSI") x = "zsi";
s += (s ? "|" : "") + x;
}
if (flags.memDS) s = "ds:[" + s + "]";
if (flags.memES) s = "es:[" + s + "]";
if (flags.implicit)
s = "<" + s + ">";
return prefix + s;
}
toAsmJitOpData() {
var oFlags = this.flags;
var mFlags = Object.create(null);
var mMemFlags = Object.create(null);
var mExtFlags = Object.create(null);
var sRegMask = 0;
if (oFlags.read && oFlags.write)
mFlags.rw = true;
else if (oFlags.write)
mFlags.write = true;
else
mFlags.read = true;
for (var k in oFlags) {
switch (k) {
case "read" : break;
case "write" : break;
case "implicit":
case "r8lo" :
case "r8hi" :
case "r16" :
case "r32" :
case "r64" :
case "creg" :
case "dreg" :
case "sreg" :
case "bnd" :
case "fp" :
case "k" :
case "mm" :
case "xmm" :
case "ymm" :
case "zmm" : mFlags[k] = true; break;
case "m8" :
case "m16" :
case "m32" :
case "m48" :
case "m64" :
case "m80" :
case "m128" :
case "m256" :
case "m512" :
case "m1024" : mFlags.mem = true; mMemFlags[k] = true; break;
case "mib" : mFlags.mem = true; mMemFlags.mib = true; break;
case "mem" : mFlags.mem = true; mMemFlags.mAny = true; break;
case "memDS" : mFlags.mem = true; mMemFlags.memDS = true; break;
case "memES" : mFlags.mem = true; mMemFlags.memES = true; break;
case "memZAX" : mFlags.mem = true; sRegMask |= 1 << 0; mMemFlags.memBase = true; break;
case "memZSI" : mFlags.mem = true; sRegMask |= 1 << 6; mMemFlags.memBase = true; break;
case "memZDI" : mFlags.mem = true; sRegMask |= 1 << 7; mMemFlags.memBase = true; break;
case "vm32x" : mFlags.vm = true; mMemFlags.vm32x = true; break;
case "vm32y" : mFlags.vm = true; mMemFlags.vm32y = true; break;
case "vm32z" : mFlags.vm = true; mMemFlags.vm32z = true; break;
case "vm64x" : mFlags.vm = true; mMemFlags.vm64x = true; break;
case "vm64y" : mFlags.vm = true; mMemFlags.vm64y = true; break;
case "vm64z" : mFlags.vm = true; mMemFlags.vm64z = true; break;
case "i4" :
case "u4" :
case "i8" :
case "u8" :
case "i16" :
case "u16" :
case "i32" :
case "u32" :
case "i64" :
case "u64" : mFlags[k] = true; break;
case "rel8" :
case "rel32" :
mFlags.i32 = true;
mFlags.i64 = true;
mFlags[k] = true;
break;
case "rel16" :
mFlags.i32 = true;
mFlags.i64 = true;
mFlags.rel32 = true;
break;
default: {
switch (k) {
case "es" : mFlags.sreg = true; sRegMask |= 1 << 1; break;
case "cs" : mFlags.sreg = true; sRegMask |= 1 << 2; break;
case "ss" : mFlags.sreg = true; sRegMask |= 1 << 3; break;
case "ds" : mFlags.sreg = true; sRegMask |= 1 << 4; break;
case "fs" : mFlags.sreg = true; sRegMask |= 1 << 5; break;
case "gs" : mFlags.sreg = true; sRegMask |= 1 << 6; break;
case "al" : mFlags.r8lo = true; sRegMask |= 1 << 0; break;
case "ah" : mFlags.r8hi = true; sRegMask |= 1 << 0; break;
case "ax" : mFlags.r16 = true; sRegMask |= 1 << 0; break;
case "eax" : mFlags.r32 = true; sRegMask |= 1 << 0; break;
case "rax" : mFlags.r64 = true; sRegMask |= 1 << 0; break;
case "bl" : mFlags.r8lo = true; sRegMask |= 1 << 3; break;
case "bh" : mFlags.r8hi = true; sRegMask |= 1 << 3; break;
case "bx" : mFlags.r16 = true; sRegMask |= 1 << 3; break;
case "ebx" : mFlags.r32 = true; sRegMask |= 1 << 3; break;
case "rbx" : mFlags.r64 = true; sRegMask |= 1 << 3; break;
case "cl" : mFlags.r8lo = true; sRegMask |= 1 << 1; break;
case "ch" : mFlags.r8hi = true; sRegMask |= 1 << 1; break;
case "cx" : mFlags.r16 = true; sRegMask |= 1 << 1; break;
case "ecx" : mFlags.r32 = true; sRegMask |= 1 << 1; break;
case "rcx" : mFlags.r64 = true; sRegMask |= 1 << 1; break;
case "dl" : mFlags.r8lo = true; sRegMask |= 1 << 2; break;
case "dh" : mFlags.r8hi = true; sRegMask |= 1 << 2; break;
case "dx" : mFlags.r16 = true; sRegMask |= 1 << 2; break;
case "edx" : mFlags.r32 = true; sRegMask |= 1 << 2; break;
case "rdx" : mFlags.r64 = true; sRegMask |= 1 << 2; break;
case "si" : mFlags.r16 = true; sRegMask |= 1 << 6; break;
case "esi" : mFlags.r32 = true; sRegMask |= 1 << 6; break;
case "rsi" : mFlags.r64 = true; sRegMask |= 1 << 6; break;
case "di" : mFlags.r16 = true; sRegMask |= 1 << 7; break;
case "edi" : mFlags.r32 = true; sRegMask |= 1 << 7; break;
case "rdi" : mFlags.r64 = true; sRegMask |= 1 << 7; break;
case "fp0" : mFlags.fp = true; sRegMask |= 1 << 0; break;
case "xmm0" : mFlags.xmm = true; sRegMask |= 1 << 0; break;
case "ymm0" : mFlags.ymm = true; sRegMask |= 1 << 0; break;
default:
console.log(`UNKNOWN OPERAND '${k}'`);
}
}
}
}
const sFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mFlags )), OpToAsmJitOp);
const sMemFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mMemFlags)), OpToAsmJitOp);
const sExtFlags = StringifyArray(SortOpArray(Object.getOwnPropertyNames(mExtFlags)), OpToAsmJitOp);
return `OSIGNATURE(${sFlags || 0}, ${sMemFlags || 0}, ${sExtFlags || 0}, ${StringUtils.decToHex(sRegMask, 2)})`;
}
}
class ISignature extends Array {
constructor(name) {
super();
this.name = name;
this.x86 = false;
this.x64 = false;
this.implicit = 0; // Number of implicit operands.
}
simplify() {
for (var i = 0; i < this.length; i++)
this[i].simplify();
}
opEquals(other) {
const len = this.length;
if (len !== other.length) return false;
for (var i = 0; i < len; i++)
if (!this[i].equals(other[i]))
return false;
return true;
}
mergeWith(other) {
// If both architectures are the same, it's fine to merge.
var ok = this.x86 === other.x86 && this.x64 === other.x64;
// If the first arch is [X86|X64] and the second [X64] it's also fine.
if (!ok && this.x86 && this.x64 && !other.x86 && other.x64)
ok = true;
// It's not ok if both signatures have different number of implicit operands.
if (!ok || this.implicit !== other.implicit)
return false;
// It's not ok if both signatures have different number of operands.
const len = this.length;
if (len !== other.length)
return false;
var xorIndex = -1;
for (var i = 0; i < len; i++) {
const xor = this[i].xor(other[i]);
if (xor === null) continue;
if (xorIndex === -1)
xorIndex = i;
else
return false;
}
// Bail if mergeWidth at operand-level failed.
if (xorIndex !== -1 && !this[xorIndex].mergeWith(other[xorIndex]))
return false;
this.x86 = this.x86 || other.x86;
this.x64 = this.x64 || other.x64;
return true;
}
toString() {
return "{" + this.join(", ") + "}";
}
}
class SignatureArray extends Array {
// Iterate over all signatures and check which operands don't need explicit memory size.
calcImplicitMemSize() {
// Calculates a hash-value (aka key) of all register operands specified by `regOps` in `inst`.
function keyOf(inst, regOps) {
var s = "";
for (var i = 0; i < inst.length; i++) {
const op = inst[i];
if (regOps & (1 << i)) {
const props = Object.getOwnPropertyNames(MapUtils.and(op.flags, RegOp));
props.sort();
s += "{" + props.join("|") + "}";
}
}
return s || "?";
}
var i;
var aIndex, bIndex;
for (aIndex = 0; aIndex < this.length; aIndex++) {
const aInst = this[aIndex];
const len = aInst.length;
var memOp = "";
var memPos = -1;
var regOps = 0;
// Check if this instruction signature has a memory operand of explicit size.
for (i = 0; i < len; i++) {
const aOp = aInst[i];
const mem = MapUtils.firstOf(aOp.flags, MemOp);
if (mem) {
// Stop if the memory operand is implicit or if there is more than one.
if (aOp.flags.mem || memPos >= 0) {
memPos = -1;
break;
}
else {
memOp = mem;
memPos = i;
}
}
else if (MapUtils.anyOf(aOp.flags, RegOp)) {
// Doesn't consider 'r/m' as we already checked 'm'.
regOps |= (1 << i);
}
}
if (memPos < 0)
continue;
// Create a `sameSizeSet` set of all instructions having the exact
// explicit memory operand at the same position and registers at
// positions matching `regOps` bits and `diffSizeSet` having memory
// operand of different size, but registers at the same positions.
const sameSizeSet = [aInst];
const diffSizeSet = [];
const diffSizeHash = Object.create(null);
for (bIndex = 0; bIndex < this.length; bIndex++) {
const bInst = this[bIndex];
if (aIndex === bIndex || len !== bInst.length) continue;
var hasMatch = 1;
for (i = 0; i < len; i++) {
if (i === memPos) continue;
const reg = MapUtils.anyOf(bInst[i].flags, RegOp);
if (regOps & (1 << i))
hasMatch &= reg;
else if (reg)
hasMatch = 0;
}
if (hasMatch) {
const bOp = bInst[memPos];
if (bOp.flags.mem) continue;
const mem = MapUtils.firstOf(bOp.flags, MemOp);
if (mem === memOp) {
sameSizeSet.push(bInst);
}
else if (mem) {
const key = keyOf(bInst, regOps);
diffSizeSet.push(bInst);
if (!diffSizeHash[key])
diffSizeHash[key] = [bInst];
else
diffSizeHash[key].push(bInst);
}
}
}
// Two cases.
// A) The memory operand is implicit if `diffSizeSet` is empty. That means
// that the instruction only uses one size for all reg combinations.
//
// B) The memory operand is implicit if `diffSizeSet` contains different
// register signatures than `sameSizeSet`.
var implicit = true;
if (!diffSizeSet.length) {
// Case A:
}
else {
// Case B: Find collisions in `sameSizeSet` and `diffSizeSet`.
for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
const bInst = sameSizeSet[bIndex];
const key = keyOf(bInst, regOps);
const diff = diffSizeHash[key];
if (diff) {
diff.forEach(function(diffInst) {
if ((bInst.x86 && !diffInst.x86) || (!bInst.x86 && diffInst.x86)) {
// If this is X86|ANY instruction and the other is X64, or vice-versa,
// then keep this implicit as it won't do any harm. These instructions
// cannot be mixed and it will make implicit the 32-bit one in cases
// where X64 introduced 64-bit ones like `cvtsi2ss`.
}
else {
implicit = false;
}
});
}
}
}
// Patch all instructions to accept implicit memory operand.
for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
const bInst = sameSizeSet[bIndex];
if (implicit) bInst[memPos].flags.mem = true;
if (DEBUG && !implicit)
console.log(`${this.name}: Explicit: ${bInst}`);
}
}
}
simplify() {
for (var i = 0; i < this.length; i++)
this[i].simplify();
}
compact() {
for (var i = 0; i < this.length; i++) {
var row = this[i];
var j = i + 1;
while (j < this.length) {
if (row.mergeWith(this[j])) {
this.splice(j, 1);
continue;
}
j++;
}
}
}
toString() {
return `[${this.join(", ")}]`;
}
}
// ----------------------------------------------------------------------------
// [X86Generator]
// ----------------------------------------------------------------------------
class X86Generator extends base.BaseGenerator {
constructor() {
super("X86");
this.opCombinations = Object.create(null);
this.maxOpRows = 0;
this.opBlackList = {
"moff8" : true,
"moff16": true,
"moff32": true,
"moff64": true
};
this.load([
"src/asmjit/x86/x86inst.cpp",
"src/asmjit/x86/x86inst.h"
]);
}
signaturesFromInsts(insts) {
const signatures = new SignatureArray();
for (var i = 0; i < insts.length; i++) {
const inst = insts[i];
const ops = inst.operands;
var row = new ISignature(inst.name);
row.x86 = (inst.arch === "ANY" || inst.arch === "X86");
row.x64 = (inst.arch === "ANY" || inst.arch === "X64");
for (var j = 0; j < ops.length; j++) {
var iop = ops[j];
var reg = iop.reg;
var mem = iop.mem;
var imm = iop.imm;
var rel = iop.rel;
// Terminate if this operand is something asmjit doesn't support
// and skip all instructions having implicit `imm` operand of `1`
// (handled fine by asmjit).
if (this.opBlackList[mem] === true || iop.immValue !== null) {
row = null;
break;
}
if (reg === "r8") reg = "r8lo";
if (reg === "seg") reg = "sreg";
if (reg === "st(i)") reg = "fp";
if (reg === "st(0)") reg = "fp0";
if (mem === "m32fp") mem = "m32";
if (mem === "m64fp") mem = "m64";
if (mem === "m80fp") mem = "m80";
if (mem === "m80bcd") mem = "m80";
if (mem === "m80dec") mem = "m80";
if (mem === "m16int") mem = "m16";
if (mem === "m32int") mem = "m32";
if (mem === "m64int") mem = "m64";
if (mem === "m16_16") mem = "m32";
if (mem === "m16_32") mem = "m48";
if (mem === "m16_64") mem = "m80";
const op = new OSignature();
if (iop.read) op.flags.read = true;
if (iop.write) op.flags.write = true;
if (iop.implicit) {
row.implicit++;
op.flags.implicit = true;
}
if (iop.memSeg) {
if (iop.memSeg === "ds") op.flags.memDS = true;
if (iop.memSeg === "es") op.flags.memES = true;
if (reg === "zax") op.flags.memZAX = true;
if (reg === "zsi") op.flags.memZSI = true;
if (reg === "zdi") op.flags.memZDI = true;
}
else if (reg) {
op.flags[reg] = true;
if (reg === "r8lo") op.flags.r8hi = true;
}
if (mem) {
op.flags[mem] = true;
// Exception: Allow LEA to contain any memory size.
if (inst.name === "lea") MapUtils.add(op.flags, MemOp);
}
if (imm) {
if (iop.immSign === "any" || iop.immSign === "signed" ) op.flags["i" + imm] = true;
if (iop.immSign === "any" || iop.immSign === "unsigned") op.flags["u" + imm] = true;
}
if (rel) op.flags["rel" + rel] = true;
row.push(op);
}
if (row)
signatures.push(row);
}
signatures.calcImplicitMemSize();
signatures.simplify();
signatures.compact();
signatures.simplify();
signatures.compact();
return signatures;
}
// --------------------------------------------------------------------------
// [Parse]
// --------------------------------------------------------------------------
parse() {
const data = this.dataOf("src/asmjit/x86/x86inst.cpp");
const re = new RegExp(
"INST\\(" +
"([A-Za-z0-9_]+)\\s*" + "," + // [01] Instruction.
"([^,]+)" + "," + // [02] Encoding.
"(.{26}[^,]*)" + "," + // [03] Opcode[0].
"(.{26}[^,]*)" + "," + // [04] Opcode[1].
"([^,]+)" + "," + // [05] Write-Index.
"([^,]+)" + "," + // [06] Write-Size.
// --- autogenerated fields ---
"([^\\)]+)" + "," + // [07] NameIndex.
"([^\\)]+)" + "," + // [08] CommonDataIndex.
"([^\\)]+)" + "\\)",// [09] OperationDataIndex.
"g");
var m;
while ((m = re.exec(data)) !== null) {
var enum_ = m[1];
var name = enum_ === "None" ? "" : enum_.toLowerCase();
var encoding = m[2].trim();
var opcode0 = m[3].trim();
var opcode1 = m[4].trim();
var writeIndex = StringUtils.trimLeft(m[5]);
var writeSize = StringUtils.trimLeft(m[6]);
const group = GenUtils.groupOf(name);
if (name && !group.length)
console.log(`INSTRUCTION '${name}' not found in asmdb`);
const flags = GenUtils.flagsOf(group);
const signatures = this.signaturesFromInsts(group);
const singleRegCase = GenUtils.singleRegCase(name);
const jumpType = GenUtils.jumpType(name);
this.addInst({
id : 0, // Instruction id (numeric value).
name : name, // Instruction name.
enum : enum_, // Instruction enum without `kId` prefix.
encoding : encoding, // Instruction encoding.
opcode0 : opcode0, // Primary opcode.
opcode1 : opcode1, // Secondary opcode.
flags : flags,
writeIndex : writeIndex,
writeSize : writeSize,
signatures : signatures, // Rows containing instruction signatures.
singleRegCase : singleRegCase,
jumpType : jumpType,
nameIndex : -1, // Instruction name-index.
altOpCodeIndex : -1, // Index to X86InstDB::altOpCodeTable.
commonDataIndex : -1,
operationDataIndex: -1,
sseToAvxDataIndex : -1,
signatureIndex : -1,
signatureCount : -1
});
this.maxOpRows = Math.max(this.maxOpRows, signatures.length);
}
if (this.instArray.length === 0)
throw new Error("X86Generator.parse(): Invalid parsing regexp (no data parsed)");
console.log("Number of Instructions: " + this.instArray.length);
}
// --------------------------------------------------------------------------
// [Generate]
// --------------------------------------------------------------------------
generate() {
// Order doesn't matter here.
this.generateIdData();
this.generateNameData();
this.generateOperationData();
this.generateSseToAvxData();
this.generateAltOpCodeData();
this.generateSignatureData();
// These must be last, and order matters.
this.generateCommonData();
this.generateInstData();
return this;
}
// --------------------------------------------------------------------------
// [Generate - AltOpCodeData]
// --------------------------------------------------------------------------
generateAltOpCodeData() {
const table = new base.IndexedArray();
for (var i = 0; i < this.instArray.length; i++) {
const inst = this.instArray[i];
inst.altOpCodeIndex = table.addIndexed(StringUtils.padLeft(inst.opcode1, 26));
}
var s = `const uint32_t X86InstDB::altOpCodeData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n`;
return this.inject("altOpCodeData", StringUtils.disclaimer(s), table.length * 4);
}
// --------------------------------------------------------------------------
// [Generate - OperationData]
// --------------------------------------------------------------------------
generateOperationData() {
const instArray = this.instArray;
const table = new base.IndexedArray();
for (var i = 0; i < instArray.length; i++) {
const inst = instArray[i];
const group = GenUtils.groupOf(inst.name);
var opFlags = GenUtils.operationFlagsOf(group).map(function(f) { return `OP_FLAG(${f})`; });
if (!opFlags.length) opFlags.push("0");
var features = GenUtils.cpuFeaturesOf(group).map(function(f) { return `FEATURE(${f})`; });
if (!features.length) features.push("0");
var [r, w] = GenUtils.specialsOf(group);
r = r.map(function(item) { return `SPECIAL(${item.replace(".", "_")})`; });
w = w.map(function(item) { return `SPECIAL(${item.replace(".", "_")})`; });
const opFlagsStr = opFlags.join(" | ");
const featuresStr = features.join(", ");
const rStr = r.join(" | ") || "0";
const wStr = w.join(" | ") || "0";
inst.operationDataIndex = table.addIndexed(`{ ${opFlagsStr}, { ${featuresStr} }, ${rStr}, ${wStr} }`);
}
var s = `#define OP_FLAG(F) X86Inst::kOperation##F\n` +
`#define FEATURE(F) CpuInfo::kX86Feature##F\n` +
`#define SPECIAL(F) x86::kSpecialReg_##F\n` +
`const X86Inst::OperationData X86InstDB::operationData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
`#undef SPECIAL\n` +
`#undef FEATURE\n` +
`#undef OP_FLAG\n` ;
return this.inject("operationData", StringUtils.disclaimer(s), table.length * 16);
}
// --------------------------------------------------------------------------
// [Generate - SseToAvxData]
// --------------------------------------------------------------------------
generateSseToAvxData() {
const instArray = this.instArray;
const instMap = this.instMap;
const table = new base.IndexedArray();
function getSseToAvxInsts(insts) {
const combinations = [];
for (var x = 0; x < insts.length; x++) {
const inst = insts[x];
const ops = inst.operands;
// SSE instruction does never share its name with AVX one.
if (/^(VEX|XOP|EVEX)$/.test(inst.prefix))
return null;
var ok = false;
for (var y = 0; y < ops.length; y++) {
if (ops[y].reg === "xmm")
ok = true;
// There is no AVX instruction that works with MMX regs.
if (ops[y].reg === "mm") {
ok = false;
break;
}
}
if (ok) combinations.push(inst);
}
return combinations.length ? combinations : null;
}
function calcSseToAvxData(insts) {
const out = {
mode : "None", // No conversion by default.
delta: 0 // 0 if no conversion is possible.
};
const sseInsts = getSseToAvxInsts(insts);
if (!sseInsts) return out;
const sseName = sseInsts[0].name;
const avxName = "v" + sseName;
const avxInsts = GenUtils.groupOf(avxName);
if (!avxInsts.length) {
if (DEBUG)
console.log(`SseToAvx: Instruction '${sseName}' has no AVX counterpart`);
return out;
}
if (avxName === "vblendvpd" || avxName === "vblendvps" || avxName === "vpblendvb") {
// Special cases first.
out.mode = "Blend";
}
else {
// Common case, deduce conversion mode by checking both SSE and AVX instructions.
const map = Object.create(null);
for (var sseIndex = 0; sseIndex < sseInsts.length; sseIndex++) {
const sseInst = sseInsts[sseIndex];
var match = false;
for (var avxIndex = 0; avxIndex < avxInsts.length; avxIndex++) {
const avxInst = avxInsts[avxIndex];
// Select only VEX instructions.
if (avxInst.prefix !== "VEX") continue;
// Check if the AVX version is the same.
if (GenUtils.eqOps(avxInst.operands, 0, sseInst.operands, 0)) {
map.raw = true;
match = true;
}
else if (avxInst.operands[0].data === "xmm" && GenUtils.eqOps(avxInst.operands, 1, sseInst.operands, 0)) {
map.nds = true;
match = true;
}
}
if (!match) {
const signature = sseInst.operands.map(function(op) { return op.data; }).join(", ");
console.log(`SseToAvx: Instruction '${sseName}(${signature})' has no AVX counterpart`);
return out;
}
}
out.mode = (map.raw && !map.nds) ? "Move" : (map.raw && map.nds) ? "MoveIfMem" : "Extend";
}
out.delta = instMap[avxName].id - instMap[sseName].id;
return out;
}
// This will receive a zero index, which means that no translation is possible.
table.addIndexed("{ " + StringUtils.padLeft(`X86Inst::kSseToAvxNone`, 27) + ", " + StringUtils.padLeft("0", 4) + " }");
for (var i = 0; i < instArray.length; i++) {
const inst = instArray[i];
// If it's not `-1` it's an AVX instruction that shares the SseToAvx
// data. So we won't touch it as it already has `sseToAvxDataIndex`.
if (inst.sseToAvxDataIndex === -1) {
const data = calcSseToAvxData(GenUtils.groupOf(inst.name));
inst.sseToAvxDataIndex = table.addIndexed("{ " + StringUtils.padLeft(`X86Inst::kSseToAvx${data.mode}`, 27) + ", " + StringUtils.padLeft(data.delta, 4) + " }");
if (data.delta !== 0)
instMap["v" + inst.name].sseToAvxDataIndex = inst.sseToAvxDataIndex;
}
}
var s = `const X86Inst::SseToAvxData X86InstDB::sseToAvxData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n`;
return this.inject("sseToAvxData", StringUtils.disclaimer(s), table.length * 2);
}
// --------------------------------------------------------------------------
// [Generate - SignatureData]
// --------------------------------------------------------------------------
generateSignatureData() {
const instArray = this.instArray;
const opMap = Object.create(null);
const opArr = [];
const signatureMap = Object.create(null);
const signatureArr = [];
const noOperand = "OSIGNATURE(0, 0, 0, 0xFF)";
opMap[noOperand] = [0];
opArr.push(noOperand);
function findSignaturesIndex(rows) {
const len = rows.length;
if (!len) return 0;
const indexes = signatureMap[rows[0].data];
if (indexes === undefined) return -1;
for (var i = 0; i < indexes.length; i++) {
const index = indexes[i];
if (index + len > signatureArr.length) continue;
var ok = true;
for (var j = 0; j < len; j++) {
if (signatureArr[index + j].data !== rows[j].data) {
ok = false;
break;
}
}
if (ok)
return index;
}
return -1;
}
function indexSignatures(signatures) {
const result = signatureArr.length;
for (var i = 0; i < signatures.length; i++) {
const signature = signatures[i];
const idx = signatureArr.length;
if (!hasOwn.call(signatureMap, signature.data))
signatureMap[signature.data] = [];
signatureMap[signature.data].push(idx);
signatureArr.push(signature);
}
return result;
}
for (var len = this.maxOpRows; len >= 0; len--) {
for (var i = 0; i < instArray.length; i++) {
const inst = instArray[i];
const signatures = inst.signatures;
if (signatures.length !== len) continue;
const signatureEntries = [];
for (var j = 0; j < len; j++) {
const signature = signatures[j];
var signatureEntry = `ISIGNATURE(${signature.length}, ${signature.x86 ? 1 : 0}, ${signature.x64 ? 1 : 0}, ${signature.implicit}`;
var signatureComment = signature.toString();
var x = 0;
while (x < signature.length) {
const h = signature[x].toAsmJitOpData();
var index = -1;
if (!hasOwn.call(opMap, h)) {
index = opArr.length;
opMap[h] = index;
opArr.push(h);
}
else {
index = opMap[h];
}
signatureEntry += `, ${StringUtils.padLeft(index, 3)}`;
x++;
}
while (x < 6) {
signatureEntry += `, ${StringUtils.padLeft(0, 3)}`;
x++;
}
signatureEntry += `)`;
signatureEntries.push({ data: signatureEntry, comment: signatureComment, refs: 0 });
}
var count = signatureEntries.length;
var index = findSignaturesIndex(signatureEntries);
if (index === -1)
index = indexSignatures(signatureEntries);
signatureArr[index].refs++;
inst.signatureIndex = index;
inst.signatureCount = count;
}
}
var s = "#define FLAG(flag) X86Inst::kOp##flag\n" +
"#define MEM(mem) X86Inst::kMemOp##mem\n" +
"#define OSIGNATURE(flags, memFlags, extFlags, regId) \\\n" +
" { uint32_t(flags), uint16_t(memFlags), uint8_t(extFlags), uint8_t(regId) }\n" +
StringUtils.makeCxxArray(opArr, "const X86Inst::OSignature X86InstDB::oSignatureData[]") +
"#undef OSIGNATURE\n" +
"#undef MEM\n" +
"#undef FLAG\n" +
"\n" +
"#define ISIGNATURE(count, x86, x64, implicit, o0, o1, o2, o3, o4, o5) \\\n" +
" { count, (x86 ? uint8_t(X86Inst::kArchMaskX86) : uint8_t(0)) | \\\n" +
" (x64 ? uint8_t(X86Inst::kArchMaskX64) : uint8_t(0)) , \\\n" +
" implicit, \\\n" +
" 0, \\\n" +
" { o0, o1, o2, o3, o4, o5 } \\\n" +
" }\n" +
StringUtils.makeCxxArrayWithComment(signatureArr, "const X86Inst::ISignature X86InstDB::iSignatureData[]") +
"#undef ISIGNATURE\n";
return this.inject("signatureData", StringUtils.disclaimer(s), opArr.length * 8 + signatureArr.length * 8);
}
// --------------------------------------------------------------------------
// [Generate - CommonData]
// --------------------------------------------------------------------------
generateCommonData() {
const table = new base.IndexedArray();
for (var i = 0; i < this.instArray.length; i++) {
const inst = this.instArray[i];
const group = GenUtils.groupOf(inst.name);
const flags = inst.flags.map(function(flag) { return `F(${flag})`; }).join("|") || "0";
const singleRegCase = `SINGLE_REG(${inst.singleRegCase})`;
const jumpType = `JUMP_TYPE(${inst.jumpType})`;
const item = "{ " + StringUtils.padLeft(flags , 54) + ", " +
StringUtils.padLeft(inst.writeIndex , 3) + ", " +
StringUtils.padLeft(inst.writeSize , 3) + ", " +
StringUtils.padLeft(inst.altOpCodeIndex , 3) + ", " +
StringUtils.padLeft(inst.signatureIndex , 3) + ", " +
StringUtils.padLeft(inst.signatureCount , 2) + ", " +
StringUtils.padLeft(jumpType , 22) + ", " +
StringUtils.padLeft(singleRegCase , 16) + ", " + "0 }";
inst.commonDataIndex = table.addIndexed(item);
}
var s = `#define F(VAL) X86Inst::kFlag##VAL\n` +
`#define JUMP_TYPE(VAL) Inst::kJumpType##VAL\n` +
`#define SINGLE_REG(VAL) X86Inst::kSingleReg##VAL\n` +
`const X86Inst::CommonData X86InstDB::commonData[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
`#undef SINGLE_REG\n` +
`#undef JUMP_TYPE\n` +
`#undef F\n`;
return this.inject("commonData", StringUtils.disclaimer(s), table.length * 12);
}
// --------------------------------------------------------------------------
// [Generate - InstData]
// --------------------------------------------------------------------------
generateInstData() {
var s = StringUtils.format(this.instArray, "", false, function(inst) {
return "INST(" +
StringUtils.padLeft(inst.enum , 16) + ", " +
StringUtils.padLeft(inst.encoding , 19) + ", " +
StringUtils.padLeft(inst.opcode0 , 26) + ", " +
StringUtils.padLeft(inst.opcode1 , 26) + ", " +
StringUtils.padLeft(inst.writeIndex , 1) + ", " +
StringUtils.padLeft(inst.writeSize , 1) + ", " +
StringUtils.padLeft(inst.nameIndex , 4) + ", " +
StringUtils.padLeft(inst.commonDataIndex , 3) + ", " +
StringUtils.padLeft(inst.operationDataIndex, 3) + ", " +
StringUtils.padLeft(inst.sseToAvxDataIndex , 2) + ")";
}) + "\n";
return this.inject("instData", s, this.instArray.length * 12);
}
// --------------------------------------------------------------------------
// [Reimplement]
// --------------------------------------------------------------------------
getCommentOf(name) {
var insts = GenUtils.groupOf(name);
if (!insts.length) return "";
var features = GenUtils.cpuFeaturesOf(insts);
var comment = GenUtils.cpuArchOf(insts);
if (features.length) {
comment += " {";
const vl = features.indexOf("AVX512_VL");
if (vl !== -1) features.splice(vl, 1);
comment += features.join("|");
if (vl !== -1) comment += "+VL";
comment += "}";
}
return comment;
}
// --------------------------------------------------------------------------
// [Print]
// --------------------------------------------------------------------------
printMissing() {
const ignored = MapUtils.arrayToMap([
"cmpsb", "cmpsw", "cmpsd", "cmpsq",
"lodsb", "lodsw", "lodsd", "lodsq",
"movsb", "movsw", "movsd", "movsq",
"scasb", "scasw", "scasd", "scasq",
"stosb", "stosw", "stosd", "stosq",
"insb" , "insw" , "insd" ,
"outsb", "outsw", "outsd",
"wait" // Maps to `fwait`, which AsmJit uses instead.
]);
var out = "";
isa.instructionNames.forEach(function(name) {
var insts = isa.query(name);
if (!this.instMap[name] && ignored[name] !== true) {
console.log(`MISSING INSTRUCTION '${name}'`);
var inst = this.newInstFromInsts(insts);
if (inst) {
out += " INST(" +
StringUtils.padLeft(inst.enum , 16) + ", " +
StringUtils.padLeft(inst.encoding , 23) + ", " +
StringUtils.padLeft(inst.opcode0 , 26) + ", " +
StringUtils.padLeft(inst.opcode1 , 26) + ", " +
StringUtils.padLeft(inst.writeIndex , 2) + ", " +
StringUtils.padLeft(inst.writeSize , 2) + ", " +
StringUtils.padLeft("0", 4) + ", " +
StringUtils.padLeft("0", 3) + ", " +
StringUtils.padLeft("0", 3) + ", " +
StringUtils.padLeft("0", 3) + "),\n";
}
}
}, this);
console.log(out);
}
newInstFromInsts(insts) {
function composeOpCode(obj) {
return `${obj.type}(${obj.prefix},${obj.opcode},${obj.o},${obj.l},${obj.w},${obj.ew},${obj.en},${obj.tt})`;
}
function GetAccess(inst) {
var operands = inst.operands;
if (!operands.length) return "";
var op = operands[0];
if (op.read && op.write)
return "RW";
else if (op.read)
return "RO";
else
return "WO";
}
var inst = insts[0];
var id = this.instArray.length;
var name = inst.name;
var enum_ = name[0].toUpperCase() + name.substr(1);
var opcode = inst.opcodeHex;
var rm = inst.rm;
var mm = inst.mm;
var pp = inst.pp;
var encoding = inst.encoding;
var prefix = inst.prefix;
var access = GetAccess(inst);
var vexL = undefined;
var vexW = undefined;
var evexW = undefined;
for (var i = 1; i < insts.length; i++) {
inst = insts[i];
if (opcode !== inst.opcode ) return null;
if (rm !== inst.rm ) return null;
if (mm !== inst.mm ) return null;
if (pp !== inst.pp ) return null;
if (encoding !== inst.encoding ) return null;
if (prefix !== inst.prefix ) return null;
if (access !== GetAccess(inst)) return null;
}
var ppmm = StringUtils.padLeft(pp, 2).replace(/ /g, "0") +
StringUtils.padLeft(mm, 4).replace(/ /g, "0") ;
var composed = composeOpCode({
type : prefix === "VEX" || prefix === "EVEX" ? "V" : "O",
prefix: ppmm,
opcode: opcode,
o : rm === "r" ? "_" : (rm ? rm : "_"),
l : vexL !== undefined ? vexL : "_",
w : vexW !== undefined ? vexW : "_",
ew : evexW !== undefined ? vexEW : "_",
en : "_",
tt : "_ "
});
return {
id : id,
name : name,
enum : enum_,
encoding : encoding,
opcode0 : composed,
opcode1 : "0",
writeIndex : "0",
writeSize : "0",
nameIndex : -1,
commonDataIndex : -1,
operationDataIndex: -1
};
}
}
// ----------------------------------------------------------------------------
// [Main]
// ----------------------------------------------------------------------------
function main() {
const gen = new X86Generator();
gen.parse();
gen.generate();
gen.printMissing();
gen.dumpTableSizes();
gen.save();
}
main();