Fix FOLD rule for CONV.flt.num(CONV.num.flt(x)) => x.
[luajit-2.0.git] / lib / dump.lua
blobbb8aa1ce63b920479fdafa0e0e014489e91da82e
1 ----------------------------------------------------------------------------
2 -- LuaJIT compiler dump 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 --
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.
11 -- Example usage:
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
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.
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.
29 -- The following dump features are available (* marks the default):
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.
40 -- The output format can be set with the following characters:
42 -- T Plain text output.
43 -- A ANSI-colored text output
44 -- H Colorized HTML + CSS output.
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.
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"
53 ------------------------------------------------------------------------------
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
71 -- Load other modules on-demand.
72 local bcline, disass
74 -- Active flag, output file handle and dump mode.
75 local active, out, dumpmode
77 ------------------------------------------------------------------------------
79 local symtab = {}
80 local nexitsym = 0
82 -- Fill symbol table with trace exit addresses.
83 local function fillsymtab(nexit)
84 local t = symtab
85 if nexitsym == 0 then
86 local ircall = vmdef.ircall
87 for i=0,#ircall do
88 local addr = ircalladdr(i)
89 if addr ~= 0 then t[addr] = ircall[i] end
90 end
91 end
92 if nexit > nexitsym then
93 for i=nexitsym,nexit-1 do
94 local addr = traceexitstub(i)
95 if addr == nil then nexit = 1000000; break end
96 t[addr] = tostring(i)
97 end
98 nexitsym = nexit
99 end
100 return t
103 local function dumpwrite(s)
104 out:write(s)
107 -- Disassemble machine code.
108 local function dump_mcode(tr)
109 local info = traceinfo(tr)
110 if not info then return end
111 local mcode, addr, loop = tracemc(tr)
112 if not mcode then return end
113 if not disass then disass = require("jit.dis_"..jit.arch) end
114 out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
115 local ctx = disass.create(mcode, addr, dumpwrite)
116 ctx.hexdump = 0
117 ctx.symtab = fillsymtab(info.nexit)
118 if loop ~= 0 then
119 symtab[addr+loop] = "LOOP"
120 ctx:disass(0, loop)
121 out:write("->LOOP:\n")
122 ctx:disass(loop, #mcode-loop)
123 symtab[addr+loop] = nil
124 else
125 ctx:disass(0, #mcode)
129 ------------------------------------------------------------------------------
131 local irtype_text = {
132 [0] = "nil",
133 "fal",
134 "tru",
135 "lud",
136 "str",
137 "p32",
138 "thr",
139 "pro",
140 "fun",
141 "p64",
142 "cdt",
143 "tab",
144 "udt",
145 "flt",
146 "num",
147 "i8 ",
148 "u8 ",
149 "i16",
150 "u16",
151 "int",
152 "u32",
153 "i64",
154 "u64",
155 "sfp",
158 local colortype_ansi = {
159 [0] = "%s",
160 "%s",
161 "%s",
162 "\027[36m%s\027[m",
163 "\027[32m%s\027[m",
164 "%s",
165 "\027[1m%s\027[m",
166 "%s",
167 "\027[1m%s\027[m",
168 "%s",
169 "\027[33m%s\027[m",
170 "\027[31m%s\027[m",
171 "\027[36m%s\027[m",
172 "\027[34m%s\027[m",
173 "\027[34m%s\027[m",
174 "\027[35m%s\027[m",
175 "\027[35m%s\027[m",
176 "\027[35m%s\027[m",
177 "\027[35m%s\027[m",
178 "\027[35m%s\027[m",
179 "\027[35m%s\027[m",
180 "\027[35m%s\027[m",
181 "\027[35m%s\027[m",
182 "\027[35m%s\027[m",
185 local function colorize_text(s, t)
186 return s
189 local function colorize_ansi(s, t)
190 return format(colortype_ansi[t], s)
193 local irtype_ansi = setmetatable({},
194 { __index = function(tab, t)
195 local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
197 local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
199 local function colorize_html(s, t)
200 s = gsub(s, "[<>&]", html_escape)
201 return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
204 local irtype_html = setmetatable({},
205 { __index = function(tab, t)
206 local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
208 local header_html = [[
209 <style type="text/css">
210 background { background: #ffffff; color: #000000; }
211 pre.ljdump {
212 font-size: 10pt;
213 background: #f0f4ff;
214 color: #000000;
215 border: 1px solid #bfcfff;
216 padding: 0.5em;
217 margin-left: 2em;
218 margin-right: 2em;
220 span.irt_str { color: #00a000; }
221 span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
222 span.irt_tab { color: #c00000; }
223 span.irt_udt, span.irt_lud { color: #00c0c0; }
224 span.irt_num { color: #4040c0; }
225 span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
226 </style>
229 local colorize, irtype
231 -- Lookup tables to convert some literals into names.
232 local litname = {
233 ["SLOAD "] = setmetatable({}, { __index = function(t, mode)
234 local s = ""
235 if band(mode, 1) ~= 0 then s = s.."P" end
236 if band(mode, 2) ~= 0 then s = s.."F" end
237 if band(mode, 4) ~= 0 then s = s.."T" end
238 if band(mode, 8) ~= 0 then s = s.."C" end
239 if band(mode, 16) ~= 0 then s = s.."R" end
240 if band(mode, 32) ~= 0 then s = s.."I" end
241 t[mode] = s
242 return s
243 end}),
244 ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
245 ["CONV "] = setmetatable({}, { __index = function(t, mode)
246 local s = irtype[band(mode, 31)]
247 s = irtype[band(shr(mode, 5), 31)].."."..s
248 if band(mode, 0x400) ~= 0 then s = s.." trunc"
249 elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
250 local c = shr(mode, 14)
251 if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
252 t[mode] = s
253 return s
254 end}),
255 ["FLOAD "] = vmdef.irfield,
256 ["FREF "] = vmdef.irfield,
257 ["FPMATH"] = vmdef.irfpm,
260 local function ctlsub(c)
261 if c == "\n" then return "\\n"
262 elseif c == "\r" then return "\\r"
263 elseif c == "\t" then return "\\t"
264 elseif c == "\r" then return "\\r"
265 else return format("\\%03d", byte(c))
269 local function fmtfunc(func, pc)
270 local fi = funcinfo(func, pc)
271 if fi.loc then
272 return fi.loc
273 elseif fi.ffid then
274 return vmdef.ffnames[fi.ffid]
275 elseif fi.addr then
276 return format("C:%x", fi.addr)
277 else
278 return "(?)"
282 local function formatk(tr, idx)
283 local k, t, slot = tracek(tr, idx)
284 local tn = type(k)
285 local s
286 if tn == "number" then
287 if k == 2^52+2^51 then
288 s = "bias"
289 else
290 s = format("%+.14g", k)
292 elseif tn == "string" then
293 s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
294 elseif tn == "function" then
295 s = fmtfunc(k)
296 elseif tn == "table" then
297 s = format("{%p}", k)
298 elseif tn == "userdata" then
299 if t == 12 then
300 s = format("userdata:%p", k)
301 else
302 s = format("[%p]", k)
303 if s == "[0x00000000]" then s = "NULL" end
305 elseif t == 21 then -- int64_t
306 s = sub(tostring(k), 1, -3)
307 if sub(s, 1, 1) ~= "-" then s = "+"..s end
308 else
309 s = tostring(k) -- For primitives.
311 s = colorize(format("%-4s", s), t)
312 if slot then
313 s = format("%s @%d", s, slot)
315 return s
318 local function printsnap(tr, snap)
319 local n = 2
320 for s=0,snap[1]-1 do
321 local sn = snap[n]
322 if shr(sn, 24) == s then
323 n = n + 1
324 local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
325 if ref < 0 then
326 out:write(formatk(tr, ref))
327 elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
328 out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
329 else
330 local m, ot, op1, op2 = traceir(tr, ref)
331 out:write(colorize(format("%04d", ref), band(ot, 31)))
333 out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
334 else
335 out:write("---- ")
338 out:write("]\n")
341 -- Dump snapshots (not interleaved with IR).
342 local function dump_snap(tr)
343 out:write("---- TRACE ", tr, " snapshots\n")
344 for i=0,1000000000 do
345 local snap = tracesnap(tr, i)
346 if not snap then break end
347 out:write(format("#%-3d %04d [ ", i, snap[0]))
348 printsnap(tr, snap)
352 -- Return a register name or stack slot for a rid/sp location.
353 local function ridsp_name(ridsp)
354 if not disass then disass = require("jit.dis_"..jit.arch) end
355 local rid = band(ridsp, 0xff)
356 if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
357 if rid < 128 then return disass.regname(rid) end
358 return ""
361 -- Recursively gather CALL* args and dump them.
362 local function dumpcallargs(tr, ins)
363 if ins < 0 then
364 out:write(formatk(tr, ins))
365 else
366 local m, ot, op1, op2 = traceir(tr, ins)
367 local oidx = 6*shr(ot, 8)
368 local op = sub(vmdef.irnames, oidx+1, oidx+6)
369 if op == "CARG " then
370 dumpcallargs(tr, op1)
371 if op2 < 0 then
372 out:write(" ", formatk(tr, op2))
373 else
374 out:write(" ", format("%04d", op2))
376 else
377 out:write(format("%04d", ins))
382 -- Dump IR and interleaved snapshots.
383 local function dump_ir(tr, dumpsnap, dumpreg)
384 local info = traceinfo(tr)
385 if not info then return end
386 local nins = info.nins
387 out:write("---- TRACE ", tr, " IR\n")
388 local irnames = vmdef.irnames
389 local snapref = 65536
390 local snap, snapno
391 if dumpsnap then
392 snap = tracesnap(tr, 0)
393 snapref = snap[0]
394 snapno = 0
396 for ins=1,nins do
397 if ins >= snapref then
398 if dumpreg then
399 out:write(format(".... SNAP #%-3d [ ", snapno))
400 else
401 out:write(format(".... SNAP #%-3d [ ", snapno))
403 printsnap(tr, snap)
404 snapno = snapno + 1
405 snap = tracesnap(tr, snapno)
406 snapref = snap and snap[0] or 65536
408 local m, ot, op1, op2, ridsp = traceir(tr, ins)
409 local oidx, t = 6*shr(ot, 8), band(ot, 31)
410 local op = sub(irnames, oidx+1, oidx+6)
411 if op == "LOOP " then
412 if dumpreg then
413 out:write(format("%04d ------------ LOOP ------------\n", ins))
414 else
415 out:write(format("%04d ------ LOOP ------------\n", ins))
417 elseif op ~= "NOP " and op ~= "CARG " and
418 (dumpreg or op ~= "RENAME") then
419 if dumpreg then
420 out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
421 else
422 out:write(format("%04d ", ins))
424 out:write(format("%s%s %s %s ",
425 band(ot, 128) == 0 and " " or ">",
426 band(ot, 64) == 0 and " " or "+",
427 irtype[t], op))
428 local m1, m2 = band(m, 3), band(m, 3*4)
429 if sub(op, 1, 4) == "CALL" then
430 if m2 == 1*4 then -- op2 == IRMlit
431 out:write(format("%-10s (", vmdef.ircall[op2]))
432 elseif op2 < 0 then
433 out:write(format("[0x%x](", tonumber((tracek(tr, op2)))))
434 else
435 out:write(format("%04d (", op2))
437 if op1 ~= -1 then dumpcallargs(tr, op1) end
438 out:write(")")
439 elseif op == "CNEW " and op2 == -1 then
440 out:write(formatk(tr, op1))
441 elseif m1 ~= 3 then -- op1 != IRMnone
442 if op1 < 0 then
443 out:write(formatk(tr, op1))
444 else
445 out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
447 if m2 ~= 3*4 then -- op2 != IRMnone
448 if m2 == 1*4 then -- op2 == IRMlit
449 local litn = litname[op]
450 if litn and litn[op2] then
451 out:write(" ", litn[op2])
452 elseif op == "UREFO " or op == "UREFC " then
453 out:write(format(" #%-3d", shr(op2, 8)))
454 else
455 out:write(format(" #%-3d", op2))
457 elseif op2 < 0 then
458 out:write(" ", formatk(tr, op2))
459 else
460 out:write(format(" %04d", op2))
464 out:write("\n")
467 if snap then
468 if dumpreg then
469 out:write(format(".... SNAP #%-3d [ ", snapno))
470 else
471 out:write(format(".... SNAP #%-3d [ ", snapno))
473 printsnap(tr, snap)
477 ------------------------------------------------------------------------------
479 local recprefix = ""
480 local recdepth = 0
482 -- Format trace error message.
483 local function fmterr(err, info)
484 if type(err) == "number" then
485 if type(info) == "function" then info = fmtfunc(info) end
486 err = format(vmdef.traceerr[err], info)
488 return err
491 -- Dump trace states.
492 local function dump_trace(what, tr, func, pc, otr, oex)
493 if what == "stop" or (what == "abort" and dumpmode.a) then
494 if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
495 elseif dumpmode.s then dump_snap(tr) end
496 if dumpmode.m then dump_mcode(tr) end
498 if what == "start" then
499 if dumpmode.H then out:write('<pre class="ljdump">\n') end
500 out:write("---- TRACE ", tr, " ", what)
501 if otr then out:write(" ", otr, "/", oex) end
502 out:write(" ", fmtfunc(func, pc), "\n")
503 recprefix = ""
504 elseif what == "stop" or what == "abort" then
505 out:write("---- TRACE ", tr, " ", what)
506 recprefix = nil
507 if what == "abort" then
508 out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
509 else
510 local info = traceinfo(tr)
511 local link, ltype = info.link, info.linktype
512 if link == tr or link == 0 then
513 out:write(" -> ", ltype, "\n")
514 elseif ltype == "root" then
515 out:write(" -> ", link, "\n")
516 else
517 out:write(" -> ", link, " ", ltype, "\n")
520 if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
521 else
522 out:write("---- TRACE ", what, "\n\n")
524 out:flush()
527 -- Dump recorded bytecode.
528 local function dump_record(tr, func, pc, depth, callee)
529 if depth ~= recdepth then
530 recdepth = depth
531 recprefix = rep(" .", depth)
533 local line
534 if pc >= 0 then
535 line = bcline(func, pc, recprefix)
536 if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
537 else
538 line = "0000 "..recprefix.." FUNCC \n"
539 callee = func
541 if pc <= 0 then
542 out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
543 else
544 out:write(line)
546 if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
547 out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
551 ------------------------------------------------------------------------------
553 -- Dump taken trace exits.
554 local function dump_texit(tr, ex, ngpr, nfpr, ...)
555 out:write("---- TRACE ", tr, " exit ", ex, "\n")
556 if dumpmode.X then
557 local regs = {...}
558 if jit.arch == "x64" then
559 for i=1,ngpr do
560 out:write(format(" %016x", regs[i]))
561 if i % 4 == 0 then out:write("\n") end
563 else
564 for i=1,ngpr do
565 out:write(format(" %08x", regs[i]))
566 if i % 8 == 0 then out:write("\n") end
569 for i=1,nfpr do
570 out:write(format(" %+17.14g", regs[ngpr+i]))
571 if i % 4 == 0 then out:write("\n") end
576 ------------------------------------------------------------------------------
578 -- Detach dump handlers.
579 local function dumpoff()
580 if active then
581 active = false
582 jit.attach(dump_texit)
583 jit.attach(dump_record)
584 jit.attach(dump_trace)
585 if out and out ~= stdout and out ~= stderr then out:close() end
586 out = nil
590 -- Open the output file and attach dump handlers.
591 local function dumpon(opt, outfile)
592 if active then dumpoff() end
594 local colormode = os.getenv("COLORTERM") and "A" or "T"
595 if opt then
596 opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
599 local m = { t=true, b=true, i=true, m=true, }
600 if opt and opt ~= "" then
601 local o = sub(opt, 1, 1)
602 if o ~= "+" and o ~= "-" then m = {} end
603 for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
605 dumpmode = m
607 if m.t or m.b or m.i or m.s or m.m then
608 jit.attach(dump_trace, "trace")
610 if m.b then
611 jit.attach(dump_record, "record")
612 if not bcline then bcline = require("jit.bc").line end
614 if m.x or m.X then
615 jit.attach(dump_texit, "texit")
618 if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
619 if outfile then
620 out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
621 else
622 out = stdout
625 m[colormode] = true
626 if colormode == "A" then
627 colorize = colorize_ansi
628 irtype = irtype_ansi
629 elseif colormode == "H" then
630 colorize = colorize_html
631 irtype = irtype_html
632 out:write(header_html)
633 else
634 colorize = colorize_text
635 irtype = irtype_text
638 active = true
641 -- Public module functions.
642 module(...)
644 on = dumpon
645 off = dumpoff
646 start = dumpon -- For -j command line option.