.
+ if opt ~= 3 and opt ~= 7 then
+ last = map_regs.w[band(rshift(op, 16), 31)]
+ operands[#operands] = last
+ end
+ x = band(rshift(op, 10), 7)
+ -- Extension.
+ if opt == 2 + band(rshift(op, 31), 1) and
+ band(rshift(op, second0 and 5 or 0), 31) == 31 then
+ if x == 0 then x = nil
+ else x = "lsl #"..x end
+ else
+ if x == 0 then x = map_extend[band(rshift(op, 13), 7)]
+ else x = map_extend[band(rshift(op, 13), 7)].." #"..x end
+ end
+ elseif p == "R" then
+ x = band(rshift(op,21), 3)
+ if x == 0 then x = nil
+ else x = "lsl #"..x*16 end
+ elseif p == "z" then
+ local n = #operands
+ if operands[n] == "sp" then operands[n] = "xzr"
+ elseif operands[n] == "wsp" then operands[n] = "wzr"
+ end
+ elseif p == "Z" then
+ x = 0
+ elseif p == "F" then
+ x = parse_fpimm8(op)
+ elseif p == "G" then
+ x = "#0x"..decode_fpmovi(op)
+ elseif p == "g" or p == "f" or p == "x" or p == "w" or
+ p == "d" or p == "s" then
+ -- These are handled in D/N/M/A.
+ elseif p == "0" then
+ if last == "sp" or last == "wsp" then
+ local n = #operands
+ operands[n] = nil
+ last = operands[n-1]
+ if altname then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if not a1 then
+ name = altname
+ elseif second0 then
+ name, altname = a2, a1
+ else
+ name, altname = a1, a2
+ end
+ end
+ end
+ second0 = true
+ elseif p == " " then
+ operands[#operands+1] = pat:match(" (.*)")
+ break
+ else
+ assert(false)
+ end
+ if x then
+ last = x
+ if type(x) == "number" then x = "#"..x end
+ operands[#operands+1] = x
+ end
+ end
+
+ return putop(ctx, name..suffix, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ ctx.pos = ofs
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_regs.x[r] end
+ return map_regs.d[r-32]
+end
+
+-- Public module functions.
+return {
+ create = create,
+ disass = disass,
+ regname = regname
+}
+
diff --git a/jit/dis_arm64be.lua b/jit/dis_arm64be.lua
new file mode 100644
index 0000000..e62a28c
--- /dev/null
+++ b/jit/dis_arm64be.lua
@@ -0,0 +1,12 @@
+----------------------------------------------------------------------------
+-- LuaJIT ARM64BE disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- ARM64 instructions are always little-endian. So just forward to the
+-- common ARM64 disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+return require((string.match(..., ".*%.") or "").."dis_arm64")
+
diff --git a/jit/dis_mips.lua b/jit/dis_mips.lua
new file mode 100644
index 0000000..fece893
--- /dev/null
+++ b/jit/dis_mips.lua
@@ -0,0 +1,694 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT/X license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles all standard MIPS32R1/R2 instructions.
+-- Default mode is big-endian, but see: dis_mipsel.lua
+------------------------------------------------------------------------------
+
+local type = type
+local byte, format = string.byte, string.format
+local match, gmatch = string.match, string.gmatch
+local concat = table.concat
+local bit = require("bit")
+local band, bor, tohex = bit.band, bit.bor, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+
+------------------------------------------------------------------------------
+-- Extended opcode maps common to all MIPS releases
+------------------------------------------------------------------------------
+
+local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
+local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
+
+local map_cop0 = {
+ shift = 25, mask = 1,
+ [0] = {
+ shift = 21, mask = 15,
+ [0] = "mfc0TDW", [4] = "mtc0TDW",
+ [10] = "rdpgprDT",
+ [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
+ [14] = "wrpgprDT",
+ }, {
+ shift = 0, mask = 63,
+ [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
+ [24] = "eret", [31] = "deret",
+ [32] = "wait",
+ },
+}
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps for MIPS R1-R5
+------------------------------------------------------------------------------
+
+local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
+
+local map_special = {
+ shift = 0, mask = 63,
+ [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
+ map_movci, map_srl, "sraDTA",
+ "sllvDTS", false, map_srlv, "sravDTS",
+ "jrS", "jalrD1S", "movzDST", "movnDST",
+ "syscallY", "breakY", false, "sync",
+ "mfhiD", "mthiS", "mfloD", "mtloS",
+ "dsllvDST", false, "dsrlvDST", "dsravDST",
+ "multST", "multuST", "divST", "divuST",
+ "dmultST", "dmultuST", "ddivST", "ddivuST",
+ "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
+ "andDST", "or|moveDST0", "xorDST", "nor|notDST0",
+ false, false, "sltDST", "sltuDST",
+ "daddDST", "dadduDST", "dsubDST", "dsubuDST",
+ "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
+ "teqSTZ", false, "tneSTZ", false,
+ "dsllDTA", false, "dsrlDTA", "dsraDTA",
+ "dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
+}
+
+local map_special2 = {
+ shift = 0, mask = 63,
+ [0] = "maddST", "madduST", "mulDST", false,
+ "msubST", "msubuST",
+ [32] = "clzDS", [33] = "cloDS",
+ [63] = "sdbbpY",
+}
+
+local map_bshfl = {
+ shift = 6, mask = 31,
+ [2] = "wsbhDT",
+ [16] = "sebDT",
+ [24] = "sehDT",
+}
+
+local map_dbshfl = {
+ shift = 6, mask = 31,
+ [2] = "dsbhDT",
+ [5] = "dshdDT",
+}
+
+local map_special3 = {
+ shift = 0, mask = 63,
+ [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
+ [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
+ [32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD",
+}
+
+local map_regimm = {
+ shift = 16, mask = 31,
+ [0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
+ false, false, false, false,
+ "tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
+ "teqiSI", false, "tneiSI", false,
+ "bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, "synciSO",
+}
+
+local map_cop1s = {
+ shift = 0, mask = 63,
+ [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
+ "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
+ "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
+ "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
+ false,
+ { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
+ "movz.sFGT", "movn.sFGT",
+ false, "recip.sFG", "rsqrt.sFG", false,
+ false, false, false, false,
+ false, false, false, false,
+ false, "cvt.d.sFG", false, false,
+ "cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
+ false, false, false, false,
+ false, false, false, false,
+ "c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
+ "c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
+ "c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
+ "c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
+}
+
+local map_cop1d = {
+ shift = 0, mask = 63,
+ [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
+ "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
+ "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
+ "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
+ false,
+ { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
+ "movz.dFGT", "movn.dFGT",
+ false, "recip.dFG", "rsqrt.dFG", false,
+ false, false, false, false,
+ false, false, false, false,
+ "cvt.s.dFG", false, false, false,
+ "cvt.w.dFG", "cvt.l.dFG", false, false,
+ false, false, false, false,
+ false, false, false, false,
+ "c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
+ "c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
+ "c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
+ "c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
+}
+
+local map_cop1ps = {
+ shift = 0, mask = 63,
+ [0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
+ false, "abs.psFG", "mov.psFG", "neg.psFG",
+ false, false, false, false,
+ false, false, false, false,
+ false,
+ { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
+ "movz.psFGT", "movn.psFGT",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, false,
+ "cvt.s.puFG", false, false, false,
+ false, false, false, false,
+ "cvt.s.plFG", false, false, false,
+ "pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
+ "c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
+ "c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
+ "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
+ "c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
+}
+
+local map_cop1w = {
+ shift = 0, mask = 63,
+ [32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
+}
+
+local map_cop1l = {
+ shift = 0, mask = 63,
+ [32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
+}
+
+local map_cop1bc = {
+ shift = 16, mask = 3,
+ [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
+}
+
+local map_cop1 = {
+ shift = 21, mask = 31,
+ [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
+ "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
+ map_cop1bc, false, false, false,
+ false, false, false, false,
+ map_cop1s, map_cop1d, false, false,
+ map_cop1w, map_cop1l, map_cop1ps,
+}
+
+local map_cop1x = {
+ shift = 0, mask = 63,
+ [0] = "lwxc1FSX", "ldxc1FSX", false, false,
+ false, "luxc1FSX", false, false,
+ "swxc1FSX", "sdxc1FSX", false, false,
+ false, "suxc1FSX", false, "prefxMSX",
+ false, false, false, false,
+ false, false, false, false,
+ false, false, false, false,
+ false, false, "alnv.psFGHS", false,
+ "madd.sFRGH", "madd.dFRGH", false, false,
+ false, false, "madd.psFRGH", false,
+ "msub.sFRGH", "msub.dFRGH", false, false,
+ false, false, "msub.psFRGH", false,
+ "nmadd.sFRGH", "nmadd.dFRGH", false, false,
+ false, false, "nmadd.psFRGH", false,
+ "nmsub.sFRGH", "nmsub.dFRGH", false, false,
+ false, false, "nmsub.psFRGH", false,
+}
+
+local map_pri = {
+ [0] = map_special, map_regimm, "jJ", "jalJ",
+ "beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
+ "addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
+ "andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
+ map_cop0, map_cop1, false, map_cop1x,
+ "beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
+ "daddiTSI", "daddiuTSI", false, false,
+ map_special2, "jalxJ", false, map_special3,
+ "lbTSO", "lhTSO", "lwlTSO", "lwTSO",
+ "lbuTSO", "lhuTSO", "lwrTSO", false,
+ "sbTSO", "shTSO", "swlTSO", "swTSO",
+ false, false, "swrTSO", "cacheNSO",
+ "llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
+ false, "ldc1HSO", "ldc2TSO", "ldTSO",
+ "scTSO", "swc1HSO", "swc2TSO", false,
+ false, "sdc1HSO", "sdc2TSO", "sdTSO",
+}
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps for MIPS R6
+------------------------------------------------------------------------------
+
+local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" }
+local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" }
+local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" }
+local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" }
+local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" }
+local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" }
+local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" }
+local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" }
+
+local map_special_r6 = {
+ shift = 0, mask = 63,
+ [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
+ false, map_srl, "sraDTA",
+ "sllvDTS", false, map_srlv, "sravDTS",
+ "jrS", "jalrD1S", false, false,
+ "syscallY", "breakY", false, "sync",
+ "clzDS", "cloDS", "dclzDS", "dcloDS",
+ "dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST",
+ map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6,
+ map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6,
+ "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
+ "andDST", "or|moveDST0", "xorDST", "nor|notDST0",
+ false, false, "sltDST", "sltuDST",
+ "daddDST", "dadduDST", "dsubDST", "dsubuDST",
+ "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
+ "teqSTZ", "seleqzDST", "tneSTZ", "selnezDST",
+ "dsllDTA", false, "dsrlDTA", "dsraDTA",
+ "dsll32DTA", false, "dsrl32DTA", "dsra32DTA",
+}
+
+local map_bshfl_r6 = {
+ shift = 9, mask = 3,
+ [1] = "alignDSTa",
+ _ = {
+ shift = 6, mask = 31,
+ [0] = "bitswapDT",
+ [2] = "wsbhDT",
+ [16] = "sebDT",
+ [24] = "sehDT",
+ }
+}
+
+local map_dbshfl_r6 = {
+ shift = 9, mask = 3,
+ [1] = "dalignDSTa",
+ _ = {
+ shift = 6, mask = 31,
+ [0] = "dbitswapDT",
+ [2] = "dsbhDT",
+ [5] = "dshdDT",
+ }
+}
+
+local map_special3_r6 = {
+ shift = 0, mask = 63,
+ [0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK",
+ [4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL",
+ [32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD",
+}
+
+local map_regimm_r6 = {
+ shift = 16, mask = 31,
+ [0] = "bltzSB", [1] = "bgezSB",
+ [6] = "dahiSI", [30] = "datiSI",
+ [23] = "sigrieI", [31] = "synciSO",
+}
+
+local map_pcrel_r6 = {
+ shift = 19, mask = 3,
+ [0] = "addiupcS2", "lwpcS2", "lwupcS2", {
+ shift = 18, mask = 1,
+ [0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" }
+ }
+}
+
+local map_cop1s_r6 = {
+ shift = 0, mask = 63,
+ [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
+ "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
+ "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
+ "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
+ "sel.sFGH", false, false, false,
+ "seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH",
+ "maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG",
+ "min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH",
+ false, "cvt.d.sFG", false, false,
+ "cvt.w.sFG", "cvt.l.sFG",
+}
+
+local map_cop1d_r6 = {
+ shift = 0, mask = 63,
+ [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
+ "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
+ "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
+ "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
+ "sel.dFGH", false, false, false,
+ "seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH",
+ "maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG",
+ "min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH",
+ "cvt.s.dFG", false, false, false,
+ "cvt.w.dFG", "cvt.l.dFG",
+}
+
+local map_cop1w_r6 = {
+ shift = 0, mask = 63,
+ [0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH",
+ "cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH",
+ "cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH",
+ "cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH",
+ false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH",
+ false, false, false, false,
+ false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH",
+ false, false, false, false,
+ "cvt.s.wFG", "cvt.d.wFG",
+}
+
+local map_cop1l_r6 = {
+ shift = 0, mask = 63,
+ [0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH",
+ "cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH",
+ "cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH",
+ "cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH",
+ false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH",
+ false, false, false, false,
+ false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH",
+ false, false, false, false,
+ "cvt.s.lFG", "cvt.d.lFG",
+}
+
+local map_cop1_r6 = {
+ shift = 21, mask = 31,
+ [0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG",
+ "mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG",
+ false, "bc1eqzHB", false, false,
+ false, "bc1nezHB", false, false,
+ map_cop1s_r6, map_cop1d_r6, false, false,
+ map_cop1w_r6, map_cop1l_r6,
+}
+
+local function maprs_popTS(rs, rt)
+ if rt == 0 then return 0 elseif rs == 0 then return 1
+ elseif rs == rt then return 2 else return 3 end
+end
+
+local map_pop06_r6 = {
+ maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB"
+}
+local map_pop07_r6 = {
+ maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB"
+}
+local map_pop26_r6 = {
+ maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB"
+}
+local map_pop27_r6 = {
+ maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB"
+}
+
+local function maprs_popS(rs, rt)
+ if rs == 0 then return 0 else return 1 end
+end
+
+local map_pop66_r6 = {
+ maprs = maprs_popS, [0] = "jicTI", "beqzcSb"
+}
+local map_pop76_r6 = {
+ maprs = maprs_popS, [0] = "jialcTI", "bnezcSb"
+}
+
+local function maprs_popST(rs, rt)
+ if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end
+end
+
+local map_pop10_r6 = {
+ maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB"
+}
+local map_pop30_r6 = {
+ maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB"
+}
+
+local map_pri_r6 = {
+ [0] = map_special_r6, map_regimm_r6, "jJ", "jalJ",
+ "beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6,
+ map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI",
+ "andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U",
+ map_cop0, map_cop1_r6, false, false,
+ false, false, map_pop26_r6, map_pop27_r6,
+ map_pop30_r6, "daddiuTSI", false, false,
+ false, "dauiTSI", false, map_special3_r6,
+ "lbTSO", "lhTSO", false, "lwTSO",
+ "lbuTSO", "lhuTSO", false, false,
+ "sbTSO", "shTSO", false, "swTSO",
+ false, false, false, false,
+ false, "lwc1HSO", "bc#", false,
+ false, "ldc1HSO", map_pop66_r6, "ldTSO",
+ false, "swc1HSO", "balc#", map_pcrel_r6,
+ false, "sdc1HSO", map_pop76_r6, "sdTSO",
+}
+
+------------------------------------------------------------------------------
+
+local map_gpr = {
+ [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
+}
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then extra = "\t->"..sym end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-7s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-7s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+local function get_be(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
+end
+
+local function get_le(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local op = ctx:get()
+ local operands = {}
+ local last = nil
+ ctx.op = op
+ ctx.rel = nil
+
+ local opat = ctx.map_pri[rshift(op, 26)]
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ if opat.maprs then
+ opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))]
+ else
+ opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
+ end
+ end
+ local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
+ local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
+ if altname then pat = pat2 end
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "S" then
+ x = map_gpr[band(rshift(op, 21), 31)]
+ elseif p == "T" then
+ x = map_gpr[band(rshift(op, 16), 31)]
+ elseif p == "D" then
+ x = map_gpr[band(rshift(op, 11), 31)]
+ elseif p == "F" then
+ x = "f"..band(rshift(op, 6), 31)
+ elseif p == "G" then
+ x = "f"..band(rshift(op, 11), 31)
+ elseif p == "H" then
+ x = "f"..band(rshift(op, 16), 31)
+ elseif p == "R" then
+ x = "f"..band(rshift(op, 21), 31)
+ elseif p == "A" then
+ x = band(rshift(op, 6), 31)
+ elseif p == "a" then
+ x = band(rshift(op, 6), 7)
+ elseif p == "E" then
+ x = band(rshift(op, 6), 31) + 32
+ elseif p == "M" then
+ x = band(rshift(op, 11), 31)
+ elseif p == "N" then
+ x = band(rshift(op, 16), 31)
+ elseif p == "C" then
+ x = band(rshift(op, 18), 7)
+ if x == 0 then x = nil end
+ elseif p == "K" then
+ x = band(rshift(op, 11), 31) + 1
+ elseif p == "P" then
+ x = band(rshift(op, 11), 31) + 33
+ elseif p == "L" then
+ x = band(rshift(op, 11), 31) - last + 1
+ elseif p == "Q" then
+ x = band(rshift(op, 11), 31) - last + 33
+ elseif p == "I" then
+ x = arshift(lshift(op, 16), 16)
+ elseif p == "2" then
+ x = arshift(lshift(op, 13), 11)
+ elseif p == "3" then
+ x = arshift(lshift(op, 14), 11)
+ elseif p == "U" then
+ x = band(op, 0xffff)
+ elseif p == "O" then
+ local disp = arshift(lshift(op, 16), 16)
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p == "X" then
+ local index = map_gpr[band(rshift(op, 16), 31)]
+ operands[#operands] = format("%s(%s)", index, last)
+ elseif p == "B" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "b" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "#" then
+ x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "J" then
+ local a = ctx.addr + ctx.pos
+ x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4
+ ctx.rel = x
+ x = format("0x%08x", x)
+ elseif p == "V" then
+ x = band(rshift(op, 8), 7)
+ if x == 0 then x = nil end
+ elseif p == "W" then
+ x = band(op, 7)
+ if x == 0 then x = nil end
+ elseif p == "Y" then
+ x = band(rshift(op, 6), 0x000fffff)
+ if x == 0 then x = nil end
+ elseif p == "Z" then
+ x = band(rshift(op, 6), 1023)
+ if x == 0 then x = nil end
+ elseif p == "0" then
+ if last == "r0" or last == 0 then
+ local n = #operands
+ operands[n] = nil
+ last = operands[n-1]
+ if altname then
+ local a1, a2 = match(altname, "([^|]*)|(.*)")
+ if a1 then name, altname = a1, a2
+ else name = altname end
+ end
+ end
+ elseif p == "1" then
+ if last == "ra" then
+ operands[#operands] = nil
+ end
+ else
+ assert(false)
+ end
+ if x then operands[#operands+1] = x; last = x end
+ end
+
+ return putop(ctx, name, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ stop = stop - stop % 4
+ ctx.pos = ofs - ofs % 4
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ ctx.get = get_be
+ ctx.map_pri = map_pri
+ return ctx
+end
+
+local function create_el(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.get = get_le
+ return ctx
+end
+
+local function create_r6(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.map_pri = map_pri_r6
+ return ctx
+end
+
+local function create_r6_el(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.get = get_le
+ ctx.map_pri = map_pri_r6
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+local function disass_el(code, addr, out)
+ create_el(code, addr, out):disass()
+end
+
+local function disass_r6(code, addr, out)
+ create_r6(code, addr, out):disass()
+end
+
+local function disass_r6_el(code, addr, out)
+ create_r6_el(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_gpr[r] end
+ return "f"..(r-32)
+end
+
+-- Public module functions.
+return {
+ create = create,
+ create_el = create_el,
+ create_r6 = create_r6,
+ create_r6_el = create_r6_el,
+ disass = disass,
+ disass_el = disass_el,
+ disass_r6 = disass_r6,
+ disass_r6_el = disass_r6_el,
+ regname = regname
+}
+
diff --git a/jit/dis_mips64.lua b/jit/dis_mips64.lua
new file mode 100644
index 0000000..d7d160a
--- /dev/null
+++ b/jit/dis_mips64.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the big-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create,
+ disass = dis_mips.disass,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64el.lua b/jit/dis_mips64el.lua
new file mode 100644
index 0000000..06767c0
--- /dev/null
+++ b/jit/dis_mips64el.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64EL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_el,
+ disass = dis_mips.disass_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64r6.lua b/jit/dis_mips64r6.lua
new file mode 100644
index 0000000..ef4a125
--- /dev/null
+++ b/jit/dis_mips64r6.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64R6 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the r6 big-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_r6,
+ disass = dis_mips.disass_r6,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mips64r6el.lua b/jit/dis_mips64r6el.lua
new file mode 100644
index 0000000..4dc6a54
--- /dev/null
+++ b/jit/dis_mips64r6el.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPS64R6EL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the r6 little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_r6_el,
+ disass = dis_mips.disass_r6_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_mipsel.lua b/jit/dis_mipsel.lua
new file mode 100644
index 0000000..1da538a
--- /dev/null
+++ b/jit/dis_mipsel.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT MIPSEL disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the little-endian functions from the
+-- MIPS disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips")
+return {
+ create = dis_mips.create_el,
+ disass = dis_mips.disass_el,
+ regname = dis_mips.regname
+}
+
diff --git a/jit/dis_ppc.lua b/jit/dis_ppc.lua
new file mode 100644
index 0000000..d8f4cfb
--- /dev/null
+++ b/jit/dis_ppc.lua
@@ -0,0 +1,591 @@
+----------------------------------------------------------------------------
+-- LuaJIT PPC disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT/X license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
+-- plus the e500 SPE instructions and some Cell/Xenon extensions.
+--
+-- NYI: VMX, VMX128
+------------------------------------------------------------------------------
+
+local type = type
+local byte, format = string.byte, string.format
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+local concat = table.concat
+local bit = require("bit")
+local band, bor, tohex = bit.band, bit.bor, bit.tohex
+local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
+
+------------------------------------------------------------------------------
+-- Primary and extended opcode maps
+------------------------------------------------------------------------------
+
+local map_crops = {
+ shift = 1, mask = 1023,
+ [0] = "mcrfXX",
+ [33] = "crnor|crnotCCC=", [129] = "crandcCCC",
+ [193] = "crxor|crclrCCC%", [225] = "crnandCCC",
+ [257] = "crandCCC", [289] = "creqv|crsetCCC%",
+ [417] = "crorcCCC", [449] = "cror|crmoveCCC=",
+ [16] = "b_lrKB", [528] = "b_ctrKB",
+ [150] = "isync",
+}
+
+local map_rlwinm = setmetatable({
+ shift = 0, mask = -1,
+},
+{ __index = function(t, x)
+ local rot = band(rshift(x, 11), 31)
+ local mb = band(rshift(x, 6), 31)
+ local me = band(rshift(x, 1), 31)
+ if mb == 0 and me == 31-rot then
+ return "slwiRR~A."
+ elseif me == 31 and mb == 32-rot then
+ return "srwiRR~-A."
+ else
+ return "rlwinmRR~AAA."
+ end
+ end
+})
+
+local map_rld = {
+ shift = 2, mask = 7,
+ [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
+ {
+ shift = 1, mask = 1,
+ [0] = "rldclRR~RM.", "rldcrRR~RM.",
+ },
+}
+
+local map_ext = setmetatable({
+ shift = 1, mask = 1023,
+
+ [0] = "cmp_YLRR", [32] = "cmpl_YLRR",
+ [4] = "twARR", [68] = "tdARR",
+
+ [8] = "subfcRRR.", [40] = "subfRRR.",
+ [104] = "negRR.", [136] = "subfeRRR.",
+ [200] = "subfzeRR.", [232] = "subfmeRR.",
+ [520] = "subfcoRRR.", [552] = "subfoRRR.",
+ [616] = "negoRR.", [648] = "subfeoRRR.",
+ [712] = "subfzeoRR.", [744] = "subfmeoRR.",
+
+ [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
+ [457] = "divduRRR.", [489] = "divdRRR.",
+ [745] = "mulldoRRR.",
+ [969] = "divduoRRR.", [1001] = "divdoRRR.",
+
+ [10] = "addcRRR.", [138] = "addeRRR.",
+ [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
+ [522] = "addcoRRR.", [650] = "addeoRRR.",
+ [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
+
+ [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
+ [459] = "divwuRRR.", [491] = "divwRRR.",
+ [747] = "mullwoRRR.",
+ [971] = "divwouRRR.", [1003] = "divwoRRR.",
+
+ [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
+
+ [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
+ [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
+ [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
+ [339] = {
+ shift = 11, mask = 1023,
+ [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
+ },
+ [467] = {
+ shift = 11, mask = 1023,
+ [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
+ },
+
+ [20] = "lwarxRR0R", [84] = "ldarxRR0R",
+
+ [21] = "ldxRR0R", [53] = "lduxRRR",
+ [149] = "stdxRR0R", [181] = "stduxRRR",
+ [341] = "lwaxRR0R", [373] = "lwauxRRR",
+
+ [23] = "lwzxRR0R", [55] = "lwzuxRRR",
+ [87] = "lbzxRR0R", [119] = "lbzuxRRR",
+ [151] = "stwxRR0R", [183] = "stwuxRRR",
+ [215] = "stbxRR0R", [247] = "stbuxRRR",
+ [279] = "lhzxRR0R", [311] = "lhzuxRRR",
+ [343] = "lhaxRR0R", [375] = "lhauxRRR",
+ [407] = "sthxRR0R", [439] = "sthuxRRR",
+
+ [54] = "dcbst-R0R", [86] = "dcbf-R0R",
+ [150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
+ [246] = "dcbtst-R0R", [278] = "dcbt-R0R",
+ [310] = "eciwxRR0R", [438] = "ecowxRR0R",
+ [470] = "dcbi-RR",
+
+ [598] = {
+ shift = 21, mask = 3,
+ [0] = "sync", "lwsync", "ptesync",
+ },
+ [758] = "dcba-RR",
+ [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
+
+ [26] = "cntlzwRR~", [58] = "cntlzdRR~",
+ [122] = "popcntbRR~",
+ [154] = "prtywRR~", [186] = "prtydRR~",
+
+ [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
+ [284] = "eqvRR~R.", [316] = "xorRR~R.",
+ [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
+ [508] = "cmpbRR~R",
+
+ [512] = "mcrxrX",
+
+ [532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
+
+ [533] = "lswxRR0R", [597] = "lswiRR0A",
+ [661] = "stswxRR0R", [725] = "stswiRR0A",
+
+ [534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
+ [790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
+
+ [535] = "lfsxFR0R", [567] = "lfsuxFRR",
+ [599] = "lfdxFR0R", [631] = "lfduxFRR",
+ [663] = "stfsxFR0R", [695] = "stfsuxFRR",
+ [727] = "stfdxFR0R", [759] = "stfduxFR0R",
+ [855] = "lfiwaxFR0R",
+ [983] = "stfiwxFR0R",
+
+ [24] = "slwRR~R.",
+
+ [27] = "sldRR~R.", [536] = "srwRR~R.",
+ [792] = "srawRR~R.", [824] = "srawiRR~A.",
+
+ [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
+ [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
+
+ [539] = "srdRR~R.",
+},
+{ __index = function(t, x)
+ if band(x, 31) == 15 then return "iselRRRC" end
+ end
+})
+
+local map_ld = {
+ shift = 0, mask = 3,
+ [0] = "ldRRE", "lduRRE", "lwaRRE",
+}
+
+local map_std = {
+ shift = 0, mask = 3,
+ [0] = "stdRRE", "stduRRE",
+}
+
+local map_fps = {
+ shift = 5, mask = 1,
+ {
+ shift = 1, mask = 15,
+ [0] = false, false, "fdivsFFF.", false,
+ "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
+ "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
+ "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
+ }
+}
+
+local map_fpd = {
+ shift = 5, mask = 1,
+ [0] = {
+ shift = 1, mask = 1023,
+ [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
+ [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
+ [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
+ [136] = "fnabsF-F.", [264] = "fabsF-F.",
+ [12] = "frspF-F.",
+ [14] = "fctiwF-F.", [15] = "fctiwzF-F.",
+ [583] = "mffsF.", [711] = "mtfsfZF.",
+ [392] = "frinF-F.", [424] = "frizF-F.",
+ [456] = "fripF-F.", [488] = "frimF-F.",
+ [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
+ },
+ {
+ shift = 1, mask = 15,
+ [0] = false, false, "fdivFFF.", false,
+ "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
+ "freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
+ "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
+ }
+}
+
+local map_spe = {
+ shift = 0, mask = 2047,
+
+ [512] = "evaddwRRR", [514] = "evaddiwRAR~",
+ [516] = "evsubwRRR~", [518] = "evsubiwRAR~",
+ [520] = "evabsRR", [521] = "evnegRR",
+ [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
+ [525] = "evcntlzwRR", [526] = "evcntlswRR",
+
+ [527] = "brincRRR",
+
+ [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
+ [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
+ [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
+
+ [544] = "evsrwuRRR", [545] = "evsrwsRRR",
+ [546] = "evsrwiuRRA", [547] = "evsrwisRRA",
+ [548] = "evslwRRR", [550] = "evslwiRRA",
+ [552] = "evrlwRRR", [553] = "evsplatiRS",
+ [554] = "evrlwiRRA", [555] = "evsplatfiRS",
+ [556] = "evmergehiRRR", [557] = "evmergeloRRR",
+ [558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
+
+ [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
+ [562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
+ [564] = "evcmpeqYRR",
+
+ [632] = "evselRRR", [633] = "evselRRRW",
+ [634] = "evselRRRW", [635] = "evselRRRW",
+ [636] = "evselRRRW", [637] = "evselRRRW",
+ [638] = "evselRRRW", [639] = "evselRRRW",
+
+ [640] = "evfsaddRRR", [641] = "evfssubRRR",
+ [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
+ [648] = "evfsmulRRR", [649] = "evfsdivRRR",
+ [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
+ [656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
+ [658] = "evfscfufR-R", [659] = "evfscfsfR-R",
+ [660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
+ [662] = "evfsctufR-R", [663] = "evfsctsfR-R",
+ [664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
+ [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
+
+ [704] = "efsaddRRR", [705] = "efssubRRR",
+ [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
+ [712] = "efsmulRRR", [713] = "efsdivRRR",
+ [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
+ [719] = "efscfdR-R",
+ [720] = "efscfuiR-R", [721] = "efscfsiR-R",
+ [722] = "efscfufR-R", [723] = "efscfsfR-R",
+ [724] = "efsctuiR-R", [725] = "efsctsiR-R",
+ [726] = "efsctufR-R", [727] = "efsctsfR-R",
+ [728] = "efsctuizR-R", [730] = "efsctsizR-R",
+ [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
+
+ [736] = "efdaddRRR", [737] = "efdsubRRR",
+ [738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
+ [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
+ [744] = "efdmulRRR", [745] = "efddivRRR",
+ [746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
+ [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
+ [751] = "efdcfsR-R",
+ [752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
+ [754] = "efdcfufR-R", [755] = "efdcfsfR-R",
+ [756] = "efdctuiR-R", [757] = "efdctsiR-R",
+ [758] = "efdctufR-R", [759] = "efdctsfR-R",
+ [760] = "efdctuizR-R", [762] = "efdctsizR-R",
+ [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
+
+ [768] = "evlddxRR0R", [769] = "evlddRR8",
+ [770] = "evldwxRR0R", [771] = "evldwRR8",
+ [772] = "evldhxRR0R", [773] = "evldhRR8",
+ [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
+ [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
+ [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
+ [784] = "evlwhexRR0R", [785] = "evlwheRR4",
+ [788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
+ [790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
+ [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
+ [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
+
+ [800] = "evstddxRR0R", [801] = "evstddRR8",
+ [802] = "evstdwxRR0R", [803] = "evstdwRR8",
+ [804] = "evstdhxRR0R", [805] = "evstdhRR8",
+ [816] = "evstwhexRR0R", [817] = "evstwheRR4",
+ [820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
+ [824] = "evstwwexRR0R", [825] = "evstwweRR4",
+ [828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
+
+ [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
+ [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
+ [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
+ [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
+ [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
+ [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
+ [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
+ [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
+ [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
+ [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
+ [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
+ [1147] = "evmwsmfaRRR",
+
+ [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
+ [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
+ [1220] = "evmraRR",
+ [1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
+ [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
+ [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
+
+ [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
+ [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
+ [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
+ [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
+ [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
+ [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
+ [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
+ [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
+ [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
+ [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
+ [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
+ [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
+ [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
+ [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
+ [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
+ [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
+ [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
+ [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
+ [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
+ [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
+ [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
+ [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
+ [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
+ [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
+ [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
+ [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
+}
+
+local map_pri = {
+ [0] = false, false, "tdiARI", "twiARI",
+ map_spe, false, false, "mulliRRI",
+ "subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
+ "addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
+ "b_KBJ", "sc", "bKJ", map_crops,
+ "rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
+ "oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
+ "andi.RR~U", "andis.RR~U", map_rld, map_ext,
+ "lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
+ "stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
+ "lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
+ "sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
+ "lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
+ "stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
+ false, false, map_ld, map_fps,
+ false, false, map_std, map_fpd,
+}
+
+------------------------------------------------------------------------------
+
+local map_gpr = {
+ [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+}
+
+local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
+
+-- Format a condition bit.
+local function condfmt(cond)
+ if cond <= 3 then
+ return map_cond[band(cond, 3)]
+ else
+ return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local pos = ctx.pos
+ local extra = ""
+ if ctx.rel then
+ local sym = ctx.symtab[ctx.rel]
+ if sym then extra = "\t->"..sym end
+ end
+ if ctx.hexdump > 0 then
+ ctx.out(format("%08x %s %-7s %s%s\n",
+ ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
+ else
+ ctx.out(format("%08x %-7s %s%s\n",
+ ctx.addr+pos, text, concat(operands, ", "), extra))
+ end
+ ctx.pos = pos + 4
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
+end
+
+-- Disassemble a single instruction.
+local function disass_ins(ctx)
+ local pos = ctx.pos
+ local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
+ local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
+ local operands = {}
+ local last = nil
+ local rs = 21
+ ctx.op = op
+ ctx.rel = nil
+
+ local opat = map_pri[rshift(b0, 2)]
+ while type(opat) ~= "string" do
+ if not opat then return unknown(ctx) end
+ opat = opat[band(rshift(op, opat.shift), opat.mask)]
+ end
+ local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
+ local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
+ if altname then pat = pat2 end
+
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "R" then
+ x = map_gpr[band(rshift(op, rs), 31)]
+ rs = rs - 5
+ elseif p == "F" then
+ x = "f"..band(rshift(op, rs), 31)
+ rs = rs - 5
+ elseif p == "A" then
+ x = band(rshift(op, rs), 31)
+ rs = rs - 5
+ elseif p == "S" then
+ x = arshift(lshift(op, 27-rs), 27)
+ rs = rs - 5
+ elseif p == "I" then
+ x = arshift(lshift(op, 16), 16)
+ elseif p == "U" then
+ x = band(op, 0xffff)
+ elseif p == "D" or p == "E" then
+ local disp = arshift(lshift(op, 16), 16)
+ if p == "E" then disp = band(disp, -4) end
+ if last == "r0" then last = "0" end
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p >= "2" and p <= "8" then
+ local disp = band(rshift(op, rs), 31) * p
+ if last == "r0" then last = "0" end
+ operands[#operands] = format("%d(%s)", disp, last)
+ elseif p == "H" then
+ x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
+ rs = rs - 5
+ elseif p == "M" then
+ x = band(rshift(op, rs), 31) + band(op, 0x20)
+ elseif p == "C" then
+ x = condfmt(band(rshift(op, rs), 31))
+ rs = rs - 5
+ elseif p == "B" then
+ local bo = rshift(op, 21)
+ local cond = band(rshift(op, 16), 31)
+ local cn = ""
+ rs = rs - 10
+ if band(bo, 4) == 0 then
+ cn = band(bo, 2) == 0 and "dnz" or "dz"
+ if band(bo, 0x10) == 0 then
+ cn = cn..(band(bo, 8) == 0 and "f" or "t")
+ end
+ if band(bo, 0x10) == 0 then x = condfmt(cond) end
+ name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
+ elseif band(bo, 0x10) == 0 then
+ cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
+ if cond > 3 then x = "cr"..rshift(cond, 2) end
+ name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
+ end
+ name = gsub(name, "_", cn)
+ elseif p == "J" then
+ x = arshift(lshift(op, 27-rs), 29-rs)*4
+ if band(op, 2) == 0 then x = ctx.addr + pos + x end
+ ctx.rel = x
+ x = "0x"..tohex(x)
+ elseif p == "K" then
+ if band(op, 1) ~= 0 then name = name.."l" end
+ if band(op, 2) ~= 0 then name = name.."a" end
+ elseif p == "X" or p == "Y" then
+ x = band(rshift(op, rs+2), 7)
+ if x == 0 and p == "Y" then x = nil else x = "cr"..x end
+ rs = rs - 5
+ elseif p == "W" then
+ x = "cr"..band(op, 7)
+ elseif p == "Z" then
+ x = band(rshift(op, rs-4), 255)
+ rs = rs - 10
+ elseif p == ">" then
+ operands[#operands] = rshift(operands[#operands], 1)
+ elseif p == "0" then
+ if last == "r0" then
+ operands[#operands] = nil
+ if altname then name = altname end
+ end
+ elseif p == "L" then
+ name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
+ elseif p == "." then
+ if band(op, 1) == 1 then name = name.."." end
+ elseif p == "N" then
+ if op == 0x60000000 then name = "nop"; break end
+ elseif p == "~" then
+ local n = #operands
+ operands[n-1], operands[n] = operands[n], operands[n-1]
+ elseif p == "=" then
+ local n = #operands
+ if last == operands[n-1] then
+ operands[n] = nil
+ name = altname
+ end
+ elseif p == "%" then
+ local n = #operands
+ if last == operands[n-1] and last == operands[n-2] then
+ operands[n] = nil
+ operands[n-1] = nil
+ name = altname
+ end
+ elseif p == "-" then
+ rs = rs - 5
+ else
+ assert(false)
+ end
+ if x then operands[#operands+1] = x; last = x end
+ end
+
+ return putop(ctx, name, operands)
+end
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ stop = stop - stop % 4
+ ctx.pos = ofs - ofs % 4
+ ctx.rel = nil
+ while ctx.pos < stop do disass_ins(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = addr or 0
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 8
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 32 then return map_gpr[r] end
+ return "f"..(r-32)
+end
+
+-- Public module functions.
+return {
+ create = create,
+ disass = disass,
+ regname = regname
+}
+
diff --git a/jit/dis_x64.lua b/jit/dis_x64.lua
new file mode 100644
index 0000000..42ae48b
--- /dev/null
+++ b/jit/dis_x64.lua
@@ -0,0 +1,17 @@
+----------------------------------------------------------------------------
+-- LuaJIT x64 disassembler wrapper module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This module just exports the 64 bit functions from the combined
+-- x86/x64 disassembler module. All the interesting stuff is there.
+------------------------------------------------------------------------------
+
+local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86")
+return {
+ create = dis_x86.create64,
+ disass = dis_x86.disass64,
+ regname = dis_x86.regname64
+}
+
diff --git a/jit/dis_x86.lua b/jit/dis_x86.lua
new file mode 100644
index 0000000..80bf721
--- /dev/null
+++ b/jit/dis_x86.lua
@@ -0,0 +1,971 @@
+----------------------------------------------------------------------------
+-- LuaJIT x86/x64 disassembler module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+-- This is a helper module used by the LuaJIT machine code dumper module.
+--
+-- Sending small code snippets to an external disassembler and mixing the
+-- output with our own stuff was too fragile. So I had to bite the bullet
+-- and write yet another x86 disassembler. Oh well ...
+--
+-- The output format is very similar to what ndisasm generates. But it has
+-- been developed independently by looking at the opcode tables from the
+-- Intel and AMD manuals. The supported instruction set is quite extensive
+-- and reflects what a current generation Intel or AMD CPU implements in
+-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
+-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
+-- (VMX/SVM) instructions.
+--
+-- Notes:
+-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
+-- * No attempt at optimization has been made -- it's fast enough for my needs.
+------------------------------------------------------------------------------
+
+local type = type
+local sub, byte, format = string.sub, string.byte, string.format
+local match, gmatch, gsub = string.match, string.gmatch, string.gsub
+local lower, rep = string.lower, string.rep
+local bit = require("bit")
+local tohex = bit.tohex
+
+-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
+local map_opc1_32 = {
+--0x
+[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
+"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
+--1x
+"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
+"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
+--2x
+"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
+"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
+--3x
+"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
+"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
+--4x
+"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
+"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
+--5x
+"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
+"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
+--6x
+"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
+"fs:seg","gs:seg","o16:","a16",
+"pushUi","imulVrmi","pushBs","imulVrms",
+"insb","insVS","outsb","outsVS",
+--7x
+"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
+"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
+--8x
+"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
+"testBmr","testVmr","xchgBrm","xchgVrm",
+"movBmr","movVmr","movBrm","movVrm",
+"movVmg","leaVrm","movWgm","popUm",
+--9x
+"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
+"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
+"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
+"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
+--Ax
+"movBao","movVao","movBoa","movVoa",
+"movsb","movsVS","cmpsb","cmpsVS",
+"testBai","testVai","stosb","stosVS",
+"lodsb","lodsVS","scasb","scasVS",
+--Bx
+"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
+"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
+--Cx
+"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
+"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
+--Dx
+"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
+"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
+--Ex
+"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
+"inBau","inVau","outBua","outVua",
+"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
+--Fx
+"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
+"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
+}
+assert(#map_opc1_32 == 255)
+
+-- Map for 1st opcode byte in 64 bit mode (overrides only).
+local map_opc1_64 = setmetatable({
+ [0x06]=false, [0x07]=false, [0x0e]=false,
+ [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
+ [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
+ [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
+ [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
+ [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
+ [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
+ [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
+ [0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
+ [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
+}, { __index = map_opc1_32 })
+
+-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
+-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
+local map_opc2 = {
+--0x
+[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
+"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
+--1x
+"movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
+"movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
+"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
+"movlpsXmr||movlpdXmr",
+"unpcklpsXrvm||unpcklpdXrvm",
+"unpckhpsXrvm||unpckhpdXrvm",
+"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
+"movhpsXmr||movhpdXmr",
+"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
+"hintnopVm","hintnopVm","endbr*hintnopVm","hintnopVm",
+--2x
+"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
+"movapsXrm||movapdXrm",
+"movapsXmr||movapdXmr",
+"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
+"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
+"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
+"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
+"ucomissXrm||ucomisdXrm",
+"comissXrm||comisdXrm",
+--3x
+"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
+"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
+--4x
+"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
+"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
+"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
+"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
+--5x
+"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
+"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
+"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
+"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
+"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
+"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
+"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
+"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
+"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
+--6x
+"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
+"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
+"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
+"||punpcklqdqXrvm","||punpckhqdqXrvm",
+"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
+--7x
+"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
+"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
+"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
+"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
+nil,nil,
+"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
+"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
+--8x
+"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
+"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
+--9x
+"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
+"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
+--Ax
+"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
+"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
+--Bx
+"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
+"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
+"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
+"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
+--Cx
+"xaddBmr","xaddVmr",
+"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
+"pinsrwPrvWmu","pextrwDrPmu",
+"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
+"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
+--Dx
+"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
+"paddqPrvm","pmullwPrvm",
+"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
+"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
+"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
+--Ex
+"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
+"pmulhuwPrvm","pmulhwPrvm",
+"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
+"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
+"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
+--Fx
+"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
+"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
+"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
+"paddbPrvm","paddwPrvm","padddPrvm","ud",
+}
+assert(map_opc2[255] == "ud")
+
+-- Map for three-byte opcodes. Can't wait for their next invention.
+local map_opc3 = {
+["38"] = { -- [66] 0f 38 xx
+--0x
+[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
+"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
+"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
+"||permilpsXrvm","||permilpdXrvm",nil,nil,
+--1x
+"||pblendvbXrma",nil,nil,nil,
+"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
+"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
+"pabsbPrm","pabswPrm","pabsdPrm",nil,
+--2x
+"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
+"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
+"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
+"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
+--3x
+"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
+"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
+"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
+"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
+--4x
+"||pmulddXrvm","||phminposuwXrm",nil,nil,
+nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
+--5x
+[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
+[0x5a] = "||broadcasti128XrlXm",
+--7x
+[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
+--8x
+[0x8c] = "||pmaskmovXrvVSm",
+[0x8e] = "||pmaskmovVSmXvr",
+--9x
+[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
+[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
+[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
+[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
+[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
+--Ax
+[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
+[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
+[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
+[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
+[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
+--Bx
+[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
+[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
+[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
+[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
+[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
+--Dx
+[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
+[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
+--Fx
+[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
+[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
+},
+
+["3a"] = { -- [66] 0f 3a xx
+--0x
+[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
+"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
+"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
+"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
+--1x
+nil,nil,nil,nil,
+"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
+"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
+nil,nil,nil,nil,
+--2x
+"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
+--3x
+[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
+--4x
+[0x40] = "||dppsXrvmu",
+[0x41] = "||dppdXrvmu",
+[0x42] = "||mpsadbwXrvmu",
+[0x44] = "||pclmulqdqXrvmu",
+[0x46] = "||perm2i128Xrvmu",
+[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
+[0x4c] = "||pblendvbXrvmb",
+--6x
+[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
+[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
+[0xdf] = "||aeskeygenassistXrmu",
+--Fx
+[0xf0] = "||| rorxVrmu",
+},
+}
+
+-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
+local map_opcvm = {
+[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
+[0xc8]="monitor",[0xc9]="mwait",
+[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
+[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
+[0xf8]="swapgs",[0xf9]="rdtscp",
+}
+
+-- Map for FP opcodes. And you thought stack machines are simple?
+local map_opcfp = {
+-- D8-DF 00-BF: opcodes with a memory operand.
+-- D8
+[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
+"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
+-- DA
+"fiaddDm","fimulDm","ficomDm","ficompDm",
+"fisubDm","fisubrDm","fidivDm","fidivrDm",
+-- DB
+"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
+-- DC
+"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
+-- DD
+"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
+-- DE
+"fiaddWm","fimulWm","ficomWm","ficompWm",
+"fisubWm","fisubrWm","fidivWm","fidivrWm",
+-- DF
+"fildWm","fisttpWm","fistWm","fistpWm",
+"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
+-- xx C0-FF: opcodes with a pseudo-register operand.
+-- D8
+"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
+-- D9
+"fldFf","fxchFf",{"fnop"},nil,
+{"fchs","fabs",nil,nil,"ftst","fxam"},
+{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
+{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
+{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
+-- DA
+"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
+-- DB
+"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
+{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
+-- DC
+"fadd toFf","fmul toFf",nil,nil,
+"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
+-- DD
+"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
+-- DE
+"faddpFf","fmulpFf",nil,{nil,"fcompp"},
+"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
+-- DF
+nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
+}
+assert(map_opcfp[126] == "fcomipFf")
+
+-- Map for opcode groups. The subkey is sp from the ModRM byte.
+local map_opcgroup = {
+ arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
+ shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
+ testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
+ testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
+ incb = { "inc", "dec" },
+ incd = { "inc", "dec", "callUmp", "$call farDmp",
+ "jmpUmp", "$jmp farDmp", "pushUm" },
+ sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
+ sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
+ "smsw", nil, "lmsw", "vm*$invlpg" },
+ bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
+ cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
+ nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
+ pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
+ pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
+ pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
+ pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
+ fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
+ nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
+ prefetch = { "prefetch", "prefetchw" },
+ prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
+}
+
+------------------------------------------------------------------------------
+
+-- Maps for register names.
+local map_regs = {
+ B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
+ B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
+ W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
+ D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
+ Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
+ M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
+ X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
+ Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
+ "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
+}
+local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
+
+-- Maps for size names.
+local map_sz2n = {
+ B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32,
+}
+local map_sz2prefix = {
+ B = "byte", W = "word", D = "dword",
+ Q = "qword",
+ M = "qword", X = "xword", Y = "yword",
+ F = "dword", G = "qword", -- No need for sizes/register names for these two.
+}
+
+------------------------------------------------------------------------------
+
+-- Output a nicely formatted line with an opcode and operands.
+local function putop(ctx, text, operands)
+ local code, pos, hex = ctx.code, ctx.pos, ""
+ local hmax = ctx.hexdump
+ if hmax > 0 then
+ for i=ctx.start,pos-1 do
+ hex = hex..format("%02X", byte(code, i, i))
+ end
+ if #hex > hmax then hex = sub(hex, 1, hmax)..". "
+ else hex = hex..rep(" ", hmax-#hex+2) end
+ end
+ if operands then text = text.." "..operands end
+ if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
+ if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
+ if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
+ if ctx.rex then
+ local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
+ (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")..
+ (ctx.vexl and "l" or "")
+ if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end
+ if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "")
+ elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end
+ ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
+ ctx.rex = false; ctx.vexl = false; ctx.vexv = false
+ end
+ if ctx.seg then
+ local text2, n = gsub(text, "%[", "["..ctx.seg..":")
+ if n == 0 then text = ctx.seg.." "..text else text = text2 end
+ ctx.seg = false
+ end
+ if ctx.lock then text = "lock "..text; ctx.lock = false end
+ local imm = ctx.imm
+ if imm then
+ local sym = ctx.symtab[imm]
+ if sym then text = text.."\t->"..sym end
+ end
+ ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
+ ctx.mrm = false
+ ctx.vexv = false
+ ctx.start = pos
+ ctx.imm = nil
+end
+
+-- Clear all prefix flags.
+local function clearprefixes(ctx)
+ ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
+ ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
+ ctx.rex = false; ctx.a32 = false; ctx.vexl = false
+end
+
+-- Fallback for incomplete opcodes at the end.
+local function incomplete(ctx)
+ ctx.pos = ctx.stop+1
+ clearprefixes(ctx)
+ return putop(ctx, "(incomplete)")
+end
+
+-- Fallback for unknown opcodes.
+local function unknown(ctx)
+ clearprefixes(ctx)
+ return putop(ctx, "(unknown)")
+end
+
+-- Return an immediate of the specified size.
+local function getimm(ctx, pos, n)
+ if pos+n-1 > ctx.stop then return incomplete(ctx) end
+ local code = ctx.code
+ if n == 1 then
+ local b1 = byte(code, pos, pos)
+ return b1
+ elseif n == 2 then
+ local b1, b2 = byte(code, pos, pos+1)
+ return b1+b2*256
+ else
+ local b1, b2, b3, b4 = byte(code, pos, pos+3)
+ local imm = b1+b2*256+b3*65536+b4*16777216
+ ctx.imm = imm
+ return imm
+ end
+end
+
+-- Process pattern string and generate the operands.
+local function putpat(ctx, name, pat)
+ local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
+ local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl
+
+ -- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
+ for p in gmatch(pat, ".") do
+ local x = nil
+ if p == "V" or p == "U" then
+ if ctx.rexw then sz = "Q"; ctx.rexw = false
+ elseif ctx.o16 then sz = "W"; ctx.o16 = false
+ elseif p == "U" and ctx.x64 then sz = "Q"
+ else sz = "D" end
+ regs = map_regs[sz]
+ elseif p == "T" then
+ if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
+ regs = map_regs[sz]
+ elseif p == "B" then
+ sz = "B"
+ regs = ctx.rex and map_regs.B64 or map_regs.B
+ elseif match(p, "[WDQMXYFG]") then
+ sz = p
+ if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
+ regs = map_regs[sz]
+ elseif p == "P" then
+ sz = ctx.o16 and "X" or "M"; ctx.o16 = false
+ if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end
+ regs = map_regs[sz]
+ elseif p == "H" then
+ name = name..(ctx.rexw and "d" or "s")
+ ctx.rexw = false
+ elseif p == "S" then
+ name = name..lower(sz)
+ elseif p == "s" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = imm <= 127 and format("+0x%02x", imm)
+ or format("-0x%02x", 256-imm)
+ pos = pos+1
+ elseif p == "u" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = format("0x%02x", imm)
+ pos = pos+1
+ elseif p == "b" then
+ local imm = getimm(ctx, pos, 1); if not imm then return end
+ x = regs[imm/16+1]
+ pos = pos+1
+ elseif p == "w" then
+ local imm = getimm(ctx, pos, 2); if not imm then return end
+ x = format("0x%x", imm)
+ pos = pos+2
+ elseif p == "o" then -- [offset]
+ if ctx.x64 then
+ local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
+ local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
+ x = format("[0x%08x%08x]", imm2, imm1)
+ pos = pos+8
+ else
+ local imm = getimm(ctx, pos, 4); if not imm then return end
+ x = format("[0x%08x]", imm)
+ pos = pos+4
+ end
+ elseif p == "i" or p == "I" then
+ local n = map_sz2n[sz]
+ if n == 8 and ctx.x64 and p == "I" then
+ local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
+ local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
+ x = format("0x%08x%08x", imm2, imm1)
+ else
+ if n == 8 then n = 4 end
+ local imm = getimm(ctx, pos, n); if not imm then return end
+ if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
+ imm = (0xffffffff+1)-imm
+ x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
+ else
+ x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
+ end
+ end
+ pos = pos+n
+ elseif p == "j" then
+ local n = map_sz2n[sz]
+ if n == 8 then n = 4 end
+ local imm = getimm(ctx, pos, n); if not imm then return end
+ if sz == "B" and imm > 127 then imm = imm-256
+ elseif imm > 2147483647 then imm = imm-4294967296 end
+ pos = pos+n
+ imm = imm + pos + ctx.addr
+ if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
+ ctx.imm = imm
+ if sz == "W" then
+ x = format("word 0x%04x", imm%65536)
+ elseif ctx.x64 then
+ local lo = imm % 0x1000000
+ x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
+ else
+ x = "0x"..tohex(imm)
+ end
+ elseif p == "R" then
+ local r = byte(code, pos-1, pos-1)%8
+ if ctx.rexb then r = r + 8; ctx.rexb = false end
+ x = regs[r+1]
+ elseif p == "a" then x = regs[1]
+ elseif p == "c" then x = "cl"
+ elseif p == "d" then x = "dx"
+ elseif p == "1" then x = "1"
+ else
+ if not mode then
+ mode = ctx.mrm
+ if not mode then
+ if pos > stop then return incomplete(ctx) end
+ mode = byte(code, pos, pos)
+ pos = pos+1
+ end
+ rm = mode%8; mode = (mode-rm)/8
+ sp = mode%8; mode = (mode-sp)/8
+ sdisp = ""
+ if mode < 3 then
+ if rm == 4 then
+ if pos > stop then return incomplete(ctx) end
+ sc = byte(code, pos, pos)
+ pos = pos+1
+ rm = sc%8; sc = (sc-rm)/8
+ rx = sc%8; sc = (sc-rx)/8
+ if ctx.rexx then rx = rx + 8; ctx.rexx = false end
+ if rx == 4 then rx = nil end
+ end
+ if mode > 0 or rm == 5 then
+ local dsz = mode
+ if dsz ~= 1 then dsz = 4 end
+ local disp = getimm(ctx, pos, dsz); if not disp then return end
+ if mode == 0 then rm = nil end
+ if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
+ if dsz == 1 and disp > 127 then
+ sdisp = format("-0x%x", 256-disp)
+ elseif disp >= 0 and disp <= 0x7fffffff then
+ sdisp = format("+0x%x", disp)
+ else
+ sdisp = format("-0x%x", (0xffffffff+1)-disp)
+ end
+ else
+ sdisp = format(ctx.x64 and not ctx.a32 and
+ not (disp >= 0 and disp <= 0x7fffffff)
+ and "0xffffffff%08x" or "0x%08x", disp)
+ end
+ pos = pos+dsz
+ end
+ end
+ if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
+ if ctx.rexr then sp = sp + 8; ctx.rexr = false end
+ end
+ if p == "m" then
+ if mode == 3 then x = regs[rm+1]
+ else
+ local aregs = ctx.a32 and map_regs.D or ctx.aregs
+ local srm, srx = "", ""
+ if rm then srm = aregs[rm+1]
+ elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
+ ctx.a32 = false
+ if rx then
+ if rm then srm = srm.."+" end
+ srx = aregs[rx+1]
+ if sc > 0 then srx = srx.."*"..(2^sc) end
+ end
+ x = format("[%s%s%s]", srm, srx, sdisp)
+ end
+ if mode < 3 and
+ (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
+ x = map_sz2prefix[sz].." "..x
+ end
+ elseif p == "r" then x = regs[sp+1]
+ elseif p == "g" then x = map_segregs[sp+1]
+ elseif p == "p" then -- Suppress prefix.
+ elseif p == "f" then x = "st"..rm
+ elseif p == "x" then
+ if sp == 0 and ctx.lock and not ctx.x64 then
+ x = "CR8"; ctx.lock = false
+ else
+ x = "CR"..sp
+ end
+ elseif p == "v" then
+ if ctx.vexv then
+ x = regs[ctx.vexv+1]; ctx.vexv = false
+ end
+ elseif p == "y" then x = "DR"..sp
+ elseif p == "z" then x = "TR"..sp
+ elseif p == "l" then vexl = false
+ elseif p == "t" then
+ else
+ error("bad pattern `"..pat.."'")
+ end
+ end
+ if x then operands = operands and operands..", "..x or x end
+ end
+ ctx.pos = pos
+ return putop(ctx, name, operands)
+end
+
+-- Forward declaration.
+local map_act
+
+-- Fetch and cache MRM byte.
+local function getmrm(ctx)
+ local mrm = ctx.mrm
+ if not mrm then
+ local pos = ctx.pos
+ if pos > ctx.stop then return nil end
+ mrm = byte(ctx.code, pos, pos)
+ ctx.pos = pos+1
+ ctx.mrm = mrm
+ end
+ return mrm
+end
+
+-- Dispatch to handler depending on pattern.
+local function dispatch(ctx, opat, patgrp)
+ if not opat then return unknown(ctx) end
+ if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
+ local p
+ if ctx.rep then
+ p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
+ ctx.rep = false
+ elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
+ else p = "^[^%|]*" end
+ opat = match(opat, p)
+ if not opat then return unknown(ctx) end
+-- ctx.rep = false; ctx.o16 = false
+ --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
+ --XXX remove in branches?
+ end
+ if match(opat, "%$") then -- reg$mem variants.
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
+ if opat == "" then return unknown(ctx) end
+ end
+ if opat == "" then return unknown(ctx) end
+ local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
+ if pat == "" and patgrp then pat = patgrp end
+ return map_act[sub(pat, 1, 1)](ctx, name, pat)
+end
+
+-- Get a pattern from an opcode map and dispatch to handler.
+local function dispatchmap(ctx, opcmap)
+ local pos = ctx.pos
+ local opat = opcmap[byte(ctx.code, pos, pos)]
+ pos = pos + 1
+ ctx.pos = pos
+ return dispatch(ctx, opat)
+end
+
+-- Map for action codes. The key is the first char after the name.
+map_act = {
+ -- Simple opcodes without operands.
+ [""] = function(ctx, name, pat)
+ return putop(ctx, name)
+ end,
+
+ -- Operand size chars fall right through.
+ B = putpat, W = putpat, D = putpat, Q = putpat,
+ V = putpat, U = putpat, T = putpat,
+ M = putpat, X = putpat, P = putpat,
+ F = putpat, G = putpat, Y = putpat,
+ H = putpat,
+
+ -- Collect prefixes.
+ [":"] = function(ctx, name, pat)
+ ctx[pat == ":" and name or sub(pat, 2)] = name
+ if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
+ end,
+
+ -- Chain to special handler specified by name.
+ ["*"] = function(ctx, name, pat)
+ return map_act[name](ctx, name, sub(pat, 2))
+ end,
+
+ -- Use named subtable for opcode group.
+ ["!"] = function(ctx, name, pat)
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
+ end,
+
+ -- o16,o32[,o64] variants.
+ sz = function(ctx, name, pat)
+ if ctx.o16 then ctx.o16 = false
+ else
+ pat = match(pat, ",(.*)")
+ if ctx.rexw then
+ local p = match(pat, ",(.*)")
+ if p then pat = p; ctx.rexw = false end
+ end
+ end
+ pat = match(pat, "^[^,]*")
+ return dispatch(ctx, pat)
+ end,
+
+ -- Two-byte opcode dispatch.
+ opc2 = function(ctx, name, pat)
+ return dispatchmap(ctx, map_opc2)
+ end,
+
+ -- Three-byte opcode dispatch.
+ opc3 = function(ctx, name, pat)
+ return dispatchmap(ctx, map_opc3[pat])
+ end,
+
+ -- VMX/SVM dispatch.
+ vm = function(ctx, name, pat)
+ return dispatch(ctx, map_opcvm[ctx.mrm])
+ end,
+
+ -- Special NOP for endbr64/endbr32.
+ endbr = function(ctx, name, pat)
+ if ctx.rep then
+ local pos = ctx.pos
+ local b = byte(ctx.code, pos)
+ local text
+ if b == 0xfa then text = "endbr64"
+ elseif b == 0xfb then text = "endbr64"
+ end
+ if text then
+ ctx.pos = pos + 1
+ ctx.rep = nil
+ return putop(ctx, text)
+ end
+ end
+ return dispatch(ctx, pat)
+ end,
+
+ -- Floating point opcode dispatch.
+ fp = function(ctx, name, pat)
+ local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
+ local rm = mrm%8
+ local idx = pat*8 + ((mrm-rm)/8)%8
+ if mrm >= 192 then idx = idx + 64 end
+ local opat = map_opcfp[idx]
+ if type(opat) == "table" then opat = opat[rm+1] end
+ return dispatch(ctx, opat)
+ end,
+
+ -- REX prefix.
+ rex = function(ctx, name, pat)
+ if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
+ for p in gmatch(pat, ".") do ctx["rex"..p] = true end
+ ctx.rex = "rex"
+ end,
+
+ -- VEX prefix.
+ vex = function(ctx, name, pat)
+ if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed.
+ ctx.rex = "vex"
+ local pos = ctx.pos
+ if ctx.mrm then
+ ctx.mrm = nil
+ pos = pos-1
+ end
+ local b = byte(ctx.code, pos, pos)
+ if not b then return incomplete(ctx) end
+ pos = pos+1
+ if b < 128 then ctx.rexr = true end
+ local m = 1
+ if pat == "3" then
+ m = b%32; b = (b-m)/32
+ local nb = b%2; b = (b-nb)/2
+ if nb == 0 then ctx.rexb = true end
+ local nx = b%2
+ if nx == 0 then ctx.rexx = true end
+ b = byte(ctx.code, pos, pos)
+ if not b then return incomplete(ctx) end
+ pos = pos+1
+ if b >= 128 then ctx.rexw = true end
+ end
+ ctx.pos = pos
+ local map
+ if m == 1 then map = map_opc2
+ elseif m == 2 then map = map_opc3["38"]
+ elseif m == 3 then map = map_opc3["3a"]
+ else return unknown(ctx) end
+ local p = b%4; b = (b-p)/4
+ if p == 1 then ctx.o16 = "o16"
+ elseif p == 2 then ctx.rep = "rep"
+ elseif p == 3 then ctx.rep = "repne" end
+ local l = b%2; b = (b-l)/2
+ if l ~= 0 then ctx.vexl = true end
+ ctx.vexv = (-1-b)%16
+ return dispatchmap(ctx, map)
+ end,
+
+ -- Special case for nop with REX prefix.
+ nop = function(ctx, name, pat)
+ return dispatch(ctx, ctx.rex and pat or "nop")
+ end,
+
+ -- Special case for 0F 77.
+ emms = function(ctx, name, pat)
+ if ctx.rex ~= "vex" then
+ return putop(ctx, "emms")
+ elseif ctx.vexl then
+ ctx.vexl = false
+ return putop(ctx, "zeroall")
+ else
+ return putop(ctx, "zeroupper")
+ end
+ end,
+}
+
+------------------------------------------------------------------------------
+
+-- Disassemble a block of code.
+local function disass_block(ctx, ofs, len)
+ if not ofs then ofs = 0 end
+ local stop = len and ofs+len or #ctx.code
+ ofs = ofs + 1
+ ctx.start = ofs
+ ctx.pos = ofs
+ ctx.stop = stop
+ ctx.imm = nil
+ ctx.mrm = false
+ clearprefixes(ctx)
+ while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
+ if ctx.pos ~= ctx.start then incomplete(ctx) end
+end
+
+-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
+local function create(code, addr, out)
+ local ctx = {}
+ ctx.code = code
+ ctx.addr = (addr or 0) - 1
+ ctx.out = out or io.write
+ ctx.symtab = {}
+ ctx.disass = disass_block
+ ctx.hexdump = 16
+ ctx.x64 = false
+ ctx.map1 = map_opc1_32
+ ctx.aregs = map_regs.D
+ return ctx
+end
+
+local function create64(code, addr, out)
+ local ctx = create(code, addr, out)
+ ctx.x64 = true
+ ctx.map1 = map_opc1_64
+ ctx.aregs = map_regs.Q
+ return ctx
+end
+
+-- Simple API: disassemble code (a string) at address and output via out.
+local function disass(code, addr, out)
+ create(code, addr, out):disass()
+end
+
+local function disass64(code, addr, out)
+ create64(code, addr, out):disass()
+end
+
+-- Return register name for RID.
+local function regname(r)
+ if r < 8 then return map_regs.D[r+1] end
+ return map_regs.X[r-7]
+end
+
+local function regname64(r)
+ if r < 16 then return map_regs.Q[r+1] end
+ return map_regs.X[r-15]
+end
+
+-- Public module functions.
+return {
+ create = create,
+ create64 = create64,
+ disass = disass,
+ disass64 = disass64,
+ regname = regname,
+ regname64 = regname64
+}
+
diff --git a/jit/dump.lua b/jit/dump.lua
new file mode 100644
index 0000000..6a700bb
--- /dev/null
+++ b/jit/dump.lua
@@ -0,0 +1,730 @@
+----------------------------------------------------------------------------
+-- LuaJIT compiler dump module.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module can be used to debug the JIT compiler itself. It dumps the
+-- code representations and structures used in various compiler stages.
+--
+-- Example usage:
+--
+-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
+-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
+-- luajit -jdump=is myapp.lua | less -R
+-- luajit -jdump=-b myapp.lua
+-- luajit -jdump=+aH,myapp.html myapp.lua
+-- luajit -jdump=ixT,myapp.dump myapp.lua
+--
+-- The first argument specifies the dump mode. The second argument gives
+-- the output file name. Default output is to stdout, unless the environment
+-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
+-- module is started.
+--
+-- Different features can be turned on or off with the dump mode. If the
+-- mode starts with a '+', the following features are added to the default
+-- set of features; a '-' removes them. Otherwise the features are replaced.
+--
+-- The following dump features are available (* marks the default):
+--
+-- * t Print a line for each started, ended or aborted trace (see also -jv).
+-- * b Dump the traced bytecode.
+-- * i Dump the IR (intermediate representation).
+-- r Augment the IR with register/stack slots.
+-- s Dump the snapshot map.
+-- * m Dump the generated machine code.
+-- x Print each taken trace exit.
+-- X Print each taken trace exit and the contents of all registers.
+-- a Print the IR of aborted traces, too.
+--
+-- The output format can be set with the following characters:
+--
+-- T Plain text output.
+-- A ANSI-colored text output
+-- H Colorized HTML + CSS output.
+--
+-- The default output format is plain text. It's set to ANSI-colored text
+-- if the COLORTERM variable is set. Note: this is independent of any output
+-- redirection, which is actually considered a feature.
+--
+-- You probably want to use less -R to enjoy viewing ANSI-colored text from
+-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local jutil = require("jit.util")
+local vmdef = require("jit.vmdef")
+local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
+local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
+local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
+local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
+local bit = require("bit")
+local band, shr, tohex = bit.band, bit.rshift, bit.tohex
+local sub, gsub, format = string.sub, string.gsub, string.format
+local byte, rep = string.byte, string.rep
+local type, tostring = type, tostring
+local stdout, stderr = io.stdout, io.stderr
+
+-- Load other modules on-demand.
+local bcline, disass
+
+-- Active flag, output file handle and dump mode.
+local active, out, dumpmode
+
+------------------------------------------------------------------------------
+
+local symtabmt = { __index = false }
+local symtab = {}
+local nexitsym = 0
+
+-- Fill nested symbol table with per-trace exit stub addresses.
+local function fillsymtab_tr(tr, nexit)
+ local t = {}
+ symtabmt.__index = t
+ if jit.arch:sub(1, 4) == "mips" then
+ t[traceexitstub(tr, 0)] = "exit"
+ return
+ end
+ for i=0,nexit-1 do
+ local addr = traceexitstub(tr, i)
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = tostring(i)
+ end
+ local addr = traceexitstub(tr, nexit)
+ if addr then t[addr] = "stack_check" end
+end
+
+-- Fill symbol table with trace exit stub addresses.
+local function fillsymtab(tr, nexit)
+ local t = symtab
+ if nexitsym == 0 then
+ local maskaddr = jit.arch == "arm" and -2
+ local ircall = vmdef.ircall
+ for i=0,#ircall do
+ local addr = ircalladdr(i)
+ if addr ~= 0 then
+ if maskaddr then addr = band(addr, maskaddr) end
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = ircall[i]
+ end
+ end
+ end
+ if nexitsym == 1000000 then -- Per-trace exit stubs.
+ fillsymtab_tr(tr, nexit)
+ elseif nexit > nexitsym then -- Shared exit stubs.
+ for i=nexitsym,nexit-1 do
+ local addr = traceexitstub(i)
+ if addr == nil then -- Fall back to per-trace exit stubs.
+ fillsymtab_tr(tr, nexit)
+ setmetatable(symtab, symtabmt)
+ nexit = 1000000
+ break
+ end
+ if addr < 0 then addr = addr + 2^32 end
+ t[addr] = tostring(i)
+ end
+ nexitsym = nexit
+ end
+ return t
+end
+
+local function dumpwrite(s)
+ out:write(s)
+end
+
+-- Disassemble machine code.
+local function dump_mcode(tr)
+ local info = traceinfo(tr)
+ if not info then return end
+ local mcode, addr, loop = tracemc(tr)
+ if not mcode then return end
+ if not disass then disass = require("jit.dis_"..jit.arch) end
+ if addr < 0 then addr = addr + 2^32 end
+ out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
+ local ctx = disass.create(mcode, addr, dumpwrite)
+ ctx.hexdump = 0
+ ctx.symtab = fillsymtab(tr, info.nexit)
+ if loop ~= 0 then
+ symtab[addr+loop] = "LOOP"
+ ctx:disass(0, loop)
+ out:write("->LOOP:\n")
+ ctx:disass(loop, #mcode-loop)
+ symtab[addr+loop] = nil
+ else
+ ctx:disass(0, #mcode)
+ end
+end
+
+------------------------------------------------------------------------------
+
+local irtype_text = {
+ [0] = "nil",
+ "fal",
+ "tru",
+ "lud",
+ "str",
+ "p32",
+ "thr",
+ "pro",
+ "fun",
+ "p64",
+ "cdt",
+ "tab",
+ "udt",
+ "flt",
+ "num",
+ "i8 ",
+ "u8 ",
+ "i16",
+ "u16",
+ "int",
+ "u32",
+ "i64",
+ "u64",
+ "sfp",
+}
+
+local colortype_ansi = {
+ [0] = "%s",
+ "%s",
+ "%s",
+ "\027[36m%s\027[m",
+ "\027[32m%s\027[m",
+ "%s",
+ "\027[1m%s\027[m",
+ "%s",
+ "\027[1m%s\027[m",
+ "%s",
+ "\027[33m%s\027[m",
+ "\027[31m%s\027[m",
+ "\027[36m%s\027[m",
+ "\027[34m%s\027[m",
+ "\027[34m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+ "\027[35m%s\027[m",
+}
+
+local function colorize_text(s)
+ return s
+end
+
+local function colorize_ansi(s, t, extra)
+ local out = format(colortype_ansi[t], s)
+ if extra then out = "\027[3m"..out end
+ return out
+end
+
+local irtype_ansi = setmetatable({},
+ { __index = function(tab, t)
+ local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
+
+local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
+
+local function colorize_html(s, t, extra)
+ s = gsub(s, "[<>&]", html_escape)
+ return format('%s',
+ irtype_text[t], extra and " irt_extra" or "", s)
+end
+
+local irtype_html = setmetatable({},
+ { __index = function(tab, t)
+ local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
+
+local header_html = [[
+
+]]
+
+local colorize, irtype
+
+-- Lookup tables to convert some literals into names.
+local litname = {
+ ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
+ local s = ""
+ if band(mode, 1) ~= 0 then s = s.."P" end
+ if band(mode, 2) ~= 0 then s = s.."F" end
+ if band(mode, 4) ~= 0 then s = s.."T" end
+ if band(mode, 8) ~= 0 then s = s.."C" end
+ if band(mode, 16) ~= 0 then s = s.."R" end
+ if band(mode, 32) ~= 0 then s = s.."I" end
+ if band(mode, 64) ~= 0 then s = s.."K" end
+ t[mode] = s
+ return s
+ end}),
+ ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
+ ["CONV "] = setmetatable({}, { __index = function(t, mode)
+ local s = irtype[band(mode, 31)]
+ s = irtype[band(shr(mode, 5), 31)].."."..s
+ if band(mode, 0x800) ~= 0 then s = s.." sext" end
+ local c = shr(mode, 12)
+ if c == 1 then s = s.." none"
+ elseif c == 2 then s = s.." index"
+ elseif c == 3 then s = s.." check" end
+ t[mode] = s
+ return s
+ end}),
+ ["FLOAD "] = vmdef.irfield,
+ ["FREF "] = vmdef.irfield,
+ ["FPMATH"] = vmdef.irfpm,
+ ["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" },
+ ["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" },
+ ["TOSTR "] = { [0] = "INT", "NUM", "CHAR" },
+}
+
+local function ctlsub(c)
+ if c == "\n" then return "\\n"
+ elseif c == "\r" then return "\\r"
+ elseif c == "\t" then return "\\t"
+ else return format("\\%03d", byte(c))
+ end
+end
+
+local function fmtfunc(func, pc)
+ local fi = funcinfo(func, pc)
+ if fi.loc then
+ return fi.loc
+ elseif fi.ffid then
+ return vmdef.ffnames[fi.ffid]
+ elseif fi.addr then
+ return format("C:%x", fi.addr)
+ else
+ return "(?)"
+ end
+end
+
+local function formatk(tr, idx, sn)
+ local k, t, slot = tracek(tr, idx)
+ local tn = type(k)
+ local s
+ if tn == "number" then
+ if t < 12 then
+ s = k == 0 and "NULL" or format("[0x%08x]", k)
+ elseif band(sn or 0, 0x30000) ~= 0 then
+ s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz"
+ elseif k == 2^52+2^51 then
+ s = "bias"
+ else
+ s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k)
+ end
+ elseif tn == "string" then
+ s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
+ elseif tn == "function" then
+ s = fmtfunc(k)
+ elseif tn == "table" then
+ s = format("{%p}", k)
+ elseif tn == "userdata" then
+ if t == 12 then
+ s = format("userdata:%p", k)
+ else
+ s = format("[%p]", k)
+ if s == "[NULL]" then s = "NULL" end
+ end
+ elseif t == 21 then -- int64_t
+ s = sub(tostring(k), 1, -3)
+ if sub(s, 1, 1) ~= "-" then s = "+"..s end
+ elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL)
+ return "----" -- Special case for LJ_FR2 slot 1.
+ else
+ s = tostring(k) -- For primitives.
+ end
+ s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0)
+ if slot then
+ s = format("%s @%d", s, slot)
+ end
+ return s
+end
+
+local function printsnap(tr, snap)
+ local n = 2
+ for s=0,snap[1]-1 do
+ local sn = snap[n]
+ if shr(sn, 24) == s then
+ n = n + 1
+ local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
+ if ref < 0 then
+ out:write(formatk(tr, ref, sn))
+ elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
+ out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
+ else
+ local m, ot, op1, op2 = traceir(tr, ref)
+ out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0))
+ end
+ out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
+ else
+ out:write("---- ")
+ end
+ end
+ out:write("]\n")
+end
+
+-- Dump snapshots (not interleaved with IR).
+local function dump_snap(tr)
+ out:write("---- TRACE ", tr, " snapshots\n")
+ for i=0,1000000000 do
+ local snap = tracesnap(tr, i)
+ if not snap then break end
+ out:write(format("#%-3d %04d [ ", i, snap[0]))
+ printsnap(tr, snap)
+ end
+end
+
+-- Return a register name or stack slot for a rid/sp location.
+local function ridsp_name(ridsp, ins)
+ if not disass then disass = require("jit.dis_"..jit.arch) end
+ local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
+ if rid == 253 or rid == 254 then
+ return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
+ end
+ if ridsp > 255 then return format("[%x]", slot*4) end
+ if rid < 128 then return disass.regname(rid) end
+ return ""
+end
+
+-- Dump CALL* function ref and return optional ctype.
+local function dumpcallfunc(tr, ins)
+ local ctype
+ if ins > 0 then
+ local m, ot, op1, op2 = traceir(tr, ins)
+ if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
+ ins = op1
+ ctype = formatk(tr, op2)
+ end
+ end
+ if ins < 0 then
+ out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
+ else
+ out:write(format("%04d (", ins))
+ end
+ return ctype
+end
+
+-- Recursively gather CALL* args and dump them.
+local function dumpcallargs(tr, ins)
+ if ins < 0 then
+ out:write(formatk(tr, ins))
+ else
+ local m, ot, op1, op2 = traceir(tr, ins)
+ local oidx = 6*shr(ot, 8)
+ local op = sub(vmdef.irnames, oidx+1, oidx+6)
+ if op == "CARG " then
+ dumpcallargs(tr, op1)
+ if op2 < 0 then
+ out:write(" ", formatk(tr, op2))
+ else
+ out:write(" ", format("%04d", op2))
+ end
+ else
+ out:write(format("%04d", ins))
+ end
+ end
+end
+
+-- Dump IR and interleaved snapshots.
+local function dump_ir(tr, dumpsnap, dumpreg)
+ local info = traceinfo(tr)
+ if not info then return end
+ local nins = info.nins
+ out:write("---- TRACE ", tr, " IR\n")
+ local irnames = vmdef.irnames
+ local snapref = 65536
+ local snap, snapno
+ if dumpsnap then
+ snap = tracesnap(tr, 0)
+ snapref = snap[0]
+ snapno = 0
+ end
+ for ins=1,nins do
+ if ins >= snapref then
+ if dumpreg then
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ else
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ end
+ printsnap(tr, snap)
+ snapno = snapno + 1
+ snap = tracesnap(tr, snapno)
+ snapref = snap and snap[0] or 65536
+ end
+ local m, ot, op1, op2, ridsp = traceir(tr, ins)
+ local oidx, t = 6*shr(ot, 8), band(ot, 31)
+ local op = sub(irnames, oidx+1, oidx+6)
+ if op == "LOOP " then
+ if dumpreg then
+ out:write(format("%04d ------------ LOOP ------------\n", ins))
+ else
+ out:write(format("%04d ------ LOOP ------------\n", ins))
+ end
+ elseif op ~= "NOP " and op ~= "CARG " and
+ (dumpreg or op ~= "RENAME") then
+ local rid = band(ridsp, 255)
+ if dumpreg then
+ out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
+ else
+ out:write(format("%04d ", ins))
+ end
+ out:write(format("%s%s %s %s ",
+ (rid == 254 or rid == 253) and "}" or
+ (band(ot, 128) == 0 and " " or ">"),
+ band(ot, 64) == 0 and " " or "+",
+ irtype[t], op))
+ local m1, m2 = band(m, 3), band(m, 3*4)
+ if sub(op, 1, 4) == "CALL" then
+ local ctype
+ if m2 == 1*4 then -- op2 == IRMlit
+ out:write(format("%-10s (", vmdef.ircall[op2]))
+ else
+ ctype = dumpcallfunc(tr, op2)
+ end
+ if op1 ~= -1 then dumpcallargs(tr, op1) end
+ out:write(")")
+ if ctype then out:write(" ctype ", ctype) end
+ elseif op == "CNEW " and op2 == -1 then
+ out:write(formatk(tr, op1))
+ elseif m1 ~= 3 then -- op1 != IRMnone
+ if op1 < 0 then
+ out:write(formatk(tr, op1))
+ else
+ out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
+ end
+ if m2 ~= 3*4 then -- op2 != IRMnone
+ if m2 == 1*4 then -- op2 == IRMlit
+ local litn = litname[op]
+ if litn and litn[op2] then
+ out:write(" ", litn[op2])
+ elseif op == "UREFO " or op == "UREFC " then
+ out:write(format(" #%-3d", shr(op2, 8)))
+ else
+ out:write(format(" #%-3d", op2))
+ end
+ elseif op2 < 0 then
+ out:write(" ", formatk(tr, op2))
+ else
+ out:write(format(" %04d", op2))
+ end
+ end
+ end
+ out:write("\n")
+ end
+ end
+ if snap then
+ if dumpreg then
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ else
+ out:write(format(".... SNAP #%-3d [ ", snapno))
+ end
+ printsnap(tr, snap)
+ end
+end
+
+------------------------------------------------------------------------------
+
+local recprefix = ""
+local recdepth = 0
+
+-- Format trace error message.
+local function fmterr(err, info)
+ if type(err) == "number" then
+ if type(info) == "function" then info = fmtfunc(info) end
+ local fmt = vmdef.traceerr[err]
+ if fmt == "NYI: bytecode %s" then
+ local oidx = 6 * info
+ info = sub(vmdef.bcnames, oidx+1, oidx+6)
+ end
+ err = format(fmt, info)
+ end
+ return err
+end
+
+-- Dump trace states.
+local function dump_trace(what, tr, func, pc, otr, oex)
+ if what == "stop" or (what == "abort" and dumpmode.a) then
+ if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
+ elseif dumpmode.s then dump_snap(tr) end
+ if dumpmode.m then dump_mcode(tr) end
+ end
+ if what == "start" then
+ if dumpmode.H then out:write('\n') end
+ out:write("---- TRACE ", tr, " ", what)
+ if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
+ out:write(" ", fmtfunc(func, pc), "\n")
+ elseif what == "stop" or what == "abort" then
+ out:write("---- TRACE ", tr, " ", what)
+ if what == "abort" then
+ out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
+ else
+ local info = traceinfo(tr)
+ local link, ltype = info.link, info.linktype
+ if link == tr or link == 0 then
+ out:write(" -> ", ltype, "\n")
+ elseif ltype == "root" then
+ out:write(" -> ", link, "\n")
+ else
+ out:write(" -> ", link, " ", ltype, "\n")
+ end
+ end
+ if dumpmode.H then out:write("\n\n") else out:write("\n") end
+ else
+ if what == "flush" then symtab, nexitsym = {}, 0 end
+ out:write("---- TRACE ", what, "\n\n")
+ end
+ out:flush()
+end
+
+-- Dump recorded bytecode.
+local function dump_record(tr, func, pc, depth)
+ if depth ~= recdepth then
+ recdepth = depth
+ recprefix = rep(" .", depth)
+ end
+ local line
+ if pc >= 0 then
+ line = bcline(func, pc, recprefix)
+ if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
+ else
+ line = "0000 "..recprefix.." FUNCC \n"
+ end
+ if pc <= 0 then
+ out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
+ else
+ out:write(line)
+ end
+ if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
+ out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
+ end
+end
+
+------------------------------------------------------------------------------
+
+local gpr64 = jit.arch:match("64")
+local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel"
+
+-- Dump taken trace exits.
+local function dump_texit(tr, ex, ngpr, nfpr, ...)
+ out:write("---- TRACE ", tr, " exit ", ex, "\n")
+ if dumpmode.X then
+ local regs = {...}
+ if gpr64 then
+ for i=1,ngpr do
+ out:write(format(" %016x", regs[i]))
+ if i % 4 == 0 then out:write("\n") end
+ end
+ else
+ for i=1,ngpr do
+ out:write(" ", tohex(regs[i]))
+ if i % 8 == 0 then out:write("\n") end
+ end
+ end
+ if fprmips32 then
+ for i=1,nfpr,2 do
+ out:write(format(" %+17.14g", regs[ngpr+i]))
+ if i % 8 == 7 then out:write("\n") end
+ end
+ else
+ for i=1,nfpr do
+ out:write(format(" %+17.14g", regs[ngpr+i]))
+ if i % 4 == 0 then out:write("\n") end
+ end
+ end
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Detach dump handlers.
+local function dumpoff()
+ if active then
+ active = false
+ jit.attach(dump_texit)
+ jit.attach(dump_record)
+ jit.attach(dump_trace)
+ if out and out ~= stdout and out ~= stderr then out:close() end
+ out = nil
+ end
+end
+
+-- Open the output file and attach dump handlers.
+local function dumpon(opt, outfile)
+ if active then dumpoff() end
+
+ local term = os.getenv("TERM")
+ local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
+ if opt then
+ opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
+ end
+
+ local m = { t=true, b=true, i=true, m=true, }
+ if opt and opt ~= "" then
+ local o = sub(opt, 1, 1)
+ if o ~= "+" and o ~= "-" then m = {} end
+ for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
+ end
+ dumpmode = m
+
+ if m.t or m.b or m.i or m.s or m.m then
+ jit.attach(dump_trace, "trace")
+ end
+ if m.b then
+ jit.attach(dump_record, "record")
+ if not bcline then bcline = require("jit.bc").line end
+ end
+ if m.x or m.X then
+ jit.attach(dump_texit, "texit")
+ end
+
+ if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stdout
+ end
+
+ m[colormode] = true
+ if colormode == "A" then
+ colorize = colorize_ansi
+ irtype = irtype_ansi
+ elseif colormode == "H" then
+ colorize = colorize_html
+ irtype = irtype_html
+ out:write(header_html)
+ else
+ colorize = colorize_text
+ irtype = irtype_text
+ end
+
+ active = true
+end
+
+-- Public module functions.
+return {
+ on = dumpon,
+ off = dumpoff,
+ start = dumpon -- For -j command line option.
+}
+
diff --git a/jit/p.lua b/jit/p.lua
new file mode 100644
index 0000000..9d938ce
--- /dev/null
+++ b/jit/p.lua
@@ -0,0 +1,309 @@
+----------------------------------------------------------------------------
+-- LuaJIT profiler.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module is a simple command line interface to the built-in
+-- low-overhead profiler of LuaJIT.
+--
+-- The lower-level API of the profiler is accessible via the "jit.profile"
+-- module or the luaJIT_profile_* C API.
+--
+-- Example usage:
+--
+-- luajit -jp myapp.lua
+-- luajit -jp=s myapp.lua
+-- luajit -jp=-s myapp.lua
+-- luajit -jp=vl myapp.lua
+-- luajit -jp=G,profile.txt myapp.lua
+--
+-- The following dump features are available:
+--
+-- f Stack dump: function name, otherwise module:line. Default mode.
+-- F Stack dump: ditto, but always prepend module.
+-- l Stack dump: module:line.
+-- stack dump depth (callee < caller). Default: 1.
+-- - Inverse stack dump depth (caller > callee).
+-- s Split stack dump after first stack level. Implies abs(depth) >= 2.
+-- p Show full path for module names.
+-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv.
+-- z Show zones. Can be combined with stack dumps, e.g. zf or fz.
+-- r Show raw sample counts. Default: show percentages.
+-- a Annotate excerpts from source code files.
+-- A Annotate complete source code files.
+-- G Produce raw output suitable for graphical tools (e.g. flame graphs).
+-- m Minimum sample percentage to be shown. Default: 3.
+-- i Sampling interval in milliseconds. Default: 10.
+--
+----------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local profile = require("jit.profile")
+local vmdef = require("jit.vmdef")
+local math = math
+local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor
+local sort, format = table.sort, string.format
+local stdout = io.stdout
+local zone -- Load jit.zone module on demand.
+
+-- Output file handle.
+local out
+
+------------------------------------------------------------------------------
+
+local prof_ud
+local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth
+local prof_ann, prof_count1, prof_count2, prof_samples
+
+local map_vmmode = {
+ N = "Compiled",
+ I = "Interpreted",
+ C = "C code",
+ G = "Garbage Collector",
+ J = "JIT Compiler",
+}
+
+-- Profiler callback.
+local function prof_cb(th, samples, vmmode)
+ prof_samples = prof_samples + samples
+ local key_stack, key_stack2, key_state
+ -- Collect keys for sample.
+ if prof_states then
+ if prof_states == "v" then
+ key_state = map_vmmode[vmmode] or vmmode
+ else
+ key_state = zone:get() or "(none)"
+ end
+ end
+ if prof_fmt then
+ key_stack = profile.dumpstack(th, prof_fmt, prof_depth)
+ key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x)
+ return vmdef.ffnames[tonumber(x)]
+ end)
+ if prof_split == 2 then
+ local k1, k2 = key_stack:match("(.-) [<>] (.*)")
+ if k2 then key_stack, key_stack2 = k1, k2 end
+ elseif prof_split == 3 then
+ key_stack2 = profile.dumpstack(th, "l", 1)
+ end
+ end
+ -- Order keys.
+ local k1, k2
+ if prof_split == 1 then
+ if key_state then
+ k1 = key_state
+ if key_stack then k2 = key_stack end
+ end
+ elseif key_stack then
+ k1 = key_stack
+ if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end
+ end
+ -- Coalesce samples in one or two levels.
+ if k1 then
+ local t1 = prof_count1
+ t1[k1] = (t1[k1] or 0) + samples
+ if k2 then
+ local t2 = prof_count2
+ local t3 = t2[k1]
+ if not t3 then t3 = {}; t2[k1] = t3 end
+ t3[k2] = (t3[k2] or 0) + samples
+ end
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Show top N list.
+local function prof_top(count1, count2, samples, indent)
+ local t, n = {}, 0
+ for k in pairs(count1) do
+ n = n + 1
+ t[n] = k
+ end
+ sort(t, function(a, b) return count1[a] > count1[b] end)
+ for i=1,n do
+ local k = t[i]
+ local v = count1[k]
+ local pct = floor(v*100/samples + 0.5)
+ if pct < prof_min then break end
+ if not prof_raw then
+ out:write(format("%s%2d%% %s\n", indent, pct, k))
+ elseif prof_raw == "r" then
+ out:write(format("%s%5d %s\n", indent, v, k))
+ else
+ out:write(format("%s %d\n", k, v))
+ end
+ if count2 then
+ local r = count2[k]
+ if r then
+ prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or
+ (prof_depth < 0 and " -> " or " <- "))
+ end
+ end
+ end
+end
+
+-- Annotate source code
+local function prof_annotate(count1, samples)
+ local files = {}
+ local ms = 0
+ for k, v in pairs(count1) do
+ local pct = floor(v*100/samples + 0.5)
+ ms = math.max(ms, v)
+ if pct >= prof_min then
+ local file, line = k:match("^(.*):(%d+)$")
+ if not file then file = k; line = 0 end
+ local fl = files[file]
+ if not fl then fl = {}; files[file] = fl; files[#files+1] = file end
+ line = tonumber(line)
+ fl[line] = prof_raw and v or pct
+ end
+ end
+ sort(files)
+ local fmtv, fmtn = " %3d%% | %s\n", " | %s\n"
+ if prof_raw then
+ local n = math.max(5, math.ceil(math.log10(ms)))
+ fmtv = "%"..n.."d | %s\n"
+ fmtn = (" "):rep(n).." | %s\n"
+ end
+ local ann = prof_ann
+ for _, file in ipairs(files) do
+ local f0 = file:byte()
+ if f0 == 40 or f0 == 91 then
+ out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file))
+ break
+ end
+ local fp, err = io.open(file)
+ if not fp then
+ out:write(format("====== ERROR: %s: %s\n", file, err))
+ break
+ end
+ out:write(format("\n====== %s ======\n", file))
+ local fl = files[file]
+ local n, show = 1, false
+ if ann ~= 0 then
+ for i=1,ann do
+ if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
+ end
+ end
+ for line in fp:lines() do
+ if line:byte() == 27 then
+ out:write("[Cannot annotate bytecode file]\n")
+ break
+ end
+ local v = fl[n]
+ if ann ~= 0 then
+ local v2 = fl[n+ann]
+ if show then
+ if v2 then show = n+ann elseif v then show = n
+ elseif show+ann < n then show = false end
+ elseif v2 then
+ show = n+ann
+ out:write(format("@@ %d @@\n", n))
+ end
+ if not show then goto next end
+ end
+ if v then
+ out:write(format(fmtv, v, line))
+ else
+ out:write(format(fmtn, line))
+ end
+ ::next::
+ n = n + 1
+ end
+ fp:close()
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Finish profiling and dump result.
+local function prof_finish()
+ if prof_ud then
+ profile.stop()
+ local samples = prof_samples
+ if samples == 0 then
+ if prof_raw ~= true then out:write("[No samples collected]\n") end
+ elseif prof_ann then
+ prof_annotate(prof_count1, samples)
+ else
+ prof_top(prof_count1, prof_count2, samples, "")
+ end
+ prof_count1 = nil
+ prof_count2 = nil
+ prof_ud = nil
+ if out ~= stdout then out:close() end
+ end
+end
+
+-- Start profiling.
+local function prof_start(mode)
+ local interval = ""
+ mode = mode:gsub("i%d*", function(s) interval = s; return "" end)
+ prof_min = 3
+ mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end)
+ prof_depth = 1
+ mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end)
+ local m = {}
+ for c in mode:gmatch(".") do m[c] = c end
+ prof_states = m.z or m.v
+ if prof_states == "z" then zone = require("jit.zone") end
+ local scope = m.l or m.f or m.F or (prof_states and "" or "f")
+ local flags = (m.p or "")
+ prof_raw = m.r
+ if m.s then
+ prof_split = 2
+ if prof_depth == -1 or m["-"] then prof_depth = -2
+ elseif prof_depth == 1 then prof_depth = 2 end
+ elseif mode:find("[fF].*l") then
+ scope = "l"
+ prof_split = 3
+ else
+ prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0
+ end
+ prof_ann = m.A and 0 or (m.a and 3)
+ if prof_ann then
+ scope = "l"
+ prof_fmt = "pl"
+ prof_split = 0
+ prof_depth = 1
+ elseif m.G and scope ~= "" then
+ prof_fmt = flags..scope.."Z;"
+ prof_depth = -100
+ prof_raw = true
+ prof_min = 0
+ elseif scope == "" then
+ prof_fmt = false
+ else
+ local sc = prof_split == 3 and m.f or m.F or scope
+ prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ")
+ end
+ prof_count1 = {}
+ prof_count2 = {}
+ prof_samples = 0
+ profile.start(scope:lower()..interval, prof_cb)
+ prof_ud = newproxy(true)
+ getmetatable(prof_ud).__gc = prof_finish
+end
+
+------------------------------------------------------------------------------
+
+local function start(mode, outfile)
+ if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stdout
+ end
+ prof_start(mode or "f")
+end
+
+-- Public module functions.
+return {
+ start = start, -- For -j command line option.
+ stop = prof_finish
+}
+
diff --git a/jit/v.lua b/jit/v.lua
new file mode 100644
index 0000000..69443d3
--- /dev/null
+++ b/jit/v.lua
@@ -0,0 +1,174 @@
+----------------------------------------------------------------------------
+-- Verbose mode of the LuaJIT compiler.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module shows verbose information about the progress of the
+-- JIT compiler. It prints one line for each generated trace. This module
+-- is useful to see which code has been compiled or where the compiler
+-- punts and falls back to the interpreter.
+--
+-- Example usage:
+--
+-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
+-- luajit -jv=myapp.out myapp.lua
+--
+-- Default output is to stderr. To redirect the output to a file, pass a
+-- filename as an argument (use '-' for stdout) or set the environment
+-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
+-- module is started.
+--
+-- The output from the first example should look like this:
+--
+-- [TRACE 1 (command line):1 loop]
+-- [TRACE 2 (1/3) (command line):1 -> 1]
+--
+-- The first number in each line is the internal trace number. Next are
+-- the file name ('(command line)') and the line number (':1') where the
+-- trace has started. Side traces also show the parent trace number and
+-- the exit number where they are attached to in parentheses ('(1/3)').
+-- An arrow at the end shows where the trace links to ('-> 1'), unless
+-- it loops to itself.
+--
+-- In this case the inner loop gets hot and is traced first, generating
+-- a root trace. Then the last exit from the 1st trace gets hot, too,
+-- and triggers generation of the 2nd trace. The side trace follows the
+-- path along the outer loop and *around* the inner loop, back to its
+-- start, and then links to the 1st trace. Yes, this may seem unusual,
+-- if you know how traditional compilers work. Trace compilers are full
+-- of surprises like this -- have fun! :-)
+--
+-- Aborted traces are shown like this:
+--
+-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
+--
+-- Don't worry -- trace aborts are quite common, even in programs which
+-- can be fully compiled. The compiler may retry several times until it
+-- finds a suitable trace.
+--
+-- Of course this doesn't work with features that are not-yet-implemented
+-- (NYI error messages). The VM simply falls back to the interpreter. This
+-- may not matter at all if the particular trace is not very high up in
+-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
+--
+-- Also check out the -jdump module, which prints all the gory details.
+--
+------------------------------------------------------------------------------
+
+-- Cache some library functions and objects.
+local jit = require("jit")
+local jutil = require("jit.util")
+local vmdef = require("jit.vmdef")
+local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
+local type, sub, format = type, string.sub, string.format
+local stdout, stderr = io.stdout, io.stderr
+
+-- Active flag and output file handle.
+local active, out
+
+------------------------------------------------------------------------------
+
+local startloc, startex
+
+local function fmtfunc(func, pc)
+ local fi = funcinfo(func, pc)
+ if fi.loc then
+ return fi.loc
+ elseif fi.ffid then
+ return vmdef.ffnames[fi.ffid]
+ elseif fi.addr then
+ return format("C:%x", fi.addr)
+ else
+ return "(?)"
+ end
+end
+
+-- Format trace error message.
+local function fmterr(err, info)
+ if type(err) == "number" then
+ if type(info) == "function" then info = fmtfunc(info) end
+ local fmt = vmdef.traceerr[err]
+ if fmt == "NYI: bytecode %s" then
+ local oidx = 6 * info
+ info = sub(vmdef.bcnames, oidx+1, oidx+6)
+ end
+ err = format(fmt, info)
+ end
+ return err
+end
+
+-- Dump trace states.
+local function dump_trace(what, tr, func, pc, otr, oex)
+ if what == "start" then
+ startloc = fmtfunc(func, pc)
+ startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or ""
+ else
+ if what == "abort" then
+ local loc = fmtfunc(func, pc)
+ if loc ~= startloc then
+ out:write(format("[TRACE --- %s%s -- %s at %s]\n",
+ startex, startloc, fmterr(otr, oex), loc))
+ else
+ out:write(format("[TRACE --- %s%s -- %s]\n",
+ startex, startloc, fmterr(otr, oex)))
+ end
+ elseif what == "stop" then
+ local info = traceinfo(tr)
+ local link, ltype = info.link, info.linktype
+ if ltype == "interpreter" then
+ out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
+ tr, startex, startloc))
+ elseif ltype == "stitch" then
+ out:write(format("[TRACE %3s %s%s %s %s]\n",
+ tr, startex, startloc, ltype, fmtfunc(func, pc)))
+ elseif link == tr or link == 0 then
+ out:write(format("[TRACE %3s %s%s %s]\n",
+ tr, startex, startloc, ltype))
+ elseif ltype == "root" then
+ out:write(format("[TRACE %3s %s%s -> %d]\n",
+ tr, startex, startloc, link))
+ else
+ out:write(format("[TRACE %3s %s%s -> %d %s]\n",
+ tr, startex, startloc, link, ltype))
+ end
+ else
+ out:write(format("[TRACE %s]\n", what))
+ end
+ out:flush()
+ end
+end
+
+------------------------------------------------------------------------------
+
+-- Detach dump handlers.
+local function dumpoff()
+ if active then
+ active = false
+ jit.attach(dump_trace)
+ if out and out ~= stdout and out ~= stderr then out:close() end
+ out = nil
+ end
+end
+
+-- Open the output file and attach dump handlers.
+local function dumpon(outfile)
+ if active then dumpoff() end
+ if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
+ if outfile then
+ out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
+ else
+ out = stderr
+ end
+ jit.attach(dump_trace, "trace")
+ active = true
+end
+
+-- Public module functions.
+return {
+ on = dumpon,
+ off = dumpoff,
+ start = dumpon -- For -j command line option.
+}
+
diff --git a/jit/vmdef.lua b/jit/vmdef.lua
new file mode 100644
index 0000000..c0b7d3a
--- /dev/null
+++ b/jit/vmdef.lua
@@ -0,0 +1,391 @@
+-- This is a generated file. DO NOT EDIT!
+
+assert(require("jit").version == "LuaJIT 2.1.1771261233", "LuaJIT core/library version mismatch")
+
+return {
+
+bcnames = "ISLT ISGE ISLE ISGT ISEQV ISNEV ISEQS ISNES ISEQN ISNEN ISEQP ISNEP ISTC ISFC IST ISF ISTYPEISNUM MOV NOT UNM LEN ADDVN SUBVN MULVN DIVVN MODVN ADDNV SUBNV MULNV DIVNV MODNV ADDVV SUBVV MULVV DIVVV MODVV POW CAT KSTR KCDATAKSHORTKNUM KPRI KNIL UGET USETV USETS USETN USETP UCLO FNEW TNEW TDUP GGET GSET TGETV TGETS TGETB TGETR TSETV TSETS TSETB TSETM TSETR CALLM CALL CALLMTCALLT ITERC ITERN VARG ISNEXTRETM RET RET0 RET1 FORI JFORI FORL IFORL JFORL ITERL IITERLJITERLLOOP ILOOP JLOOP JMP FUNCF IFUNCFJFUNCFFUNCV IFUNCVJFUNCVFUNCC FUNCCW",
+
+irnames = "LT GE LE GT ULT UGE ULE UGT EQ NE ABC RETF NOP BASE PVAL GCSTEPHIOP LOOP USE PHI RENAMEPROF KPRI KINT KGC KPTR KKPTR KNULL KNUM KINT64KSLOT BNOT BSWAP BAND BOR BXOR BSHL BSHR BSAR BROL BROR ADD SUB MUL DIV MOD POW NEG ABS LDEXP MIN MAX FPMATHADDOV SUBOV MULOV AREF HREFK HREF NEWREFUREFO UREFC FREF TMPREFSTRREFLREF ALOAD HLOAD ULOAD FLOAD XLOAD SLOAD VLOAD ALEN ASTOREHSTOREUSTOREFSTOREXSTORESNEW XSNEW TNEW TDUP CNEW CNEWI BUFHDRBUFPUTBUFSTRTBAR OBAR XBAR CONV TOBIT TOSTR STRTO CALLN CALLA CALLL CALLS CALLXSCARG ",
+
+irfpm = { [0]="floor", "ceil", "trunc", "sqrt", "log", "log2", "other", },
+
+irfield = { [0]="str.len", "func.env", "func.pc", "func.ffid", "thread.env", "tab.meta", "tab.array", "tab.node", "tab.asize", "tab.hmask", "tab.nomm", "udata.meta", "udata.udtype", "udata.file", "sbuf.w", "sbuf.e", "sbuf.b", "sbuf.l", "sbuf.ref", "sbuf.r", "cdata.ctypeid", "cdata.ptr", "cdata.int", "cdata.int64", "cdata.int64_4", },
+
+ircall = {
+[0]="lj_str_cmp",
+"lj_str_find",
+"lj_str_new",
+"lj_strscan_num",
+"lj_strfmt_int",
+"lj_strfmt_num",
+"lj_strfmt_char",
+"lj_strfmt_putint",
+"lj_strfmt_putnum",
+"lj_strfmt_putquoted",
+"lj_strfmt_putfxint",
+"lj_strfmt_putfnum_int",
+"lj_strfmt_putfnum_uint",
+"lj_strfmt_putfnum",
+"lj_strfmt_putfstr",
+"lj_strfmt_putfchar",
+"lj_buf_putmem",
+"lj_buf_putstr",
+"lj_buf_putchar",
+"lj_buf_putstr_reverse",
+"lj_buf_putstr_lower",
+"lj_buf_putstr_upper",
+"lj_buf_putstr_rep",
+"lj_buf_puttab",
+"lj_bufx_set",
+"lj_bufx_more",
+"lj_serialize_put",
+"lj_serialize_get",
+"lj_serialize_encode",
+"lj_serialize_decode",
+"lj_buf_tostr",
+"lj_tab_new_ah",
+"lj_tab_new1",
+"lj_tab_dup",
+"lj_tab_clear",
+"lj_tab_newkey",
+"lj_tab_keyindex",
+"lj_vm_next",
+"lj_tab_len",
+"lj_tab_len_hint",
+"lj_gc_step_jit",
+"lj_gc_barrieruv",
+"lj_mem_newgco",
+"lj_prng_u64d",
+"lj_vm_modi",
+"log10",
+"exp",
+"sin",
+"cos",
+"tan",
+"asin",
+"acos",
+"atan",
+"sinh",
+"cosh",
+"tanh",
+"fputc",
+"fwrite",
+"fflush",
+"lj_vm_floor",
+"lj_vm_ceil",
+"lj_vm_trunc",
+"sqrt",
+"log",
+"lj_vm_log2",
+"pow",
+"atan2",
+"ldexp",
+"lj_vm_tobit",
+"softfp_add",
+"softfp_sub",
+"softfp_mul",
+"softfp_div",
+"softfp_cmp",
+"softfp_i2d",
+"softfp_d2i",
+"lj_vm_sfmin",
+"lj_vm_sfmax",
+"lj_vm_tointg",
+"softfp_ui2d",
+"softfp_f2d",
+"softfp_d2f",
+"softfp_i2f",
+"softfp_ui2f",
+"softfp_f2i",
+"fp64_l2d",
+"fp64_ul2d",
+"fp64_l2f",
+"fp64_ul2f",
+"lj_vm_num2u64",
+"lj_carith_divi64",
+"lj_carith_divu64",
+"lj_carith_modi64",
+"lj_carith_modu64",
+"lj_carith_powi64",
+"lj_carith_powu64",
+"lj_cdata_newv",
+"lj_cdata_setfin",
+"strlen",
+"memcpy",
+"memset",
+"lj_vm_errno",
+"lj_carith_mul64",
+"lj_carith_shl64",
+"lj_carith_shr64",
+"lj_carith_sar64",
+"lj_carith_rol64",
+"lj_carith_ror64",
+},
+
+traceerr = {
+[0]="error thrown or hook called during recording",
+"trace too short",
+"trace too long",
+"trace too deep",
+"too many snapshots",
+"blacklisted",
+"retry recording",
+"NYI: bytecode %s",
+"leaving loop in root trace",
+"inner loop in root trace",
+"loop unroll limit reached",
+"bad argument type",
+"JIT compilation disabled for function",
+"call unroll limit reached",
+"down-recursion, restarting",
+"NYI: unsupported variant of FastFunc %s",
+"NYI: return to lower frame",
+"store with nil or NaN key",
+"missing metamethod",
+"looping index lookup",
+"NYI: mixed sparse/dense table",
+"symbol not in cache",
+"NYI: unsupported C type conversion",
+"NYI: unsupported C function type",
+"guard would always fail",
+"too many PHIs",
+"persistent type instability",
+"failed to allocate mcode memory",
+"machine code too long",
+"hit mcode limit (retrying)",
+"too many spill slots",
+"inconsistent register allocation",
+"NYI: cannot assemble IR instruction %d",
+"NYI: PHI shuffling too complex",
+"NYI: register coalescing too complex",
+},
+
+ffnames = {
+[0]="Lua",
+"C",
+"assert",
+"type",
+"next",
+"pairs",
+"ipairs_aux",
+"ipairs",
+"getmetatable",
+"setmetatable",
+"getfenv",
+"setfenv",
+"rawget",
+"rawset",
+"rawequal",
+"unpack",
+"select",
+"tonumber",
+"tostring",
+"error",
+"pcall",
+"xpcall",
+"loadfile",
+"load",
+"loadstring",
+"dofile",
+"gcinfo",
+"collectgarbage",
+"newproxy",
+"print",
+"coroutine.status",
+"coroutine.running",
+"coroutine.isyieldable",
+"coroutine.create",
+"coroutine.yield",
+"coroutine.resume",
+"coroutine.wrap_aux",
+"coroutine.wrap",
+"math.abs",
+"math.floor",
+"math.ceil",
+"math.sqrt",
+"math.log10",
+"math.exp",
+"math.sin",
+"math.cos",
+"math.tan",
+"math.asin",
+"math.acos",
+"math.atan",
+"math.sinh",
+"math.cosh",
+"math.tanh",
+"math.frexp",
+"math.modf",
+"math.log",
+"math.atan2",
+"math.pow",
+"math.fmod",
+"math.ldexp",
+"math.min",
+"math.max",
+"math.random",
+"math.randomseed",
+"bit.tobit",
+"bit.bnot",
+"bit.bswap",
+"bit.lshift",
+"bit.rshift",
+"bit.arshift",
+"bit.rol",
+"bit.ror",
+"bit.band",
+"bit.bor",
+"bit.bxor",
+"bit.tohex",
+"string.byte",
+"string.char",
+"string.sub",
+"string.rep",
+"string.reverse",
+"string.lower",
+"string.upper",
+"string.dump",
+"string.find",
+"string.match",
+"string.gmatch_aux",
+"string.gmatch",
+"string.gsub",
+"string.format",
+"table.maxn",
+"table.insert",
+"table.concat",
+"table.sort",
+"table.new",
+"table.clear",
+"io.method.close",
+"io.method.read",
+"io.method.write",
+"io.method.flush",
+"io.method.seek",
+"io.method.setvbuf",
+"io.method.lines",
+"io.method.__gc",
+"io.method.__tostring",
+"io.open",
+"io.popen",
+"io.tmpfile",
+"io.close",
+"io.read",
+"io.write",
+"io.flush",
+"io.input",
+"io.output",
+"io.lines",
+"io.type",
+"os.execute",
+"os.remove",
+"os.rename",
+"os.tmpname",
+"os.getenv",
+"os.exit",
+"os.clock",
+"os.date",
+"os.time",
+"os.difftime",
+"os.setlocale",
+"debug.getregistry",
+"debug.getmetatable",
+"debug.setmetatable",
+"debug.getfenv",
+"debug.setfenv",
+"debug.getinfo",
+"debug.getlocal",
+"debug.setlocal",
+"debug.getupvalue",
+"debug.setupvalue",
+"debug.upvalueid",
+"debug.upvaluejoin",
+"debug.sethook",
+"debug.gethook",
+"debug.debug",
+"debug.traceback",
+"jit.on",
+"jit.off",
+"jit.flush",
+"jit.status",
+"jit.security",
+"jit.attach",
+"jit.util.funcinfo",
+"jit.util.funcbc",
+"jit.util.funck",
+"jit.util.funcuvname",
+"jit.util.traceinfo",
+"jit.util.traceir",
+"jit.util.tracek",
+"jit.util.tracesnap",
+"jit.util.tracemc",
+"jit.util.traceexitstub",
+"jit.util.ircalladdr",
+"jit.opt.start",
+"jit.profile.start",
+"jit.profile.stop",
+"jit.profile.dumpstack",
+"ffi.meta.__index",
+"ffi.meta.__newindex",
+"ffi.meta.__eq",
+"ffi.meta.__len",
+"ffi.meta.__lt",
+"ffi.meta.__le",
+"ffi.meta.__concat",
+"ffi.meta.__call",
+"ffi.meta.__add",
+"ffi.meta.__sub",
+"ffi.meta.__mul",
+"ffi.meta.__div",
+"ffi.meta.__mod",
+"ffi.meta.__pow",
+"ffi.meta.__unm",
+"ffi.meta.__tostring",
+"ffi.meta.__pairs",
+"ffi.meta.__ipairs",
+"ffi.clib.__index",
+"ffi.clib.__newindex",
+"ffi.clib.__gc",
+"ffi.callback.free",
+"ffi.callback.set",
+"ffi.cdef",
+"ffi.new",
+"ffi.cast",
+"ffi.typeof",
+"ffi.typeinfo",
+"ffi.istype",
+"ffi.sizeof",
+"ffi.alignof",
+"ffi.offsetof",
+"ffi.errno",
+"ffi.string",
+"ffi.copy",
+"ffi.fill",
+"ffi.abi",
+"ffi.metatype",
+"ffi.gc",
+"ffi.load",
+"buffer.method.free",
+"buffer.method.reset",
+"buffer.method.skip",
+"buffer.method.set",
+"buffer.method.put",
+"buffer.method.putf",
+"buffer.method.get",
+"buffer.method.putcdata",
+"buffer.method.reserve",
+"buffer.method.commit",
+"buffer.method.ref",
+"buffer.method.encode",
+"buffer.method.decode",
+"buffer.method.__gc",
+"buffer.method.__tostring",
+"buffer.method.__len",
+"buffer.new",
+"buffer.encode",
+"buffer.decode",
+},
+
+}
+
diff --git a/jit/zone.lua b/jit/zone.lua
new file mode 100644
index 0000000..0ed6935
--- /dev/null
+++ b/jit/zone.lua
@@ -0,0 +1,45 @@
+----------------------------------------------------------------------------
+-- LuaJIT profiler zones.
+--
+-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
+-- Released under the MIT license. See Copyright Notice in luajit.h
+----------------------------------------------------------------------------
+--
+-- This module implements a simple hierarchical zone model.
+--
+-- Example usage:
+--
+-- local zone = require("jit.zone")
+-- zone("AI")
+-- ...
+-- zone("A*")
+-- ...
+-- print(zone:get()) --> "A*"
+-- ...
+-- zone()
+-- ...
+-- print(zone:get()) --> "AI"
+-- ...
+-- zone()
+--
+----------------------------------------------------------------------------
+
+local remove = table.remove
+
+return setmetatable({
+ flush = function(t)
+ for i=#t,1,-1 do t[i] = nil end
+ end,
+ get = function(t)
+ return t[#t]
+ end
+}, {
+ __call = function(t, zone)
+ if zone then
+ t[#t+1] = zone
+ else
+ return (assert(remove(t), "empty zone stack"))
+ end
+ end
+})
+
diff --git a/lib/luajit/libluajit-5.1.dll.a b/lib/luajit/libluajit-5.1.dll.a
deleted file mode 100644
index cbcb3bd9f339e24662b4e580e3edfba7f94c3cd5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 95976
zcmeI5eXML(RlxV$wzPfJ@_3X|%A-EY;{m0-cjkMB(iX53S}qhytpy6xxpU9Gcix*j
zb2}gJ11lm(`Kq~+r3`#0L8)Qp+R0%Y+7${D#0
z<)sga3dqZT8|CHS
z5fzXlx1e1Au&96>{Y#WrUnMFaue~4Tbzcw_kk>zjl3Xh)AhivYhWuQR)_Xw(~y``fAL^|sRfx>XB!I@w9by?K9a1lxz(JH4!uY-aQ6aJ<=D
zTWoBk)2wY~qyHY>*nay?e{ib5nfrdDrcay9vY!MR+PdAJZ)LZmjx0_N=UD@#qV2;;
zb~9*b>&C_~yIHAd`+B;z*vwjJCE2Xy_du!4_Wmq)7}k_dTJ+8h)6u%~4Wxi=XSXM(
z-M5kgww+F=v*BdyzSF2+`(o#Gf3!$XPKKV1r*`ay-acq6P=d|;oa6~%BbXc=S-86b
z@6hj^EN1C+y+7}J+a0ZX|75;3l^pvvq(h4x>B`)rv^Sp^CEAP^+iU6ctp71a>Dq86
zw8tYP-#0Nz{1n)i&D>X6t!L$bk~X4m8*D7bgL&q+oKGAze%9J#GD`d2uQ{I>CD_d7
z{rSTEKN*eMn$Gp_>h8|W(P}GsR5KPzLt8Upr6)f@hZ=3Moyz@StQ@_z98CuOQRcg}
zPFrkECZ{q6sM6avvVR}BH(Ey)>G*W!6E>7eZ|{%fH*RDsNC`I6bhc1|*04_++`2RB
z54?ZrG}S8Hp8XR~Q`D%f{WEfh-5AuHlFDv(cMdlrJM#Hv{@tk3@0Kqz?^YVUHGOd5
zAAk9YS_c+8i`mxcjr^a%*BeWSH}01yGgOid{i98zq`rf+WW|3YX~_!z*4Zy4t+5(<
zfWbbEK}Flm-`d|Lqc(QK%`N%o%7-%e#F4*|-edmg{%0FC*`4PQ8#UPt2BXPrG0h!h
zt0zkn7W{8EYGo(?d;YOO1#FwFogAfJs38q(9j9kKs_->#Il~>Jaq6hG9sTHb52Y0;
zwG9L`&s%Atbzm`?^w+&uuNAb-Wdk>WS(VPL5Bj-?BiHo{+1zR0yoR;ND!n;_41+bT(GYJfZoQmW4wcsn3p
zfi|5^CvF~33bid8?43xzzS#EKe0&Y>8pyP4F89apOZaMKx04Mg1Fcb8Cp+id{T-;+
zxMe)(dq;dQL5tA
zda>4M)Yip#n!0JjYENs_R!D#|zs0J`nNf*0Q<*rd`TrDYWVL9O&gP3u{w%GmR$0j`
z!ujhMYE~gTb2gAIq?~S|sO+>-*-X>T;mpsrS7jzdN^Rr&ua1Y>0&OBBNh9}%T~%#8
zsg2$CWPRcFQ_+spXeU#uTpv~1IcjA$*_l(+bPj=|B`eds`({Q>cBZNJ?TlL6rQ^|X
z%|D*d1xKZ8
zLD_B$$LrY;w%T*F+G-^K@HuM1LsBcd^lU1lV$RN0qg^fl=#Zecsz8Ki55
zFn>B}L|U>khs8Mt(#)u(-JN&bLuH;`tyk?;+wE$tRjt+Q&K~S$q(1|ifXTYsjKZ6Y
zR$J{%CqTL^8F<})F!eX(?E7BE>Ayj3%e(`Ui)_2?zRPYuksa1?Ms>KS^=eZO{5)_#
zeyJ{$t|v)SZFG-MPGpo)pI25J9ZuQilr2u#-8dqdMnN
zo%5*9c~s{-s&gLIIgjd`M|IAl8s|}s^QgvoRO39VaURt;k7}GpHO`|N=TVLGsLFX%
znv&Z8>lQI+$k%6U}fJW4o^63(N9^C;mwN;r=a&ZC6$DB(OxIFGm>
zkaQW3k}l&>(q%kKx{OCjm+>g+G9D#e#-pUmc$9P*kCHC)8zmjiqYmd$hx4ezdDP)N
z>Tn))IFCADa~`!hkJ_9^ZO)@M=TV#UsLgrQ;yh|`9}+
zTAW8M&Z8FRQH%4a#d*}?JZf&X909L&D_@370b@T+WbiIYYwb3<;MrBwWsra5+Q5AOhGs{{HoK{wA)^!Kvfdi|Ri{zXfIa^7}#qdO2QaL_^lHO4{KL>B1e}rC`
z$K*+E*CZ9#Jw`9gqp@p~>I(TpPE5+&k|#-_dPm3QG!m(B_oX6VUql-9tt+lZeLi@D
z9-%k*YDK(Q>Y+00%}+USqE_{~YI_~IdbN`~a(^N}`4TDe_|cK8sh9HZDk*T}z9HgT-CSdk1M&`7vlD;3Lw}-CD|FTlT_&G?3tqx*
z>W3pDc|h}IScK#9w3Gvmoe<{8n_md*o{@4DZ)biXuzOa@b-Z0mZc;b+CBChn+lS=I
z@4pIOJ?8z5epX0uooQ2W)IsEAKY0UBFICUryWMB*F@fDN{~hH$e1g#^Kk+wq|8ek%
z#zyTs9QljHn>OUY%kdeok0+
zBH4ixEs9}?#a{p1X1$x^2X=oiel7!Njymc!-mQo`|_f7CHY
zelGkTS)n$IKhG&oPOa)D%6rx^n-NEy&l+;zoUo;hI_EnFj$x-xO;A|WiMXEl8N&{q
zEm)pRxllcExAP+zewtoE+6_eb9PeoiCFa=kX~PekOS-hN=X~eRGVHXe^As3+$UMGm
z`BRo1IL&xj`&xJowV6Jvs)bsYtMSyjUdegS$dlyhIV!8VdsUpDg9KCFpa8sE#rb)B
zg*Z17c96Ggs4tOsFd76SVb}6@rs52vlc5#oPXayqEcEiUS}%|u!p-)xj;Pmow<4DhQRJABlZjQR(RcgIz_H{da>Tu>Pbg++
z&5AO$QlT{+I(fqx(sES!c+)IaO|IqJs8~ite^RmIu4PgtenQ8yg4S?hVo
z_%h{>yBx{YZpzbkh4#{kq4Tcq!|>BBR$GpERO@4kA2Kv-ob;@MyyMPlsYY3hh0Js19XR_JUnxvZOALC1+Yt~QrFH(CX2%lYg%meKSd
z*A(f+mX@;B+I2m_e9qJ85DsB6$dra`s}&19AJO#C%8N^wGs_hac3o>S&rdTcR$q>o
zpC8lw;1VKdOB8*A`&{!0IcH~~%0|s9bFEjiMxF4$_(S0;zWDQy$4{9u=kPw4M#U{0
z5x@NWEg={cXG<;;Q1Vs361-YRqaw9F*supB7j@vpO*AG`W*
znew3E@h@)?e3v5YH&cOb)r(c2Bkqfx(Hzmb;7geek#k-{W|jFk)THz#EDo8lWwW7J
zYC)$JKXNTcxa@?ym4~0ub1TdhG;H|M?sAc*U91Kj@kqu3$*~L4mx`X?kqo)WDzoCg
zr_INoE){q7me&Exwj(X2?-jHYicc
z6Q6{_QxpS?i{1`^NPG!NcxMNDzmY!IuKvSXzRvE{GTq1(5)=|~Yl|OFcEaY&v
z%2#Rz2P_e89CXs>j6NTSng#38@y4MCi6p$FO^GNf5{Giv@R0BOpST#E0IrKY3
zkGdvanfhPBbLb^0$~iy$XjPt_ZowLKypd-@?BEqtmNfEYFAoD55b1LCbgBhsK2xE{
zWwQIBbsi~x;0j#wvb?2=J;7BU?VJOT(d5bL6stN%oG+i!_;8bdhxB+NzIPBc=xhKo%3C*JYOyB)(X~|sd?~`pqd;-zI!{THC#O}gvK(*J*(7r0^>CMS^}?Ry
zL^<)R$f?TfQmOnH@7zKUKF52zZehGt<&?-t
zA6)cDWy*De$FpSALFfFiLpp|@np;-hlb(pj)z=k0JB!)Y=?#71j}nbb1dpqG4muQN
z^bARKdF!y=dc-rE2bQG%XpxqPqTm{{qD48EMMx_de%h1|13-I6BC@u9_E={4tQmf2
zm4q_2Wx?}Pfg$Z+1|p+nDC)We=Y5JGL*{FSeM1qoW=l(vxlY$`d#9&nj{#doo2RI1
z)(zW_ML-bTc#0k{6yXX2U|F6q(?YRGJD7<@M$1rCcgxUw#-fPFzIv9Tq3oEIh@jxP
zVi0l0(zz@?O2x3#bj#3tvJ>(7rXMow&?*9D;wSigQ{aaj%rIot3`cFZD7_~~5nm&`T7HLa&nBQ!IH-W{JvdWVq_O1?xSzia0*qZ@8lM1(r5Gk%JkU
ztUAw8ty-vh9Wh5AFdUikV~Ju@p>~cO%y4AY3`eHr3hS{^b`@Xs=>3MH!C*9*EvBWq
z3QwUrws9~6lA~uxYUNuqV2WyzxYdlOMI4ddXGo$IE0(rpBL_1eSvA8^hbq*T9fgsu
zZD%+#lO1hDie;_w`;eo7oPb`U7A^RyPCLpmmmICgGgD6rRj(tS1@a?`naSG8Q93SH
z%`teMy>`w4$7mRGOgj`7Z74Ry7j5oUa31MrLgsisiiK_Yyy_%3G;=?ZNXGB`WBgAAm@gr>Bt=szXQI%eq_9Gn(|*
z%XD3X;9eFw=3qk#hM8KSnsdZg0eg>Orc4=6aIOr9xC8R~HA76J&@AbQ+o*RVG3jhJ
z-|EYrd3kc2;5KSR&bh(H>KJ<3`xRxV(YQB$HrZW@p5g4=FdePq;)f+7D7dHPI_N;;
zs~L{ERJ94}X+^w(@4GZdPKUij90kux9L|h`9!8EfPf@d4sKy*|TlP*(5zUWY!nSNE
z+v(|Wyy<2^j*g+HPUjB>#h{4kS=IE=yy&H*$IN~<#h&5J=oosM`!#1b3xel-CiD(X
zkG+~mnR4j@nUHnRYrb
z(?dFjo=&kkH09W~)rw`t&s&Kedx?<})enQ`$V-B(gFZ#BK2K8HjEU~YA&f%ji$k{&
zNy~c;6-g0YFU3@W934YXtyl$`Mm=mCie<*n4-q}X@y0}gR-PJS@Okl)6y=;Bezb~V
zr%|Z>9PzBxw-7sUJ(}(QOeR4k;wQL*DEToC`V2X0hND)oN_4!r(3@S329w3ORP*MG
zXlmo)>x11)>@4pU
z!6ZB8;$JR3M#;pUR*_0{qeIq~pIFXrepyOw?*k}dA
zOtVmhIN~!U>d4GiIyj|go0h4a3O-X}U>$U$j;m!TYE$KRKg*%M7T?dRAw{qVYKd~=
z@GJ)$bGgCSd1B-RO#u~s%RfhrS%2A73vDeYmf3+-MNEIb(;p9^eqWv*Rq%WYa?SzA
zsu*@mD+KuL$xg%>asuqE_Regju>O0Q*a_{8lXDgwt6|7#6ssUps}^6>If3LX#?y3T
zS;;}?TyjXmkkcwuNsf5^)8k0a{M=62#yLaRKV9t^=UjLkCBsjrPzBkva5nP9a#q&s
zH9yn-AYD^u^_8jJ3hie}hKz$AM2)*Q-C7lphuCT
zWhkl@sxX`Rr}4$2*C9nS_YBJN^uJyLiJQFH*_GEoAf>QLda-<#1
za%9v@JZcuINk`1lt29T`bZ2d`u_1GyCFRIE=yBw#8IIb8D%26zhK?dflbt!O+FT}%
z!fQj;K^G%e%TUxUR;MQB;;Rk4(oi&_rQ?^fRg=7sgBgshI?qwV^wjsWBiT%;8$ToH
zdc)ChJWn^5wpEiHk%JkIteWAdUZ_@WsI&1XmU%+2Adb@UXt=hV^~K4KcFrfrC>eg5
z#p=`f_AC~Dj(Gf(DI=nD*6ldQF$%fzaYeM{J&to|e!M!7)iLz6_p8&+p;S-#R_q6n
z9&=^S^7QY+tJB6gH~KhAhMz9YAPdWaC|23lE0$Rw*BO57nzZF%D14+<;$j`laO7^t
zv(&8Z*R8{1(aVt~xX$NN_skpzU6MRC!%?GH-I}c9i%EwqM;pUYdKTy_PfQB069F*j
zU=}0NG8DB6RjniLm%YrVs6+&XXGTPjmsmSWhMx}gw8AnYn)euA6uQ>(gQFfY4wWYg
zg|5uI+5=q3!E79|`aDUkWWQn^mKVJ=OH!%Yv+(hJOHwW#>2X7DJik?=7!wqcB0hWm
z8sunmwpc4sBnmxyezm6@b3>3;FtMjms5%|-OzM|tW^g7ou6A6WRxG@KFBe&bZagjL
zOzW&lsQe8KOD(GY1jV0-$M3I3mNtgtby}*dL~$s*UuGS2gORIcDC!idSVugY=*2|Q
zNS++M#IqZr56{eQ^wfEd+UBhJuxLcRx%m2LS0P8~*;K9~xWpVe4!YsUQ!^aZ3stQR
zb(W)8#$R8gIZ}&5m#SwLzE%UR2W`_L(lF#Ssq(xZfvA7akG&p3a@5LAbh2xCa_jJZ
zz2l%8fjl+CQM*v3I^w+gg$_rsm;q2%9*)BEY6XmPFo%)WG8A(#M%(g*OaFIICQKwq0T1_=cn-9e@Bjg1RM=*DEGA>gz3Lp1Sz-R|^@yO^I
zlIp}pSVSW0_(;0aA&JiTC{IKxNRoCiN0QMqBsKS|*wm)cBcEyifI||U{ZJl~!mHRS
zCXs_Vla~T>
zF;X!JoXatU$~-^aYN6TJ5%Y6E^CMSlhSAIA
zM~gCD)1?4EX7v_<#GF}WE-Fe47z0>J*CdWN`wO+MX
zZMUnnR<%~Imnbs9_2c1Y&&+>2D%Aaoc%OWPGxK+G))Ka>5yM-nSXDdT*6$+X4OV-e
ztS{-fKj1?S`cPRdLs5q+)Im|pjLF%kmB`4jSJb-9i5|N}twWC8Zc*znZ&(Nr~LKN*xCos9RH=9p(e?Jr=Fg>=dK=3WIC(y(a2rEatx&bQLDrV4hKvS#MXD>D
zh_PFwy4tbZEm9rf?aUt!@-@h>Zs6_A!8|N&LL-&PQnkL=-qBN)%0t$5(qp*U-0Buo
WsQVRhmwe1dE9)pvm%BYn*#80gt?5tz
diff --git a/lib/luajit/libluajit.a b/lib/luajit/libluajit.a
new file mode 100644
index 0000000000000000000000000000000000000000..34f40f260d7aff308d135af9d92e802000566a44
GIT binary patch
literal 799482
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