diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 2 | ||||
| -rw-r--r-- | src/jit/.gitignore | 1 | ||||
| -rw-r--r-- | src/jit/bc.lua | 192 | ||||
| -rw-r--r-- | src/jit/bcsave.lua | 500 | ||||
| -rw-r--r-- | src/jit/dis_arm.lua | 543 | ||||
| -rw-r--r-- | src/jit/dis_mips.lua | 428 | ||||
| -rw-r--r-- | src/jit/dis_mipsel.lua | 20 | ||||
| -rw-r--r-- | src/jit/dis_ppc.lua | 591 | ||||
| -rw-r--r-- | src/jit/dis_x64.lua | 20 | ||||
| -rw-r--r-- | src/jit/dis_x86.lua | 836 | ||||
| -rw-r--r-- | src/jit/dump.lua | 696 | ||||
| -rw-r--r-- | src/jit/v.lua | 167 | ||||
| -rw-r--r-- | src/msvcbuild.bat | 2 |
13 files changed, 3996 insertions, 2 deletions
diff --git a/src/Makefile b/src/Makefile index a0e98068..5977c373 100644 --- a/src/Makefile +++ b/src/Makefile | |||
| @@ -401,7 +401,7 @@ LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \ | |||
| 401 | LJVMCORE_O= $(LJVM_O) $(LJCORE_O) | 401 | LJVMCORE_O= $(LJVM_O) $(LJCORE_O) |
| 402 | LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) | 402 | LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) |
| 403 | 403 | ||
| 404 | LIB_VMDEF= ../lib/vmdef.lua | 404 | LIB_VMDEF= jit/vmdef.lua |
| 405 | LIB_VMDEFP= $(LIB_VMDEF) | 405 | LIB_VMDEFP= $(LIB_VMDEF) |
| 406 | 406 | ||
| 407 | LUAJIT_O= luajit.o | 407 | LUAJIT_O= luajit.o |
diff --git a/src/jit/.gitignore b/src/jit/.gitignore new file mode 100644 index 00000000..500e2855 --- /dev/null +++ b/src/jit/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| vmdef.lua | |||
diff --git a/src/jit/bc.lua b/src/jit/bc.lua new file mode 100644 index 00000000..15317bcd --- /dev/null +++ b/src/jit/bc.lua | |||
| @@ -0,0 +1,192 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT bytecode listing module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module lists the bytecode of a Lua function. If it's loaded by -jbc | ||
| 9 | -- it hooks into the parser and lists all functions of a chunk as they | ||
| 10 | -- are parsed. | ||
| 11 | -- | ||
| 12 | -- Example usage: | ||
| 13 | -- | ||
| 14 | -- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)' | ||
| 15 | -- luajit -jbc=- foo.lua | ||
| 16 | -- luajit -jbc=foo.list foo.lua | ||
| 17 | -- | ||
| 18 | -- Default output is to stderr. To redirect the output to a file, pass a | ||
| 19 | -- filename as an argument (use '-' for stdout) or set the environment | ||
| 20 | -- variable LUAJIT_LISTFILE. The file is overwritten every time the module | ||
| 21 | -- is started. | ||
| 22 | -- | ||
| 23 | -- This module can also be used programmatically: | ||
| 24 | -- | ||
| 25 | -- local bc = require("jit.bc") | ||
| 26 | -- | ||
| 27 | -- local function foo() print("hello") end | ||
| 28 | -- | ||
| 29 | -- bc.dump(foo) --> -- BYTECODE -- [...] | ||
| 30 | -- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello" | ||
| 31 | -- | ||
| 32 | -- local out = { | ||
| 33 | -- -- Do something with each line: | ||
| 34 | -- write = function(t, ...) io.write(...) end, | ||
| 35 | -- close = function(t) end, | ||
| 36 | -- flush = function(t) end, | ||
| 37 | -- } | ||
| 38 | -- bc.dump(foo, out) | ||
| 39 | -- | ||
| 40 | ------------------------------------------------------------------------------ | ||
| 41 | |||
| 42 | -- Cache some library functions and objects. | ||
| 43 | local jit = require("jit") | ||
| 44 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 45 | local jutil = require("jit.util") | ||
| 46 | local vmdef = require("jit.vmdef") | ||
| 47 | local bit = require("bit") | ||
| 48 | local sub, gsub, format = string.sub, string.gsub, string.format | ||
| 49 | local byte, band, shr = string.byte, bit.band, bit.rshift | ||
| 50 | local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck | ||
| 51 | local funcuvname = jutil.funcuvname | ||
| 52 | local bcnames = vmdef.bcnames | ||
| 53 | local stdout, stderr = io.stdout, io.stderr | ||
| 54 | |||
| 55 | ------------------------------------------------------------------------------ | ||
| 56 | |||
| 57 | local function ctlsub(c) | ||
| 58 | if c == "\n" then return "\\n" | ||
| 59 | elseif c == "\r" then return "\\r" | ||
| 60 | elseif c == "\t" then return "\\t" | ||
| 61 | elseif c == "\r" then return "\\r" | ||
| 62 | else return format("\\%03d", byte(c)) | ||
| 63 | end | ||
| 64 | end | ||
| 65 | |||
| 66 | -- Return one bytecode line. | ||
| 67 | local function bcline(func, pc, prefix) | ||
| 68 | local ins, m = funcbc(func, pc) | ||
| 69 | if not ins then return end | ||
| 70 | local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) | ||
| 71 | local a = band(shr(ins, 8), 0xff) | ||
| 72 | local oidx = 6*band(ins, 0xff) | ||
| 73 | local op = sub(bcnames, oidx+1, oidx+6) | ||
| 74 | local s = format("%04d %s %-6s %3s ", | ||
| 75 | pc, prefix or " ", op, ma == 0 and "" or a) | ||
| 76 | local d = shr(ins, 16) | ||
| 77 | if mc == 13*128 then -- BCMjump | ||
| 78 | return format("%s=> %04d\n", s, pc+d-0x7fff) | ||
| 79 | end | ||
| 80 | if mb ~= 0 then | ||
| 81 | d = band(d, 0xff) | ||
| 82 | elseif mc == 0 then | ||
| 83 | return s.."\n" | ||
| 84 | end | ||
| 85 | local kc | ||
| 86 | if mc == 10*128 then -- BCMstr | ||
| 87 | kc = funck(func, -d-1) | ||
| 88 | kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) | ||
| 89 | elseif mc == 9*128 then -- BCMnum | ||
| 90 | kc = funck(func, d) | ||
| 91 | if op == "TSETM " then kc = kc - 2^52 end | ||
| 92 | elseif mc == 12*128 then -- BCMfunc | ||
| 93 | local fi = funcinfo(funck(func, -d-1)) | ||
| 94 | if fi.ffid then | ||
| 95 | kc = vmdef.ffnames[fi.ffid] | ||
| 96 | else | ||
| 97 | kc = fi.loc | ||
| 98 | end | ||
| 99 | elseif mc == 5*128 then -- BCMuv | ||
| 100 | kc = funcuvname(func, d) | ||
| 101 | end | ||
| 102 | if ma == 5 then -- BCMuv | ||
| 103 | local ka = funcuvname(func, a) | ||
| 104 | if kc then kc = ka.." ; "..kc else kc = ka end | ||
| 105 | end | ||
| 106 | if mb ~= 0 then | ||
| 107 | local b = shr(ins, 24) | ||
| 108 | if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end | ||
| 109 | return format("%s%3d %3d\n", s, b, d) | ||
| 110 | end | ||
| 111 | if kc then return format("%s%3d ; %s\n", s, d, kc) end | ||
| 112 | if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits | ||
| 113 | return format("%s%3d\n", s, d) | ||
| 114 | end | ||
| 115 | |||
| 116 | -- Collect branch targets of a function. | ||
| 117 | local function bctargets(func) | ||
| 118 | local target = {} | ||
| 119 | for pc=1,1000000000 do | ||
| 120 | local ins, m = funcbc(func, pc) | ||
| 121 | if not ins then break end | ||
| 122 | if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end | ||
| 123 | end | ||
| 124 | return target | ||
| 125 | end | ||
| 126 | |||
| 127 | -- Dump bytecode instructions of a function. | ||
| 128 | local function bcdump(func, out, all) | ||
| 129 | if not out then out = stdout end | ||
| 130 | local fi = funcinfo(func) | ||
| 131 | if all and fi.children then | ||
| 132 | for n=-1,-1000000000,-1 do | ||
| 133 | local k = funck(func, n) | ||
| 134 | if not k then break end | ||
| 135 | if type(k) == "proto" then bcdump(k, out, true) end | ||
| 136 | end | ||
| 137 | end | ||
| 138 | out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined)) | ||
| 139 | local target = bctargets(func) | ||
| 140 | for pc=1,1000000000 do | ||
| 141 | local s = bcline(func, pc, target[pc] and "=>") | ||
| 142 | if not s then break end | ||
| 143 | out:write(s) | ||
| 144 | end | ||
| 145 | out:write("\n") | ||
| 146 | out:flush() | ||
| 147 | end | ||
| 148 | |||
| 149 | ------------------------------------------------------------------------------ | ||
| 150 | |||
| 151 | -- Active flag and output file handle. | ||
| 152 | local active, out | ||
| 153 | |||
| 154 | -- List handler. | ||
| 155 | local function h_list(func) | ||
| 156 | return bcdump(func, out) | ||
| 157 | end | ||
| 158 | |||
| 159 | -- Detach list handler. | ||
| 160 | local function bclistoff() | ||
| 161 | if active then | ||
| 162 | active = false | ||
| 163 | jit.attach(h_list) | ||
| 164 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 165 | out = nil | ||
| 166 | end | ||
| 167 | end | ||
| 168 | |||
| 169 | -- Open the output file and attach list handler. | ||
| 170 | local function bcliston(outfile) | ||
| 171 | if active then bclistoff() end | ||
| 172 | if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end | ||
| 173 | if outfile then | ||
| 174 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 175 | else | ||
| 176 | out = stderr | ||
| 177 | end | ||
| 178 | jit.attach(h_list, "bc") | ||
| 179 | active = true | ||
| 180 | end | ||
| 181 | |||
| 182 | -- Public module functions. | ||
| 183 | module(...) | ||
| 184 | |||
| 185 | line = bcline | ||
| 186 | dump = bcdump | ||
| 187 | targets = bctargets | ||
| 188 | |||
| 189 | on = bcliston | ||
| 190 | off = bclistoff | ||
| 191 | start = bcliston -- For -j command line option. | ||
| 192 | |||
diff --git a/src/jit/bcsave.lua b/src/jit/bcsave.lua new file mode 100644 index 00000000..70cb6925 --- /dev/null +++ b/src/jit/bcsave.lua | |||
| @@ -0,0 +1,500 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT module to save/list bytecode. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module saves or lists the bytecode for an input file. | ||
| 9 | -- It's run by the -b command line option. | ||
| 10 | -- | ||
| 11 | ------------------------------------------------------------------------------ | ||
| 12 | |||
| 13 | local jit = require("jit") | ||
| 14 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 15 | |||
| 16 | -- Symbol name prefix for LuaJIT bytecode. | ||
| 17 | local LJBC_PREFIX = "luaJIT_BC_" | ||
| 18 | |||
| 19 | ------------------------------------------------------------------------------ | ||
| 20 | |||
| 21 | local function usage() | ||
| 22 | io.stderr:write[[ | ||
| 23 | Save LuaJIT bytecode: luajit -b[options] input output | ||
| 24 | -l Only list bytecode. | ||
| 25 | -s Strip debug info (default). | ||
| 26 | -g Keep debug info. | ||
| 27 | -n name Set module name (default: auto-detect from input name). | ||
| 28 | -t type Set output file type (default: auto-detect from output name). | ||
| 29 | -a arch Override architecture for object files (default: native). | ||
| 30 | -o os Override OS for object files (default: native). | ||
| 31 | -e chunk Use chunk string as input. | ||
| 32 | -- Stop handling options. | ||
| 33 | - Use stdin as input and/or stdout as output. | ||
| 34 | |||
| 35 | File types: c h obj o raw (default) | ||
| 36 | ]] | ||
| 37 | os.exit(1) | ||
| 38 | end | ||
| 39 | |||
| 40 | local function check(ok, ...) | ||
| 41 | if ok then return ok, ... end | ||
| 42 | io.stderr:write("luajit: ", ...) | ||
| 43 | io.stderr:write("\n") | ||
| 44 | os.exit(1) | ||
| 45 | end | ||
| 46 | |||
| 47 | local function readfile(input) | ||
| 48 | if type(input) == "function" then return input end | ||
| 49 | if input == "-" then input = nil end | ||
| 50 | return check(loadfile(input)) | ||
| 51 | end | ||
| 52 | |||
| 53 | local function savefile(name, mode) | ||
| 54 | if name == "-" then return io.stdout end | ||
| 55 | return check(io.open(name, mode)) | ||
| 56 | end | ||
| 57 | |||
| 58 | ------------------------------------------------------------------------------ | ||
| 59 | |||
| 60 | local map_type = { | ||
| 61 | raw = "raw", c = "c", h = "h", o = "obj", obj = "obj", | ||
| 62 | } | ||
| 63 | |||
| 64 | local map_arch = { | ||
| 65 | x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true, | ||
| 66 | mips = true, mipsel = true, | ||
| 67 | } | ||
| 68 | |||
| 69 | local map_os = { | ||
| 70 | linux = true, windows = true, osx = true, freebsd = true, netbsd = true, | ||
| 71 | openbsd = true, solaris = true, | ||
| 72 | } | ||
| 73 | |||
| 74 | local function checkarg(str, map, err) | ||
| 75 | str = string.lower(str) | ||
| 76 | local s = check(map[str], "unknown ", err) | ||
| 77 | return s == true and str or s | ||
| 78 | end | ||
| 79 | |||
| 80 | local function detecttype(str) | ||
| 81 | local ext = string.match(string.lower(str), "%.(%a+)$") | ||
| 82 | return map_type[ext] or "raw" | ||
| 83 | end | ||
| 84 | |||
| 85 | local function checkmodname(str) | ||
| 86 | check(string.match(str, "^[%w_.%-]+$"), "bad module name") | ||
| 87 | return string.gsub(str, "[%.%-]", "_") | ||
| 88 | end | ||
| 89 | |||
| 90 | local function detectmodname(str) | ||
| 91 | if type(str) == "string" then | ||
| 92 | local tail = string.match(str, "[^/\\]+$") | ||
| 93 | if tail then str = tail end | ||
| 94 | local head = string.match(str, "^(.*)%.[^.]*$") | ||
| 95 | if head then str = head end | ||
| 96 | str = string.match(str, "^[%w_.%-]+") | ||
| 97 | else | ||
| 98 | str = nil | ||
| 99 | end | ||
| 100 | check(str, "cannot derive module name, use -n name") | ||
| 101 | return string.gsub(str, "[%.%-]", "_") | ||
| 102 | end | ||
| 103 | |||
| 104 | ------------------------------------------------------------------------------ | ||
| 105 | |||
| 106 | local function bcsave_tail(fp, output, s) | ||
| 107 | local ok, err = fp:write(s) | ||
| 108 | if ok and output ~= "-" then ok, err = fp:close() end | ||
| 109 | check(ok, "cannot write ", output, ": ", err) | ||
| 110 | end | ||
| 111 | |||
| 112 | local function bcsave_raw(output, s) | ||
| 113 | local fp = savefile(output, "wb") | ||
| 114 | bcsave_tail(fp, output, s) | ||
| 115 | end | ||
| 116 | |||
| 117 | local function bcsave_c(ctx, output, s) | ||
| 118 | local fp = savefile(output, "w") | ||
| 119 | if ctx.type == "c" then | ||
| 120 | fp:write(string.format([[ | ||
| 121 | #ifdef _cplusplus | ||
| 122 | extern "C" | ||
| 123 | #endif | ||
| 124 | #ifdef _WIN32 | ||
| 125 | __declspec(dllexport) | ||
| 126 | #endif | ||
| 127 | const char %s%s[] = { | ||
| 128 | ]], LJBC_PREFIX, ctx.modname)) | ||
| 129 | else | ||
| 130 | fp:write(string.format([[ | ||
| 131 | #define %s%s_SIZE %d | ||
| 132 | static const char %s%s[] = { | ||
| 133 | ]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname)) | ||
| 134 | end | ||
| 135 | local t, n, m = {}, 0, 0 | ||
| 136 | for i=1,#s do | ||
| 137 | local b = tostring(string.byte(s, i)) | ||
| 138 | m = m + #b + 1 | ||
| 139 | if m > 78 then | ||
| 140 | fp:write(table.concat(t, ",", 1, n), ",\n") | ||
| 141 | n, m = 0, #b + 1 | ||
| 142 | end | ||
| 143 | n = n + 1 | ||
| 144 | t[n] = b | ||
| 145 | end | ||
| 146 | bcsave_tail(fp, output, table.concat(t, ",", 1, n).."\n};\n") | ||
| 147 | end | ||
| 148 | |||
| 149 | local function bcsave_elfobj(ctx, output, s, ffi) | ||
| 150 | ffi.cdef[[ | ||
| 151 | typedef struct { | ||
| 152 | uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; | ||
| 153 | uint16_t type, machine; | ||
| 154 | uint32_t version; | ||
| 155 | uint32_t entry, phofs, shofs; | ||
| 156 | uint32_t flags; | ||
| 157 | uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; | ||
| 158 | } ELF32header; | ||
| 159 | typedef struct { | ||
| 160 | uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; | ||
| 161 | uint16_t type, machine; | ||
| 162 | uint32_t version; | ||
| 163 | uint64_t entry, phofs, shofs; | ||
| 164 | uint32_t flags; | ||
| 165 | uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; | ||
| 166 | } ELF64header; | ||
| 167 | typedef struct { | ||
| 168 | uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; | ||
| 169 | } ELF32sectheader; | ||
| 170 | typedef struct { | ||
| 171 | uint32_t name, type; | ||
| 172 | uint64_t flags, addr, ofs, size; | ||
| 173 | uint32_t link, info; | ||
| 174 | uint64_t align, entsize; | ||
| 175 | } ELF64sectheader; | ||
| 176 | typedef struct { | ||
| 177 | uint32_t name, value, size; | ||
| 178 | uint8_t info, other; | ||
| 179 | uint16_t sectidx; | ||
| 180 | } ELF32symbol; | ||
| 181 | typedef struct { | ||
| 182 | uint32_t name; | ||
| 183 | uint8_t info, other; | ||
| 184 | uint16_t sectidx; | ||
| 185 | uint64_t value, size; | ||
| 186 | } ELF64symbol; | ||
| 187 | typedef struct { | ||
| 188 | ELF32header hdr; | ||
| 189 | ELF32sectheader sect[6]; | ||
| 190 | ELF32symbol sym[2]; | ||
| 191 | uint8_t space[4096]; | ||
| 192 | } ELF32obj; | ||
| 193 | typedef struct { | ||
| 194 | ELF64header hdr; | ||
| 195 | ELF64sectheader sect[6]; | ||
| 196 | ELF64symbol sym[2]; | ||
| 197 | uint8_t space[4096]; | ||
| 198 | } ELF64obj; | ||
| 199 | ]] | ||
| 200 | local symname = LJBC_PREFIX..ctx.modname | ||
| 201 | local is64, isbe = false, false | ||
| 202 | if ctx.arch == "x64" then | ||
| 203 | is64 = true | ||
| 204 | elseif ctx.arch == "ppc" or ctx.arch == "ppcspe" or ctx.arch == "mips" then | ||
| 205 | isbe = true | ||
| 206 | end | ||
| 207 | |||
| 208 | -- Handle different host/target endianess. | ||
| 209 | local function f32(x) return x end | ||
| 210 | local f16, fofs = f32, f32 | ||
| 211 | if ffi.abi("be") ~= isbe then | ||
| 212 | f32 = bit.bswap | ||
| 213 | function f16(x) return bit.rshift(bit.bswap(x), 16) end | ||
| 214 | if is64 then | ||
| 215 | function fofs(x) return bit.bswap(x)*(2ll^32) end | ||
| 216 | else | ||
| 217 | fofs = f32 | ||
| 218 | end | ||
| 219 | end | ||
| 220 | |||
| 221 | -- Create ELF object and fill in header. | ||
| 222 | local o = ffi.new(is64 and "ELF64obj" or "ELF32obj") | ||
| 223 | local hdr = o.hdr | ||
| 224 | if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi. | ||
| 225 | local bf = assert(io.open("/bin/ls", "rb")) | ||
| 226 | local bs = bf:read(9) | ||
| 227 | bf:close() | ||
| 228 | ffi.copy(o, bs, 9) | ||
| 229 | check(hdr.emagic[0] == 127, "no support for writing native object files") | ||
| 230 | else | ||
| 231 | hdr.emagic = "\127ELF" | ||
| 232 | hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0 | ||
| 233 | end | ||
| 234 | hdr.eclass = is64 and 2 or 1 | ||
| 235 | hdr.eendian = isbe and 2 or 1 | ||
| 236 | hdr.eversion = 1 | ||
| 237 | hdr.type = f16(1) | ||
| 238 | hdr.machine = f16(({ x86=3, x64=62, arm=40, ppc=20, ppcspe=20, mips=8, mipsel=8 })[ctx.arch]) | ||
| 239 | if ctx.arch == "mips" or ctx.arch == "mipsel" then | ||
| 240 | hdr.flags = 0x50001006 | ||
| 241 | end | ||
| 242 | hdr.version = f32(1) | ||
| 243 | hdr.shofs = fofs(ffi.offsetof(o, "sect")) | ||
| 244 | hdr.ehsize = f16(ffi.sizeof(hdr)) | ||
| 245 | hdr.shentsize = f16(ffi.sizeof(o.sect[0])) | ||
| 246 | hdr.shnum = f16(6) | ||
| 247 | hdr.shstridx = f16(2) | ||
| 248 | |||
| 249 | -- Fill in sections and symbols. | ||
| 250 | local sofs, ofs = ffi.offsetof(o, "space"), 1 | ||
| 251 | for i,name in ipairs{ | ||
| 252 | ".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack", | ||
| 253 | } do | ||
| 254 | local sect = o.sect[i] | ||
| 255 | sect.align = fofs(1) | ||
| 256 | sect.name = f32(ofs) | ||
| 257 | ffi.copy(o.space+ofs, name) | ||
| 258 | ofs = ofs + #name+1 | ||
| 259 | end | ||
| 260 | o.sect[1].type = f32(2) -- .symtab | ||
| 261 | o.sect[1].link = f32(3) | ||
| 262 | o.sect[1].info = f32(1) | ||
| 263 | o.sect[1].align = fofs(8) | ||
| 264 | o.sect[1].ofs = fofs(ffi.offsetof(o, "sym")) | ||
| 265 | o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0])) | ||
| 266 | o.sect[1].size = fofs(ffi.sizeof(o.sym)) | ||
| 267 | o.sym[1].name = f32(1) | ||
| 268 | o.sym[1].sectidx = f16(4) | ||
| 269 | o.sym[1].size = fofs(#s) | ||
| 270 | o.sym[1].info = 17 | ||
| 271 | o.sect[2].type = f32(3) -- .shstrtab | ||
| 272 | o.sect[2].ofs = fofs(sofs) | ||
| 273 | o.sect[2].size = fofs(ofs) | ||
| 274 | o.sect[3].type = f32(3) -- .strtab | ||
| 275 | o.sect[3].ofs = fofs(sofs + ofs) | ||
| 276 | o.sect[3].size = fofs(#symname+1) | ||
| 277 | ffi.copy(o.space+ofs+1, symname) | ||
| 278 | ofs = ofs + #symname + 2 | ||
| 279 | o.sect[4].type = f32(1) -- .rodata | ||
| 280 | o.sect[4].flags = fofs(2) | ||
| 281 | o.sect[4].ofs = fofs(sofs + ofs) | ||
| 282 | o.sect[4].size = fofs(#s) | ||
| 283 | o.sect[5].type = f32(1) -- .note.GNU-stack | ||
| 284 | o.sect[5].ofs = fofs(sofs + ofs + #s) | ||
| 285 | |||
| 286 | -- Write ELF object file. | ||
| 287 | local fp = savefile(output, "wb") | ||
| 288 | fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) | ||
| 289 | bcsave_tail(fp, output, s) | ||
| 290 | end | ||
| 291 | |||
| 292 | local function bcsave_peobj(ctx, output, s, ffi) | ||
| 293 | ffi.cdef[[ | ||
| 294 | typedef struct { | ||
| 295 | uint16_t arch, nsects; | ||
| 296 | uint32_t time, symtabofs, nsyms; | ||
| 297 | uint16_t opthdrsz, flags; | ||
| 298 | } PEheader; | ||
| 299 | typedef struct { | ||
| 300 | char name[8]; | ||
| 301 | uint32_t vsize, vaddr, size, ofs, relocofs, lineofs; | ||
| 302 | uint16_t nreloc, nline; | ||
| 303 | uint32_t flags; | ||
| 304 | } PEsection; | ||
| 305 | typedef struct __attribute((packed)) { | ||
| 306 | union { | ||
| 307 | char name[8]; | ||
| 308 | uint32_t nameref[2]; | ||
| 309 | }; | ||
| 310 | uint32_t value; | ||
| 311 | int16_t sect; | ||
| 312 | uint16_t type; | ||
| 313 | uint8_t scl, naux; | ||
| 314 | } PEsym; | ||
| 315 | typedef struct __attribute((packed)) { | ||
| 316 | uint32_t size; | ||
| 317 | uint16_t nreloc, nline; | ||
| 318 | uint32_t cksum; | ||
| 319 | uint16_t assoc; | ||
| 320 | uint8_t comdatsel, unused[3]; | ||
| 321 | } PEsymaux; | ||
| 322 | typedef struct { | ||
| 323 | PEheader hdr; | ||
| 324 | PEsection sect[2]; | ||
| 325 | // Must be an even number of symbol structs. | ||
| 326 | PEsym sym0; | ||
| 327 | PEsymaux sym0aux; | ||
| 328 | PEsym sym1; | ||
| 329 | PEsymaux sym1aux; | ||
| 330 | PEsym sym2; | ||
| 331 | PEsym sym3; | ||
| 332 | uint32_t strtabsize; | ||
| 333 | uint8_t space[4096]; | ||
| 334 | } PEobj; | ||
| 335 | ]] | ||
| 336 | local symname = LJBC_PREFIX..ctx.modname | ||
| 337 | local is64 = false | ||
| 338 | if ctx.arch == "x86" then | ||
| 339 | symname = "_"..symname | ||
| 340 | elseif ctx.arch == "x64" then | ||
| 341 | is64 = true | ||
| 342 | end | ||
| 343 | local symexport = " /EXPORT:"..symname..",DATA " | ||
| 344 | |||
| 345 | -- The file format is always little-endian. Swap if the host is big-endian. | ||
| 346 | local function f32(x) return x end | ||
| 347 | local f16 = f32 | ||
| 348 | if ffi.abi("be") then | ||
| 349 | f32 = bit.bswap | ||
| 350 | function f16(x) return bit.rshift(bit.bswap(x), 16) end | ||
| 351 | end | ||
| 352 | |||
| 353 | -- Create PE object and fill in header. | ||
| 354 | local o = ffi.new("PEobj") | ||
| 355 | local hdr = o.hdr | ||
| 356 | hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch]) | ||
| 357 | hdr.nsects = f16(2) | ||
| 358 | hdr.symtabofs = f32(ffi.offsetof(o, "sym0")) | ||
| 359 | hdr.nsyms = f32(6) | ||
| 360 | |||
| 361 | -- Fill in sections and symbols. | ||
| 362 | o.sect[0].name = ".drectve" | ||
| 363 | o.sect[0].size = f32(#symexport) | ||
| 364 | o.sect[0].flags = f32(0x00100a00) | ||
| 365 | o.sym0.sect = f16(1) | ||
| 366 | o.sym0.scl = 3 | ||
| 367 | o.sym0.name = ".drectve" | ||
| 368 | o.sym0.naux = 1 | ||
| 369 | o.sym0aux.size = f32(#symexport) | ||
| 370 | o.sect[1].name = ".rdata" | ||
| 371 | o.sect[1].size = f32(#s) | ||
| 372 | o.sect[1].flags = f32(0x40300040) | ||
| 373 | o.sym1.sect = f16(2) | ||
| 374 | o.sym1.scl = 3 | ||
| 375 | o.sym1.name = ".rdata" | ||
| 376 | o.sym1.naux = 1 | ||
| 377 | o.sym1aux.size = f32(#s) | ||
| 378 | o.sym2.sect = f16(2) | ||
| 379 | o.sym2.scl = 2 | ||
| 380 | o.sym2.nameref[1] = f32(4) | ||
| 381 | o.sym3.sect = f16(-1) | ||
| 382 | o.sym3.scl = 2 | ||
| 383 | o.sym3.value = f32(1) | ||
| 384 | o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant. | ||
| 385 | ffi.copy(o.space, symname) | ||
| 386 | local ofs = #symname + 1 | ||
| 387 | o.strtabsize = f32(ofs + 4) | ||
| 388 | o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs) | ||
| 389 | ffi.copy(o.space + ofs, symexport) | ||
| 390 | ofs = ofs + #symexport | ||
| 391 | o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs) | ||
| 392 | |||
| 393 | -- Write PE object file. | ||
| 394 | local fp = savefile(output, "wb") | ||
| 395 | fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) | ||
| 396 | bcsave_tail(fp, output, s) | ||
| 397 | end | ||
| 398 | |||
| 399 | local function bcsave_machobj(ctx, output, s, ffi) | ||
| 400 | check(false, "NYI: no support for writing OSX object files") | ||
| 401 | end | ||
| 402 | |||
| 403 | local function bcsave_obj(ctx, output, s) | ||
| 404 | local ok, ffi = pcall(require, "ffi") | ||
| 405 | check(ok, "FFI library required to write this file type") | ||
| 406 | if ctx.os == "windows" then | ||
| 407 | return bcsave_peobj(ctx, output, s, ffi) | ||
| 408 | elseif ctx.os == "osx" then | ||
| 409 | return bcsave_machobj(ctx, output, s, ffi) | ||
| 410 | else | ||
| 411 | return bcsave_elfobj(ctx, output, s, ffi) | ||
| 412 | end | ||
| 413 | end | ||
| 414 | |||
| 415 | ------------------------------------------------------------------------------ | ||
| 416 | |||
| 417 | local function bclist(input, output) | ||
| 418 | local f = readfile(input) | ||
| 419 | require("jit.bc").dump(f, savefile(output, "w"), true) | ||
| 420 | end | ||
| 421 | |||
| 422 | local function bcsave(ctx, input, output) | ||
| 423 | local f = readfile(input) | ||
| 424 | local s = string.dump(f, ctx.strip) | ||
| 425 | local t = ctx.type | ||
| 426 | if not t then | ||
| 427 | t = detecttype(output) | ||
| 428 | ctx.type = t | ||
| 429 | end | ||
| 430 | if t == "raw" then | ||
| 431 | bcsave_raw(output, s) | ||
| 432 | else | ||
| 433 | if not ctx.modname then ctx.modname = detectmodname(input) end | ||
| 434 | if t == "obj" then | ||
| 435 | bcsave_obj(ctx, output, s) | ||
| 436 | else | ||
| 437 | bcsave_c(ctx, output, s) | ||
| 438 | end | ||
| 439 | end | ||
| 440 | end | ||
| 441 | |||
| 442 | local function docmd(...) | ||
| 443 | local arg = {...} | ||
| 444 | local n = 1 | ||
| 445 | local list = false | ||
| 446 | local ctx = { | ||
| 447 | strip = true, arch = jit.arch, os = string.lower(jit.os), | ||
| 448 | type = false, modname = false, | ||
| 449 | } | ||
| 450 | while n <= #arg do | ||
| 451 | local a = arg[n] | ||
| 452 | if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then | ||
| 453 | table.remove(arg, n) | ||
| 454 | if a == "--" then break end | ||
| 455 | for m=2,#a do | ||
| 456 | local opt = string.sub(a, m, m) | ||
| 457 | if opt == "l" then | ||
| 458 | list = true | ||
| 459 | elseif opt == "s" then | ||
| 460 | ctx.strip = true | ||
| 461 | elseif opt == "g" then | ||
| 462 | ctx.strip = false | ||
| 463 | else | ||
| 464 | if arg[n] == nil or m ~= #a then usage() end | ||
| 465 | if opt == "e" then | ||
| 466 | if n ~= 1 then usage() end | ||
| 467 | arg[1] = check(loadstring(arg[1])) | ||
| 468 | elseif opt == "n" then | ||
| 469 | ctx.modname = checkmodname(table.remove(arg, n)) | ||
| 470 | elseif opt == "t" then | ||
| 471 | ctx.type = checkarg(table.remove(arg, n), map_type, "file type") | ||
| 472 | elseif opt == "a" then | ||
| 473 | ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture") | ||
| 474 | elseif opt == "o" then | ||
| 475 | ctx.os = checkarg(table.remove(arg, n), map_os, "OS name") | ||
| 476 | else | ||
| 477 | usage() | ||
| 478 | end | ||
| 479 | end | ||
| 480 | end | ||
| 481 | else | ||
| 482 | n = n + 1 | ||
| 483 | end | ||
| 484 | end | ||
| 485 | if list then | ||
| 486 | if #arg == 0 or #arg > 2 then usage() end | ||
| 487 | bclist(arg[1], arg[2] or "-") | ||
| 488 | else | ||
| 489 | if #arg ~= 2 then usage() end | ||
| 490 | bcsave(ctx, arg[1], arg[2]) | ||
| 491 | end | ||
| 492 | end | ||
| 493 | |||
| 494 | ------------------------------------------------------------------------------ | ||
| 495 | |||
| 496 | -- Public module functions. | ||
| 497 | module(...) | ||
| 498 | |||
| 499 | start = docmd -- Process -b command line option. | ||
| 500 | |||
diff --git a/src/jit/dis_arm.lua b/src/jit/dis_arm.lua new file mode 100644 index 00000000..0fcd1bed --- /dev/null +++ b/src/jit/dis_arm.lua | |||
| @@ -0,0 +1,543 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT ARM disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
| 8 | -- | ||
| 9 | -- It disassembles most user-mode ARMv7 instructions | ||
| 10 | -- NYI: Advanced SIMD and VFP instructions. | ||
| 11 | ------------------------------------------------------------------------------ | ||
| 12 | |||
| 13 | local type = type | ||
| 14 | local sub, byte, format = string.sub, string.byte, string.format | ||
| 15 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
| 16 | local concat = table.concat | ||
| 17 | local bit = require("bit") | ||
| 18 | local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex | ||
| 19 | local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift | ||
| 20 | |||
| 21 | ------------------------------------------------------------------------------ | ||
| 22 | -- Opcode maps | ||
| 23 | ------------------------------------------------------------------------------ | ||
| 24 | |||
| 25 | local map_loadc = { | ||
| 26 | shift = 9, mask = 7, | ||
| 27 | [5] = { | ||
| 28 | shift = 0, mask = 0 -- NYI VFP load/store. | ||
| 29 | }, | ||
| 30 | _ = { | ||
| 31 | shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc. | ||
| 32 | }, | ||
| 33 | } | ||
| 34 | |||
| 35 | local map_datac = { | ||
| 36 | shift = 24, mask = 1, | ||
| 37 | [0] = { | ||
| 38 | shift = 9, mask = 7, | ||
| 39 | [5] = { | ||
| 40 | shift = 0, mask = 0 -- NYI VFP data. | ||
| 41 | }, | ||
| 42 | _ = { | ||
| 43 | shift = 0, mask = 0 -- NYI cdp, mcr, mrc. | ||
| 44 | }, | ||
| 45 | }, | ||
| 46 | "svcT", | ||
| 47 | } | ||
| 48 | |||
| 49 | local map_loadcu = { | ||
| 50 | shift = 0, mask = 0, -- NYI unconditional CP load/store. | ||
| 51 | } | ||
| 52 | |||
| 53 | local map_datacu = { | ||
| 54 | shift = 0, mask = 0, -- NYI unconditional CP data. | ||
| 55 | } | ||
| 56 | |||
| 57 | local map_simddata = { | ||
| 58 | shift = 0, mask = 0, -- NYI SIMD data. | ||
| 59 | } | ||
| 60 | |||
| 61 | local map_simdload = { | ||
| 62 | shift = 0, mask = 0, -- NYI SIMD load/store, preload. | ||
| 63 | } | ||
| 64 | |||
| 65 | local map_preload = { | ||
| 66 | shift = 0, mask = 0, -- NYI preload. | ||
| 67 | } | ||
| 68 | |||
| 69 | local map_media = { | ||
| 70 | shift = 20, mask = 31, | ||
| 71 | [0] = false, | ||
| 72 | { --01 | ||
| 73 | shift = 5, mask = 7, | ||
| 74 | [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM", | ||
| 75 | "sadd8DNM", false, false, "ssub8DNM", | ||
| 76 | }, | ||
| 77 | { --02 | ||
| 78 | shift = 5, mask = 7, | ||
| 79 | [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM", | ||
| 80 | "qadd8DNM", false, false, "qsub8DNM", | ||
| 81 | }, | ||
| 82 | { --03 | ||
| 83 | shift = 5, mask = 7, | ||
| 84 | [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM", | ||
| 85 | "shadd8DNM", false, false, "shsub8DNM", | ||
| 86 | }, | ||
| 87 | false, | ||
| 88 | { --05 | ||
| 89 | shift = 5, mask = 7, | ||
| 90 | [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM", | ||
| 91 | "uadd8DNM", false, false, "usub8DNM", | ||
| 92 | }, | ||
| 93 | { --06 | ||
| 94 | shift = 5, mask = 7, | ||
| 95 | [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM", | ||
| 96 | "uqadd8DNM", false, false, "uqsub8DNM", | ||
| 97 | }, | ||
| 98 | { --07 | ||
| 99 | shift = 5, mask = 7, | ||
| 100 | [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM", | ||
| 101 | "uhadd8DNM", false, false, "uhsub8DNM", | ||
| 102 | }, | ||
| 103 | { --08 | ||
| 104 | shift = 5, mask = 7, | ||
| 105 | [0] = "pkhbtDNMU", false, "pkhtbDNMU", | ||
| 106 | { shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", }, | ||
| 107 | "pkhbtDNMU", "selDNM", "pkhtbDNMU", | ||
| 108 | }, | ||
| 109 | false, | ||
| 110 | { --0a | ||
| 111 | shift = 5, mask = 7, | ||
| 112 | [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu", | ||
| 113 | { shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", }, | ||
| 114 | "ssatDxMu", false, "ssatDxMu", | ||
| 115 | }, | ||
| 116 | { --0b | ||
| 117 | shift = 5, mask = 7, | ||
| 118 | [0] = "ssatDxMu", "revDM", "ssatDxMu", | ||
| 119 | { shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", }, | ||
| 120 | "ssatDxMu", "rev16DM", "ssatDxMu", | ||
| 121 | }, | ||
| 122 | { --0c | ||
| 123 | shift = 5, mask = 7, | ||
| 124 | [3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", }, | ||
| 125 | }, | ||
| 126 | false, | ||
| 127 | { --0e | ||
| 128 | shift = 5, mask = 7, | ||
| 129 | [0] = "usatDwMu", "usat16DwM", "usatDwMu", | ||
| 130 | { shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", }, | ||
| 131 | "usatDwMu", false, "usatDwMu", | ||
| 132 | }, | ||
| 133 | { --0f | ||
| 134 | shift = 5, mask = 7, | ||
| 135 | [0] = "usatDwMu", "rbitDM", "usatDwMu", | ||
| 136 | { shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", }, | ||
| 137 | "usatDwMu", "revshDM", "usatDwMu", | ||
| 138 | }, | ||
| 139 | { --10 | ||
| 140 | shift = 12, mask = 15, | ||
| 141 | [15] = { | ||
| 142 | shift = 5, mask = 7, | ||
| 143 | "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS", | ||
| 144 | }, | ||
| 145 | _ = { | ||
| 146 | shift = 5, mask = 7, | ||
| 147 | [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD", | ||
| 148 | }, | ||
| 149 | }, | ||
| 150 | false, false, false, | ||
| 151 | { --14 | ||
| 152 | shift = 5, mask = 7, | ||
| 153 | [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS", | ||
| 154 | }, | ||
| 155 | { --15 | ||
| 156 | shift = 5, mask = 7, | ||
| 157 | [0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", }, | ||
| 158 | { shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", }, | ||
| 159 | false, false, false, false, | ||
| 160 | "smmlsNMSD", "smmlsrNMSD", | ||
| 161 | }, | ||
| 162 | false, false, | ||
| 163 | { --18 | ||
| 164 | shift = 5, mask = 7, | ||
| 165 | [0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", }, | ||
| 166 | }, | ||
| 167 | false, | ||
| 168 | { --1a | ||
| 169 | shift = 5, mask = 3, [2] = "sbfxDMvw", | ||
| 170 | }, | ||
| 171 | { --1b | ||
| 172 | shift = 5, mask = 3, [2] = "sbfxDMvw", | ||
| 173 | }, | ||
| 174 | { --1c | ||
| 175 | shift = 5, mask = 3, | ||
| 176 | [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, | ||
| 177 | }, | ||
| 178 | { --1d | ||
| 179 | shift = 5, mask = 3, | ||
| 180 | [0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, | ||
| 181 | }, | ||
| 182 | { --1e | ||
| 183 | shift = 5, mask = 3, [2] = "ubfxDMvw", | ||
| 184 | }, | ||
| 185 | { --1f | ||
| 186 | shift = 5, mask = 3, [2] = "ubfxDMvw", | ||
| 187 | }, | ||
| 188 | } | ||
| 189 | |||
| 190 | local map_load = { | ||
| 191 | shift = 21, mask = 9, | ||
| 192 | { | ||
| 193 | shift = 20, mask = 5, | ||
| 194 | [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL", | ||
| 195 | }, | ||
| 196 | _ = { | ||
| 197 | shift = 20, mask = 5, | ||
| 198 | [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL", | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | local map_load1 = { | ||
| 203 | shift = 4, mask = 1, | ||
| 204 | [0] = map_load, map_media, | ||
| 205 | } | ||
| 206 | |||
| 207 | local map_loadm = { | ||
| 208 | shift = 20, mask = 1, | ||
| 209 | [0] = { | ||
| 210 | shift = 23, mask = 3, | ||
| 211 | [0] = "stmdaNR", "stmNR", | ||
| 212 | { shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR", | ||
| 213 | }, | ||
| 214 | { | ||
| 215 | shift = 23, mask = 3, | ||
| 216 | [0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", }, | ||
| 217 | "ldmdbNR", "ldmibNR", | ||
| 218 | }, | ||
| 219 | } | ||
| 220 | |||
| 221 | local map_data = { | ||
| 222 | shift = 21, mask = 15, | ||
| 223 | [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs", | ||
| 224 | "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs", | ||
| 225 | "tstNP", "teqNP", "cmpNP", "cmnNP", | ||
| 226 | "orrDNPs", "movDPs", "bicDNPs", "mvnDPs", | ||
| 227 | } | ||
| 228 | |||
| 229 | local map_mul = { | ||
| 230 | shift = 21, mask = 7, | ||
| 231 | [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS", | ||
| 232 | "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs", | ||
| 233 | } | ||
| 234 | |||
| 235 | local map_sync = { | ||
| 236 | shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd. | ||
| 237 | [0] = "swpDMN", false, false, false, | ||
| 238 | "swpbDMN", false, false, false, | ||
| 239 | "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN", | ||
| 240 | "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN", | ||
| 241 | } | ||
| 242 | |||
| 243 | local map_mulh = { | ||
| 244 | shift = 21, mask = 3, | ||
| 245 | [0] = { shift = 5, mask = 3, | ||
| 246 | [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", }, | ||
| 247 | { shift = 5, mask = 3, | ||
| 248 | [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", }, | ||
| 249 | { shift = 5, mask = 3, | ||
| 250 | [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", }, | ||
| 251 | { shift = 5, mask = 3, | ||
| 252 | [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", }, | ||
| 253 | } | ||
| 254 | |||
| 255 | local map_misc = { | ||
| 256 | shift = 4, mask = 7, | ||
| 257 | -- NYI: decode PSR bits of msr. | ||
| 258 | [0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", }, | ||
| 259 | { shift = 21, mask = 3, "bxM", false, "clzDM", }, | ||
| 260 | { shift = 21, mask = 3, "bxjM", }, | ||
| 261 | { shift = 21, mask = 3, "blxM", }, | ||
| 262 | false, | ||
| 263 | { shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", }, | ||
| 264 | false, | ||
| 265 | { shift = 21, mask = 3, "bkptK", }, | ||
| 266 | } | ||
| 267 | |||
| 268 | local map_datar = { | ||
| 269 | shift = 4, mask = 9, | ||
| 270 | [9] = { | ||
| 271 | shift = 5, mask = 3, | ||
| 272 | [0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, }, | ||
| 273 | { shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", }, | ||
| 274 | { shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", }, | ||
| 275 | { shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", }, | ||
| 276 | }, | ||
| 277 | _ = { | ||
| 278 | shift = 20, mask = 25, | ||
| 279 | [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, }, | ||
| 280 | _ = { | ||
| 281 | shift = 0, mask = 0xffffffff, | ||
| 282 | [bor(0xe1a00000)] = "nop", | ||
| 283 | _ = map_data, | ||
| 284 | } | ||
| 285 | }, | ||
| 286 | } | ||
| 287 | |||
| 288 | local map_datai = { | ||
| 289 | shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12. | ||
| 290 | [16] = "movwDW", [20] = "movtDW", | ||
| 291 | [18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", }, | ||
| 292 | [22] = "msrNW", | ||
| 293 | _ = map_data, | ||
| 294 | } | ||
| 295 | |||
| 296 | local map_branch = { | ||
| 297 | shift = 24, mask = 1, | ||
| 298 | [0] = "bB", "blB" | ||
| 299 | } | ||
| 300 | |||
| 301 | local map_condins = { | ||
| 302 | [0] = map_datar, map_datai, map_load, map_load1, | ||
| 303 | map_loadm, map_branch, map_loadc, map_datac | ||
| 304 | } | ||
| 305 | |||
| 306 | -- NYI: setend. | ||
| 307 | local map_uncondins = { | ||
| 308 | [0] = false, map_simddata, map_simdload, map_preload, | ||
| 309 | false, "blxB", map_loadcu, map_datacu, | ||
| 310 | } | ||
| 311 | |||
| 312 | ------------------------------------------------------------------------------ | ||
| 313 | |||
| 314 | local map_gpr = { | ||
| 315 | [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | ||
| 316 | "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", | ||
| 317 | } | ||
| 318 | |||
| 319 | local map_cond = { | ||
| 320 | [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", | ||
| 321 | "hi", "ls", "ge", "lt", "gt", "le", "al", | ||
| 322 | } | ||
| 323 | |||
| 324 | local map_shift = { [0] = "lsl", "lsr", "asr", "ror", } | ||
| 325 | |||
| 326 | ------------------------------------------------------------------------------ | ||
| 327 | |||
| 328 | -- Output a nicely formatted line with an opcode and operands. | ||
| 329 | local function putop(ctx, text, operands) | ||
| 330 | local pos = ctx.pos | ||
| 331 | local extra = "" | ||
| 332 | if ctx.rel then | ||
| 333 | local sym = ctx.symtab[ctx.rel] | ||
| 334 | if sym then | ||
| 335 | extra = "\t->"..sym | ||
| 336 | elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then | ||
| 337 | extra = "\t; 0x"..tohex(ctx.rel) | ||
| 338 | end | ||
| 339 | end | ||
| 340 | if ctx.hexdump > 0 then | ||
| 341 | ctx.out(format("%08x %s %-5s %s%s\n", | ||
| 342 | ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) | ||
| 343 | else | ||
| 344 | ctx.out(format("%08x %-5s %s%s\n", | ||
| 345 | ctx.addr+pos, text, concat(operands, ", "), extra)) | ||
| 346 | end | ||
| 347 | ctx.pos = pos + 4 | ||
| 348 | end | ||
| 349 | |||
| 350 | -- Fallback for unknown opcodes. | ||
| 351 | local function unknown(ctx) | ||
| 352 | return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) | ||
| 353 | end | ||
| 354 | |||
| 355 | -- Format operand 2 of load/store opcodes. | ||
| 356 | local function fmtload(ctx, op, pos) | ||
| 357 | local base = map_gpr[band(rshift(op, 16), 15)] | ||
| 358 | local x, ofs | ||
| 359 | local ext = (band(op, 0x04000000) == 0) | ||
| 360 | if not ext and band(op, 0x02000000) == 0 then | ||
| 361 | ofs = band(op, 4095) | ||
| 362 | if band(op, 0x00800000) == 0 then ofs = -ofs end | ||
| 363 | if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end | ||
| 364 | ofs = "#"..ofs | ||
| 365 | elseif ext and band(op, 0x00400000) ~= 0 then | ||
| 366 | ofs = band(op, 15) + band(rshift(op, 4), 0xf0) | ||
| 367 | if band(op, 0x00800000) == 0 then ofs = -ofs end | ||
| 368 | if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end | ||
| 369 | ofs = "#"..ofs | ||
| 370 | else | ||
| 371 | ofs = map_gpr[band(op, 15)] | ||
| 372 | if ext or band(op, 0xfe0) == 0 then | ||
| 373 | elseif band(op, 0xfe0) == 0x60 then | ||
| 374 | ofs = format("%s, rrx", ofs) | ||
| 375 | else | ||
| 376 | local sh = band(rshift(op, 7), 31) | ||
| 377 | if sh == 0 then sh = 32 end | ||
| 378 | ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh) | ||
| 379 | end | ||
| 380 | if band(op, 0x00800000) == 0 then ofs = "-"..ofs end | ||
| 381 | end | ||
| 382 | if ofs == "#0" then | ||
| 383 | x = format("[%s]", base) | ||
| 384 | elseif band(op, 0x01000000) == 0 then | ||
| 385 | x = format("[%s], %s", base, ofs) | ||
| 386 | else | ||
| 387 | x = format("[%s, %s]", base, ofs) | ||
| 388 | end | ||
| 389 | if band(op, 0x01200000) == 0x01200000 then x = x.."!" end | ||
| 390 | return x | ||
| 391 | end | ||
| 392 | |||
| 393 | -- Disassemble a single instruction. | ||
| 394 | local function disass_ins(ctx) | ||
| 395 | local pos = ctx.pos | ||
| 396 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
| 397 | local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) | ||
| 398 | local operands = {} | ||
| 399 | local suffix = "" | ||
| 400 | local last, name, pat | ||
| 401 | ctx.op = op | ||
| 402 | ctx.rel = nil | ||
| 403 | |||
| 404 | local cond = rshift(op, 28) | ||
| 405 | local opat | ||
| 406 | if cond == 15 then | ||
| 407 | opat = map_uncondins[band(rshift(op, 25), 7)] | ||
| 408 | else | ||
| 409 | if cond ~= 14 then suffix = map_cond[cond] end | ||
| 410 | opat = map_condins[band(rshift(op, 25), 7)] | ||
| 411 | end | ||
| 412 | while type(opat) ~= "string" do | ||
| 413 | if not opat then return unknown(ctx) end | ||
| 414 | opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ | ||
| 415 | end | ||
| 416 | name, pat = match(opat, "^([a-z0-9]*)(.*)") | ||
| 417 | |||
| 418 | for p in gmatch(pat, ".") do | ||
| 419 | local x = nil | ||
| 420 | if p == "D" then | ||
| 421 | x = map_gpr[band(rshift(op, 12), 15)] | ||
| 422 | elseif p == "N" then | ||
| 423 | x = map_gpr[band(rshift(op, 16), 15)] | ||
| 424 | elseif p == "S" then | ||
| 425 | x = map_gpr[band(rshift(op, 8), 15)] | ||
| 426 | elseif p == "M" then | ||
| 427 | x = map_gpr[band(op, 15)] | ||
| 428 | elseif p == "P" then | ||
| 429 | if band(op, 0x02000000) ~= 0 then | ||
| 430 | x = ror(band(op, 255), 2*band(rshift(op, 8), 15)) | ||
| 431 | else | ||
| 432 | x = map_gpr[band(op, 15)] | ||
| 433 | if band(op, 0xff0) ~= 0 then | ||
| 434 | operands[#operands+1] = x | ||
| 435 | local s = map_shift[band(rshift(op, 5), 3)] | ||
| 436 | local r = nil | ||
| 437 | if band(op, 0xf90) == 0 then | ||
| 438 | if s == "ror" then s = "rrx" else r = "#32" end | ||
| 439 | elseif band(op, 0x10) == 0 then | ||
| 440 | r = "#"..band(rshift(op, 7), 31) | ||
| 441 | else | ||
| 442 | r = map_gpr[band(rshift(op, 8), 15)] | ||
| 443 | end | ||
| 444 | if name == "mov" then name = s; x = r | ||
| 445 | elseif r then x = format("%s %s", s, r) | ||
| 446 | else x = s end | ||
| 447 | end | ||
| 448 | end | ||
| 449 | elseif p == "L" then | ||
| 450 | x = fmtload(ctx, op, pos, false) | ||
| 451 | elseif p == "B" then | ||
| 452 | local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6) | ||
| 453 | if cond == 15 then addr = addr + band(rshift(op, 23), 2) end | ||
| 454 | ctx.rel = addr | ||
| 455 | x = "0x"..tohex(addr) | ||
| 456 | elseif p == "R" then | ||
| 457 | if band(op, 0x00200000) ~= 0 and #operands == 1 then | ||
| 458 | operands[1] = operands[1].."!" | ||
| 459 | end | ||
| 460 | local t = {} | ||
| 461 | for i=0,15 do | ||
| 462 | if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end | ||
| 463 | end | ||
| 464 | x = "{"..concat(t, ", ").."}" | ||
| 465 | elseif p == "W" then | ||
| 466 | x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000) | ||
| 467 | elseif p == "T" then | ||
| 468 | x = "#0x"..tohex(band(op, 0x00ffffff), 6) | ||
| 469 | elseif p == "U" then | ||
| 470 | x = band(rshift(op, 7), 31) | ||
| 471 | if x == 0 then x = nil end | ||
| 472 | elseif p == "u" then | ||
| 473 | x = band(rshift(op, 7), 31) | ||
| 474 | if band(op, 0x40) == 0 then | ||
| 475 | if x == 0 then x = nil else x = "lsl #"..x end | ||
| 476 | else | ||
| 477 | if x == 0 then x = "asr #32" else x = "asr #"..x end | ||
| 478 | end | ||
| 479 | elseif p == "v" then | ||
| 480 | x = band(rshift(op, 7), 31) | ||
| 481 | elseif p == "w" then | ||
| 482 | x = band(rshift(op, 16), 31) | ||
| 483 | elseif p == "x" then | ||
| 484 | x = band(rshift(op, 16), 31) + 1 | ||
| 485 | elseif p == "X" then | ||
| 486 | x = band(rshift(op, 16), 31) - last + 1 | ||
| 487 | elseif p == "K" then | ||
| 488 | x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4) | ||
| 489 | elseif p == "s" then | ||
| 490 | if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end | ||
| 491 | else | ||
| 492 | assert(false) | ||
| 493 | end | ||
| 494 | if x then | ||
| 495 | last = x | ||
| 496 | if type(x) == "number" then x = "#"..x end | ||
| 497 | operands[#operands+1] = x | ||
| 498 | end | ||
| 499 | end | ||
| 500 | |||
| 501 | return putop(ctx, name..suffix, operands) | ||
| 502 | end | ||
| 503 | |||
| 504 | ------------------------------------------------------------------------------ | ||
| 505 | |||
| 506 | -- Disassemble a block of code. | ||
| 507 | local function disass_block(ctx, ofs, len) | ||
| 508 | if not ofs then ofs = 0 end | ||
| 509 | local stop = len and ofs+len or #ctx.code | ||
| 510 | ctx.pos = ofs | ||
| 511 | ctx.rel = nil | ||
| 512 | while ctx.pos < stop do disass_ins(ctx) end | ||
| 513 | end | ||
| 514 | |||
| 515 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 516 | local function create_(code, addr, out) | ||
| 517 | local ctx = {} | ||
| 518 | ctx.code = code | ||
| 519 | ctx.addr = addr or 0 | ||
| 520 | ctx.out = out or io.write | ||
| 521 | ctx.symtab = {} | ||
| 522 | ctx.disass = disass_block | ||
| 523 | ctx.hexdump = 8 | ||
| 524 | return ctx | ||
| 525 | end | ||
| 526 | |||
| 527 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 528 | local function disass_(code, addr, out) | ||
| 529 | create_(code, addr, out):disass() | ||
| 530 | end | ||
| 531 | |||
| 532 | -- Return register name for RID. | ||
| 533 | local function regname_(r) | ||
| 534 | return map_gpr[r] | ||
| 535 | end | ||
| 536 | |||
| 537 | -- Public module functions. | ||
| 538 | module(...) | ||
| 539 | |||
| 540 | create = create_ | ||
| 541 | disass = disass_ | ||
| 542 | regname = regname_ | ||
| 543 | |||
diff --git a/src/jit/dis_mips.lua b/src/jit/dis_mips.lua new file mode 100644 index 00000000..2edfdf40 --- /dev/null +++ b/src/jit/dis_mips.lua | |||
| @@ -0,0 +1,428 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT MIPS disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2011 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
| 8 | -- | ||
| 9 | -- It disassembles all standard MIPS32R1/R2 instructions. | ||
| 10 | -- Default mode is big-endian, but see: dis_mipsel.lua | ||
| 11 | ------------------------------------------------------------------------------ | ||
| 12 | |||
| 13 | local type = type | ||
| 14 | local sub, byte, format = string.sub, string.byte, string.format | ||
| 15 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
| 16 | local concat = table.concat | ||
| 17 | local bit = require("bit") | ||
| 18 | local band, bor, tohex = bit.band, bit.bor, bit.tohex | ||
| 19 | local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift | ||
| 20 | |||
| 21 | ------------------------------------------------------------------------------ | ||
| 22 | -- Primary and extended opcode maps | ||
| 23 | ------------------------------------------------------------------------------ | ||
| 24 | |||
| 25 | local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", } | ||
| 26 | local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", } | ||
| 27 | local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", } | ||
| 28 | |||
| 29 | local map_special = { | ||
| 30 | shift = 0, mask = 63, | ||
| 31 | [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, | ||
| 32 | map_movci, map_srl, "sraDTA", | ||
| 33 | "sllvDTS", false, map_srlv, "sravDTS", | ||
| 34 | "jrS", "jalrD1S", "movzDST", "movnDST", | ||
| 35 | "syscallY", "breakY", false, "sync", | ||
| 36 | "mfhiD", "mthiS", "mfloD", "mtloS", | ||
| 37 | false, false, false, false, | ||
| 38 | "multST", "multuST", "divST", "divuST", | ||
| 39 | false, false, false, false, | ||
| 40 | "addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", | ||
| 41 | "andDST", "orDST", "xorDST", "nor|notDST0", | ||
| 42 | false, false, "sltDST", "sltuDST", | ||
| 43 | false, false, false, false, | ||
| 44 | "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", | ||
| 45 | "teqSTZ", false, "tneSTZ", | ||
| 46 | } | ||
| 47 | |||
| 48 | local map_special2 = { | ||
| 49 | shift = 0, mask = 63, | ||
| 50 | [0] = "maddST", "madduST", "mulDST", false, | ||
| 51 | "msubST", "msubuST", | ||
| 52 | [32] = "clzDS", [33] = "cloDS", | ||
| 53 | [63] = "sdbbpY", | ||
| 54 | } | ||
| 55 | |||
| 56 | local map_bshfl = { | ||
| 57 | shift = 6, mask = 31, | ||
| 58 | [2] = "wsbhDT", | ||
| 59 | [16] = "sebDT", | ||
| 60 | [24] = "sehDT", | ||
| 61 | } | ||
| 62 | |||
| 63 | local map_special3 = { | ||
| 64 | shift = 0, mask = 63, | ||
| 65 | [0] = "extTSAK", [4] = "insTSAL", | ||
| 66 | [32] = map_bshfl, | ||
| 67 | [59] = "rdhwrTD", | ||
| 68 | } | ||
| 69 | |||
| 70 | local map_regimm = { | ||
| 71 | shift = 16, mask = 31, | ||
| 72 | [0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB", | ||
| 73 | false, false, false, false, | ||
| 74 | "tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI", | ||
| 75 | "teqiSI", false, "tneiSI", false, | ||
| 76 | "bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB", | ||
| 77 | false, false, false, false, | ||
| 78 | false, false, false, false, | ||
| 79 | false, false, false, "synciSO", | ||
| 80 | } | ||
| 81 | |||
| 82 | local map_cop0 = { | ||
| 83 | shift = 25, mask = 1, | ||
| 84 | [0] = { | ||
| 85 | shift = 21, mask = 15, | ||
| 86 | [0] = "mfc0TDW", [4] = "mtc0TDW", | ||
| 87 | [10] = "rdpgprDT", | ||
| 88 | [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", }, | ||
| 89 | [14] = "wrpgprDT", | ||
| 90 | }, { | ||
| 91 | shift = 0, mask = 63, | ||
| 92 | [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp", | ||
| 93 | [24] = "eret", [31] = "deret", | ||
| 94 | [32] = "wait", | ||
| 95 | }, | ||
| 96 | } | ||
| 97 | |||
| 98 | local map_cop1s = { | ||
| 99 | shift = 0, mask = 63, | ||
| 100 | [0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", | ||
| 101 | "sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", | ||
| 102 | "round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", | ||
| 103 | "round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", | ||
| 104 | false, | ||
| 105 | { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" }, | ||
| 106 | "movz.sFGT", "movn.sFGT", | ||
| 107 | false, "recip.sFG", "rsqrt.sFG", false, | ||
| 108 | false, false, false, false, | ||
| 109 | false, false, false, false, | ||
| 110 | false, "cvt.d.sFG", false, false, | ||
| 111 | "cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false, | ||
| 112 | false, false, false, false, | ||
| 113 | false, false, false, false, | ||
| 114 | "c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH", | ||
| 115 | "c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH", | ||
| 116 | "c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH", | ||
| 117 | "c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH", | ||
| 118 | } | ||
| 119 | |||
| 120 | local map_cop1d = { | ||
| 121 | shift = 0, mask = 63, | ||
| 122 | [0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", | ||
| 123 | "sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", | ||
| 124 | "round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", | ||
| 125 | "round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", | ||
| 126 | false, | ||
| 127 | { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" }, | ||
| 128 | "movz.dFGT", "movn.dFGT", | ||
| 129 | false, "recip.dFG", "rsqrt.dFG", false, | ||
| 130 | false, false, false, false, | ||
| 131 | false, false, false, false, | ||
| 132 | "cvt.s.dFG", false, false, false, | ||
| 133 | "cvt.w.dFG", "cvt.l.dFG", false, false, | ||
| 134 | false, false, false, false, | ||
| 135 | false, false, false, false, | ||
| 136 | "c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH", | ||
| 137 | "c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH", | ||
| 138 | "c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH", | ||
| 139 | "c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH", | ||
| 140 | } | ||
| 141 | |||
| 142 | local map_cop1ps = { | ||
| 143 | shift = 0, mask = 63, | ||
| 144 | [0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false, | ||
| 145 | false, "abs.psFG", "mov.psFG", "neg.psFG", | ||
| 146 | false, false, false, false, | ||
| 147 | false, false, false, false, | ||
| 148 | false, | ||
| 149 | { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" }, | ||
| 150 | "movz.psFGT", "movn.psFGT", | ||
| 151 | false, false, false, false, | ||
| 152 | false, false, false, false, | ||
| 153 | false, false, false, false, | ||
| 154 | "cvt.s.puFG", false, false, false, | ||
| 155 | false, false, false, false, | ||
| 156 | "cvt.s.plFG", false, false, false, | ||
| 157 | "pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH", | ||
| 158 | "c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH", | ||
| 159 | "c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH", | ||
| 160 | "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH", | ||
| 161 | "c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH", | ||
| 162 | } | ||
| 163 | |||
| 164 | local map_cop1w = { | ||
| 165 | shift = 0, mask = 63, | ||
| 166 | [32] = "cvt.s.wFG", [33] = "cvt.d.wFG", | ||
| 167 | } | ||
| 168 | |||
| 169 | local map_cop1l = { | ||
| 170 | shift = 0, mask = 63, | ||
| 171 | [32] = "cvt.s.lFG", [33] = "cvt.d.lFG", | ||
| 172 | } | ||
| 173 | |||
| 174 | local map_cop1bc = { | ||
| 175 | shift = 16, mask = 3, | ||
| 176 | [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB", | ||
| 177 | } | ||
| 178 | |||
| 179 | local map_cop1 = { | ||
| 180 | shift = 21, mask = 31, | ||
| 181 | [0] = "mfc1TG", false, "cfc1TG", "mfhc1TG", | ||
| 182 | "mtc1TG", false, "ctc1TG", "mthc1TG", | ||
| 183 | map_cop1bc, false, false, false, | ||
| 184 | false, false, false, false, | ||
| 185 | map_cop1s, map_cop1d, false, false, | ||
| 186 | map_cop1w, map_cop1l, map_cop1ps, | ||
| 187 | } | ||
| 188 | |||
| 189 | local map_cop1x = { | ||
| 190 | shift = 0, mask = 63, | ||
| 191 | [0] = "lwxc1FSX", "ldxc1FSX", false, false, | ||
| 192 | false, "luxc1FSX", false, false, | ||
| 193 | "swxc1FSX", "sdxc1FSX", false, false, | ||
| 194 | false, "suxc1FSX", false, "prefxMSX", | ||
| 195 | false, false, false, false, | ||
| 196 | false, false, false, false, | ||
| 197 | false, false, false, false, | ||
| 198 | false, false, "alnv.psFGHS", false, | ||
| 199 | "madd.sFRGH", "madd.dFRGH", false, false, | ||
| 200 | false, false, "madd.psFRGH", false, | ||
| 201 | "msub.sFRGH", "msub.dFRGH", false, false, | ||
| 202 | false, false, "msub.psFRGH", false, | ||
| 203 | "nmadd.sFRGH", "nmadd.dFRGH", false, false, | ||
| 204 | false, false, "nmadd.psFRGH", false, | ||
| 205 | "nmsub.sFRGH", "nmsub.dFRGH", false, false, | ||
| 206 | false, false, "nmsub.psFRGH", false, | ||
| 207 | } | ||
| 208 | |||
| 209 | local map_pri = { | ||
| 210 | [0] = map_special, map_regimm, "jJ", "jalJ", | ||
| 211 | "beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB", | ||
| 212 | "addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI", | ||
| 213 | "andiTSU", "ori|liTS0U", "xoriTSU", "luiTU", | ||
| 214 | map_cop0, map_cop1, false, map_cop1x, | ||
| 215 | "beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB", | ||
| 216 | false, false, false, false, | ||
| 217 | map_special2, false, false, map_special3, | ||
| 218 | "lbTSO", "lhTSO", "lwlTSO", "lwTSO", | ||
| 219 | "lbuTSO", "lhuTSO", "lwrTSO", false, | ||
| 220 | "sbTSO", "shTSO", "swlTSO", "swTSO", | ||
| 221 | false, false, "swrTSO", "cacheNSO", | ||
| 222 | "llTSO", "lwc1HSO", "lwc2TSO", "prefNSO", | ||
| 223 | false, "ldc1HSO", "ldc2TSO", false, | ||
| 224 | "scTSO", "swc1HSO", "swc2TSO", false, | ||
| 225 | false, "sdc1HSO", "sdc2TSO", false, | ||
| 226 | } | ||
| 227 | |||
| 228 | ------------------------------------------------------------------------------ | ||
| 229 | |||
| 230 | local map_gpr = { | ||
| 231 | [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | ||
| 232 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | ||
| 233 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | ||
| 234 | "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra", | ||
| 235 | } | ||
| 236 | |||
| 237 | ------------------------------------------------------------------------------ | ||
| 238 | |||
| 239 | -- Output a nicely formatted line with an opcode and operands. | ||
| 240 | local function putop(ctx, text, operands) | ||
| 241 | local pos = ctx.pos | ||
| 242 | local extra = "" | ||
| 243 | if ctx.rel then | ||
| 244 | local sym = ctx.symtab[ctx.rel] | ||
| 245 | if sym then extra = "\t->"..sym end | ||
| 246 | end | ||
| 247 | if ctx.hexdump > 0 then | ||
| 248 | ctx.out(format("%08x %s %-7s %s%s\n", | ||
| 249 | ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) | ||
| 250 | else | ||
| 251 | ctx.out(format("%08x %-7s %s%s\n", | ||
| 252 | ctx.addr+pos, text, concat(operands, ", "), extra)) | ||
| 253 | end | ||
| 254 | ctx.pos = pos + 4 | ||
| 255 | end | ||
| 256 | |||
| 257 | -- Fallback for unknown opcodes. | ||
| 258 | local function unknown(ctx) | ||
| 259 | return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) | ||
| 260 | end | ||
| 261 | |||
| 262 | local function get_be(ctx) | ||
| 263 | local pos = ctx.pos | ||
| 264 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
| 265 | return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) | ||
| 266 | end | ||
| 267 | |||
| 268 | local function get_le(ctx) | ||
| 269 | local pos = ctx.pos | ||
| 270 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
| 271 | return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) | ||
| 272 | end | ||
| 273 | |||
| 274 | -- Disassemble a single instruction. | ||
| 275 | local function disass_ins(ctx) | ||
| 276 | local op = ctx:get() | ||
| 277 | local operands = {} | ||
| 278 | local last = nil | ||
| 279 | ctx.op = op | ||
| 280 | ctx.rel = nil | ||
| 281 | |||
| 282 | local opat = map_pri[rshift(op, 26)] | ||
| 283 | while type(opat) ~= "string" do | ||
| 284 | if not opat then return unknown(ctx) end | ||
| 285 | opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ | ||
| 286 | end | ||
| 287 | local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") | ||
| 288 | local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") | ||
| 289 | if altname then pat = pat2 end | ||
| 290 | |||
| 291 | for p in gmatch(pat, ".") do | ||
| 292 | local x = nil | ||
| 293 | if p == "S" then | ||
| 294 | x = map_gpr[band(rshift(op, 21), 31)] | ||
| 295 | elseif p == "T" then | ||
| 296 | x = map_gpr[band(rshift(op, 16), 31)] | ||
| 297 | elseif p == "D" then | ||
| 298 | x = map_gpr[band(rshift(op, 11), 31)] | ||
| 299 | elseif p == "F" then | ||
| 300 | x = "f"..band(rshift(op, 6), 31) | ||
| 301 | elseif p == "G" then | ||
| 302 | x = "f"..band(rshift(op, 11), 31) | ||
| 303 | elseif p == "H" then | ||
| 304 | x = "f"..band(rshift(op, 16), 31) | ||
| 305 | elseif p == "R" then | ||
| 306 | x = "f"..band(rshift(op, 21), 31) | ||
| 307 | elseif p == "A" then | ||
| 308 | x = band(rshift(op, 6), 31) | ||
| 309 | elseif p == "M" then | ||
| 310 | x = band(rshift(op, 11), 31) | ||
| 311 | elseif p == "N" then | ||
| 312 | x = band(rshift(op, 16), 31) | ||
| 313 | elseif p == "C" then | ||
| 314 | x = band(rshift(op, 18), 7) | ||
| 315 | if x == 0 then x = nil end | ||
| 316 | elseif p == "K" then | ||
| 317 | x = band(rshift(op, 11), 31) + 1 | ||
| 318 | elseif p == "L" then | ||
| 319 | x = band(rshift(op, 11), 31) - last + 1 | ||
| 320 | elseif p == "I" then | ||
| 321 | x = arshift(lshift(op, 16), 16) | ||
| 322 | elseif p == "U" then | ||
| 323 | x = band(op, 0xffff) | ||
| 324 | elseif p == "O" then | ||
| 325 | local disp = arshift(lshift(op, 16), 16) | ||
| 326 | operands[#operands] = format("%d(%s)", disp, last) | ||
| 327 | elseif p == "X" then | ||
| 328 | local index = map_gpr[band(rshift(op, 16), 31)] | ||
| 329 | operands[#operands] = format("%s(%s)", index, last) | ||
| 330 | elseif p == "B" then | ||
| 331 | x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 16)*4 + 4 | ||
| 332 | ctx.rel = x | ||
| 333 | x = "0x"..tohex(x) | ||
| 334 | elseif p == "J" then | ||
| 335 | x = band(ctx.addr + ctx.pos, 0xf0000000) + band(op, 0x03ffffff)*4 | ||
| 336 | ctx.rel = x | ||
| 337 | x = "0x"..tohex(x) | ||
| 338 | elseif p == "V" then | ||
| 339 | x = band(rshift(op, 8), 7) | ||
| 340 | if x == 0 then x = nil end | ||
| 341 | elseif p == "W" then | ||
| 342 | x = band(op, 7) | ||
| 343 | if x == 0 then x = nil end | ||
| 344 | elseif p == "Y" then | ||
| 345 | x = band(rshift(op, 6), 0x000fffff) | ||
| 346 | if x == 0 then x = nil end | ||
| 347 | elseif p == "Z" then | ||
| 348 | x = band(rshift(op, 6), 1023) | ||
| 349 | if x == 0 then x = nil end | ||
| 350 | elseif p == "0" then | ||
| 351 | if last == "r0" or last == 0 then | ||
| 352 | local n = #operands | ||
| 353 | operands[n] = nil | ||
| 354 | last = operands[n-1] | ||
| 355 | if altname then | ||
| 356 | local a1, a2 = match(altname, "([^|]*)|(.*)") | ||
| 357 | if a1 then name, altname = a1, a2 | ||
| 358 | else name = altname end | ||
| 359 | end | ||
| 360 | end | ||
| 361 | elseif p == "1" then | ||
| 362 | if last == "ra" then | ||
| 363 | operands[#operands] = nil | ||
| 364 | end | ||
| 365 | else | ||
| 366 | assert(false) | ||
| 367 | end | ||
| 368 | if x then operands[#operands+1] = x; last = x end | ||
| 369 | end | ||
| 370 | |||
| 371 | return putop(ctx, name, operands) | ||
| 372 | end | ||
| 373 | |||
| 374 | ------------------------------------------------------------------------------ | ||
| 375 | |||
| 376 | -- Disassemble a block of code. | ||
| 377 | local function disass_block(ctx, ofs, len) | ||
| 378 | if not ofs then ofs = 0 end | ||
| 379 | local stop = len and ofs+len or #ctx.code | ||
| 380 | stop = stop - stop % 4 | ||
| 381 | ctx.pos = ofs - ofs % 4 | ||
| 382 | ctx.rel = nil | ||
| 383 | while ctx.pos < stop do disass_ins(ctx) end | ||
| 384 | end | ||
| 385 | |||
| 386 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 387 | local function create_(code, addr, out) | ||
| 388 | local ctx = {} | ||
| 389 | ctx.code = code | ||
| 390 | ctx.addr = addr or 0 | ||
| 391 | ctx.out = out or io.write | ||
| 392 | ctx.symtab = {} | ||
| 393 | ctx.disass = disass_block | ||
| 394 | ctx.hexdump = 8 | ||
| 395 | ctx.get = get_be | ||
| 396 | return ctx | ||
| 397 | end | ||
| 398 | |||
| 399 | local function create_el_(code, addr, out) | ||
| 400 | local ctx = create_(code, addr, out) | ||
| 401 | ctx.get = get_le | ||
| 402 | return ctx | ||
| 403 | end | ||
| 404 | |||
| 405 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 406 | local function disass_(code, addr, out) | ||
| 407 | create_(code, addr, out):disass() | ||
| 408 | end | ||
| 409 | |||
| 410 | local function disass_el_(code, addr, out) | ||
| 411 | create_el_(code, addr, out):disass() | ||
| 412 | end | ||
| 413 | |||
| 414 | -- Return register name for RID. | ||
| 415 | local function regname_(r) | ||
| 416 | if r < 32 then return map_gpr[r] end | ||
| 417 | return "f"..(r-32) | ||
| 418 | end | ||
| 419 | |||
| 420 | -- Public module functions. | ||
| 421 | module(...) | ||
| 422 | |||
| 423 | create = create_ | ||
| 424 | create_el = create_el_ | ||
| 425 | disass = disass_ | ||
| 426 | disass_el = disass_el_ | ||
| 427 | regname = regname_ | ||
| 428 | |||
diff --git a/src/jit/dis_mipsel.lua b/src/jit/dis_mipsel.lua new file mode 100644 index 00000000..4c5a6515 --- /dev/null +++ b/src/jit/dis_mipsel.lua | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT MIPSEL disassembler wrapper module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2011 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This module just exports the little-endian functions from the | ||
| 8 | -- MIPS disassembler module. All the interesting stuff is there. | ||
| 9 | ------------------------------------------------------------------------------ | ||
| 10 | |||
| 11 | local require = require | ||
| 12 | |||
| 13 | module(...) | ||
| 14 | |||
| 15 | local dis_mips = require(_PACKAGE.."dis_mips") | ||
| 16 | |||
| 17 | create = dis_mips.create_el | ||
| 18 | disass = dis_mips.disass_el | ||
| 19 | regname = dis_mips.regname | ||
| 20 | |||
diff --git a/src/jit/dis_ppc.lua b/src/jit/dis_ppc.lua new file mode 100644 index 00000000..ad8599fc --- /dev/null +++ b/src/jit/dis_ppc.lua | |||
| @@ -0,0 +1,591 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT PPC disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT/X license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
| 8 | -- | ||
| 9 | -- It disassembles all common, non-privileged 32/64 bit PowerPC instructions | ||
| 10 | -- plus the e500 SPE instructions and some Cell/Xenon extensions. | ||
| 11 | -- | ||
| 12 | -- NYI: VMX, VMX128 | ||
| 13 | ------------------------------------------------------------------------------ | ||
| 14 | |||
| 15 | local type = type | ||
| 16 | local sub, byte, format = string.sub, string.byte, string.format | ||
| 17 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
| 18 | local concat = table.concat | ||
| 19 | local bit = require("bit") | ||
| 20 | local band, bor, tohex = bit.band, bit.bor, bit.tohex | ||
| 21 | local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift | ||
| 22 | |||
| 23 | ------------------------------------------------------------------------------ | ||
| 24 | -- Primary and extended opcode maps | ||
| 25 | ------------------------------------------------------------------------------ | ||
| 26 | |||
| 27 | local map_crops = { | ||
| 28 | shift = 1, mask = 1023, | ||
| 29 | [0] = "mcrfXX", | ||
| 30 | [33] = "crnor|crnotCCC=", [129] = "crandcCCC", | ||
| 31 | [193] = "crxor|crclrCCC%", [225] = "crnandCCC", | ||
| 32 | [257] = "crandCCC", [289] = "creqv|crsetCCC%", | ||
| 33 | [417] = "crorcCCC", [449] = "cror|crmoveCCC=", | ||
| 34 | [16] = "b_lrKB", [528] = "b_ctrKB", | ||
| 35 | [150] = "isync", | ||
| 36 | } | ||
| 37 | |||
| 38 | local map_rlwinm = setmetatable({ | ||
| 39 | shift = 0, mask = -1, | ||
| 40 | }, | ||
| 41 | { __index = function(t, x) | ||
| 42 | local rot = band(rshift(x, 11), 31) | ||
| 43 | local mb = band(rshift(x, 6), 31) | ||
| 44 | local me = band(rshift(x, 1), 31) | ||
| 45 | if mb == 0 and me == 31-rot then | ||
| 46 | return "slwiRR~A." | ||
| 47 | elseif me == 31 and mb == 32-rot then | ||
| 48 | return "srwiRR~-A." | ||
| 49 | else | ||
| 50 | return "rlwinmRR~AAA." | ||
| 51 | end | ||
| 52 | end | ||
| 53 | }) | ||
| 54 | |||
| 55 | local map_rld = { | ||
| 56 | shift = 2, mask = 7, | ||
| 57 | [0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.", | ||
| 58 | { | ||
| 59 | shift = 1, mask = 1, | ||
| 60 | [0] = "rldclRR~HM.", "rldcrRR~HM.", | ||
| 61 | }, | ||
| 62 | } | ||
| 63 | |||
| 64 | local map_ext = setmetatable({ | ||
| 65 | shift = 1, mask = 1023, | ||
| 66 | |||
| 67 | [0] = "cmp_YLRR", [32] = "cmpl_YLRR", | ||
| 68 | [4] = "twARR", [68] = "tdARR", | ||
| 69 | |||
| 70 | [8] = "subfcRRR.", [40] = "subfRRR.", | ||
| 71 | [104] = "negRR.", [136] = "subfeRRR.", | ||
| 72 | [200] = "subfzeRR.", [232] = "subfmeRR.", | ||
| 73 | [520] = "subfcoRRR.", [552] = "subfoRRR.", | ||
| 74 | [616] = "negoRR.", [648] = "subfeoRRR.", | ||
| 75 | [712] = "subfzeoRR.", [744] = "subfmeoRR.", | ||
| 76 | |||
| 77 | [9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.", | ||
| 78 | [457] = "divduRRR.", [489] = "divdRRR.", | ||
| 79 | [745] = "mulldoRRR.", | ||
| 80 | [969] = "divduoRRR.", [1001] = "divdoRRR.", | ||
| 81 | |||
| 82 | [10] = "addcRRR.", [138] = "addeRRR.", | ||
| 83 | [202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.", | ||
| 84 | [522] = "addcoRRR.", [650] = "addeoRRR.", | ||
| 85 | [714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.", | ||
| 86 | |||
| 87 | [11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.", | ||
| 88 | [459] = "divwuRRR.", [491] = "divwRRR.", | ||
| 89 | [747] = "mullwoRRR.", | ||
| 90 | [971] = "divwouRRR.", [1003] = "divwoRRR.", | ||
| 91 | |||
| 92 | [15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR", | ||
| 93 | |||
| 94 | [144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", }, | ||
| 95 | [19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", }, | ||
| 96 | [371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", }, | ||
| 97 | [339] = { | ||
| 98 | shift = 11, mask = 1023, | ||
| 99 | [32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR", | ||
| 100 | }, | ||
| 101 | [467] = { | ||
| 102 | shift = 11, mask = 1023, | ||
| 103 | [32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR", | ||
| 104 | }, | ||
| 105 | |||
| 106 | [20] = "lwarxRR0R", [84] = "ldarxRR0R", | ||
| 107 | |||
| 108 | [21] = "ldxRR0R", [53] = "lduxRRR", | ||
| 109 | [149] = "stdxRR0R", [181] = "stduxRRR", | ||
| 110 | [341] = "lwaxRR0R", [373] = "lwauxRRR", | ||
| 111 | |||
| 112 | [23] = "lwzxRR0R", [55] = "lwzuxRRR", | ||
| 113 | [87] = "lbzxRR0R", [119] = "lbzuxRRR", | ||
| 114 | [151] = "stwxRR0R", [183] = "stwuxRRR", | ||
| 115 | [215] = "stbxRR0R", [247] = "stbuxRRR", | ||
| 116 | [279] = "lhzxRR0R", [311] = "lhzuxRRR", | ||
| 117 | [343] = "lhaxRR0R", [375] = "lhauxRRR", | ||
| 118 | [407] = "sthxRR0R", [439] = "sthuxRRR", | ||
| 119 | |||
| 120 | [54] = "dcbst-R0R", [86] = "dcbf-R0R", | ||
| 121 | [150] = "stwcxRR0R.", [214] = "stdcxRR0R.", | ||
| 122 | [246] = "dcbtst-R0R", [278] = "dcbt-R0R", | ||
| 123 | [310] = "eciwxRR0R", [438] = "ecowxRR0R", | ||
| 124 | [470] = "dcbi-RR", | ||
| 125 | |||
| 126 | [598] = { | ||
| 127 | shift = 21, mask = 3, | ||
| 128 | [0] = "sync", "lwsync", "ptesync", | ||
| 129 | }, | ||
| 130 | [758] = "dcba-RR", | ||
| 131 | [854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R", | ||
| 132 | |||
| 133 | [26] = "cntlzwRR~", [58] = "cntlzdRR~", | ||
| 134 | [122] = "popcntbRR~", | ||
| 135 | [154] = "prtywRR~", [186] = "prtydRR~", | ||
| 136 | |||
| 137 | [28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.", | ||
| 138 | [284] = "eqvRR~R.", [316] = "xorRR~R.", | ||
| 139 | [412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.", | ||
| 140 | [508] = "cmpbRR~R", | ||
| 141 | |||
| 142 | [512] = "mcrxrX", | ||
| 143 | |||
| 144 | [532] = "ldbrxRR0R", [660] = "stdbrxRR0R", | ||
| 145 | |||
| 146 | [533] = "lswxRR0R", [597] = "lswiRR0A", | ||
| 147 | [661] = "stswxRR0R", [725] = "stswiRR0A", | ||
| 148 | |||
| 149 | [534] = "lwbrxRR0R", [662] = "stwbrxRR0R", | ||
| 150 | [790] = "lhbrxRR0R", [918] = "sthbrxRR0R", | ||
| 151 | |||
| 152 | [535] = "lfsxFR0R", [567] = "lfsuxFRR", | ||
| 153 | [599] = "lfdxFR0R", [631] = "lfduxFRR", | ||
| 154 | [663] = "stfsxFR0R", [695] = "stfsuxFRR", | ||
| 155 | [727] = "stfdxFR0R", [759] = "stfduxFR0R", | ||
| 156 | [855] = "lfiwaxFR0R", | ||
| 157 | [983] = "stfiwxFR0R", | ||
| 158 | |||
| 159 | [24] = "slwRR~R.", | ||
| 160 | |||
| 161 | [27] = "sldRR~R.", [536] = "srwRR~R.", | ||
| 162 | [792] = "srawRR~R.", [824] = "srawiRR~A.", | ||
| 163 | |||
| 164 | [794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.", | ||
| 165 | [922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.", | ||
| 166 | |||
| 167 | [539] = "srdRR~R.", | ||
| 168 | }, | ||
| 169 | { __index = function(t, x) | ||
| 170 | if band(x, 31) == 15 then return "iselRRRC" end | ||
| 171 | end | ||
| 172 | }) | ||
| 173 | |||
| 174 | local map_ld = { | ||
| 175 | shift = 0, mask = 3, | ||
| 176 | [0] = "ldRRE", "lduRRE", "lwaRRE", | ||
| 177 | } | ||
| 178 | |||
| 179 | local map_std = { | ||
| 180 | shift = 0, mask = 3, | ||
| 181 | [0] = "stdRRE", "stduRRE", | ||
| 182 | } | ||
| 183 | |||
| 184 | local map_fps = { | ||
| 185 | shift = 5, mask = 1, | ||
| 186 | { | ||
| 187 | shift = 1, mask = 15, | ||
| 188 | [0] = false, false, "fdivsFFF.", false, | ||
| 189 | "fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false, | ||
| 190 | "fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false, | ||
| 191 | "fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.", | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | local map_fpd = { | ||
| 196 | shift = 5, mask = 1, | ||
| 197 | [0] = { | ||
| 198 | shift = 1, mask = 1023, | ||
| 199 | [0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX", | ||
| 200 | [38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>", | ||
| 201 | [8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.", | ||
| 202 | [136] = "fnabsF-F.", [264] = "fabsF-F.", | ||
| 203 | [12] = "frspF-F.", | ||
| 204 | [14] = "fctiwF-F.", [15] = "fctiwzF-F.", | ||
| 205 | [583] = "mffsF.", [711] = "mtfsfZF.", | ||
| 206 | [392] = "frinF-F.", [424] = "frizF-F.", | ||
| 207 | [456] = "fripF-F.", [488] = "frimF-F.", | ||
| 208 | [814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.", | ||
| 209 | }, | ||
| 210 | { | ||
| 211 | shift = 1, mask = 15, | ||
| 212 | [0] = false, false, "fdivFFF.", false, | ||
| 213 | "fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.", | ||
| 214 | "freF-F.", "fmulFF-F.", "frsqrteF-F.", false, | ||
| 215 | "fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.", | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | local map_spe = { | ||
| 220 | shift = 0, mask = 2047, | ||
| 221 | |||
| 222 | [512] = "evaddwRRR", [514] = "evaddiwRAR~", | ||
| 223 | [516] = "evsubwRRR~", [518] = "evsubiwRAR~", | ||
| 224 | [520] = "evabsRR", [521] = "evnegRR", | ||
| 225 | [522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR", | ||
| 226 | [525] = "evcntlzwRR", [526] = "evcntlswRR", | ||
| 227 | |||
| 228 | [527] = "brincRRR", | ||
| 229 | |||
| 230 | [529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR", | ||
| 231 | [535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=", | ||
| 232 | [537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR", | ||
| 233 | |||
| 234 | [544] = "evsrwuRRR", [545] = "evsrwsRRR", | ||
| 235 | [546] = "evsrwiuRRA", [547] = "evsrwisRRA", | ||
| 236 | [548] = "evslwRRR", [550] = "evslwiRRA", | ||
| 237 | [552] = "evrlwRRR", [553] = "evsplatiRS", | ||
| 238 | [554] = "evrlwiRRA", [555] = "evsplatfiRS", | ||
| 239 | [556] = "evmergehiRRR", [557] = "evmergeloRRR", | ||
| 240 | [558] = "evmergehiloRRR", [559] = "evmergelohiRRR", | ||
| 241 | |||
| 242 | [560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR", | ||
| 243 | [562] = "evcmpltuYRR", [563] = "evcmpltsYRR", | ||
| 244 | [564] = "evcmpeqYRR", | ||
| 245 | |||
| 246 | [632] = "evselRRR", [633] = "evselRRRW", | ||
| 247 | [634] = "evselRRRW", [635] = "evselRRRW", | ||
| 248 | [636] = "evselRRRW", [637] = "evselRRRW", | ||
| 249 | [638] = "evselRRRW", [639] = "evselRRRW", | ||
| 250 | |||
| 251 | [640] = "evfsaddRRR", [641] = "evfssubRRR", | ||
| 252 | [644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR", | ||
| 253 | [648] = "evfsmulRRR", [649] = "evfsdivRRR", | ||
| 254 | [652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR", | ||
| 255 | [656] = "evfscfuiR-R", [657] = "evfscfsiR-R", | ||
| 256 | [658] = "evfscfufR-R", [659] = "evfscfsfR-R", | ||
| 257 | [660] = "evfsctuiR-R", [661] = "evfsctsiR-R", | ||
| 258 | [662] = "evfsctufR-R", [663] = "evfsctsfR-R", | ||
| 259 | [664] = "evfsctuizR-R", [666] = "evfsctsizR-R", | ||
| 260 | [668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR", | ||
| 261 | |||
| 262 | [704] = "efsaddRRR", [705] = "efssubRRR", | ||
| 263 | [708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR", | ||
| 264 | [712] = "efsmulRRR", [713] = "efsdivRRR", | ||
| 265 | [716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR", | ||
| 266 | [719] = "efscfdR-R", | ||
| 267 | [720] = "efscfuiR-R", [721] = "efscfsiR-R", | ||
| 268 | [722] = "efscfufR-R", [723] = "efscfsfR-R", | ||
| 269 | [724] = "efsctuiR-R", [725] = "efsctsiR-R", | ||
| 270 | [726] = "efsctufR-R", [727] = "efsctsfR-R", | ||
| 271 | [728] = "efsctuizR-R", [730] = "efsctsizR-R", | ||
| 272 | [732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR", | ||
| 273 | |||
| 274 | [736] = "efdaddRRR", [737] = "efdsubRRR", | ||
| 275 | [738] = "efdcfuidR-R", [739] = "efdcfsidR-R", | ||
| 276 | [740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR", | ||
| 277 | [744] = "efdmulRRR", [745] = "efddivRRR", | ||
| 278 | [746] = "efdctuidzR-R", [747] = "efdctsidzR-R", | ||
| 279 | [748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR", | ||
| 280 | [751] = "efdcfsR-R", | ||
| 281 | [752] = "efdcfuiR-R", [753] = "efdcfsiR-R", | ||
| 282 | [754] = "efdcfufR-R", [755] = "efdcfsfR-R", | ||
| 283 | [756] = "efdctuiR-R", [757] = "efdctsiR-R", | ||
| 284 | [758] = "efdctufR-R", [759] = "efdctsfR-R", | ||
| 285 | [760] = "efdctuizR-R", [762] = "efdctsizR-R", | ||
| 286 | [764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR", | ||
| 287 | |||
| 288 | [768] = "evlddxRR0R", [769] = "evlddRR8", | ||
| 289 | [770] = "evldwxRR0R", [771] = "evldwRR8", | ||
| 290 | [772] = "evldhxRR0R", [773] = "evldhRR8", | ||
| 291 | [776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2", | ||
| 292 | [780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2", | ||
| 293 | [782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2", | ||
| 294 | [784] = "evlwhexRR0R", [785] = "evlwheRR4", | ||
| 295 | [788] = "evlwhouxRR0R", [789] = "evlwhouRR4", | ||
| 296 | [790] = "evlwhosxRR0R", [791] = "evlwhosRR4", | ||
| 297 | [792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4", | ||
| 298 | [796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4", | ||
| 299 | |||
| 300 | [800] = "evstddxRR0R", [801] = "evstddRR8", | ||
| 301 | [802] = "evstdwxRR0R", [803] = "evstdwRR8", | ||
| 302 | [804] = "evstdhxRR0R", [805] = "evstdhRR8", | ||
| 303 | [816] = "evstwhexRR0R", [817] = "evstwheRR4", | ||
| 304 | [820] = "evstwhoxRR0R", [821] = "evstwhoRR4", | ||
| 305 | [824] = "evstwwexRR0R", [825] = "evstwweRR4", | ||
| 306 | [828] = "evstwwoxRR0R", [829] = "evstwwoRR4", | ||
| 307 | |||
| 308 | [1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR", | ||
| 309 | [1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR", | ||
| 310 | [1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR", | ||
| 311 | [1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR", | ||
| 312 | [1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR", | ||
| 313 | [1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR", | ||
| 314 | [1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR", | ||
| 315 | [1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR", | ||
| 316 | [1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR", | ||
| 317 | [1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR", | ||
| 318 | [1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR", | ||
| 319 | [1147] = "evmwsmfaRRR", | ||
| 320 | |||
| 321 | [1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR", | ||
| 322 | [1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR", | ||
| 323 | [1220] = "evmraRR", | ||
| 324 | [1222] = "evdivwsRRR", [1223] = "evdivwuRRR", | ||
| 325 | [1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR", | ||
| 326 | [1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR", | ||
| 327 | |||
| 328 | [1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR", | ||
| 329 | [1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR", | ||
| 330 | [1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR", | ||
| 331 | [1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR", | ||
| 332 | [1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR", | ||
| 333 | [1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR", | ||
| 334 | [1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR", | ||
| 335 | [1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR", | ||
| 336 | [1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR", | ||
| 337 | [1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR", | ||
| 338 | [1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR", | ||
| 339 | [1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR", | ||
| 340 | [1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR", | ||
| 341 | [1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR", | ||
| 342 | [1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR", | ||
| 343 | [1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR", | ||
| 344 | [1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR", | ||
| 345 | [1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR", | ||
| 346 | [1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR", | ||
| 347 | [1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR", | ||
| 348 | [1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR", | ||
| 349 | [1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR", | ||
| 350 | [1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR", | ||
| 351 | [1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR", | ||
| 352 | [1491] = "evmwssfanRRR", [1496] = "evmwumianRRR", | ||
| 353 | [1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR", | ||
| 354 | } | ||
| 355 | |||
| 356 | local map_pri = { | ||
| 357 | [0] = false, false, "tdiARI", "twiARI", | ||
| 358 | map_spe, false, false, "mulliRRI", | ||
| 359 | "subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI", | ||
| 360 | "addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I", | ||
| 361 | "b_KBJ", "sc", "bKJ", map_crops, | ||
| 362 | "rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.", | ||
| 363 | "oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U", | ||
| 364 | "andi.RR~U", "andis.RR~U", map_rld, map_ext, | ||
| 365 | "lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD", | ||
| 366 | "stwRRD", "stwuRRD", "stbRRD", "stbuRRD", | ||
| 367 | "lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD", | ||
| 368 | "sthRRD", "sthuRRD", "lmwRRD", "stmwRRD", | ||
| 369 | "lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD", | ||
| 370 | "stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD", | ||
| 371 | false, false, map_ld, map_fps, | ||
| 372 | false, false, map_std, map_fpd, | ||
| 373 | } | ||
| 374 | |||
| 375 | ------------------------------------------------------------------------------ | ||
| 376 | |||
| 377 | local map_gpr = { | ||
| 378 | [0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", | ||
| 379 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | ||
| 380 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | ||
| 381 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | ||
| 382 | } | ||
| 383 | |||
| 384 | local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", } | ||
| 385 | |||
| 386 | -- Format a condition bit. | ||
| 387 | local function condfmt(cond) | ||
| 388 | if cond <= 3 then | ||
| 389 | return map_cond[band(cond, 3)] | ||
| 390 | else | ||
| 391 | return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)]) | ||
| 392 | end | ||
| 393 | end | ||
| 394 | |||
| 395 | ------------------------------------------------------------------------------ | ||
| 396 | |||
| 397 | -- Output a nicely formatted line with an opcode and operands. | ||
| 398 | local function putop(ctx, text, operands) | ||
| 399 | local pos = ctx.pos | ||
| 400 | local extra = "" | ||
| 401 | if ctx.rel then | ||
| 402 | local sym = ctx.symtab[ctx.rel] | ||
| 403 | if sym then extra = "\t->"..sym end | ||
| 404 | end | ||
| 405 | if ctx.hexdump > 0 then | ||
| 406 | ctx.out(format("%08x %s %-7s %s%s\n", | ||
| 407 | ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) | ||
| 408 | else | ||
| 409 | ctx.out(format("%08x %-7s %s%s\n", | ||
| 410 | ctx.addr+pos, text, concat(operands, ", "), extra)) | ||
| 411 | end | ||
| 412 | ctx.pos = pos + 4 | ||
| 413 | end | ||
| 414 | |||
| 415 | -- Fallback for unknown opcodes. | ||
| 416 | local function unknown(ctx) | ||
| 417 | return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) | ||
| 418 | end | ||
| 419 | |||
| 420 | -- Disassemble a single instruction. | ||
| 421 | local function disass_ins(ctx) | ||
| 422 | local pos = ctx.pos | ||
| 423 | local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) | ||
| 424 | local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) | ||
| 425 | local operands = {} | ||
| 426 | local last = nil | ||
| 427 | local rs = 21 | ||
| 428 | ctx.op = op | ||
| 429 | ctx.rel = nil | ||
| 430 | |||
| 431 | local opat = map_pri[rshift(b0, 2)] | ||
| 432 | while type(opat) ~= "string" do | ||
| 433 | if not opat then return unknown(ctx) end | ||
| 434 | opat = opat[band(rshift(op, opat.shift), opat.mask)] | ||
| 435 | end | ||
| 436 | local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") | ||
| 437 | local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)") | ||
| 438 | if altname then pat = pat2 end | ||
| 439 | |||
| 440 | for p in gmatch(pat, ".") do | ||
| 441 | local x = nil | ||
| 442 | if p == "R" then | ||
| 443 | x = map_gpr[band(rshift(op, rs), 31)] | ||
| 444 | rs = rs - 5 | ||
| 445 | elseif p == "F" then | ||
| 446 | x = "f"..band(rshift(op, rs), 31) | ||
| 447 | rs = rs - 5 | ||
| 448 | elseif p == "A" then | ||
| 449 | x = band(rshift(op, rs), 31) | ||
| 450 | rs = rs - 5 | ||
| 451 | elseif p == "S" then | ||
| 452 | x = arshift(lshift(op, 27-rs), 27) | ||
| 453 | rs = rs - 5 | ||
| 454 | elseif p == "I" then | ||
| 455 | x = arshift(lshift(op, 16), 16) | ||
| 456 | elseif p == "U" then | ||
| 457 | x = band(op, 0xffff) | ||
| 458 | elseif p == "D" or p == "E" then | ||
| 459 | local disp = arshift(lshift(op, 16), 16) | ||
| 460 | if p == "E" then disp = band(disp, -4) end | ||
| 461 | if last == "r0" then last = "0" end | ||
| 462 | operands[#operands] = format("%d(%s)", disp, last) | ||
| 463 | elseif p >= "2" and p <= "8" then | ||
| 464 | local disp = band(rshift(op, rs), 31) * p | ||
| 465 | if last == "r0" then last = "0" end | ||
| 466 | operands[#operands] = format("%d(%s)", disp, last) | ||
| 467 | elseif p == "H" then | ||
| 468 | x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4) | ||
| 469 | rs = rs - 5 | ||
| 470 | elseif p == "M" then | ||
| 471 | x = band(rshift(op, rs), 31) + band(op, 0x20) | ||
| 472 | elseif p == "C" then | ||
| 473 | x = condfmt(band(rshift(op, rs), 31)) | ||
| 474 | rs = rs - 5 | ||
| 475 | elseif p == "B" then | ||
| 476 | local bo = rshift(op, 21) | ||
| 477 | local cond = band(rshift(op, 16), 31) | ||
| 478 | local cn = "" | ||
| 479 | rs = rs - 10 | ||
| 480 | if band(bo, 4) == 0 then | ||
| 481 | cn = band(bo, 2) == 0 and "dnz" or "dz" | ||
| 482 | if band(bo, 0x10) == 0 then | ||
| 483 | cn = cn..(band(bo, 8) == 0 and "f" or "t") | ||
| 484 | end | ||
| 485 | if band(bo, 0x10) == 0 then x = condfmt(cond) end | ||
| 486 | name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") | ||
| 487 | elseif band(bo, 0x10) == 0 then | ||
| 488 | cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)] | ||
| 489 | if cond > 3 then x = "cr"..rshift(cond, 2) end | ||
| 490 | name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") | ||
| 491 | end | ||
| 492 | name = gsub(name, "_", cn) | ||
| 493 | elseif p == "J" then | ||
| 494 | x = arshift(lshift(op, 27-rs), 29-rs)*4 | ||
| 495 | if band(op, 2) == 0 then x = ctx.addr + pos + x end | ||
| 496 | ctx.rel = x | ||
| 497 | x = "0x"..tohex(x) | ||
| 498 | elseif p == "K" then | ||
| 499 | if band(op, 1) ~= 0 then name = name.."l" end | ||
| 500 | if band(op, 2) ~= 0 then name = name.."a" end | ||
| 501 | elseif p == "X" or p == "Y" then | ||
| 502 | x = band(rshift(op, rs+2), 7) | ||
| 503 | if x == 0 and p == "Y" then x = nil else x = "cr"..x end | ||
| 504 | rs = rs - 5 | ||
| 505 | elseif p == "W" then | ||
| 506 | x = "cr"..band(op, 7) | ||
| 507 | elseif p == "Z" then | ||
| 508 | x = band(rshift(op, rs-4), 255) | ||
| 509 | rs = rs - 10 | ||
| 510 | elseif p == ">" then | ||
| 511 | operands[#operands] = rshift(operands[#operands], 1) | ||
| 512 | elseif p == "0" then | ||
| 513 | if last == "r0" then | ||
| 514 | operands[#operands] = nil | ||
| 515 | if altname then name = altname end | ||
| 516 | end | ||
| 517 | elseif p == "L" then | ||
| 518 | name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w") | ||
| 519 | elseif p == "." then | ||
| 520 | if band(op, 1) == 1 then name = name.."." end | ||
| 521 | elseif p == "N" then | ||
| 522 | if op == 0x60000000 then name = "nop"; break end | ||
| 523 | elseif p == "~" then | ||
| 524 | local n = #operands | ||
| 525 | operands[n-1], operands[n] = operands[n], operands[n-1] | ||
| 526 | elseif p == "=" then | ||
| 527 | local n = #operands | ||
| 528 | if last == operands[n-1] then | ||
| 529 | operands[n] = nil | ||
| 530 | name = altname | ||
| 531 | end | ||
| 532 | elseif p == "%" then | ||
| 533 | local n = #operands | ||
| 534 | if last == operands[n-1] and last == operands[n-2] then | ||
| 535 | operands[n] = nil | ||
| 536 | operands[n-1] = nil | ||
| 537 | name = altname | ||
| 538 | end | ||
| 539 | elseif p == "-" then | ||
| 540 | rs = rs - 5 | ||
| 541 | else | ||
| 542 | assert(false) | ||
| 543 | end | ||
| 544 | if x then operands[#operands+1] = x; last = x end | ||
| 545 | end | ||
| 546 | |||
| 547 | return putop(ctx, name, operands) | ||
| 548 | end | ||
| 549 | |||
| 550 | ------------------------------------------------------------------------------ | ||
| 551 | |||
| 552 | -- Disassemble a block of code. | ||
| 553 | local function disass_block(ctx, ofs, len) | ||
| 554 | if not ofs then ofs = 0 end | ||
| 555 | local stop = len and ofs+len or #ctx.code | ||
| 556 | stop = stop - stop % 4 | ||
| 557 | ctx.pos = ofs - ofs % 4 | ||
| 558 | ctx.rel = nil | ||
| 559 | while ctx.pos < stop do disass_ins(ctx) end | ||
| 560 | end | ||
| 561 | |||
| 562 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 563 | local function create_(code, addr, out) | ||
| 564 | local ctx = {} | ||
| 565 | ctx.code = code | ||
| 566 | ctx.addr = addr or 0 | ||
| 567 | ctx.out = out or io.write | ||
| 568 | ctx.symtab = {} | ||
| 569 | ctx.disass = disass_block | ||
| 570 | ctx.hexdump = 8 | ||
| 571 | return ctx | ||
| 572 | end | ||
| 573 | |||
| 574 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 575 | local function disass_(code, addr, out) | ||
| 576 | create_(code, addr, out):disass() | ||
| 577 | end | ||
| 578 | |||
| 579 | -- Return register name for RID. | ||
| 580 | local function regname_(r) | ||
| 581 | if r < 32 then return map_gpr[r] end | ||
| 582 | return "f"..(r-32) | ||
| 583 | end | ||
| 584 | |||
| 585 | -- Public module functions. | ||
| 586 | module(...) | ||
| 587 | |||
| 588 | create = create_ | ||
| 589 | disass = disass_ | ||
| 590 | regname = regname_ | ||
| 591 | |||
diff --git a/src/jit/dis_x64.lua b/src/jit/dis_x64.lua new file mode 100644 index 00000000..d95d7ded --- /dev/null +++ b/src/jit/dis_x64.lua | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT x64 disassembler wrapper module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This module just exports the 64 bit functions from the combined | ||
| 8 | -- x86/x64 disassembler module. All the interesting stuff is there. | ||
| 9 | ------------------------------------------------------------------------------ | ||
| 10 | |||
| 11 | local require = require | ||
| 12 | |||
| 13 | module(...) | ||
| 14 | |||
| 15 | local dis_x86 = require(_PACKAGE.."dis_x86") | ||
| 16 | |||
| 17 | create = dis_x86.create64 | ||
| 18 | disass = dis_x86.disass64 | ||
| 19 | regname = dis_x86.regname64 | ||
| 20 | |||
diff --git a/src/jit/dis_x86.lua b/src/jit/dis_x86.lua new file mode 100644 index 00000000..5aeeb449 --- /dev/null +++ b/src/jit/dis_x86.lua | |||
| @@ -0,0 +1,836 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT x86/x64 disassembler module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- This is a helper module used by the LuaJIT machine code dumper module. | ||
| 8 | -- | ||
| 9 | -- Sending small code snippets to an external disassembler and mixing the | ||
| 10 | -- output with our own stuff was too fragile. So I had to bite the bullet | ||
| 11 | -- and write yet another x86 disassembler. Oh well ... | ||
| 12 | -- | ||
| 13 | -- The output format is very similar to what ndisasm generates. But it has | ||
| 14 | -- been developed independently by looking at the opcode tables from the | ||
| 15 | -- Intel and AMD manuals. The supported instruction set is quite extensive | ||
| 16 | -- and reflects what a current generation Intel or AMD CPU implements in | ||
| 17 | -- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3, | ||
| 18 | -- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM) | ||
| 19 | -- instructions. | ||
| 20 | -- | ||
| 21 | -- Notes: | ||
| 22 | -- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported. | ||
| 23 | -- * No attempt at optimization has been made -- it's fast enough for my needs. | ||
| 24 | -- * The public API may change when more architectures are added. | ||
| 25 | ------------------------------------------------------------------------------ | ||
| 26 | |||
| 27 | local type = type | ||
| 28 | local sub, byte, format = string.sub, string.byte, string.format | ||
| 29 | local match, gmatch, gsub = string.match, string.gmatch, string.gsub | ||
| 30 | local lower, rep = string.lower, string.rep | ||
| 31 | |||
| 32 | -- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on. | ||
| 33 | local map_opc1_32 = { | ||
| 34 | --0x | ||
| 35 | [0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es", | ||
| 36 | "orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*", | ||
| 37 | --1x | ||
| 38 | "adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss", | ||
| 39 | "sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds", | ||
| 40 | --2x | ||
| 41 | "andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa", | ||
| 42 | "subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das", | ||
| 43 | --3x | ||
| 44 | "xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa", | ||
| 45 | "cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas", | ||
| 46 | --4x | ||
| 47 | "incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR", | ||
| 48 | "decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR", | ||
| 49 | --5x | ||
| 50 | "pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR", | ||
| 51 | "popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR", | ||
| 52 | --6x | ||
| 53 | "sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr", | ||
| 54 | "fs:seg","gs:seg","o16:","a16", | ||
| 55 | "pushUi","imulVrmi","pushBs","imulVrms", | ||
| 56 | "insb","insVS","outsb","outsVS", | ||
| 57 | --7x | ||
| 58 | "joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj", | ||
| 59 | "jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj", | ||
| 60 | --8x | ||
| 61 | "arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms", | ||
| 62 | "testBmr","testVmr","xchgBrm","xchgVrm", | ||
| 63 | "movBmr","movVmr","movBrm","movVrm", | ||
| 64 | "movVmg","leaVrm","movWgm","popUm", | ||
| 65 | --9x | ||
| 66 | "nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR", | ||
| 67 | "xchgVaR","xchgVaR","xchgVaR","xchgVaR", | ||
| 68 | "sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait", | ||
| 69 | "sz*pushfw,pushf","sz*popfw,popf","sahf","lahf", | ||
| 70 | --Ax | ||
| 71 | "movBao","movVao","movBoa","movVoa", | ||
| 72 | "movsb","movsVS","cmpsb","cmpsVS", | ||
| 73 | "testBai","testVai","stosb","stosVS", | ||
| 74 | "lodsb","lodsVS","scasb","scasVS", | ||
| 75 | --Bx | ||
| 76 | "movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi", | ||
| 77 | "movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI", | ||
| 78 | --Cx | ||
| 79 | "shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi", | ||
| 80 | "enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS", | ||
| 81 | --Dx | ||
| 82 | "shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb", | ||
| 83 | "fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7", | ||
| 84 | --Ex | ||
| 85 | "loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj", | ||
| 86 | "inBau","inVau","outBua","outVua", | ||
| 87 | "callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda", | ||
| 88 | --Fx | ||
| 89 | "lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm", | ||
| 90 | "clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm", | ||
| 91 | } | ||
| 92 | assert(#map_opc1_32 == 255) | ||
| 93 | |||
| 94 | -- Map for 1st opcode byte in 64 bit mode (overrides only). | ||
| 95 | local map_opc1_64 = setmetatable({ | ||
| 96 | [0x06]=false, [0x07]=false, [0x0e]=false, | ||
| 97 | [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false, | ||
| 98 | [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false, | ||
| 99 | [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:", | ||
| 100 | [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb", | ||
| 101 | [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb", | ||
| 102 | [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb", | ||
| 103 | [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb", | ||
| 104 | [0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false, | ||
| 105 | [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false, | ||
| 106 | }, { __index = map_opc1_32 }) | ||
| 107 | |||
| 108 | -- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you. | ||
| 109 | -- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2 | ||
| 110 | local map_opc2 = { | ||
| 111 | --0x | ||
| 112 | [0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret", | ||
| 113 | "invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu", | ||
| 114 | --1x | ||
| 115 | "movupsXrm|movssXrm|movupdXrm|movsdXrm", | ||
| 116 | "movupsXmr|movssXmr|movupdXmr|movsdXmr", | ||
| 117 | "movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm", | ||
| 118 | "movlpsXmr||movlpdXmr", | ||
| 119 | "unpcklpsXrm||unpcklpdXrm", | ||
| 120 | "unpckhpsXrm||unpckhpdXrm", | ||
| 121 | "movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm", | ||
| 122 | "movhpsXmr||movhpdXmr", | ||
| 123 | "$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm", | ||
| 124 | "hintnopVm","hintnopVm","hintnopVm","hintnopVm", | ||
| 125 | --2x | ||
| 126 | "movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil, | ||
| 127 | "movapsXrm||movapdXrm", | ||
| 128 | "movapsXmr||movapdXmr", | ||
| 129 | "cvtpi2psXrMm|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt", | ||
| 130 | "movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr", | ||
| 131 | "cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm", | ||
| 132 | "cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm", | ||
| 133 | "ucomissXrm||ucomisdXrm", | ||
| 134 | "comissXrm||comisdXrm", | ||
| 135 | --3x | ||
| 136 | "wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec", | ||
| 137 | "opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil, | ||
| 138 | --4x | ||
| 139 | "cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm", | ||
| 140 | "cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm", | ||
| 141 | "cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm", | ||
| 142 | "cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm", | ||
| 143 | --5x | ||
| 144 | "movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm", | ||
| 145 | "rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm", | ||
| 146 | "andpsXrm||andpdXrm","andnpsXrm||andnpdXrm", | ||
| 147 | "orpsXrm||orpdXrm","xorpsXrm||xorpdXrm", | ||
| 148 | "addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm", | ||
| 149 | "cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm", | ||
| 150 | "cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm", | ||
| 151 | "subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm", | ||
| 152 | "divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm", | ||
| 153 | --6x | ||
| 154 | "punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm", | ||
| 155 | "pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm", | ||
| 156 | "punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm", | ||
| 157 | "||punpcklqdqXrm","||punpckhqdqXrm", | ||
| 158 | "movPrVSm","movqMrm|movdquXrm|movdqaXrm", | ||
| 159 | --7x | ||
| 160 | "pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu", | ||
| 161 | "pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu", | ||
| 162 | "pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|", | ||
| 163 | "vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$", | ||
| 164 | nil,nil, | ||
| 165 | "||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm", | ||
| 166 | "movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr", | ||
| 167 | --8x | ||
| 168 | "joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj", | ||
| 169 | "jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj", | ||
| 170 | --9x | ||
| 171 | "setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm", | ||
| 172 | "setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm", | ||
| 173 | --Ax | ||
| 174 | "push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil, | ||
| 175 | "push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm", | ||
| 176 | --Bx | ||
| 177 | "cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr", | ||
| 178 | "$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt", | ||
| 179 | "|popcntVrm","ud2Dp","bt!Vmu","btcVmr", | ||
| 180 | "bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt", | ||
| 181 | --Cx | ||
| 182 | "xaddBmr","xaddVmr", | ||
| 183 | "cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|", | ||
| 184 | "pinsrwPrWmu","pextrwDrPmu", | ||
| 185 | "shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp", | ||
| 186 | "bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR", | ||
| 187 | --Dx | ||
| 188 | "||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm", | ||
| 189 | "paddqPrm","pmullwPrm", | ||
| 190 | "|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm", | ||
| 191 | "psubusbPrm","psubuswPrm","pminubPrm","pandPrm", | ||
| 192 | "paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm", | ||
| 193 | --Ex | ||
| 194 | "pavgbPrm","psrawPrm","psradPrm","pavgwPrm", | ||
| 195 | "pmulhuwPrm","pmulhwPrm", | ||
| 196 | "|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr", | ||
| 197 | "psubsbPrm","psubswPrm","pminswPrm","porPrm", | ||
| 198 | "paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm", | ||
| 199 | --Fx | ||
| 200 | "|||lddquXrm","psllwPrm","pslldPrm","psllqPrm", | ||
| 201 | "pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$", | ||
| 202 | "psubbPrm","psubwPrm","psubdPrm","psubqPrm", | ||
| 203 | "paddbPrm","paddwPrm","padddPrm","ud", | ||
| 204 | } | ||
| 205 | assert(map_opc2[255] == "ud") | ||
| 206 | |||
| 207 | -- Map for three-byte opcodes. Can't wait for their next invention. | ||
| 208 | local map_opc3 = { | ||
| 209 | ["38"] = { -- [66] 0f 38 xx | ||
| 210 | --0x | ||
| 211 | [0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm", | ||
| 212 | "pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm", | ||
| 213 | "psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm", | ||
| 214 | nil,nil,nil,nil, | ||
| 215 | --1x | ||
| 216 | "||pblendvbXrma",nil,nil,nil, | ||
| 217 | "||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm", | ||
| 218 | nil,nil,nil,nil, | ||
| 219 | "pabsbPrm","pabswPrm","pabsdPrm",nil, | ||
| 220 | --2x | ||
| 221 | "||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm", | ||
| 222 | "||pmovsxwqXrm","||pmovsxdqXrm",nil,nil, | ||
| 223 | "||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm", | ||
| 224 | nil,nil,nil,nil, | ||
| 225 | --3x | ||
| 226 | "||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm", | ||
| 227 | "||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm", | ||
| 228 | "||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm", | ||
| 229 | "||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm", | ||
| 230 | --4x | ||
| 231 | "||pmulddXrm","||phminposuwXrm", | ||
| 232 | --Fx | ||
| 233 | [0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt", | ||
| 234 | }, | ||
| 235 | |||
| 236 | ["3a"] = { -- [66] 0f 3a xx | ||
| 237 | --0x | ||
| 238 | [0x00]=nil,nil,nil,nil,nil,nil,nil,nil, | ||
| 239 | "||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu", | ||
| 240 | "||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu", | ||
| 241 | --1x | ||
| 242 | nil,nil,nil,nil, | ||
| 243 | "||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru", | ||
| 244 | nil,nil,nil,nil,nil,nil,nil,nil, | ||
| 245 | --2x | ||
| 246 | "||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil, | ||
| 247 | --4x | ||
| 248 | [0x40] = "||dppsXrmu", | ||
| 249 | [0x41] = "||dppdXrmu", | ||
| 250 | [0x42] = "||mpsadbwXrmu", | ||
| 251 | --6x | ||
| 252 | [0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu", | ||
| 253 | [0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu", | ||
| 254 | }, | ||
| 255 | } | ||
| 256 | |||
| 257 | -- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands). | ||
| 258 | local map_opcvm = { | ||
| 259 | [0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff", | ||
| 260 | [0xc8]="monitor",[0xc9]="mwait", | ||
| 261 | [0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave", | ||
| 262 | [0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga", | ||
| 263 | [0xf8]="swapgs",[0xf9]="rdtscp", | ||
| 264 | } | ||
| 265 | |||
| 266 | -- Map for FP opcodes. And you thought stack machines are simple? | ||
| 267 | local map_opcfp = { | ||
| 268 | -- D8-DF 00-BF: opcodes with a memory operand. | ||
| 269 | -- D8 | ||
| 270 | [0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm", | ||
| 271 | "fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm", | ||
| 272 | -- DA | ||
| 273 | "fiaddDm","fimulDm","ficomDm","ficompDm", | ||
| 274 | "fisubDm","fisubrDm","fidivDm","fidivrDm", | ||
| 275 | -- DB | ||
| 276 | "fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp", | ||
| 277 | -- DC | ||
| 278 | "faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm", | ||
| 279 | -- DD | ||
| 280 | "fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm", | ||
| 281 | -- DE | ||
| 282 | "fiaddWm","fimulWm","ficomWm","ficompWm", | ||
| 283 | "fisubWm","fisubrWm","fidivWm","fidivrWm", | ||
| 284 | -- DF | ||
| 285 | "fildWm","fisttpWm","fistWm","fistpWm", | ||
| 286 | "fbld twordFmp","fildQm","fbstp twordFmp","fistpQm", | ||
| 287 | -- xx C0-FF: opcodes with a pseudo-register operand. | ||
| 288 | -- D8 | ||
| 289 | "faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf", | ||
| 290 | -- D9 | ||
| 291 | "fldFf","fxchFf",{"fnop"},nil, | ||
| 292 | {"fchs","fabs",nil,nil,"ftst","fxam"}, | ||
| 293 | {"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"}, | ||
| 294 | {"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"}, | ||
| 295 | {"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"}, | ||
| 296 | -- DA | ||
| 297 | "fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil, | ||
| 298 | -- DB | ||
| 299 | "fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf", | ||
| 300 | {nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil, | ||
| 301 | -- DC | ||
| 302 | "fadd toFf","fmul toFf",nil,nil, | ||
| 303 | "fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf", | ||
| 304 | -- DD | ||
| 305 | "ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil, | ||
| 306 | -- DE | ||
| 307 | "faddpFf","fmulpFf",nil,{nil,"fcompp"}, | ||
| 308 | "fsubrpFf","fsubpFf","fdivrpFf","fdivpFf", | ||
| 309 | -- DF | ||
| 310 | nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil, | ||
| 311 | } | ||
| 312 | assert(map_opcfp[126] == "fcomipFf") | ||
| 313 | |||
| 314 | -- Map for opcode groups. The subkey is sp from the ModRM byte. | ||
| 315 | local map_opcgroup = { | ||
| 316 | arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }, | ||
| 317 | shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" }, | ||
| 318 | testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" }, | ||
| 319 | testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" }, | ||
| 320 | incb = { "inc", "dec" }, | ||
| 321 | incd = { "inc", "dec", "callUmp", "$call farDmp", | ||
| 322 | "jmpUmp", "$jmp farDmp", "pushUm" }, | ||
| 323 | sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" }, | ||
| 324 | sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt", | ||
| 325 | "smsw", nil, "lmsw", "vm*$invlpg" }, | ||
| 326 | bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" }, | ||
| 327 | cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil, | ||
| 328 | nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" }, | ||
| 329 | pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" }, | ||
| 330 | pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" }, | ||
| 331 | pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" }, | ||
| 332 | pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" }, | ||
| 333 | fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr", | ||
| 334 | nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" }, | ||
| 335 | prefetch = { "prefetch", "prefetchw" }, | ||
| 336 | prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" }, | ||
| 337 | } | ||
| 338 | |||
| 339 | ------------------------------------------------------------------------------ | ||
| 340 | |||
| 341 | -- Maps for register names. | ||
| 342 | local map_regs = { | ||
| 343 | B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", | ||
| 344 | "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, | ||
| 345 | B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", | ||
| 346 | "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, | ||
| 347 | W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", | ||
| 348 | "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, | ||
| 349 | D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", | ||
| 350 | "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, | ||
| 351 | Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", | ||
| 352 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, | ||
| 353 | M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", | ||
| 354 | "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext! | ||
| 355 | X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", | ||
| 356 | "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" }, | ||
| 357 | } | ||
| 358 | local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" } | ||
| 359 | |||
| 360 | -- Maps for size names. | ||
| 361 | local map_sz2n = { | ||
| 362 | B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, | ||
| 363 | } | ||
| 364 | local map_sz2prefix = { | ||
| 365 | B = "byte", W = "word", D = "dword", | ||
| 366 | Q = "qword", | ||
| 367 | M = "qword", X = "xword", | ||
| 368 | F = "dword", G = "qword", -- No need for sizes/register names for these two. | ||
| 369 | } | ||
| 370 | |||
| 371 | ------------------------------------------------------------------------------ | ||
| 372 | |||
| 373 | -- Output a nicely formatted line with an opcode and operands. | ||
| 374 | local function putop(ctx, text, operands) | ||
| 375 | local code, pos, hex = ctx.code, ctx.pos, "" | ||
| 376 | local hmax = ctx.hexdump | ||
| 377 | if hmax > 0 then | ||
| 378 | for i=ctx.start,pos-1 do | ||
| 379 | hex = hex..format("%02X", byte(code, i, i)) | ||
| 380 | end | ||
| 381 | if #hex > hmax then hex = sub(hex, 1, hmax)..". " | ||
| 382 | else hex = hex..rep(" ", hmax-#hex+2) end | ||
| 383 | end | ||
| 384 | if operands then text = text.." "..operands end | ||
| 385 | if ctx.o16 then text = "o16 "..text; ctx.o16 = false end | ||
| 386 | if ctx.a32 then text = "a32 "..text; ctx.a32 = false end | ||
| 387 | if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end | ||
| 388 | if ctx.rex then | ||
| 389 | local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "").. | ||
| 390 | (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "") | ||
| 391 | if t ~= "" then text = "rex."..t.." "..text end | ||
| 392 | ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false | ||
| 393 | ctx.rex = false | ||
| 394 | end | ||
| 395 | if ctx.seg then | ||
| 396 | local text2, n = gsub(text, "%[", "["..ctx.seg..":") | ||
| 397 | if n == 0 then text = ctx.seg.." "..text else text = text2 end | ||
| 398 | ctx.seg = false | ||
| 399 | end | ||
| 400 | if ctx.lock then text = "lock "..text; ctx.lock = false end | ||
| 401 | local imm = ctx.imm | ||
| 402 | if imm then | ||
| 403 | local sym = ctx.symtab[imm] | ||
| 404 | if sym then text = text.."\t->"..sym end | ||
| 405 | end | ||
| 406 | ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text)) | ||
| 407 | ctx.mrm = false | ||
| 408 | ctx.start = pos | ||
| 409 | ctx.imm = nil | ||
| 410 | end | ||
| 411 | |||
| 412 | -- Clear all prefix flags. | ||
| 413 | local function clearprefixes(ctx) | ||
| 414 | ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false | ||
| 415 | ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false | ||
| 416 | ctx.rex = false; ctx.a32 = false | ||
| 417 | end | ||
| 418 | |||
| 419 | -- Fallback for incomplete opcodes at the end. | ||
| 420 | local function incomplete(ctx) | ||
| 421 | ctx.pos = ctx.stop+1 | ||
| 422 | clearprefixes(ctx) | ||
| 423 | return putop(ctx, "(incomplete)") | ||
| 424 | end | ||
| 425 | |||
| 426 | -- Fallback for unknown opcodes. | ||
| 427 | local function unknown(ctx) | ||
| 428 | clearprefixes(ctx) | ||
| 429 | return putop(ctx, "(unknown)") | ||
| 430 | end | ||
| 431 | |||
| 432 | -- Return an immediate of the specified size. | ||
| 433 | local function getimm(ctx, pos, n) | ||
| 434 | if pos+n-1 > ctx.stop then return incomplete(ctx) end | ||
| 435 | local code = ctx.code | ||
| 436 | if n == 1 then | ||
| 437 | local b1 = byte(code, pos, pos) | ||
| 438 | return b1 | ||
| 439 | elseif n == 2 then | ||
| 440 | local b1, b2 = byte(code, pos, pos+1) | ||
| 441 | return b1+b2*256 | ||
| 442 | else | ||
| 443 | local b1, b2, b3, b4 = byte(code, pos, pos+3) | ||
| 444 | local imm = b1+b2*256+b3*65536+b4*16777216 | ||
| 445 | ctx.imm = imm | ||
| 446 | return imm | ||
| 447 | end | ||
| 448 | end | ||
| 449 | |||
| 450 | -- Process pattern string and generate the operands. | ||
| 451 | local function putpat(ctx, name, pat) | ||
| 452 | local operands, regs, sz, mode, sp, rm, sc, rx, sdisp | ||
| 453 | local code, pos, stop = ctx.code, ctx.pos, ctx.stop | ||
| 454 | |||
| 455 | -- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz | ||
| 456 | for p in gmatch(pat, ".") do | ||
| 457 | local x = nil | ||
| 458 | if p == "V" or p == "U" then | ||
| 459 | if ctx.rexw then sz = "Q"; ctx.rexw = false | ||
| 460 | elseif ctx.o16 then sz = "W"; ctx.o16 = false | ||
| 461 | elseif p == "U" and ctx.x64 then sz = "Q" | ||
| 462 | else sz = "D" end | ||
| 463 | regs = map_regs[sz] | ||
| 464 | elseif p == "T" then | ||
| 465 | if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end | ||
| 466 | regs = map_regs[sz] | ||
| 467 | elseif p == "B" then | ||
| 468 | sz = "B" | ||
| 469 | regs = ctx.rex and map_regs.B64 or map_regs.B | ||
| 470 | elseif match(p, "[WDQMXFG]") then | ||
| 471 | sz = p | ||
| 472 | regs = map_regs[sz] | ||
| 473 | elseif p == "P" then | ||
| 474 | sz = ctx.o16 and "X" or "M"; ctx.o16 = false | ||
| 475 | regs = map_regs[sz] | ||
| 476 | elseif p == "S" then | ||
| 477 | name = name..lower(sz) | ||
| 478 | elseif p == "s" then | ||
| 479 | local imm = getimm(ctx, pos, 1); if not imm then return end | ||
| 480 | x = imm <= 127 and format("+0x%02x", imm) | ||
| 481 | or format("-0x%02x", 256-imm) | ||
| 482 | pos = pos+1 | ||
| 483 | elseif p == "u" then | ||
| 484 | local imm = getimm(ctx, pos, 1); if not imm then return end | ||
| 485 | x = format("0x%02x", imm) | ||
| 486 | pos = pos+1 | ||
| 487 | elseif p == "w" then | ||
| 488 | local imm = getimm(ctx, pos, 2); if not imm then return end | ||
| 489 | x = format("0x%x", imm) | ||
| 490 | pos = pos+2 | ||
| 491 | elseif p == "o" then -- [offset] | ||
| 492 | if ctx.x64 then | ||
| 493 | local imm1 = getimm(ctx, pos, 4); if not imm1 then return end | ||
| 494 | local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end | ||
| 495 | x = format("[0x%08x%08x]", imm2, imm1) | ||
| 496 | pos = pos+8 | ||
| 497 | else | ||
| 498 | local imm = getimm(ctx, pos, 4); if not imm then return end | ||
| 499 | x = format("[0x%08x]", imm) | ||
| 500 | pos = pos+4 | ||
| 501 | end | ||
| 502 | elseif p == "i" or p == "I" then | ||
| 503 | local n = map_sz2n[sz] | ||
| 504 | if n == 8 and ctx.x64 and p == "I" then | ||
| 505 | local imm1 = getimm(ctx, pos, 4); if not imm1 then return end | ||
| 506 | local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end | ||
| 507 | x = format("0x%08x%08x", imm2, imm1) | ||
| 508 | else | ||
| 509 | if n == 8 then n = 4 end | ||
| 510 | local imm = getimm(ctx, pos, n); if not imm then return end | ||
| 511 | if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then | ||
| 512 | imm = (0xffffffff+1)-imm | ||
| 513 | x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm) | ||
| 514 | else | ||
| 515 | x = format(imm > 65535 and "0x%08x" or "0x%x", imm) | ||
| 516 | end | ||
| 517 | end | ||
| 518 | pos = pos+n | ||
| 519 | elseif p == "j" then | ||
| 520 | local n = map_sz2n[sz] | ||
| 521 | if n == 8 then n = 4 end | ||
| 522 | local imm = getimm(ctx, pos, n); if not imm then return end | ||
| 523 | if sz == "B" and imm > 127 then imm = imm-256 | ||
| 524 | elseif imm > 2147483647 then imm = imm-4294967296 end | ||
| 525 | pos = pos+n | ||
| 526 | imm = imm + pos + ctx.addr | ||
| 527 | if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end | ||
| 528 | ctx.imm = imm | ||
| 529 | if sz == "W" then | ||
| 530 | x = format("word 0x%04x", imm%65536) | ||
| 531 | elseif ctx.x64 then | ||
| 532 | local lo = imm % 0x1000000 | ||
| 533 | x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo) | ||
| 534 | else | ||
| 535 | x = format("0x%08x", imm) | ||
| 536 | end | ||
| 537 | elseif p == "R" then | ||
| 538 | local r = byte(code, pos-1, pos-1)%8 | ||
| 539 | if ctx.rexb then r = r + 8; ctx.rexb = false end | ||
| 540 | x = regs[r+1] | ||
| 541 | elseif p == "a" then x = regs[1] | ||
| 542 | elseif p == "c" then x = "cl" | ||
| 543 | elseif p == "d" then x = "dx" | ||
| 544 | elseif p == "1" then x = "1" | ||
| 545 | else | ||
| 546 | if not mode then | ||
| 547 | mode = ctx.mrm | ||
| 548 | if not mode then | ||
| 549 | if pos > stop then return incomplete(ctx) end | ||
| 550 | mode = byte(code, pos, pos) | ||
| 551 | pos = pos+1 | ||
| 552 | end | ||
| 553 | rm = mode%8; mode = (mode-rm)/8 | ||
| 554 | sp = mode%8; mode = (mode-sp)/8 | ||
| 555 | sdisp = "" | ||
| 556 | if mode < 3 then | ||
| 557 | if rm == 4 then | ||
| 558 | if pos > stop then return incomplete(ctx) end | ||
| 559 | sc = byte(code, pos, pos) | ||
| 560 | pos = pos+1 | ||
| 561 | rm = sc%8; sc = (sc-rm)/8 | ||
| 562 | rx = sc%8; sc = (sc-rx)/8 | ||
| 563 | if ctx.rexx then rx = rx + 8; ctx.rexx = false end | ||
| 564 | if rx == 4 then rx = nil end | ||
| 565 | end | ||
| 566 | if mode > 0 or rm == 5 then | ||
| 567 | local dsz = mode | ||
| 568 | if dsz ~= 1 then dsz = 4 end | ||
| 569 | local disp = getimm(ctx, pos, dsz); if not disp then return end | ||
| 570 | if mode == 0 then rm = nil end | ||
| 571 | if rm or rx or (not sc and ctx.x64 and not ctx.a32) then | ||
| 572 | if dsz == 1 and disp > 127 then | ||
| 573 | sdisp = format("-0x%x", 256-disp) | ||
| 574 | elseif disp >= 0 and disp <= 0x7fffffff then | ||
| 575 | sdisp = format("+0x%x", disp) | ||
| 576 | else | ||
| 577 | sdisp = format("-0x%x", (0xffffffff+1)-disp) | ||
| 578 | end | ||
| 579 | else | ||
| 580 | sdisp = format(ctx.x64 and not ctx.a32 and | ||
| 581 | not (disp >= 0 and disp <= 0x7fffffff) | ||
| 582 | and "0xffffffff%08x" or "0x%08x", disp) | ||
| 583 | end | ||
| 584 | pos = pos+dsz | ||
| 585 | end | ||
| 586 | end | ||
| 587 | if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end | ||
| 588 | if ctx.rexr then sp = sp + 8; ctx.rexr = false end | ||
| 589 | end | ||
| 590 | if p == "m" then | ||
| 591 | if mode == 3 then x = regs[rm+1] | ||
| 592 | else | ||
| 593 | local aregs = ctx.a32 and map_regs.D or ctx.aregs | ||
| 594 | local srm, srx = "", "" | ||
| 595 | if rm then srm = aregs[rm+1] | ||
| 596 | elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end | ||
| 597 | ctx.a32 = false | ||
| 598 | if rx then | ||
| 599 | if rm then srm = srm.."+" end | ||
| 600 | srx = aregs[rx+1] | ||
| 601 | if sc > 0 then srx = srx.."*"..(2^sc) end | ||
| 602 | end | ||
| 603 | x = format("[%s%s%s]", srm, srx, sdisp) | ||
| 604 | end | ||
| 605 | if mode < 3 and | ||
| 606 | (not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck. | ||
| 607 | x = map_sz2prefix[sz].." "..x | ||
| 608 | end | ||
| 609 | elseif p == "r" then x = regs[sp+1] | ||
| 610 | elseif p == "g" then x = map_segregs[sp+1] | ||
| 611 | elseif p == "p" then -- Suppress prefix. | ||
| 612 | elseif p == "f" then x = "st"..rm | ||
| 613 | elseif p == "x" then | ||
| 614 | if sp == 0 and ctx.lock and not ctx.x64 then | ||
| 615 | x = "CR8"; ctx.lock = false | ||
| 616 | else | ||
| 617 | x = "CR"..sp | ||
| 618 | end | ||
| 619 | elseif p == "y" then x = "DR"..sp | ||
| 620 | elseif p == "z" then x = "TR"..sp | ||
| 621 | elseif p == "t" then | ||
| 622 | else | ||
| 623 | error("bad pattern `"..pat.."'") | ||
| 624 | end | ||
| 625 | end | ||
| 626 | if x then operands = operands and operands..", "..x or x end | ||
| 627 | end | ||
| 628 | ctx.pos = pos | ||
| 629 | return putop(ctx, name, operands) | ||
| 630 | end | ||
| 631 | |||
| 632 | -- Forward declaration. | ||
| 633 | local map_act | ||
| 634 | |||
| 635 | -- Fetch and cache MRM byte. | ||
| 636 | local function getmrm(ctx) | ||
| 637 | local mrm = ctx.mrm | ||
| 638 | if not mrm then | ||
| 639 | local pos = ctx.pos | ||
| 640 | if pos > ctx.stop then return nil end | ||
| 641 | mrm = byte(ctx.code, pos, pos) | ||
| 642 | ctx.pos = pos+1 | ||
| 643 | ctx.mrm = mrm | ||
| 644 | end | ||
| 645 | return mrm | ||
| 646 | end | ||
| 647 | |||
| 648 | -- Dispatch to handler depending on pattern. | ||
| 649 | local function dispatch(ctx, opat, patgrp) | ||
| 650 | if not opat then return unknown(ctx) end | ||
| 651 | if match(opat, "%|") then -- MMX/SSE variants depending on prefix. | ||
| 652 | local p | ||
| 653 | if ctx.rep then | ||
| 654 | p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)" | ||
| 655 | ctx.rep = false | ||
| 656 | elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false | ||
| 657 | else p = "^[^%|]*" end | ||
| 658 | opat = match(opat, p) | ||
| 659 | if not opat then return unknown(ctx) end | ||
| 660 | -- ctx.rep = false; ctx.o16 = false | ||
| 661 | --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi] | ||
| 662 | --XXX remove in branches? | ||
| 663 | end | ||
| 664 | if match(opat, "%$") then -- reg$mem variants. | ||
| 665 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 666 | opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)") | ||
| 667 | if opat == "" then return unknown(ctx) end | ||
| 668 | end | ||
| 669 | if opat == "" then return unknown(ctx) end | ||
| 670 | local name, pat = match(opat, "^([a-z0-9 ]*)(.*)") | ||
| 671 | if pat == "" and patgrp then pat = patgrp end | ||
| 672 | return map_act[sub(pat, 1, 1)](ctx, name, pat) | ||
| 673 | end | ||
| 674 | |||
| 675 | -- Get a pattern from an opcode map and dispatch to handler. | ||
| 676 | local function dispatchmap(ctx, opcmap) | ||
| 677 | local pos = ctx.pos | ||
| 678 | local opat = opcmap[byte(ctx.code, pos, pos)] | ||
| 679 | pos = pos + 1 | ||
| 680 | ctx.pos = pos | ||
| 681 | return dispatch(ctx, opat) | ||
| 682 | end | ||
| 683 | |||
| 684 | -- Map for action codes. The key is the first char after the name. | ||
| 685 | map_act = { | ||
| 686 | -- Simple opcodes without operands. | ||
| 687 | [""] = function(ctx, name, pat) | ||
| 688 | return putop(ctx, name) | ||
| 689 | end, | ||
| 690 | |||
| 691 | -- Operand size chars fall right through. | ||
| 692 | B = putpat, W = putpat, D = putpat, Q = putpat, | ||
| 693 | V = putpat, U = putpat, T = putpat, | ||
| 694 | M = putpat, X = putpat, P = putpat, | ||
| 695 | F = putpat, G = putpat, | ||
| 696 | |||
| 697 | -- Collect prefixes. | ||
| 698 | [":"] = function(ctx, name, pat) | ||
| 699 | ctx[pat == ":" and name or sub(pat, 2)] = name | ||
| 700 | if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes. | ||
| 701 | end, | ||
| 702 | |||
| 703 | -- Chain to special handler specified by name. | ||
| 704 | ["*"] = function(ctx, name, pat) | ||
| 705 | return map_act[name](ctx, name, sub(pat, 2)) | ||
| 706 | end, | ||
| 707 | |||
| 708 | -- Use named subtable for opcode group. | ||
| 709 | ["!"] = function(ctx, name, pat) | ||
| 710 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 711 | return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2)) | ||
| 712 | end, | ||
| 713 | |||
| 714 | -- o16,o32[,o64] variants. | ||
| 715 | sz = function(ctx, name, pat) | ||
| 716 | if ctx.o16 then ctx.o16 = false | ||
| 717 | else | ||
| 718 | pat = match(pat, ",(.*)") | ||
| 719 | if ctx.rexw then | ||
| 720 | local p = match(pat, ",(.*)") | ||
| 721 | if p then pat = p; ctx.rexw = false end | ||
| 722 | end | ||
| 723 | end | ||
| 724 | pat = match(pat, "^[^,]*") | ||
| 725 | return dispatch(ctx, pat) | ||
| 726 | end, | ||
| 727 | |||
| 728 | -- Two-byte opcode dispatch. | ||
| 729 | opc2 = function(ctx, name, pat) | ||
| 730 | return dispatchmap(ctx, map_opc2) | ||
| 731 | end, | ||
| 732 | |||
| 733 | -- Three-byte opcode dispatch. | ||
| 734 | opc3 = function(ctx, name, pat) | ||
| 735 | return dispatchmap(ctx, map_opc3[pat]) | ||
| 736 | end, | ||
| 737 | |||
| 738 | -- VMX/SVM dispatch. | ||
| 739 | vm = function(ctx, name, pat) | ||
| 740 | return dispatch(ctx, map_opcvm[ctx.mrm]) | ||
| 741 | end, | ||
| 742 | |||
| 743 | -- Floating point opcode dispatch. | ||
| 744 | fp = function(ctx, name, pat) | ||
| 745 | local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end | ||
| 746 | local rm = mrm%8 | ||
| 747 | local idx = pat*8 + ((mrm-rm)/8)%8 | ||
| 748 | if mrm >= 192 then idx = idx + 64 end | ||
| 749 | local opat = map_opcfp[idx] | ||
| 750 | if type(opat) == "table" then opat = opat[rm+1] end | ||
| 751 | return dispatch(ctx, opat) | ||
| 752 | end, | ||
| 753 | |||
| 754 | -- REX prefix. | ||
| 755 | rex = function(ctx, name, pat) | ||
| 756 | if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed. | ||
| 757 | for p in gmatch(pat, ".") do ctx["rex"..p] = true end | ||
| 758 | ctx.rex = true | ||
| 759 | end, | ||
| 760 | |||
| 761 | -- Special case for nop with REX prefix. | ||
| 762 | nop = function(ctx, name, pat) | ||
| 763 | return dispatch(ctx, ctx.rex and pat or "nop") | ||
| 764 | end, | ||
| 765 | } | ||
| 766 | |||
| 767 | ------------------------------------------------------------------------------ | ||
| 768 | |||
| 769 | -- Disassemble a block of code. | ||
| 770 | local function disass_block(ctx, ofs, len) | ||
| 771 | if not ofs then ofs = 0 end | ||
| 772 | local stop = len and ofs+len or #ctx.code | ||
| 773 | ofs = ofs + 1 | ||
| 774 | ctx.start = ofs | ||
| 775 | ctx.pos = ofs | ||
| 776 | ctx.stop = stop | ||
| 777 | ctx.imm = nil | ||
| 778 | ctx.mrm = false | ||
| 779 | clearprefixes(ctx) | ||
| 780 | while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end | ||
| 781 | if ctx.pos ~= ctx.start then incomplete(ctx) end | ||
| 782 | end | ||
| 783 | |||
| 784 | -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). | ||
| 785 | local function create_(code, addr, out) | ||
| 786 | local ctx = {} | ||
| 787 | ctx.code = code | ||
| 788 | ctx.addr = (addr or 0) - 1 | ||
| 789 | ctx.out = out or io.write | ||
| 790 | ctx.symtab = {} | ||
| 791 | ctx.disass = disass_block | ||
| 792 | ctx.hexdump = 16 | ||
| 793 | ctx.x64 = false | ||
| 794 | ctx.map1 = map_opc1_32 | ||
| 795 | ctx.aregs = map_regs.D | ||
| 796 | return ctx | ||
| 797 | end | ||
| 798 | |||
| 799 | local function create64_(code, addr, out) | ||
| 800 | local ctx = create_(code, addr, out) | ||
| 801 | ctx.x64 = true | ||
| 802 | ctx.map1 = map_opc1_64 | ||
| 803 | ctx.aregs = map_regs.Q | ||
| 804 | return ctx | ||
| 805 | end | ||
| 806 | |||
| 807 | -- Simple API: disassemble code (a string) at address and output via out. | ||
| 808 | local function disass_(code, addr, out) | ||
| 809 | create_(code, addr, out):disass() | ||
| 810 | end | ||
| 811 | |||
| 812 | local function disass64_(code, addr, out) | ||
| 813 | create64_(code, addr, out):disass() | ||
| 814 | end | ||
| 815 | |||
| 816 | -- Return register name for RID. | ||
| 817 | local function regname_(r) | ||
| 818 | if r < 8 then return map_regs.D[r+1] end | ||
| 819 | return map_regs.X[r-7] | ||
| 820 | end | ||
| 821 | |||
| 822 | local function regname64_(r) | ||
| 823 | if r < 16 then return map_regs.Q[r+1] end | ||
| 824 | return map_regs.X[r-15] | ||
| 825 | end | ||
| 826 | |||
| 827 | -- Public module functions. | ||
| 828 | module(...) | ||
| 829 | |||
| 830 | create = create_ | ||
| 831 | create64 = create64_ | ||
| 832 | disass = disass_ | ||
| 833 | disass64 = disass64_ | ||
| 834 | regname = regname_ | ||
| 835 | regname64 = regname64_ | ||
| 836 | |||
diff --git a/src/jit/dump.lua b/src/jit/dump.lua new file mode 100644 index 00000000..3d62c4ea --- /dev/null +++ b/src/jit/dump.lua | |||
| @@ -0,0 +1,696 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- LuaJIT compiler dump module. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module can be used to debug the JIT compiler itself. It dumps the | ||
| 9 | -- code representations and structures used in various compiler stages. | ||
| 10 | -- | ||
| 11 | -- Example usage: | ||
| 12 | -- | ||
| 13 | -- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)" | ||
| 14 | -- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R | ||
| 15 | -- luajit -jdump=is myapp.lua | less -R | ||
| 16 | -- luajit -jdump=-b myapp.lua | ||
| 17 | -- luajit -jdump=+aH,myapp.html myapp.lua | ||
| 18 | -- luajit -jdump=ixT,myapp.dump myapp.lua | ||
| 19 | -- | ||
| 20 | -- The first argument specifies the dump mode. The second argument gives | ||
| 21 | -- the output file name. Default output is to stdout, unless the environment | ||
| 22 | -- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the | ||
| 23 | -- module is started. | ||
| 24 | -- | ||
| 25 | -- Different features can be turned on or off with the dump mode. If the | ||
| 26 | -- mode starts with a '+', the following features are added to the default | ||
| 27 | -- set of features; a '-' removes them. Otherwise the features are replaced. | ||
| 28 | -- | ||
| 29 | -- The following dump features are available (* marks the default): | ||
| 30 | -- | ||
| 31 | -- * t Print a line for each started, ended or aborted trace (see also -jv). | ||
| 32 | -- * b Dump the traced bytecode. | ||
| 33 | -- * i Dump the IR (intermediate representation). | ||
| 34 | -- r Augment the IR with register/stack slots. | ||
| 35 | -- s Dump the snapshot map. | ||
| 36 | -- * m Dump the generated machine code. | ||
| 37 | -- x Print each taken trace exit. | ||
| 38 | -- X Print each taken trace exit and the contents of all registers. | ||
| 39 | -- | ||
| 40 | -- The output format can be set with the following characters: | ||
| 41 | -- | ||
| 42 | -- T Plain text output. | ||
| 43 | -- A ANSI-colored text output | ||
| 44 | -- H Colorized HTML + CSS output. | ||
| 45 | -- | ||
| 46 | -- The default output format is plain text. It's set to ANSI-colored text | ||
| 47 | -- if the COLORTERM variable is set. Note: this is independent of any output | ||
| 48 | -- redirection, which is actually considered a feature. | ||
| 49 | -- | ||
| 50 | -- You probably want to use less -R to enjoy viewing ANSI-colored text from | ||
| 51 | -- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R" | ||
| 52 | -- | ||
| 53 | ------------------------------------------------------------------------------ | ||
| 54 | |||
| 55 | -- Cache some library functions and objects. | ||
| 56 | local jit = require("jit") | ||
| 57 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 58 | local jutil = require("jit.util") | ||
| 59 | local vmdef = require("jit.vmdef") | ||
| 60 | local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc | ||
| 61 | local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek | ||
| 62 | local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap | ||
| 63 | local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr | ||
| 64 | local bit = require("bit") | ||
| 65 | local band, shl, shr = bit.band, bit.lshift, bit.rshift | ||
| 66 | local sub, gsub, format = string.sub, string.gsub, string.format | ||
| 67 | local byte, char, rep = string.byte, string.char, string.rep | ||
| 68 | local type, tostring = type, tostring | ||
| 69 | local stdout, stderr = io.stdout, io.stderr | ||
| 70 | |||
| 71 | -- Load other modules on-demand. | ||
| 72 | local bcline, disass | ||
| 73 | |||
| 74 | -- Active flag, output file handle and dump mode. | ||
| 75 | local active, out, dumpmode | ||
| 76 | |||
| 77 | ------------------------------------------------------------------------------ | ||
| 78 | |||
| 79 | local symtabmt = { __index = false } | ||
| 80 | local symtab = {} | ||
| 81 | local nexitsym = 0 | ||
| 82 | |||
| 83 | -- Fill nested symbol table with per-trace exit stub addresses. | ||
| 84 | local function fillsymtab_tr(tr, nexit) | ||
| 85 | local t = {} | ||
| 86 | symtabmt.__index = t | ||
| 87 | if jit.arch == "mips" or jit.arch == "mipsel" then | ||
| 88 | t[traceexitstub(tr, 0)] = "exit" | ||
| 89 | return | ||
| 90 | end | ||
| 91 | for i=0,nexit-1 do | ||
| 92 | local addr = traceexitstub(tr, i) | ||
| 93 | t[addr] = tostring(i) | ||
| 94 | end | ||
| 95 | local addr = traceexitstub(tr, nexit) | ||
| 96 | if addr then t[addr] = "stack_check" end | ||
| 97 | end | ||
| 98 | |||
| 99 | -- Fill symbol table with trace exit stub addresses. | ||
| 100 | local function fillsymtab(tr, nexit) | ||
| 101 | local t = symtab | ||
| 102 | if nexitsym == 0 then | ||
| 103 | local ircall = vmdef.ircall | ||
| 104 | for i=0,#ircall do | ||
| 105 | local addr = ircalladdr(i) | ||
| 106 | if addr ~= 0 then t[addr] = ircall[i] end | ||
| 107 | end | ||
| 108 | end | ||
| 109 | if nexitsym == 1000000 then -- Per-trace exit stubs. | ||
| 110 | fillsymtab_tr(tr, nexit) | ||
| 111 | elseif nexit > nexitsym then -- Shared exit stubs. | ||
| 112 | for i=nexitsym,nexit-1 do | ||
| 113 | local addr = traceexitstub(i) | ||
| 114 | if addr == nil then -- Fall back to per-trace exit stubs. | ||
| 115 | fillsymtab_tr(tr, nexit) | ||
| 116 | setmetatable(symtab, symtabmt) | ||
| 117 | nexit = 1000000 | ||
| 118 | break | ||
| 119 | end | ||
| 120 | t[addr] = tostring(i) | ||
| 121 | end | ||
| 122 | nexitsym = nexit | ||
| 123 | end | ||
| 124 | return t | ||
| 125 | end | ||
| 126 | |||
| 127 | local function dumpwrite(s) | ||
| 128 | out:write(s) | ||
| 129 | end | ||
| 130 | |||
| 131 | -- Disassemble machine code. | ||
| 132 | local function dump_mcode(tr) | ||
| 133 | local info = traceinfo(tr) | ||
| 134 | if not info then return end | ||
| 135 | local mcode, addr, loop = tracemc(tr) | ||
| 136 | if not mcode then return end | ||
| 137 | if not disass then disass = require("jit.dis_"..jit.arch) end | ||
| 138 | out:write("---- TRACE ", tr, " mcode ", #mcode, "\n") | ||
| 139 | local ctx = disass.create(mcode, addr, dumpwrite) | ||
| 140 | ctx.hexdump = 0 | ||
| 141 | ctx.symtab = fillsymtab(tr, info.nexit) | ||
| 142 | if loop ~= 0 then | ||
| 143 | symtab[addr+loop] = "LOOP" | ||
| 144 | ctx:disass(0, loop) | ||
| 145 | out:write("->LOOP:\n") | ||
| 146 | ctx:disass(loop, #mcode-loop) | ||
| 147 | symtab[addr+loop] = nil | ||
| 148 | else | ||
| 149 | ctx:disass(0, #mcode) | ||
| 150 | end | ||
| 151 | end | ||
| 152 | |||
| 153 | ------------------------------------------------------------------------------ | ||
| 154 | |||
| 155 | local irtype_text = { | ||
| 156 | [0] = "nil", | ||
| 157 | "fal", | ||
| 158 | "tru", | ||
| 159 | "lud", | ||
| 160 | "str", | ||
| 161 | "p32", | ||
| 162 | "thr", | ||
| 163 | "pro", | ||
| 164 | "fun", | ||
| 165 | "p64", | ||
| 166 | "cdt", | ||
| 167 | "tab", | ||
| 168 | "udt", | ||
| 169 | "flt", | ||
| 170 | "num", | ||
| 171 | "i8 ", | ||
| 172 | "u8 ", | ||
| 173 | "i16", | ||
| 174 | "u16", | ||
| 175 | "int", | ||
| 176 | "u32", | ||
| 177 | "i64", | ||
| 178 | "u64", | ||
| 179 | "sfp", | ||
| 180 | } | ||
| 181 | |||
| 182 | local colortype_ansi = { | ||
| 183 | [0] = "%s", | ||
| 184 | "%s", | ||
| 185 | "%s", | ||
| 186 | "\027[36m%s\027[m", | ||
| 187 | "\027[32m%s\027[m", | ||
| 188 | "%s", | ||
| 189 | "\027[1m%s\027[m", | ||
| 190 | "%s", | ||
| 191 | "\027[1m%s\027[m", | ||
| 192 | "%s", | ||
| 193 | "\027[33m%s\027[m", | ||
| 194 | "\027[31m%s\027[m", | ||
| 195 | "\027[36m%s\027[m", | ||
| 196 | "\027[34m%s\027[m", | ||
| 197 | "\027[34m%s\027[m", | ||
| 198 | "\027[35m%s\027[m", | ||
| 199 | "\027[35m%s\027[m", | ||
| 200 | "\027[35m%s\027[m", | ||
| 201 | "\027[35m%s\027[m", | ||
| 202 | "\027[35m%s\027[m", | ||
| 203 | "\027[35m%s\027[m", | ||
| 204 | "\027[35m%s\027[m", | ||
| 205 | "\027[35m%s\027[m", | ||
| 206 | "\027[35m%s\027[m", | ||
| 207 | } | ||
| 208 | |||
| 209 | local function colorize_text(s, t) | ||
| 210 | return s | ||
| 211 | end | ||
| 212 | |||
| 213 | local function colorize_ansi(s, t) | ||
| 214 | return format(colortype_ansi[t], s) | ||
| 215 | end | ||
| 216 | |||
| 217 | local irtype_ansi = setmetatable({}, | ||
| 218 | { __index = function(tab, t) | ||
| 219 | local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end }) | ||
| 220 | |||
| 221 | local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", } | ||
| 222 | |||
| 223 | local function colorize_html(s, t) | ||
| 224 | s = gsub(s, "[<>&]", html_escape) | ||
| 225 | return format('<span class="irt_%s">%s</span>', irtype_text[t], s) | ||
| 226 | end | ||
| 227 | |||
| 228 | local irtype_html = setmetatable({}, | ||
| 229 | { __index = function(tab, t) | ||
| 230 | local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end }) | ||
| 231 | |||
| 232 | local header_html = [[ | ||
| 233 | <style type="text/css"> | ||
| 234 | background { background: #ffffff; color: #000000; } | ||
| 235 | pre.ljdump { | ||
| 236 | font-size: 10pt; | ||
| 237 | background: #f0f4ff; | ||
| 238 | color: #000000; | ||
| 239 | border: 1px solid #bfcfff; | ||
| 240 | padding: 0.5em; | ||
| 241 | margin-left: 2em; | ||
| 242 | margin-right: 2em; | ||
| 243 | } | ||
| 244 | span.irt_str { color: #00a000; } | ||
| 245 | span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; } | ||
| 246 | span.irt_tab { color: #c00000; } | ||
| 247 | span.irt_udt, span.irt_lud { color: #00c0c0; } | ||
| 248 | span.irt_num { color: #4040c0; } | ||
| 249 | span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; } | ||
| 250 | </style> | ||
| 251 | ]] | ||
| 252 | |||
| 253 | local colorize, irtype | ||
| 254 | |||
| 255 | -- Lookup tables to convert some literals into names. | ||
| 256 | local litname = { | ||
| 257 | ["SLOAD "] = setmetatable({}, { __index = function(t, mode) | ||
| 258 | local s = "" | ||
| 259 | if band(mode, 1) ~= 0 then s = s.."P" end | ||
| 260 | if band(mode, 2) ~= 0 then s = s.."F" end | ||
| 261 | if band(mode, 4) ~= 0 then s = s.."T" end | ||
| 262 | if band(mode, 8) ~= 0 then s = s.."C" end | ||
| 263 | if band(mode, 16) ~= 0 then s = s.."R" end | ||
| 264 | if band(mode, 32) ~= 0 then s = s.."I" end | ||
| 265 | t[mode] = s | ||
| 266 | return s | ||
| 267 | end}), | ||
| 268 | ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", }, | ||
| 269 | ["CONV "] = setmetatable({}, { __index = function(t, mode) | ||
| 270 | local s = irtype[band(mode, 31)] | ||
| 271 | s = irtype[band(shr(mode, 5), 31)].."."..s | ||
| 272 | if band(mode, 0x400) ~= 0 then s = s.." trunc" | ||
| 273 | elseif band(mode, 0x800) ~= 0 then s = s.." sext" end | ||
| 274 | local c = shr(mode, 14) | ||
| 275 | if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end | ||
| 276 | t[mode] = s | ||
| 277 | return s | ||
| 278 | end}), | ||
| 279 | ["FLOAD "] = vmdef.irfield, | ||
| 280 | ["FREF "] = vmdef.irfield, | ||
| 281 | ["FPMATH"] = vmdef.irfpm, | ||
| 282 | } | ||
| 283 | |||
| 284 | local function ctlsub(c) | ||
| 285 | if c == "\n" then return "\\n" | ||
| 286 | elseif c == "\r" then return "\\r" | ||
| 287 | elseif c == "\t" then return "\\t" | ||
| 288 | elseif c == "\r" then return "\\r" | ||
| 289 | else return format("\\%03d", byte(c)) | ||
| 290 | end | ||
| 291 | end | ||
| 292 | |||
| 293 | local function fmtfunc(func, pc) | ||
| 294 | local fi = funcinfo(func, pc) | ||
| 295 | if fi.loc then | ||
| 296 | return fi.loc | ||
| 297 | elseif fi.ffid then | ||
| 298 | return vmdef.ffnames[fi.ffid] | ||
| 299 | elseif fi.addr then | ||
| 300 | return format("C:%x", fi.addr) | ||
| 301 | else | ||
| 302 | return "(?)" | ||
| 303 | end | ||
| 304 | end | ||
| 305 | |||
| 306 | local function formatk(tr, idx) | ||
| 307 | local k, t, slot = tracek(tr, idx) | ||
| 308 | local tn = type(k) | ||
| 309 | local s | ||
| 310 | if tn == "number" then | ||
| 311 | if k == 2^52+2^51 then | ||
| 312 | s = "bias" | ||
| 313 | else | ||
| 314 | s = format("%+.14g", k) | ||
| 315 | end | ||
| 316 | elseif tn == "string" then | ||
| 317 | s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub)) | ||
| 318 | elseif tn == "function" then | ||
| 319 | s = fmtfunc(k) | ||
| 320 | elseif tn == "table" then | ||
| 321 | s = format("{%p}", k) | ||
| 322 | elseif tn == "userdata" then | ||
| 323 | if t == 12 then | ||
| 324 | s = format("userdata:%p", k) | ||
| 325 | else | ||
| 326 | s = format("[%p]", k) | ||
| 327 | if s == "[0x00000000]" then s = "NULL" end | ||
| 328 | end | ||
| 329 | elseif t == 21 then -- int64_t | ||
| 330 | s = sub(tostring(k), 1, -3) | ||
| 331 | if sub(s, 1, 1) ~= "-" then s = "+"..s end | ||
| 332 | else | ||
| 333 | s = tostring(k) -- For primitives. | ||
| 334 | end | ||
| 335 | s = colorize(format("%-4s", s), t) | ||
| 336 | if slot then | ||
| 337 | s = format("%s @%d", s, slot) | ||
| 338 | end | ||
| 339 | return s | ||
| 340 | end | ||
| 341 | |||
| 342 | local function printsnap(tr, snap) | ||
| 343 | local n = 2 | ||
| 344 | for s=0,snap[1]-1 do | ||
| 345 | local sn = snap[n] | ||
| 346 | if shr(sn, 24) == s then | ||
| 347 | n = n + 1 | ||
| 348 | local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS | ||
| 349 | if ref < 0 then | ||
| 350 | out:write(formatk(tr, ref)) | ||
| 351 | elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM | ||
| 352 | out:write(colorize(format("%04d/%04d", ref, ref+1), 14)) | ||
| 353 | else | ||
| 354 | local m, ot, op1, op2 = traceir(tr, ref) | ||
| 355 | out:write(colorize(format("%04d", ref), band(ot, 31))) | ||
| 356 | end | ||
| 357 | out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME | ||
| 358 | else | ||
| 359 | out:write("---- ") | ||
| 360 | end | ||
| 361 | end | ||
| 362 | out:write("]\n") | ||
| 363 | end | ||
| 364 | |||
| 365 | -- Dump snapshots (not interleaved with IR). | ||
| 366 | local function dump_snap(tr) | ||
| 367 | out:write("---- TRACE ", tr, " snapshots\n") | ||
| 368 | for i=0,1000000000 do | ||
| 369 | local snap = tracesnap(tr, i) | ||
| 370 | if not snap then break end | ||
| 371 | out:write(format("#%-3d %04d [ ", i, snap[0])) | ||
| 372 | printsnap(tr, snap) | ||
| 373 | end | ||
| 374 | end | ||
| 375 | |||
| 376 | -- Return a register name or stack slot for a rid/sp location. | ||
| 377 | local function ridsp_name(ridsp) | ||
| 378 | if not disass then disass = require("jit.dis_"..jit.arch) end | ||
| 379 | local rid = band(ridsp, 0xff) | ||
| 380 | if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end | ||
| 381 | if rid < 128 then return disass.regname(rid) end | ||
| 382 | return "" | ||
| 383 | end | ||
| 384 | |||
| 385 | -- Dump CALL* function ref and return optional ctype. | ||
| 386 | local function dumpcallfunc(tr, ins) | ||
| 387 | local ctype | ||
| 388 | if ins > 0 then | ||
| 389 | local m, ot, op1, op2 = traceir(tr, ins) | ||
| 390 | if band(ot, 31) == 0 then -- nil type means CARG(func, ctype). | ||
| 391 | ins = op1 | ||
| 392 | ctype = formatk(tr, op2) | ||
| 393 | end | ||
| 394 | end | ||
| 395 | if ins < 0 then | ||
| 396 | out:write(format("[0x%x](", tonumber((tracek(tr, ins))))) | ||
| 397 | else | ||
| 398 | out:write(format("%04d (", ins)) | ||
| 399 | end | ||
| 400 | return ctype | ||
| 401 | end | ||
| 402 | |||
| 403 | -- Recursively gather CALL* args and dump them. | ||
| 404 | local function dumpcallargs(tr, ins) | ||
| 405 | if ins < 0 then | ||
| 406 | out:write(formatk(tr, ins)) | ||
| 407 | else | ||
| 408 | local m, ot, op1, op2 = traceir(tr, ins) | ||
| 409 | local oidx = 6*shr(ot, 8) | ||
| 410 | local op = sub(vmdef.irnames, oidx+1, oidx+6) | ||
| 411 | if op == "CARG " then | ||
| 412 | dumpcallargs(tr, op1) | ||
| 413 | if op2 < 0 then | ||
| 414 | out:write(" ", formatk(tr, op2)) | ||
| 415 | else | ||
| 416 | out:write(" ", format("%04d", op2)) | ||
| 417 | end | ||
| 418 | else | ||
| 419 | out:write(format("%04d", ins)) | ||
| 420 | end | ||
| 421 | end | ||
| 422 | end | ||
| 423 | |||
| 424 | -- Dump IR and interleaved snapshots. | ||
| 425 | local function dump_ir(tr, dumpsnap, dumpreg) | ||
| 426 | local info = traceinfo(tr) | ||
| 427 | if not info then return end | ||
| 428 | local nins = info.nins | ||
| 429 | out:write("---- TRACE ", tr, " IR\n") | ||
| 430 | local irnames = vmdef.irnames | ||
| 431 | local snapref = 65536 | ||
| 432 | local snap, snapno | ||
| 433 | if dumpsnap then | ||
| 434 | snap = tracesnap(tr, 0) | ||
| 435 | snapref = snap[0] | ||
| 436 | snapno = 0 | ||
| 437 | end | ||
| 438 | for ins=1,nins do | ||
| 439 | if ins >= snapref then | ||
| 440 | if dumpreg then | ||
| 441 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 442 | else | ||
| 443 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 444 | end | ||
| 445 | printsnap(tr, snap) | ||
| 446 | snapno = snapno + 1 | ||
| 447 | snap = tracesnap(tr, snapno) | ||
| 448 | snapref = snap and snap[0] or 65536 | ||
| 449 | end | ||
| 450 | local m, ot, op1, op2, ridsp = traceir(tr, ins) | ||
| 451 | local oidx, t = 6*shr(ot, 8), band(ot, 31) | ||
| 452 | local op = sub(irnames, oidx+1, oidx+6) | ||
| 453 | if op == "LOOP " then | ||
| 454 | if dumpreg then | ||
| 455 | out:write(format("%04d ------------ LOOP ------------\n", ins)) | ||
| 456 | else | ||
| 457 | out:write(format("%04d ------ LOOP ------------\n", ins)) | ||
| 458 | end | ||
| 459 | elseif op ~= "NOP " and op ~= "CARG " and | ||
| 460 | (dumpreg or op ~= "RENAME") then | ||
| 461 | if dumpreg then | ||
| 462 | out:write(format("%04d %-5s ", ins, ridsp_name(ridsp))) | ||
| 463 | else | ||
| 464 | out:write(format("%04d ", ins)) | ||
| 465 | end | ||
| 466 | out:write(format("%s%s %s %s ", | ||
| 467 | band(ot, 128) == 0 and " " or ">", | ||
| 468 | band(ot, 64) == 0 and " " or "+", | ||
| 469 | irtype[t], op)) | ||
| 470 | local m1, m2 = band(m, 3), band(m, 3*4) | ||
| 471 | if sub(op, 1, 4) == "CALL" then | ||
| 472 | local ctype | ||
| 473 | if m2 == 1*4 then -- op2 == IRMlit | ||
| 474 | out:write(format("%-10s (", vmdef.ircall[op2])) | ||
| 475 | else | ||
| 476 | ctype = dumpcallfunc(tr, op2) | ||
| 477 | end | ||
| 478 | if op1 ~= -1 then dumpcallargs(tr, op1) end | ||
| 479 | out:write(")") | ||
| 480 | if ctype then out:write(" ctype ", ctype) end | ||
| 481 | elseif op == "CNEW " and op2 == -1 then | ||
| 482 | out:write(formatk(tr, op1)) | ||
| 483 | elseif m1 ~= 3 then -- op1 != IRMnone | ||
| 484 | if op1 < 0 then | ||
| 485 | out:write(formatk(tr, op1)) | ||
| 486 | else | ||
| 487 | out:write(format(m1 == 0 and "%04d" or "#%-3d", op1)) | ||
| 488 | end | ||
| 489 | if m2 ~= 3*4 then -- op2 != IRMnone | ||
| 490 | if m2 == 1*4 then -- op2 == IRMlit | ||
| 491 | local litn = litname[op] | ||
| 492 | if litn and litn[op2] then | ||
| 493 | out:write(" ", litn[op2]) | ||
| 494 | elseif op == "UREFO " or op == "UREFC " then | ||
| 495 | out:write(format(" #%-3d", shr(op2, 8))) | ||
| 496 | else | ||
| 497 | out:write(format(" #%-3d", op2)) | ||
| 498 | end | ||
| 499 | elseif op2 < 0 then | ||
| 500 | out:write(" ", formatk(tr, op2)) | ||
| 501 | else | ||
| 502 | out:write(format(" %04d", op2)) | ||
| 503 | end | ||
| 504 | end | ||
| 505 | end | ||
| 506 | out:write("\n") | ||
| 507 | end | ||
| 508 | end | ||
| 509 | if snap then | ||
| 510 | if dumpreg then | ||
| 511 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 512 | else | ||
| 513 | out:write(format(".... SNAP #%-3d [ ", snapno)) | ||
| 514 | end | ||
| 515 | printsnap(tr, snap) | ||
| 516 | end | ||
| 517 | end | ||
| 518 | |||
| 519 | ------------------------------------------------------------------------------ | ||
| 520 | |||
| 521 | local recprefix = "" | ||
| 522 | local recdepth = 0 | ||
| 523 | |||
| 524 | -- Format trace error message. | ||
| 525 | local function fmterr(err, info) | ||
| 526 | if type(err) == "number" then | ||
| 527 | if type(info) == "function" then info = fmtfunc(info) end | ||
| 528 | err = format(vmdef.traceerr[err], info) | ||
| 529 | end | ||
| 530 | return err | ||
| 531 | end | ||
| 532 | |||
| 533 | -- Dump trace states. | ||
| 534 | local function dump_trace(what, tr, func, pc, otr, oex) | ||
| 535 | if what == "stop" or (what == "abort" and dumpmode.a) then | ||
| 536 | if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop") | ||
| 537 | elseif dumpmode.s then dump_snap(tr) end | ||
| 538 | if dumpmode.m then dump_mcode(tr) end | ||
| 539 | end | ||
| 540 | if what == "start" then | ||
| 541 | if dumpmode.H then out:write('<pre class="ljdump">\n') end | ||
| 542 | out:write("---- TRACE ", tr, " ", what) | ||
| 543 | if otr then out:write(" ", otr, "/", oex) end | ||
| 544 | out:write(" ", fmtfunc(func, pc), "\n") | ||
| 545 | recprefix = "" | ||
| 546 | elseif what == "stop" or what == "abort" then | ||
| 547 | out:write("---- TRACE ", tr, " ", what) | ||
| 548 | recprefix = nil | ||
| 549 | if what == "abort" then | ||
| 550 | out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n") | ||
| 551 | else | ||
| 552 | local info = traceinfo(tr) | ||
| 553 | local link, ltype = info.link, info.linktype | ||
| 554 | if link == tr or link == 0 then | ||
| 555 | out:write(" -> ", ltype, "\n") | ||
| 556 | elseif ltype == "root" then | ||
| 557 | out:write(" -> ", link, "\n") | ||
| 558 | else | ||
| 559 | out:write(" -> ", link, " ", ltype, "\n") | ||
| 560 | end | ||
| 561 | end | ||
| 562 | if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end | ||
| 563 | else | ||
| 564 | out:write("---- TRACE ", what, "\n\n") | ||
| 565 | end | ||
| 566 | out:flush() | ||
| 567 | end | ||
| 568 | |||
| 569 | -- Dump recorded bytecode. | ||
| 570 | local function dump_record(tr, func, pc, depth, callee) | ||
| 571 | if depth ~= recdepth then | ||
| 572 | recdepth = depth | ||
| 573 | recprefix = rep(" .", depth) | ||
| 574 | end | ||
| 575 | local line | ||
| 576 | if pc >= 0 then | ||
| 577 | line = bcline(func, pc, recprefix) | ||
| 578 | if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end | ||
| 579 | else | ||
| 580 | line = "0000 "..recprefix.." FUNCC \n" | ||
| 581 | callee = func | ||
| 582 | end | ||
| 583 | if pc <= 0 then | ||
| 584 | out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n") | ||
| 585 | else | ||
| 586 | out:write(line) | ||
| 587 | end | ||
| 588 | if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC | ||
| 589 | out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond. | ||
| 590 | end | ||
| 591 | end | ||
| 592 | |||
| 593 | ------------------------------------------------------------------------------ | ||
| 594 | |||
| 595 | -- Dump taken trace exits. | ||
| 596 | local function dump_texit(tr, ex, ngpr, nfpr, ...) | ||
| 597 | out:write("---- TRACE ", tr, " exit ", ex, "\n") | ||
| 598 | if dumpmode.X then | ||
| 599 | local regs = {...} | ||
| 600 | if jit.arch == "x64" then | ||
| 601 | for i=1,ngpr do | ||
| 602 | out:write(format(" %016x", regs[i])) | ||
| 603 | if i % 4 == 0 then out:write("\n") end | ||
| 604 | end | ||
| 605 | else | ||
| 606 | for i=1,ngpr do | ||
| 607 | out:write(format(" %08x", regs[i])) | ||
| 608 | if i % 8 == 0 then out:write("\n") end | ||
| 609 | end | ||
| 610 | end | ||
| 611 | if jit.arch == "mips" or jit.arch == "mipsel" then | ||
| 612 | for i=1,nfpr,2 do | ||
| 613 | out:write(format(" %+17.14g", regs[ngpr+i])) | ||
| 614 | if i % 8 == 7 then out:write("\n") end | ||
| 615 | end | ||
| 616 | else | ||
| 617 | for i=1,nfpr do | ||
| 618 | out:write(format(" %+17.14g", regs[ngpr+i])) | ||
| 619 | if i % 4 == 0 then out:write("\n") end | ||
| 620 | end | ||
| 621 | end | ||
| 622 | end | ||
| 623 | end | ||
| 624 | |||
| 625 | ------------------------------------------------------------------------------ | ||
| 626 | |||
| 627 | -- Detach dump handlers. | ||
| 628 | local function dumpoff() | ||
| 629 | if active then | ||
| 630 | active = false | ||
| 631 | jit.attach(dump_texit) | ||
| 632 | jit.attach(dump_record) | ||
| 633 | jit.attach(dump_trace) | ||
| 634 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 635 | out = nil | ||
| 636 | end | ||
| 637 | end | ||
| 638 | |||
| 639 | -- Open the output file and attach dump handlers. | ||
| 640 | local function dumpon(opt, outfile) | ||
| 641 | if active then dumpoff() end | ||
| 642 | |||
| 643 | local colormode = os.getenv("COLORTERM") and "A" or "T" | ||
| 644 | if opt then | ||
| 645 | opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end) | ||
| 646 | end | ||
| 647 | |||
| 648 | local m = { t=true, b=true, i=true, m=true, } | ||
| 649 | if opt and opt ~= "" then | ||
| 650 | local o = sub(opt, 1, 1) | ||
| 651 | if o ~= "+" and o ~= "-" then m = {} end | ||
| 652 | for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end | ||
| 653 | end | ||
| 654 | dumpmode = m | ||
| 655 | |||
| 656 | if m.t or m.b or m.i or m.s or m.m then | ||
| 657 | jit.attach(dump_trace, "trace") | ||
| 658 | end | ||
| 659 | if m.b then | ||
| 660 | jit.attach(dump_record, "record") | ||
| 661 | if not bcline then bcline = require("jit.bc").line end | ||
| 662 | end | ||
| 663 | if m.x or m.X then | ||
| 664 | jit.attach(dump_texit, "texit") | ||
| 665 | end | ||
| 666 | |||
| 667 | if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end | ||
| 668 | if outfile then | ||
| 669 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 670 | else | ||
| 671 | out = stdout | ||
| 672 | end | ||
| 673 | |||
| 674 | m[colormode] = true | ||
| 675 | if colormode == "A" then | ||
| 676 | colorize = colorize_ansi | ||
| 677 | irtype = irtype_ansi | ||
| 678 | elseif colormode == "H" then | ||
| 679 | colorize = colorize_html | ||
| 680 | irtype = irtype_html | ||
| 681 | out:write(header_html) | ||
| 682 | else | ||
| 683 | colorize = colorize_text | ||
| 684 | irtype = irtype_text | ||
| 685 | end | ||
| 686 | |||
| 687 | active = true | ||
| 688 | end | ||
| 689 | |||
| 690 | -- Public module functions. | ||
| 691 | module(...) | ||
| 692 | |||
| 693 | on = dumpon | ||
| 694 | off = dumpoff | ||
| 695 | start = dumpon -- For -j command line option. | ||
| 696 | |||
diff --git a/src/jit/v.lua b/src/jit/v.lua new file mode 100644 index 00000000..8f58fe3b --- /dev/null +++ b/src/jit/v.lua | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | ---------------------------------------------------------------------------- | ||
| 2 | -- Verbose mode of the LuaJIT compiler. | ||
| 3 | -- | ||
| 4 | -- Copyright (C) 2005-2012 Mike Pall. All rights reserved. | ||
| 5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
| 6 | ---------------------------------------------------------------------------- | ||
| 7 | -- | ||
| 8 | -- This module shows verbose information about the progress of the | ||
| 9 | -- JIT compiler. It prints one line for each generated trace. This module | ||
| 10 | -- is useful to see which code has been compiled or where the compiler | ||
| 11 | -- punts and falls back to the interpreter. | ||
| 12 | -- | ||
| 13 | -- Example usage: | ||
| 14 | -- | ||
| 15 | -- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end" | ||
| 16 | -- luajit -jv=myapp.out myapp.lua | ||
| 17 | -- | ||
| 18 | -- Default output is to stderr. To redirect the output to a file, pass a | ||
| 19 | -- filename as an argument (use '-' for stdout) or set the environment | ||
| 20 | -- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the | ||
| 21 | -- module is started. | ||
| 22 | -- | ||
| 23 | -- The output from the first example should look like this: | ||
| 24 | -- | ||
| 25 | -- [TRACE 1 (command line):1 loop] | ||
| 26 | -- [TRACE 2 (1/3) (command line):1 -> 1] | ||
| 27 | -- | ||
| 28 | -- The first number in each line is the internal trace number. Next are | ||
| 29 | -- the file name ('(command line)') and the line number (':1') where the | ||
| 30 | -- trace has started. Side traces also show the parent trace number and | ||
| 31 | -- the exit number where they are attached to in parentheses ('(1/3)'). | ||
| 32 | -- An arrow at the end shows where the trace links to ('-> 1'), unless | ||
| 33 | -- it loops to itself. | ||
| 34 | -- | ||
| 35 | -- In this case the inner loop gets hot and is traced first, generating | ||
| 36 | -- a root trace. Then the last exit from the 1st trace gets hot, too, | ||
| 37 | -- and triggers generation of the 2nd trace. The side trace follows the | ||
| 38 | -- path along the outer loop and *around* the inner loop, back to its | ||
| 39 | -- start, and then links to the 1st trace. Yes, this may seem unusual, | ||
| 40 | -- if you know how traditional compilers work. Trace compilers are full | ||
| 41 | -- of surprises like this -- have fun! :-) | ||
| 42 | -- | ||
| 43 | -- Aborted traces are shown like this: | ||
| 44 | -- | ||
| 45 | -- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50] | ||
| 46 | -- | ||
| 47 | -- Don't worry -- trace aborts are quite common, even in programs which | ||
| 48 | -- can be fully compiled. The compiler may retry several times until it | ||
| 49 | -- finds a suitable trace. | ||
| 50 | -- | ||
| 51 | -- Of course this doesn't work with features that are not-yet-implemented | ||
| 52 | -- (NYI error messages). The VM simply falls back to the interpreter. This | ||
| 53 | -- may not matter at all if the particular trace is not very high up in | ||
| 54 | -- the CPU usage profile. Oh, and the interpreter is quite fast, too. | ||
| 55 | -- | ||
| 56 | -- Also check out the -jdump module, which prints all the gory details. | ||
| 57 | -- | ||
| 58 | ------------------------------------------------------------------------------ | ||
| 59 | |||
| 60 | -- Cache some library functions and objects. | ||
| 61 | local jit = require("jit") | ||
| 62 | assert(jit.version_num == 20000, "LuaJIT core/library version mismatch") | ||
| 63 | local jutil = require("jit.util") | ||
| 64 | local vmdef = require("jit.vmdef") | ||
| 65 | local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo | ||
| 66 | local type, format = type, string.format | ||
| 67 | local stdout, stderr = io.stdout, io.stderr | ||
| 68 | |||
| 69 | -- Active flag and output file handle. | ||
| 70 | local active, out | ||
| 71 | |||
| 72 | ------------------------------------------------------------------------------ | ||
| 73 | |||
| 74 | local startloc, startex | ||
| 75 | |||
| 76 | local function fmtfunc(func, pc) | ||
| 77 | local fi = funcinfo(func, pc) | ||
| 78 | if fi.loc then | ||
| 79 | return fi.loc | ||
| 80 | elseif fi.ffid then | ||
| 81 | return vmdef.ffnames[fi.ffid] | ||
| 82 | elseif fi.addr then | ||
| 83 | return format("C:%x", fi.addr) | ||
| 84 | else | ||
| 85 | return "(?)" | ||
| 86 | end | ||
| 87 | end | ||
| 88 | |||
| 89 | -- Format trace error message. | ||
| 90 | local function fmterr(err, info) | ||
| 91 | if type(err) == "number" then | ||
| 92 | if type(info) == "function" then info = fmtfunc(info) end | ||
| 93 | err = format(vmdef.traceerr[err], info) | ||
| 94 | end | ||
| 95 | return err | ||
| 96 | end | ||
| 97 | |||
| 98 | -- Dump trace states. | ||
| 99 | local function dump_trace(what, tr, func, pc, otr, oex) | ||
| 100 | if what == "start" then | ||
| 101 | startloc = fmtfunc(func, pc) | ||
| 102 | startex = otr and "("..otr.."/"..oex..") " or "" | ||
| 103 | else | ||
| 104 | if what == "abort" then | ||
| 105 | local loc = fmtfunc(func, pc) | ||
| 106 | if loc ~= startloc then | ||
| 107 | out:write(format("[TRACE --- %s%s -- %s at %s]\n", | ||
| 108 | startex, startloc, fmterr(otr, oex), loc)) | ||
| 109 | else | ||
| 110 | out:write(format("[TRACE --- %s%s -- %s]\n", | ||
| 111 | startex, startloc, fmterr(otr, oex))) | ||
| 112 | end | ||
| 113 | elseif what == "stop" then | ||
| 114 | local info = traceinfo(tr) | ||
| 115 | local link, ltype = info.link, info.linktype | ||
| 116 | if ltype == "interpreter" then | ||
| 117 | out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n", | ||
| 118 | tr, startex, startloc)) | ||
| 119 | elseif link == tr or link == 0 then | ||
| 120 | out:write(format("[TRACE %3s %s%s %s]\n", | ||
| 121 | tr, startex, startloc, ltype)) | ||
| 122 | elseif ltype == "root" then | ||
| 123 | out:write(format("[TRACE %3s %s%s -> %d]\n", | ||
| 124 | tr, startex, startloc, link)) | ||
| 125 | else | ||
| 126 | out:write(format("[TRACE %3s %s%s -> %d %s]\n", | ||
| 127 | tr, startex, startloc, link, ltype)) | ||
| 128 | end | ||
| 129 | else | ||
| 130 | out:write(format("[TRACE %s]\n", what)) | ||
| 131 | end | ||
| 132 | out:flush() | ||
| 133 | end | ||
| 134 | end | ||
| 135 | |||
| 136 | ------------------------------------------------------------------------------ | ||
| 137 | |||
| 138 | -- Detach dump handlers. | ||
| 139 | local function dumpoff() | ||
| 140 | if active then | ||
| 141 | active = false | ||
| 142 | jit.attach(dump_trace) | ||
| 143 | if out and out ~= stdout and out ~= stderr then out:close() end | ||
| 144 | out = nil | ||
| 145 | end | ||
| 146 | end | ||
| 147 | |||
| 148 | -- Open the output file and attach dump handlers. | ||
| 149 | local function dumpon(outfile) | ||
| 150 | if active then dumpoff() end | ||
| 151 | if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end | ||
| 152 | if outfile then | ||
| 153 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
| 154 | else | ||
| 155 | out = stderr | ||
| 156 | end | ||
| 157 | jit.attach(dump_trace, "trace") | ||
| 158 | active = true | ||
| 159 | end | ||
| 160 | |||
| 161 | -- Public module functions. | ||
| 162 | module(...) | ||
| 163 | |||
| 164 | on = dumpon | ||
| 165 | off = dumpoff | ||
| 166 | start = dumpon -- For -j command line option. | ||
| 167 | |||
diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat index 09782db4..63467308 100644 --- a/src/msvcbuild.bat +++ b/src/msvcbuild.bat | |||
| @@ -46,7 +46,7 @@ buildvm -m libdef -o lj_libdef.h %ALL_LIB% | |||
| 46 | @if errorlevel 1 goto :BAD | 46 | @if errorlevel 1 goto :BAD |
| 47 | buildvm -m recdef -o lj_recdef.h %ALL_LIB% | 47 | buildvm -m recdef -o lj_recdef.h %ALL_LIB% |
| 48 | @if errorlevel 1 goto :BAD | 48 | @if errorlevel 1 goto :BAD |
| 49 | buildvm -m vmdef -o ..\lib\vmdef.lua %ALL_LIB% | 49 | buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB% |
| 50 | @if errorlevel 1 goto :BAD | 50 | @if errorlevel 1 goto :BAD |
| 51 | buildvm -m folddef -o lj_folddef.h lj_opt_fold.c | 51 | buildvm -m folddef -o lj_folddef.h lj_opt_fold.c |
| 52 | @if errorlevel 1 goto :BAD | 52 | @if errorlevel 1 goto :BAD |
