1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413 |
- // [AsmJit]
- // Machine Code Generation for C++.
- //
- // [License]
- // Zlib - See LICENSE.md file in the package.
- // ============================================================================
- // tablegen-x86.js
- //
- // 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 core = require("./tablegen.js");
- const asmdb = core.asmdb;
- const kIndent = core.kIndent;
- const Lang = core.Lang;
- const CxxUtils = core.CxxUtils;
- const MapUtils = core.MapUtils;
- const ArrayUtils = core.ArrayUtils;
- const StringUtils = core.StringUtils;
- const IndexedArray = core.IndexedArray;
- const hasOwn = Object.prototype.hasOwnProperty;
- const disclaimer = StringUtils.disclaimer;
- const FAIL = core.FAIL;
- const DEBUG = core.DEBUG;
- const decToHex = StringUtils.decToHex;
- // ============================================================================
- // [tablegen.x86.x86isa]
- // ============================================================================
- // Create the X86 database and add some special cases recognized by AsmJit.
- const x86isa = new asmdb.x86.ISA({
- instructions: [
- // Imul in [reg, imm] form is encoded as [reg, reg, imm].
- ["imul", "r16, ib" , "RMI" , "66 6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul", "r32, ib" , "RMI" , "6B /r ib" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul", "r64, ib" , "RMI" , "REX.W 6B /r ib", "X64 OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul", "r16, iw" , "RMI" , "66 69 /r iw" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul", "r32, id" , "RMI" , "69 /r id" , "ANY OF=W SF=W ZF=U AF=U PF=U CF=W"],
- ["imul", "r64, id" , "RMI" , "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 string instructions
- // (like cmps, movs, etc...) 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 }
- };
- // ============================================================================
- // [tablegen.x86.Filter]
- // ============================================================================
- class Filter {
- static unique(instArray) {
- const result = [];
- const known = {};
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- if (inst.attributes.AltForm)
- continue;
- const s = inst.operands.map((op) => { return op.isImm() ? "imm" : op.toString(); }).join(", ");
- if (known[s] === true)
- continue;
- known[s] = true;
- result.push(inst);
- }
- return result;
- }
- static noAltForm(instArray) {
- const result = [];
- for (var i = 0; i < instArray.length; i++) {
- const inst = instArray[i];
- if (inst.attributes.AltForm)
- continue;
- result.push(inst);
- }
- return result;
- }
- static byArch(instArray, arch) {
- return instArray.filter(function(inst) {
- return inst.arch === "ANY" || inst.arch === arch;
- });
- }
- }
- // ============================================================================
- // [tablegen.x86.GenUtils]
- // ============================================================================
- class GenUtils {
- static cpuArchOf(dbInsts) {
- var anyArch = false;
- var x86Arch = false;
- var x64Arch = false;
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- if (dbInst.arch === "ANY") anyArch = true;
- if (dbInst.arch === "X86") x86Arch = true;
- if (dbInst.arch === "X64") x64Arch = true;
- }
- return anyArch || (x86Arch && x64Arch) ? "" : x86Arch ? "(X86)" : "(X64)";
- }
- static cpuFeaturesOf(dbInsts) {
- return ArrayUtils.sorted(dbInsts.unionCpuFeatures());
- }
- static flagsOf(dbInsts) {
- 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 = dbInsts.length > 0 && /^(?:bndldx|bndstx)$/.test(dbInsts[0].name);
- if (mib) f.Mib = true;
- var mmx = false;
- var vec = false;
- for (i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- if (dbInst.name === "emms")
- mmx = true;
- if (dbInst.name === "vzeroall" || dbInst.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 < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- if (dbInst.attributes.Lock ) f.Lock = true;
- if (dbInst.attributes.XAcquire ) f.XAcquire = true;
- if (dbInst.attributes.XRelease ) f.XRelease = true;
- if (dbInst.attributes.BND ) f.Rep = true;
- if (dbInst.attributes.REP ) f.Rep = true;
- if (dbInst.attributes.REPNE ) f.Rep = true;
- if (dbInst.attributes.RepIgnored) f.RepIgnored = true;
- if (dbInst.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 (dbInst.vsibReg)
- f.Vsib = true;
- if (dbInst.prefix === "VEX" || dbInst.prefix === "XOP")
- f.Vex = true;
- if (dbInst.prefix === "EVEX") {
- f.Evex = true;
- if (dbInst.kmask) f.Avx512K = true;
- if (dbInst.zmask) f.Avx512Z = true;
- if (dbInst.er) f.Avx512ER = true;
- if (dbInst.sae) f.Avx512SAE = true;
- if (dbInst.broadcast) f["Avx512B" + String(dbInst.elementSize)] = true;
- if (dbInst.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 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 "sbb" :
- 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 fixedRegOf(reg) {
- switch (reg) {
- case "es" : return 1;
- case "cs" : return 2;
- case "ss" : return 3;
- case "ds" : return 4;
- case "fs" : return 5;
- case "gs" : return 6;
- case "ah" : return 0;
- case "ch" : return 1;
- case "dh" : return 2;
- case "bh" : return 3;
- case "al" : case "ax": case "eax": case "rax": case "zax": return 0;
- case "cl" : case "cx": case "ecx": case "rcx": case "zcx": return 1;
- case "dl" : case "dx": case "edx": case "rdx": case "zdx": return 2;
- case "bl" : case "bx": case "ebx": case "rbx": case "zbx": return 3;
- case "spl" : case "sp": case "esp": case "rsp": case "zsp": return 4;
- case "bpl" : case "bp": case "ebp": case "rbp": case "zbp": return 5;
- case "sil" : case "si": case "esi": case "rsi": case "zsi": return 6;
- case "dil" : case "di": case "edi": case "rdi": case "zdi": return 7;
- case "st0" : return 0;
- case "xmm0": return 0;
- case "ymm0": return 0;
- case "zmm0": return 0;
- default:
- return -1;
- }
- }
- static controlType(dbInsts) {
- if (dbInsts.checkAttribute("Control", "Jump")) return "Jump";
- if (dbInsts.checkAttribute("Control", "Call")) return "Call";
- if (dbInsts.checkAttribute("Control", "Branch")) return "Branch";
- if (dbInsts.checkAttribute("Control", "Return")) return "Return";
- return "None";
- }
- }
- // ============================================================================
- // [tablegen.x86.X86TableGen]
- // ============================================================================
- class X86TableGen extends core.TableGen {
- constructor() {
- super("X86");
- }
- // --------------------------------------------------------------------------
- // [Query]
- // --------------------------------------------------------------------------
- // Get instructions (dbInsts) having the same name as understood by AsmJit.
- query(name) {
- const remapped = RemappedInsts[name];
- if (!remapped) return x86isa.query(name);
- const dbInsts = x86isa.query(remapped.names);
- const rep = remapped.rep;
- if (rep === null) return dbInsts;
- return dbInsts.filter((inst) => {
- return rep === !!(inst.attributes.REP || inst.attributes.REPNE);
- });
- }
- // --------------------------------------------------------------------------
- // [Parse / Merge]
- // --------------------------------------------------------------------------
- parse() {
- const data = this.dataOfFile("src/asmjit/x86/x86instdb.cpp");
- const re = new RegExp(
- "INST\\(" +
- "([A-Za-z0-9_]+)\\s*" + "," + // [01] Instruction.
- "([^,]+)" + "," + // [02] Encoding.
- "(.{26}[^,]*)" + "," + // [03] Opcode[0].
- "(.{26}[^,]*)" + "," + // [04] Opcode[1].
- // --- autogenerated fields ---
- "([^\\)]+)" + "," + // [05] MainOpcodeIndex.
- "([^\\)]+)" + "," + // [06] AltOpcodeIndex.
- "([^\\)]+)" + "," + // [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();
- const dbInsts = this.query(name);
- if (name && !dbInsts.length)
- FAIL(`Instruction '${name}' not found in asmdb`);
- const flags = GenUtils.flagsOf(dbInsts);
- const controlType = GenUtils.controlType(dbInsts);
- const singleRegCase = GenUtils.singleRegCase(name);
- this.addInst({
- id : 0, // Instruction id (numeric value).
- name : name, // Instruction name.
- enum : enum_, // Instruction enum without `kId` prefix.
- dbInsts : dbInsts, // All dbInsts returned from asmdb query.
- encoding : encoding, // Instruction encoding.
- opcode0 : opcode0, // Primary opcode.
- opcode1 : opcode1, // Secondary opcode.
- flags : flags,
- signatures : null, // Instruction signatures.
- controlType : controlType,
- singleRegCase : singleRegCase,
- mainOpcodeValue : -1, // Main opcode value (0.255 hex).
- mainOpcodeIndex : -1, // Index to InstDB::_mainOpcodeTable.
- altOpcodeIndex : -1, // Index to InstDB::_altOpcodeTable.
- nameIndex : -1, // Index to InstDB::_nameData.
- commonInfoIndexA : -1,
- commomInfoIndexB : -1,
- signatureIndex : -1,
- signatureCount : -1
- });
- }
- if (this.insts.length === 0)
- FAIL("X86TableGen.parse(): Invalid parsing regexp (no data parsed)");
- console.log("Number of Instructions: " + this.insts.length);
- }
- merge() {
- var s = StringUtils.format(this.insts, "", true, function(inst) {
- return "INST(" +
- String(inst.enum ).padEnd(17) + ", " +
- String(inst.encoding ).padEnd(19) + ", " +
- String(inst.opcode0 ).padEnd(26) + ", " +
- String(inst.opcode1 ).padEnd(26) + ", " +
- String(inst.mainOpcodeIndex ).padEnd( 3) + ", " +
- String(inst.altOpcodeIndex ).padEnd( 3) + ", " +
- String(inst.nameIndex ).padEnd( 5) + ", " +
- String(inst.commonInfoIndexA).padEnd( 3) + ", " +
- String(inst.commomInfoIndexB).padEnd( 3) + ")";
- }) + "\n";
- this.inject("InstInfo", s, this.insts.length * 8);
- }
- // --------------------------------------------------------------------------
- // [Other]
- // --------------------------------------------------------------------------
- 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 = "";
- x86isa.instructionNames.forEach(function(name) {
- var dbInsts = x86isa.query(name);
- if (!this.instMap[name] && ignored[name] !== true) {
- console.log(`MISSING INSTRUCTION '${name}'`);
- var inst = this.newInstFromGroup(dbInsts);
- if (inst) {
- out += " INST(" +
- String(inst.enum ).padEnd(17) + ", " +
- String(inst.encoding ).padEnd(19) + ", " +
- String(inst.opcode0 ).padEnd(26) + ", " +
- String(inst.opcode1 ).padEnd(26) + ", " +
- String("0" ).padEnd( 4) + ", " +
- String("0" ).padEnd( 3) + ", " +
- String("0" ).padEnd( 3) + "),\n";
- }
- }
- }, this);
- console.log(out);
- }
- newInstFromGroup(dbInsts) {
- function composeOpCode(obj) {
- return `${obj.type}(${obj.prefix},${obj.opcode},${obj.o},${obj.l},${obj.w},${obj.ew},${obj.en},${obj.tt})`;
- }
- function GetAccess(dbInst) {
- var operands = dbInst.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";
- }
- function isVecPrefix(s) {
- return s === "VEX" || s === "EVEX" || s === "XOP";
- }
- var dbi = dbInsts[0];
- var id = this.insts.length;
- var name = dbi.name;
- var enum_ = name[0].toUpperCase() + name.substr(1);
- var opcode = dbi.opcodeHex;
- var rm = dbi.rm;
- var mm = dbi.mm;
- var pp = dbi.pp;
- var encoding = dbi.encoding;
- var isVec = isVecPrefix(dbi.prefix);
- var access = GetAccess(dbi);
- var vexL = undefined;
- var vexW = undefined;
- var evexW = undefined;
- for (var i = 0; i < dbInsts.length; i++) {
- dbi = dbInsts[i];
- if (dbi.prefix === "VEX" || dbi.prefix === "XOP") {
- var newVexL = String(dbi.l === "128" ? 0 : dbi.l === "256" ? 1 : dbi.l === "512" ? 2 : "_");
- var newVexW = String(dbi.w === "W0" ? 0 : dbi.w === "W1" ? 1 : "_");
- if (vexL !== undefined && vexL !== newVexL)
- vexL = "x";
- else
- vexL = newVexL;
- if (vexW !== undefined && vexW !== newVexW)
- vexW = "x";
- else
- vexW = newVexW;
- }
- if (dbi.prefix === "EVEX") {
- var newEvexW = String(dbi.w === "W0" ? 0 : dbi.w === "W1" ? 1 : "_");
- if (evexW !== undefined && evexW !== newEvexW)
- evexW = "x";
- else
- evexW = newEvexW;
- }
- if (opcode !== dbi.opcodeHex ) { console.log(`ISSUE: Opcode ${opcode} != ${dbi.opcodeHex}`); return null; }
- if (rm !== dbi.rm ) { console.log(`ISSUE: RM ${rm} != ${dbi.rm}`); return null; }
- if (mm !== dbi.mm ) { console.log(`ISSUE: MM ${mm} != ${dbi.mm}`); return null; }
- if (pp !== dbi.pp ) { console.log(`ISSUE: PP ${pp} != ${dbi.pp}`); return null; }
- if (encoding !== dbi.encoding ) { console.log(`ISSUE: Enc ${encoding} != ${dbi.encoding}`); return null; }
- if (access !== GetAccess(dbi)) { console.log(`ISSUE: Access ${access} != ${GetAccess(dbi)}`); return null; }
- if (isVec != isVecPrefix(dbi.prefix)) { console.log(`ISSUE: Vex/Non-Vex mismatch`); return null; }
- }
- var ppmm = pp.padEnd(2).replace(/ /g, "0") +
- mm.padEnd(4).replace(/ /g, "0") ;
- var composed = composeOpCode({
- type : isVec ? "V" : "O",
- prefix: ppmm,
- opcode: opcode,
- o : rm === "r" ? "_" : (rm ? rm : "_"),
- l : vexL !== undefined ? vexL : "_",
- w : vexW !== undefined ? vexW : "_",
- ew : evexW !== undefined ? evexW : "_",
- en : "_",
- tt : "_ "
- });
- return {
- id : id,
- name : name,
- enum : enum_,
- encoding : encoding,
- opcode0 : composed,
- opcode1 : "0",
- nameIndex : -1,
- commonInfoIndexA : -1,
- commomInfoIndexB : -1
- };
- }
- // --------------------------------------------------------------------------
- // [Hooks]
- // --------------------------------------------------------------------------
- onBeforeRun() {
- this.load([
- "src/asmjit/x86/x86globals.h",
- "src/asmjit/x86/x86instdb.cpp",
- "src/asmjit/x86/x86instdb.h",
- "src/asmjit/x86/x86instdb_p.h"
- ]);
- this.parse();
- }
- onAfterRun() {
- this.merge();
- this.save();
- this.dumpTableSizes();
- this.printMissing();
- }
- }
- // ============================================================================
- // [tablegen.x86.IdEnum]
- // ============================================================================
- class IdEnum extends core.IdEnum {
- constructor() {
- super("IdEnum");
- }
- comment(inst) {
- function filterAVX(features, avx) {
- return features.filter(function(item) { return /^(AVX|FMA)/.test(item) === avx; });
- }
- var dbInsts = inst.dbInsts;
- if (!dbInsts.length) return "Invalid instruction id.";
- var text = "";
- var features = GenUtils.cpuFeaturesOf(dbInsts);
- if (features.length) {
- text += "{";
- const avxFeatures = filterAVX(features, true);
- const otherFeatures = filterAVX(features, false);
- const vl = avxFeatures.indexOf("AVX512_VL");
- if (vl !== -1) avxFeatures.splice(vl, 1);
- const fma = avxFeatures.indexOf("FMA");
- if (fma !== -1) { avxFeatures.splice(fma, 1); avxFeatures.splice(0, 0, "FMA"); }
- text += avxFeatures.join("|");
- if (vl !== -1) text += "+VL";
- if (otherFeatures.length)
- text += (avxFeatures.length ? " & " : "") + otherFeatures.join("|");
- text += "}";
- }
- var arch = GenUtils.cpuArchOf(dbInsts);
- if (arch)
- text += (text ? " " : "") + arch;
- return `Instruction '${inst.name}'${(text ? " " + text : "")}.`;
- }
- }
- // ============================================================================
- // [tablegen.x86.NameTable]
- // ============================================================================
- class NameTable extends core.NameTable {
- constructor() {
- super("NameTable");
- }
- }
- // ============================================================================
- // [tablegen.x86.AltOpcodeTable]
- // ============================================================================
- class AltOpcodeTable extends core.Task {
- constructor() {
- super("AltOpcodeTable");
- }
- run() {
- const insts = this.ctx.insts;
- const mainOpcodeTable = new IndexedArray();
- const altOpcodeTable = new IndexedArray();
- mainOpcodeTable.addIndexed("O(000000,00,0,0,0,0,0,_ )");
- function indexOpcode(opcode) {
- if (opcode === "0")
- return ["00", 0];
- // O_FPU(__,__OP,_)
- if (opcode.startsWith("O_FPU(")) {
- var value = opcode.substring(11, 13);
- var remaining = opcode.substring(0, 11) + "00" + opcode.substring(13);
- return [value, mainOpcodeTable.addIndexed(remaining.padEnd(26))];
- }
- // X(______,OP,_,_,_,_,_,_ )
- if (opcode.startsWith("O_FPU(") || opcode.startsWith("O(") || opcode.startsWith("V(") || opcode.startsWith("E(")) {
- var value = opcode.substring(9, 11);
- var remaining = opcode.substring(0, 9) + "00" + opcode.substring(11);
- remaining = remaining.replace(/,[_xI],/g, ",0,");
- remaining = remaining.replace(/,[_xI],/g, ",0,");
- return [value, mainOpcodeTable.addIndexed(remaining.padEnd(26))];
- }
- FAIL(`Failed to process opcode '${opcode}'`);
- }
- insts.map((inst) => {
- const [value, index] = indexOpcode(inst.opcode0);
- inst.mainOpcodeValue = value;
- inst.mainOpcodeIndex = index;
- inst.altOpcodeIndex = altOpcodeTable.addIndexed(inst.opcode1.padEnd(26));
- });
- // console.log(mainOpcodeTable.length);
- // console.log(StringUtils.format(mainOpcodeTable, kIndent, true));
- this.inject("MainOpcodeTable",
- disclaimer(`const uint32_t InstDB::_mainOpcodeTable[] = {\n${StringUtils.format(mainOpcodeTable, kIndent, true)}\n};\n`),
- mainOpcodeTable.length * 4);
- this.inject("AltOpcodeTable",
- disclaimer(`const uint32_t InstDB::_altOpcodeTable[] = {\n${StringUtils.format(altOpcodeTable, kIndent, true)}\n};\n`),
- altOpcodeTable.length * 4);
- }
- }
- // ============================================================================
- // [tablegen.x86.SseToAvxTable]
- // ============================================================================
- /*
- // Removed from asmjit.
- class InstSseToAvxTable extends core.Task {
- constructor() {
- super("InstSseToAvxTable", ["IdEnum"]);
- }
- run() {
- const insts = this.ctx.insts;
- const dataTable = new IndexedArray();
- const indexTable = [];
- function add(data) {
- return dataTable.addIndexed("{ " + `SseToAvxData::kMode${data.mode}`.padEnd(28) + ", " + String(data.delta).padEnd(4) + " }");
- }
- // This will receive a zero index, which means that no SseToAvx or AvxToSSe translation is possible.
- const kInvalidIndex = add({ mode: "None", delta: 0 });
- insts.forEach((inst) => { indexTable.push(kInvalidIndex); });
- insts.forEach((inst) => {
- // If it's not `kInvalidIndex` it's an AVX instruction that shares the
- // SseToAvx data. We won't touch it as it already has the index assigned.
- if (indexTable[inst.id] === kInvalidIndex) {
- const data = this.calcSseToAvxData(inst.dbInsts);
- const index = add(data);
- indexTable[inst.id] = index;
- if (data.delta !== 0)
- indexTable[this.ctx.instMap["v" + inst.name].id] = index;
- }
- });
- this.inject("SseToAvxIndex",
- disclaimer(`static const uint8_t sseToAvxIndex[] = {\n${StringUtils.format(indexTable, kIndent, -1)}\n};\n`),
- indexTable.length * 1);
- this.inject("SseToAvxTable",
- disclaimer(`static const SseToAvxData sseToAvxData[] = {\n${StringUtils.format(dataTable, kIndent, true)}\n};\n`),
- dataTable.length * 2);
- }
- filterSseToAvx(dbInsts) {
- const filtered = [];
- for (var x = 0; x < dbInsts.length; x++) {
- const dbInst = dbInsts[x];
- const ops = dbInst.operands;
- // SSE instruction does never share its name with AVX one.
- if (/^(VEX|XOP|EVEX)$/.test(dbInst.prefix))
- return [];
- var ok = false;
- for (var y = 0; y < ops.length; y++) {
- // There is no AVX instruction that works with MMX regs.
- if (ops[y].reg === "mm") { ok = false; break; }
- if (ops[y].reg === "xmm") { ok = true; }
- }
- if (ok)
- filtered.push(dbInst);
- }
- return filtered;
- }
- calcSseToAvxData(dbInsts) {
- const data = {
- mode : "None", // No conversion by default.
- delta: 0 // 0 if no conversion is possible.
- };
- const dbSseInsts = this.filterSseToAvx(dbInsts);
- if (!dbSseInsts.length)
- return data;
- const sseName = dbSseInsts[0].name;
- const avxName = "v" + sseName;
- const dbAvxInsts = this.ctx.query(avxName);
- if (!dbAvxInsts.length) {
- DEBUG(`SseToAvx: Instruction '${sseName}' has no AVX counterpart`);
- return data;
- }
- if (avxName === "vblendvpd" || avxName === "vblendvps" || avxName === "vpblendvb") {
- // Special cases first.
- data.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 < dbSseInsts.length; sseIndex++) {
- const sseInst = dbSseInsts[sseIndex];
- var match = false;
- for (var avxIndex = 0; avxIndex < dbAvxInsts.length; avxIndex++) {
- const avxInst = dbAvxInsts[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 data;
- }
- }
- data.mode = (map.raw && !map.nds) ? "Move" : (map.raw && map.nds) ? "MoveIfMem" : "Extend";
- }
- data.delta = this.ctx.instMap[avxName].id - this.ctx.instMap[sseName].id;
- return data;
- }
- }
- */
- // ============================================================================
- // [tablegen.x86.InstSignatureTable]
- // ============================================================================
- const RegOp = MapUtils.arrayToMap(["al", "ah", "ax", "eax", "rax", "cl", "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd"]);
- const MemOp = MapUtils.arrayToMap(["m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024"]);
- const cmpOp = StringUtils.makePriorityCompare([
- "r8lo", "r8hi", "r16", "r32", "r64", "xmm", "ymm", "zmm", "mm", "k", "sreg", "creg", "dreg", "st", "bnd",
- "mem", "vm", "m8", "m16", "m32", "m48", "m64", "m80", "m128", "m256", "m512", "m1024",
- "mib",
- "vm32x", "vm32y", "vm32z", "vm64x", "vm64y", "vm64z",
- "memBase", "memES", "memDS",
- "i4", "u4", "i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64",
- "rel8", "rel32",
- "implicit"
- ]);
- const OpToAsmJitOp = {
- "implicit": "F(Implicit)",
- "r8lo" : "F(GpbLo)",
- "r8hi" : "F(GpbHi)",
- "r16" : "F(Gpw)",
- "r32" : "F(Gpd)",
- "r64" : "F(Gpq)",
- "xmm" : "F(Xmm)",
- "ymm" : "F(Ymm)",
- "zmm" : "F(Zmm)",
- "mm" : "F(Mm)",
- "k" : "F(KReg)",
- "sreg" : "F(SReg)",
- "creg" : "F(CReg)",
- "dreg" : "F(DReg)",
- "st" : "F(St)",
- "bnd" : "F(Bnd)",
- "mem" : "F(Mem)",
- "vm" : "F(Vm)",
- "i4" : "F(I4)",
- "u4" : "F(U4)",
- "i8" : "F(I8)",
- "u8" : "F(U8)",
- "i16" : "F(I16)",
- "u16" : "F(U16)",
- "i32" : "F(I32)",
- "u32" : "F(U32)",
- "i64" : "F(I64)",
- "u64" : "F(U64)",
- "rel8" : "F(Rel8)",
- "rel32" : "F(Rel32)",
- "m8" : "M(M8)",
- "m16" : "M(M16)",
- "m32" : "M(M32)",
- "m48" : "M(M48)",
- "m64" : "M(M64)",
- "m80" : "M(M80)",
- "m128" : "M(M128)",
- "m256" : "M(M256)",
- "m512" : "M(M512)",
- "m1024" : "M(M1024)",
- "mib" : "M(Mib)",
- "mAny" : "M(Any)",
- "vm32x" : "M(Vm32x)",
- "vm32y" : "M(Vm32y)",
- "vm32z" : "M(Vm32z)",
- "vm64x" : "M(Vm64x)",
- "vm64y" : "M(Vm64y)",
- "vm64z" : "M(Vm64z)",
- "memBase" : "M(BaseOnly)",
- "memDS" : "M(Ds)",
- "memES" : "M(Es)"
- };
- function StringifyArray(a, map) {
- var s = "";
- for (var i = 0; i < a.length; i++) {
- const op = a[i];
- if (!hasOwn.call(map, op))
- FAIL(`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 = asmdb.x86.Utils.regIndexOf(k);
- const kind = asmdb.x86.Utils.regKindOf(k);
- if (kind)
- hasReg = true;
- if (index !== null && index !== -1)
- indexKind = kind;
- }
- if (hasReg) {
- for (k in bf) {
- const index = asmdb.x86.Utils.regIndexOf(k);
- if (index !== null && index !== -1) {
- const kind = asmdb.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;
- // 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;
- 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 s;
- }
- toAsmJitOpData() {
- var oFlags = this.flags;
- var mFlags = Object.create(null);
- var mMemFlags = Object.create(null);
- var mExtFlags = Object.create(null);
- var sRegMask = 0;
- for (var k in oFlags) {
- switch (k) {
- case "implicit":
- case "r8lo" :
- case "r8hi" :
- case "r16" :
- case "r32" :
- case "r64" :
- case "creg" :
- case "dreg" :
- case "sreg" :
- case "bnd" :
- case "st" :
- 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 "memBase" : mFlags.mem = true; mMemFlags.memBase = 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; break;
- case "memZSI" : mFlags.mem = true; sRegMask |= 1 << 6; break;
- case "memZDI" : mFlags.mem = true; sRegMask |= 1 << 7; 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 "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 "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 "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 "st0" : mFlags.st = 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(ArrayUtils.sorted(mFlags , cmpOp), OpToAsmJitOp);
- const sMemFlags = StringifyArray(ArrayUtils.sorted(mMemFlags, cmpOp), OpToAsmJitOp);
- const sExtFlags = StringifyArray(ArrayUtils.sorted(mExtFlags, cmpOp), OpToAsmJitOp);
- return `ROW(${sFlags || 0}, ${sMemFlags || 0}, ${sExtFlags || 0}, ${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))
- s += "{" + ArrayUtils.sorted(MapUtils.and(op.flags, RegOp)).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 has implicit-size 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 has implicit-size if `diffSizeSet` is empty. That
- // means that the instruction only uses one size for all reg combinations.
- //
- // B) The memory operand has implicit-size 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((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-size memory operand.
- for (bIndex = 0; bIndex < sameSizeSet.length; bIndex++) {
- const bInst = sameSizeSet[bIndex];
- if (implicit) bInst[memPos].flags.mem = true;
- if (!implicit)
- DEBUG(`${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(", ")}]`;
- }
- }
- class InstSignatureTable extends core.Task {
- constructor() {
- super("InstSignatureTable");
- this.maxOpRows = 0;
- this.opBlackList = {
- "moff8" : true,
- "moff16": true,
- "moff32": true,
- "moff64": true
- };
- }
- run() {
- const insts = this.ctx.insts;
- insts.forEach((inst) => {
- inst.signatures = this.makeSignatures(Filter.noAltForm(inst.dbInsts));
- this.maxOpRows = Math.max(this.maxOpRows, inst.signatures.length);
- });
- const iSignatureMap = Object.create(null);
- const iSignatureArr = [];
- const oSignatureMap = Object.create(null);
- const oSignatureArr = [];
- // Must be first to be assigned to zero.
- const oSignatureNone = "ROW(0, 0, 0, 0xFF)";
- oSignatureMap[oSignatureNone] = [0];
- oSignatureArr.push(oSignatureNone);
- function findSignaturesIndex(rows) {
- const len = rows.length;
- if (!len) return 0;
- const indexes = iSignatureMap[rows[0].data];
- if (indexes === undefined) return -1;
- for (var i = 0; i < indexes.length; i++) {
- const index = indexes[i];
- if (index + len > iSignatureArr.length) continue;
- var ok = true;
- for (var j = 0; j < len; j++) {
- if (iSignatureArr[index + j].data !== rows[j].data) {
- ok = false;
- break;
- }
- }
- if (ok)
- return index;
- }
- return -1;
- }
- function indexSignatures(signatures) {
- const result = iSignatureArr.length;
- for (var i = 0; i < signatures.length; i++) {
- const signature = signatures[i];
- const idx = iSignatureArr.length;
- if (!hasOwn.call(iSignatureMap, signature.data))
- iSignatureMap[signature.data] = [];
- iSignatureMap[signature.data].push(idx);
- iSignatureArr.push(signature);
- }
- return result;
- }
- for (var len = this.maxOpRows; len >= 0; len--) {
- insts.forEach((inst) => {
- const signatures = inst.signatures;
- if (signatures.length === len) {
- const signatureEntries = [];
- for (var j = 0; j < len; j++) {
- const signature = signatures[j];
- var signatureEntry = `ROW(${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(oSignatureMap, h)) {
- index = oSignatureArr.length;
- oSignatureMap[h] = index;
- oSignatureArr.push(h);
- }
- else {
- index = oSignatureMap[h];
- }
- signatureEntry += `, ${String(index).padEnd(3)}`;
- x++;
- }
- while (x < 6) {
- signatureEntry += `, ${String(0).padEnd(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);
- iSignatureArr[index].refs++;
- inst.signatureIndex = index;
- inst.signatureCount = count;
- }
- });
- }
- var s = `#define ROW(count, x86, x64, implicit, o0, o1, o2, o3, o4, o5) \\\n` +
- ` { count, (x86 ? uint8_t(InstDB::kModeX86) : uint8_t(0)) | \\\n` +
- ` (x64 ? uint8_t(InstDB::kModeX64) : uint8_t(0)) , \\\n` +
- ` implicit, \\\n` +
- ` 0, \\\n` +
- ` { o0, o1, o2, o3, o4, o5 } \\\n` +
- ` }\n` +
- StringUtils.makeCxxArrayWithComment(iSignatureArr, "const InstDB::InstSignature InstDB::_instSignatureTable[]") +
- `#undef ROW\n` +
- `\n` +
- `#define ROW(flags, mFlags, extFlags, regId) { uint32_t(flags), uint16_t(mFlags), uint8_t(extFlags), uint8_t(regId) }\n` +
- `#define F(VAL) InstDB::kOp##VAL\n` +
- `#define M(VAL) InstDB::kMemOp##VAL\n` +
- StringUtils.makeCxxArray(oSignatureArr, "const InstDB::OpSignature InstDB::_opSignatureTable[]") +
- `#undef M\n` +
- `#undef F\n` +
- `#undef ROW\n`;
- this.inject("InstSignatureTable", disclaimer(s), oSignatureArr.length * 8 + iSignatureArr.length * 8);
- }
- makeSignatures(dbInsts) {
- const signatures = new SignatureArray();
- for (var i = 0; i < dbInsts.length; i++) {
- const inst = dbInsts[i];
- const ops = inst.operands;
- // NOTE: This changed from having reg|mem merged into creating two signatures
- // instead. Imagine two instructions in one `dbInsts` array:
- //
- // 1. mov reg, reg/mem
- // 2. mov reg/mem, reg
- //
- // If we merge them and then unmerge, we will have 4 signatures, when iterated:
- //
- // 1a. mov reg, reg
- // 1b. mov reg, mem
- // 2a. mov reg, reg
- // 2b. mov mem, reg
- //
- // So, instead of merging them here, we insert separated signatures and let
- // the tool merge them in a way that can be easily unmerged at runtime into:
- //
- // 1a. mov reg, reg
- // 1b. mov reg, mem
- // 2b. mov mem, reg
- var modrmCount = 1;
- for (var modrm = 0; modrm < modrmCount; modrm++) {
- 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`,
- // which are handled fine by asmjit.
- if (this.opBlackList[mem] === true || iop.immValue !== null)
- break;
- if (reg === "r8") reg = "r8lo";
- if (reg === "seg") reg = "sreg";
- if (reg === "st(i)") reg = "st";
- if (reg === "st(0)") reg = "st0";
- 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";
- if (reg && mem) {
- if (modrmCount === 1) {
- mem = null;
- modrmCount++;
- }
- else {
- reg = null;
- }
- }
- const op = new OSignature();
- if (iop.implicit) {
- row.implicit++;
- op.flags.implicit = true;
- }
- const seg = iop.memSeg;
- if (seg) {
- if (seg === "ds") op.flags.memDS = true;
- if (seg === "es") op.flags.memES = true;
- if (reg === "reg") { op.flags.memBase = true; }
- if (reg === "r32") { op.flags.memBase = true; }
- if (reg === "r64") { op.flags.memBase = true; }
- if (reg === "zax") { op.flags.memBase = true; op.flags.memZAX = true; }
- if (reg === "zsi") { op.flags.memBase = true; op.flags.memZSI = true; }
- if (reg === "zdi") { op.flags.memBase = true; 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 use 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);
- }
- // Not equal if we terminated the loop.
- if (j === ops.length)
- signatures.push(row);
- }
- }
- signatures.calcImplicitMemSize();
- signatures.simplify();
- signatures.compact();
- signatures.simplify();
- signatures.compact();
- return signatures;
- }
- }
- // ============================================================================
- // [tablegen.x86.InstCommonInfoTableB]
- // ============================================================================
- class InstCommonInfoTableB extends core.Task {
- constructor() {
- super("InstCommonInfoTableB");
- }
- run() {
- const insts = this.ctx.insts;
- const commonTableB = new IndexedArray();
- const rwInfoTable = new IndexedArray();
- // If the instruction doesn't read any flags it should point to the first index.
- rwInfoTable.addIndexed(`{ 0, 0 }`);
- insts.forEach((inst) => {
- const dbInsts = inst.dbInsts;
- var features = GenUtils.cpuFeaturesOf(dbInsts).map(function(f) { return `EXT(${f})`; }).join(", ");
- if (!features) features = "0";
- var [r, w] = this.rwFlagsOf(dbInsts);
- const rData = r.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0";
- const wData = w.map(function(flag) { return `FLAG(${flag})`; }).join(" | ") || "0";
- const rwDataIndex = rwInfoTable.addIndexed(`{ ${rData}, ${wData} }`);
- inst.commomInfoIndexB = commonTableB.addIndexed(`{ { ${features} }, ${rwDataIndex}, 0 }`);
- });
- var s = `#define EXT(VAL) uint32_t(Features::k##VAL)\n` +
- `const InstDB::CommonInfoTableB InstDB::_commonInfoTableB[] = {\n${StringUtils.format(commonTableB, kIndent, true)}\n};\n` +
- `#undef EXT\n` +
- `\n` +
- `#define FLAG(VAL) uint32_t(Status::k##VAL)\n` +
- `const InstDB::RWFlagsInfoTable InstDB::_rwFlagsInfoTable[] = {\n${StringUtils.format(rwInfoTable, kIndent, true)}\n};\n` +
- `#undef FLAG\n`;
- this.inject("InstCommonInfoTableB", disclaimer(s), commonTableB.length * 8 + rwInfoTable.length * 8);
- }
- rwFlagsOf(dbInsts) {
- const r = Object.create(null);
- const w = Object.create(null);
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- // Omit special cases, this is handled well in C++ code.
- if (dbInst.name === "mov")
- continue;
- const specialRegs = dbInst.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 (dbInst.name === "mov")
- continue;
- for (var specialReg in specialRegs) {
- var flag = "";
- switch (specialReg) {
- case "FLAGS.CF": flag = "CF"; break;
- case "FLAGS.OF": flag = "OF"; break;
- case "FLAGS.SF": flag = "SF"; break;
- case "FLAGS.ZF": flag = "ZF"; break;
- case "FLAGS.AF": flag = "AF"; break;
- case "FLAGS.PF": flag = "PF"; break;
- case "FLAGS.DF": flag = "DF"; break;
- case "FLAGS.IF": flag = "IF"; break;
- //case "FLAGS.TF": flag = "TF"; break;
- case "FLAGS.AC": flag = "AC"; break;
- case "X86SW.C0": flag = "C0"; break;
- case "X86SW.C1": flag = "C1"; break;
- case "X86SW.C2": flag = "C2"; break;
- case "X86SW.C3": flag = "C3"; break;
- default:
- continue;
- }
- switch (specialRegs[specialReg]) {
- case "R":
- r[flag] = true;
- break;
- case "X":
- r[flag] = true;
- // ... fallthrough ...
- case "W":
- case "U":
- case "0":
- case "1":
- w[flag] = true;
- break;
- }
- }
- }
- return [ArrayUtils.sorted(r), ArrayUtils.sorted(w)];
- }
- }
- // ============================================================================
- // [tablegen.x86.InstRWInfoTable]
- // ============================================================================
- const NOT_MEM_AMBIGUOUS = MapUtils.arrayToMap([
- "call", "movq"
- ]);
- class InstRWInfoTable extends core.Task {
- constructor() {
- super("InstRWInfoTable");
- this.rwInfoIndex = [];
- this.rwInfoTable = new IndexedArray();
- this.rmInfoTable = new IndexedArray();
- this.opInfoTable = new IndexedArray();
- const _ = null;
- this.rwCategoryByName = {
- "imul" : "Imul",
- "mov" : "Mov",
- "movhpd" : "Movh64",
- "movhps" : "Movh64",
- "vmaskmovpd": "Vmaskmov",
- "vmaskmovps": "Vmaskmov",
- "vmovddup" : "Vmovddup",
- "vmovmskpd" : "Vmovmskpd",
- "vmovmskps" : "Vmovmskps",
- "vpmaskmovd": "Vmaskmov",
- "vpmaskmovq": "Vmaskmov"
- };
- this.rwCategoryByData = {
- Vmov1_8: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 8}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 16}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
- ],
- Vmov1_4: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 32}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width:128}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
- ],
- Vmov1_2: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 64}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width:128}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width:256}, {access: "R", flags: {}, fixed: -1, index: 0, width:512},_,_,_,_]
- ],
- Vmov2_1: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width:256},_,_,_,_]
- ],
- Vmov4_1: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width:128},_,_,_,_]
- ],
- Vmov8_1: [
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 128}, {access: "R", flags: {}, fixed: -1, index: 0, width: 16},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 256}, {access: "R", flags: {}, fixed: -1, index: 0, width: 32},_,_,_,_],
- [{access: "W", flags: {}, fixed: -1, index: 0, width: 512}, {access: "R", flags: {}, fixed: -1, index: 0, width: 64},_,_,_,_]
- ]
- };
- }
- run() {
- const insts = this.ctx.insts;
- const noRmInfo = CxxUtils.struct(
- "InstDB::RWInfoRm::kCategory" + "None".padEnd(10),
- StringUtils.decToHex(0, 2),
- String(0).padEnd(2),
- CxxUtils.flags({}),
- "0"
- );
- const noOpInfo = CxxUtils.struct(
- "0x0000000000000000u",
- "0x0000000000000000u",
- "0xFF",
- CxxUtils.struct(0),
- "0"
- );
- this.rmInfoTable.addIndexed(noRmInfo);
- this.opInfoTable.addIndexed(noOpInfo);
- insts.forEach((inst) => {
- // Alternate forms would only mess this up, so filter them out.
- const dbInsts = Filter.noAltForm(inst.dbInsts);
- // The best we can do is to divide instructions that have 2 operands and others.
- // This gives us the highest chance of preventing special cases (which were not
- // entirely avoided).
- const o2Insts = dbInsts.filter((inst) => { return inst.operands.length === 2; });
- const oxInsts = dbInsts.filter((inst) => { return inst.operands.length !== 2; });
- const rwInfoArray = [this.rwInfo(o2Insts), this.rwInfo(oxInsts)];
- const rmInfoArray = [this.rmInfo(o2Insts), this.rmInfo(oxInsts)];
- for (var i = 0; i < 2; i++) {
- const rwInfo = rwInfoArray[i];
- const rmInfo = rmInfoArray[i];
- const rwOps = rwInfo.rwOps;
- const rwOpsIndex = [];
- for (var j = 0; j < rwOps.length; j++) {
- const op = rwOps[j];
- if (!op) {
- rwOpsIndex.push(this.opInfoTable.addIndexed(noOpInfo));
- continue;
- }
- const flags = {};
- const opAcc = op.access;
- if (opAcc === "R") flags.Read = true;
- if (opAcc === "W") flags.Write = true;
- if (opAcc === "X") flags.RW = true;
- Lang.merge(flags, op.flags);
- const rIndex = opAcc === "X" || opAcc === "R" ? op.index : -1;
- const rWidth = opAcc === "X" || opAcc === "R" ? op.width : -1;
- const wIndex = opAcc === "X" || opAcc === "W" ? op.index : -1;
- const wWidth = opAcc === "X" || opAcc === "W" ? op.width : -1;
- const opData = CxxUtils.struct(
- this.byteMaskFromBitRanges([{ start: rIndex, end: rIndex + rWidth - 1 }]) + "u",
- this.byteMaskFromBitRanges([{ start: wIndex, end: wIndex + wWidth - 1 }]) + "u",
- StringUtils.decToHex(op.fixed === -1 ? 0xFF : op.fixed, 2),
- CxxUtils.struct(0),
- CxxUtils.flags(flags, function(flag) { return "OpRWInfo::k" + flag; })
- );
- rwOpsIndex.push(this.opInfoTable.addIndexed(opData));
- }
- const rmData = CxxUtils.struct(
- "InstDB::RWInfoRm::kCategory" + rmInfo.category.padEnd(10),
- StringUtils.decToHex(rmInfo.rmIndexes, 2),
- String(Math.max(rmInfo.memFixed, 0)).padEnd(2),
- CxxUtils.flags({ "InstDB::RWInfoRm::kFlagAmbiguous": Boolean(rmInfo.memAmbiguous) }),
- rmInfo.memExtension === "None" ? "0" : "Features::k" + rmInfo.memExtension
- );
- const rwData = CxxUtils.struct(
- "InstDB::RWInfo::kCategory" + rwInfo.category.padEnd(10),
- String(this.rmInfoTable.addIndexed(rmData)).padEnd(2),
- CxxUtils.struct(...(rwOpsIndex.map(function(item) { return String(item).padEnd(2); })))
- );
- this.rwInfoIndex.push(this.rwInfoTable.addIndexed(rwData));
- }
- });
- var s = "";
- s += "const uint8_t InstDB::rwInfoIndex[Inst::_kIdCount * 2] = {\n" + StringUtils.format(this.rwInfoIndex, kIndent, -1) + "\n};\n";
- s += "\n";
- s += "const InstDB::RWInfo InstDB::rwInfo[] = {\n" + StringUtils.format(this.rwInfoTable, kIndent, true) + "\n};\n";
- s += "\n";
- s += "const InstDB::RWInfoOp InstDB::rwInfoOp[] = {\n" + StringUtils.format(this.opInfoTable, kIndent, true) + "\n};\n";
- s += "\n";
- s += "const InstDB::RWInfoRm InstDB::rwInfoRm[] = {\n" + StringUtils.format(this.rmInfoTable, kIndent, true) + "\n};\n";
- const size = this.rwInfoIndex.length +
- this.rwInfoTable.length * 8 +
- this.rmInfoTable.length * 4 +
- this.opInfoTable.length * 24;
- this.inject("InstRWInfoTable", disclaimer(s), size);
- }
- byteMaskFromBitRanges(ranges) {
- const arr = [];
- for (var i = 0; i < 64; i++)
- arr.push(0);
- for (var i = 0; i < ranges.length; i++) {
- const start = ranges[i].start;
- const end = ranges[i].end;
- if (start < 0)
- continue;
- for (var j = start; j <= end; j++) {
- const bytePos = j >> 3;
- if (bytePos < 0 || bytePos >= arr.length)
- FAIL(`Range ${start}:${end} cannot be used to create a byte-mask`);
- arr[bytePos] = 1;
- }
- }
- var s = "0x";
- for (var i = arr.length - 4; i >= 0; i -= 4) {
- const value = (arr[i + 3] << 3) | (arr[i + 2] << 2) | (arr[i + 1] << 1) | arr[i];
- s += value.toString(16).toUpperCase();
- }
- return s;
- }
- // Read/Write Info
- // ---------------
- rwInfo(dbInsts) {
- function nullOps() {
- return [null, null, null, null, null, null];
- }
- function makeRwFromOp(op) {
- if (!op.isRegOrMem())
- return null;
- return {
- access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?",
- flags: {},
- fixed: GenUtils.fixedRegOf(op.reg),
- index: op.rwxIndex,
- width: op.rwxWidth
- };
- }
- function queryRwGeneric(dbInsts, step) {
- var rwOps = nullOps();
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- for (var j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (!op.isRegOrMem())
- continue;
- const opSize = op.isReg() ? op.regSize : op.memSize;
- var d = {
- access: op.read && op.write ? "X" : op.read ? "R" : op.write ? "W" : "?",
- flags: {},
- fixed: -1,
- index: -1,
- width: -1
- };
- if (op.isReg())
- d.fixed = GenUtils.fixedRegOf(op.reg);
- else
- d.fixed = GenUtils.fixedRegOf(op.mem);
- if (op.zext)
- d.flags.ZExt = true;
- if ((step === -1 || step === j) || op.rwxIndex !== 0 || op.rwxWidth !== opSize) {
- d.index = op.rwxIndex;
- d.width = op.rwxWidth;
- }
- if (d.fixed !== -1) {
- if (op.memSeg)
- d.flags.MemPhysId = true;
- else
- d.flags.RegPhysId = true;
- }
- if (rwOps[j] === null) {
- rwOps[j] = d;
- }
- else {
- if (!Lang.deepEqExcept(rwOps[j], d, { "fixed": true, "flags": true }))
- return null;
- if (rwOps[j].fixed === -1)
- rwOps[j].fixed = d.fixed;
- Lang.merge(rwOps[j].flags, d.flags);
- }
- }
- }
- return { category: "Generic", rwOps };
- }
- function queryRwByData(dbInsts, rwOpsArray) {
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- const rwOps = nullOps();
- for (var j = 0; j < operands.length; j++)
- rwOps[j] = makeRwFromOp(operands[j])
- var match = 0;
- for (var j = 0; j < rwOpsArray.length; j++)
- match |= Lang.deepEq(rwOps, rwOpsArray[j]);
- if (!match)
- return false;
- }
- return true;
- }
- function dumpRwToData(dbInsts) {
- const out = [];
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- const rwOps = nullOps();
- for (var j = 0; j < operands.length; j++)
- rwOps[j] = makeRwFromOp(operands[j])
- if (ArrayUtils.deepIndexOf(out, rwOps) !== -1)
- continue;
- out.push(rwOps);
- }
- return out;
- }
- // Some instructions are just special...
- const name = dbInsts.length ? dbInsts[0].name : "";
- if (name in this.rwCategoryByName)
- return { category: this.rwCategoryByName[name], rwOps: nullOps() };
- // Generic rules.
- for (var i = -1; i <= 6; i++) {
- const rwInfo = queryRwGeneric(dbInsts, i);
- if (rwInfo)
- return rwInfo;
- }
- // Specific rules.
- for (var k in this.rwCategoryByData)
- if (queryRwByData(dbInsts, this.rwCategoryByData[k]))
- return { category: k, rwOps: nullOps() };
- // FAILURE: Missing data to categorize this instruction.
- if (name) {
- const items = dumpRwToData(dbInsts)
- console.log(`RW: ${dbInsts.length ? dbInsts[0].name : ""}:`);
- items.forEach((item) => {
- console.log(" " + JSON.stringify(item));
- });
- }
- return null;
- }
- // Reg/Mem Info
- // ------------
- rmInfo(dbInsts) {
- const info = {
- category: "None",
- rmIndexes: this.rmReplaceableIndexes(dbInsts),
- memFixed: this.rmFixedSize(dbInsts),
- memAmbiguous: this.rmIsAmbiguous(dbInsts),
- memConsistent: this.rmIsConsistent(dbInsts),
- memExtension: this.rmExtension(dbInsts)
- };
- if (info.memFixed !== -1)
- info.category = "Fixed";
- else if (info.memConsistent)
- info.category = "Consistent";
- else if (info.rmIndexes)
- info.category = this.rmReplaceableCategory(dbInsts);
- return info;
- }
- rmReplaceableCategory(dbInsts) {
- var category = null;
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- var rs = -1;
- var ms = -1;
- for (var j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (op.isMem())
- ms = op.memSize;
- else if (op.isReg())
- rs = Math.max(rs, op.regSize);
- }
- var c = (rs === -1 ) ? "None" :
- (ms === -1 ) ? "None" :
- (ms === rs ) ? "Fixed" :
- (ms === rs / 2) ? "Half" :
- (ms === rs / 4) ? "Quarter" :
- (ms === rs / 8) ? "Eighth" : "Unknown";
- if (category === null)
- category = c;
- else if (category !== c) {
- if (dbInst.name === "mov" || dbInst.name === "vmovddup")
- return "None"; // Special case
- return StringUtils.capitalize(dbInst.name); // Special case.
- }
- }
- if (category === "Unknown")
- console.log(`Instruction '${dbInsts[0].name}' has no RMInfo category.`);
- return category || "Unknown";
- }
- rmReplaceableIndexes(dbInsts) {
- function maskOf(inst, fn) {
- var m = 0;
- var operands = inst.operands;
- for (var i = 0; i < operands.length; i++)
- if (fn(operands[i]))
- m |= (1 << i);
- return m;
- }
- function getRegIndexes(inst) { return maskOf(inst, function(op) { return op.isReg(); }); };
- function getMemIndexes(inst) { return maskOf(inst, function(op) { return op.isMem(); }); };
- var mask = 0;
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- var mi = getMemIndexes(dbInst);
- var ri = getRegIndexes(dbInst) & ~mi;
- if (!mi)
- continue;
- const match = dbInsts.some((inst) => {
- var ti = getRegIndexes(inst);
- return ((ri & ti) === ri && (mi & ti) === mi);
- });
- if (!match)
- return 0;
- mask |= mi;
- }
- return mask;
- }
- rmFixedSize(insts) {
- var savedOp = null;
- for (var i = 0; i < insts.length; i++) {
- const inst = insts[i];
- const operands = inst.operands;
- for (var j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (op.mem) {
- if (savedOp && savedOp.mem !== op.mem)
- return -1;
- savedOp = op;
- }
- }
- }
- return savedOp ? Math.max(savedOp.memSize, 0) / 8 : -1;
- }
- rmIsConsistent(insts) {
- var hasMem = 0;
- for (var i = 0; i < insts.length; i++) {
- const inst = insts[i];
- const operands = inst.operands;
- for (var j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (op.mem) {
- hasMem = 1;
- if (!op.reg)
- return 0;
- if (asmdb.x86.Utils.regSize(op.reg) !== op.memSize)
- return 0;
- }
- }
- }
- return hasMem;
- }
- rmIsAmbiguous(dbInsts) {
- function isAmbiguous(dbInsts) {
- const memMap = {};
- const immMap = {};
- for (var i = 0; i < dbInsts.length; i++) {
- const dbInst = dbInsts[i];
- const operands = dbInst.operands;
- var memStr = "";
- var immStr = "";
- var hasMem = false;
- var hasImm = false;
- for (var j = 0; j < operands.length; j++) {
- const op = operands[j];
- if (j) {
- memStr += ", ";
- immStr += ", ";
- }
- if (op.isImm()) {
- immStr += "imm";
- hasImm = true;
- }
- else {
- immStr += op.toString();
- }
- if (op.mem) {
- memStr += "m";
- hasMem = true;
- }
- else {
- memStr += op.isImm() ? "imm" : op.toString();
- }
- }
- if (hasImm) {
- if (immMap[immStr] === true)
- continue;
- immMap[immStr] = true;
- }
- if (hasMem) {
- if (memMap[memStr] === true)
- return 1;
- memMap[memStr] = true;
- }
- }
- return 0;
- }
- const uniqueInsts = Filter.unique(dbInsts);
- // Special cases.
- if (!dbInsts.length)
- return 0;
- if (NOT_MEM_AMBIGUOUS[dbInsts[0].name])
- return 0;
- return (isAmbiguous(Filter.byArch(uniqueInsts, "X86")) << 0) |
- (isAmbiguous(Filter.byArch(uniqueInsts, "X64")) << 1) ;
- }
- rmExtension(dbInsts) {
- if (!dbInsts.length)
- return "None";
- const name = dbInsts[0].name;
- switch (name) {
- case "pextrw":
- return "SSE4_1";
- case "vpslldq":
- case "vpsrldq":
- return "AVX512_BW";
- default:
- return "None";
- }
- }
- }
- // ============================================================================
- // [tablegen.x86.InstCommonTable]
- // ============================================================================
- class InstCommonTable extends core.Task {
- constructor() {
- super("InstCommonTable", [
- "IdEnum",
- "NameTable",
- "InstSignatureTable",
- "InstCommonInfoTableB",
- "InstRWInfoTable"
- ]);
- }
- run() {
- const insts = this.ctx.insts;
- const table = new IndexedArray();
- insts.forEach((inst) => {
- const flags = inst.flags.map(function(flag) { return `F(${flag})`; }).join("|") || "0";
- const singleRegCase = `SINGLE_REG(${inst.singleRegCase})`;
- const controlType = `CONTROL(${inst.controlType})`;
- const row = "{ " +
- String(flags ).padEnd(54) + ", " +
- String(inst.signatureIndex).padEnd( 3) + ", " +
- String(inst.signatureCount).padEnd( 2) + ", " +
- String(controlType ).padEnd(16) + ", " +
- String(singleRegCase ).padEnd(16) + ", " + "0 }";
- inst.commonInfoIndexA = table.addIndexed(row);
- });
- var s = `#define F(VAL) InstDB::kFlag##VAL\n` +
- `#define CONTROL(VAL) Inst::kControl##VAL\n` +
- `#define SINGLE_REG(VAL) InstDB::kSingleReg##VAL\n` +
- `const InstDB::CommonInfo InstDB::_commonInfoTable[] = {\n${StringUtils.format(table, kIndent, true)}\n};\n` +
- `#undef SINGLE_REG\n` +
- `#undef CONTROL\n` +
- `#undef F\n`;
- this.inject("InstCommonTable", disclaimer(s), table.length * 8);
- }
- }
- // ============================================================================
- // [Main]
- // ============================================================================
- new X86TableGen()
- .addTask(new IdEnum())
- .addTask(new NameTable())
- .addTask(new AltOpcodeTable())
- .addTask(new InstSignatureTable())
- .addTask(new InstCommonInfoTableB())
- .addTask(new InstRWInfoTable())
- .addTask(new InstCommonTable())
- .run();
|