src/jit/bc.lua - luajit-2.0-src

Functions defined

Source code

  1. ----------------------------------------------------------------------------
  2. -- LuaJIT bytecode listing module.
  3. --
  4. -- Copyright (C) 2005-2015 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. -- Cache some library functions and objects.
  42. local jit = require("jit")
  43. assert(jit.version_num == 20100, "LuaJIT core/library version mismatch")
  44. local jutil = require("jit.util")
  45. local vmdef = require("jit.vmdef")
  46. local bit = require("bit")
  47. local sub, gsub, format = string.sub, string.gsub, string.format
  48. local byte, band, shr = string.byte, bit.band, bit.rshift
  49. local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
  50. local funcuvname = jutil.funcuvname
  51. local bcnames = vmdef.bcnames
  52. local stdout, stderr = io.stdout, io.stderr

  53. ------------------------------------------------------------------------------

  54. local function ctlsub(c)
  55.   if c == "\n" then return "\\n"
  56.   elseif c == "\r" then return "\\r"
  57.   elseif c == "\t" then return "\\t"
  58.   else return format("\\%03d", byte(c))
  59.   end
  60. end

  61. -- Return one bytecode line.
  62. local function bcline(func, pc, prefix)
  63.   local ins, m = funcbc(func, pc)
  64.   if not ins then return end
  65.   local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
  66.   local a = band(shr(ins, 8), 0xff)
  67.   local oidx = 6*band(ins, 0xff)
  68.   local op = sub(bcnames, oidx+1, oidx+6)
  69.   local s = format("%04d %s %-6s %3s ",
  70.     pc, prefix or "  ", op, ma == 0 and "" or a)
  71.   local d = shr(ins, 16)
  72.   if mc == 13*128 then -- BCMjump
  73.     return format("%s=> %04d\n", s, pc+d-0x7fff)
  74.   end
  75.   if mb ~= 0 then
  76.     d = band(d, 0xff)
  77.   elseif mc == 0 then
  78.     return s.."\n"
  79.   end
  80.   local kc
  81.   if mc == 10*128 then -- BCMstr
  82.     kc = funck(func, -d-1)
  83.     kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
  84.   elseif mc == 9*128 then -- BCMnum
  85.     kc = funck(func, d)
  86.     if op == "TSETM " then kc = kc - 2^52 end
  87.   elseif mc == 12*128 then -- BCMfunc
  88.     local fi = funcinfo(funck(func, -d-1))
  89.     if fi.ffid then
  90.       kc = vmdef.ffnames[fi.ffid]
  91.     else
  92.       kc = fi.loc
  93.     end
  94.   elseif mc == 5*128 then -- BCMuv
  95.     kc = funcuvname(func, d)
  96.   end
  97.   if ma == 5 then -- BCMuv
  98.     local ka = funcuvname(func, a)
  99.     if kc then kc = ka.." ; "..kc else kc = ka end
  100.   end
  101.   if mb ~= 0 then
  102.     local b = shr(ins, 24)
  103.     if kc then return format("%s%3d %3d  ; %s\n", s, b, d, kc) end
  104.     return format("%s%3d %3d\n", s, b, d)
  105.   end
  106.   if kc then return format("%s%3d      ; %s\n", s, d, kc) end
  107.   if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
  108.   return format("%s%3d\n", s, d)
  109. end

  110. -- Collect branch targets of a function.
  111. local function bctargets(func)
  112.   local target = {}
  113.   for pc=1,1000000000 do
  114.     local ins, m = funcbc(func, pc)
  115.     if not ins then break end
  116.     if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
  117.   end
  118.   return target
  119. end

  120. -- Dump bytecode instructions of a function.
  121. local function bcdump(func, out, all)
  122.   if not out then out = stdout end
  123.   local fi = funcinfo(func)
  124.   if all and fi.children then
  125.     for n=-1,-1000000000,-1 do
  126.       local k = funck(func, n)
  127.       if not k then break end
  128.       if type(k) == "proto" then bcdump(k, out, true) end
  129.     end
  130.   end
  131.   out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
  132.   local target = bctargets(func)
  133.   for pc=1,1000000000 do
  134.     local s = bcline(func, pc, target[pc] and "=>")
  135.     if not s then break end
  136.     out:write(s)
  137.   end
  138.   out:write("\n")
  139.   out:flush()
  140. end

  141. ------------------------------------------------------------------------------

  142. -- Active flag and output file handle.
  143. local active, out

  144. -- List handler.
  145. local function h_list(func)
  146.   return bcdump(func, out)
  147. end

  148. -- Detach list handler.
  149. local function bclistoff()
  150.   if active then
  151.     active = false
  152.     jit.attach(h_list)
  153.     if out and out ~= stdout and out ~= stderr then out:close() end
  154.     out = nil
  155.   end
  156. end

  157. -- Open the output file and attach list handler.
  158. local function bcliston(outfile)
  159.   if active then bclistoff() end
  160.   if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
  161.   if outfile then
  162.     out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
  163.   else
  164.     out = stderr
  165.   end
  166.   jit.attach(h_list, "bc")
  167.   active = true
  168. end

  169. -- Public module functions.
  170. return {
  171.   line = bcline,
  172.   dump = bcdump,
  173.   targets = bctargets,
  174.   on = bcliston,
  175.   off = bclistoff,
  176.   start = bcliston -- For -j command line option.
  177. }