bc.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. ----------------------------------------------------------------------------
  2. -- LuaJIT bytecode listing module.
  3. --
  4. -- Copyright (C) 2005-2025 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. local jutil = require("jit.util")
  44. local vmdef = require("jit.vmdef")
  45. local bit = require("bit")
  46. local sub, gsub, format = string.sub, string.gsub, string.format
  47. local byte, band, shr = string.byte, bit.band, bit.rshift
  48. local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
  49. local funcuvname = jutil.funcuvname
  50. local bcnames = vmdef.bcnames
  51. local stdout, stderr = io.stdout, io.stderr
  52. ------------------------------------------------------------------------------
  53. local function ctlsub(c)
  54. if c == "\n" then return "\\n"
  55. elseif c == "\r" then return "\\r"
  56. elseif c == "\t" then return "\\t"
  57. else return format("\\%03d", byte(c))
  58. end
  59. end
  60. -- Return one bytecode line.
  61. local function bcline(func, pc, prefix)
  62. local ins, m = funcbc(func, pc)
  63. if not ins then return end
  64. local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
  65. local a = band(shr(ins, 8), 0xff)
  66. local oidx = 6*band(ins, 0xff)
  67. local op = sub(bcnames, oidx+1, oidx+6)
  68. local s = format("%04d %s %-6s %3s ",
  69. pc, prefix or " ", op, ma == 0 and "" or a)
  70. local d = shr(ins, 16)
  71. if mc == 13*128 then -- BCMjump
  72. return format("%s=> %04d\n", s, pc+d-0x7fff)
  73. end
  74. if mb ~= 0 then
  75. d = band(d, 0xff)
  76. elseif mc == 0 then
  77. return s.."\n"
  78. end
  79. local kc
  80. if mc == 10*128 then -- BCMstr
  81. kc = funck(func, -d-1)
  82. kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
  83. elseif mc == 9*128 then -- BCMnum
  84. kc = funck(func, d)
  85. if op == "TSETM " then kc = kc - 2^52 end
  86. elseif mc == 12*128 then -- BCMfunc
  87. local fi = funcinfo(funck(func, -d-1))
  88. if fi.ffid then
  89. kc = vmdef.ffnames[fi.ffid]
  90. else
  91. kc = fi.loc
  92. end
  93. elseif mc == 5*128 then -- BCMuv
  94. kc = funcuvname(func, d)
  95. end
  96. if ma == 5 then -- BCMuv
  97. local ka = funcuvname(func, a)
  98. if kc then kc = ka.." ; "..kc else kc = ka end
  99. end
  100. if mb ~= 0 then
  101. local b = shr(ins, 24)
  102. if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
  103. return format("%s%3d %3d\n", s, b, d)
  104. end
  105. if kc then return format("%s%3d ; %s\n", s, d, kc) end
  106. if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
  107. return format("%s%3d\n", s, d)
  108. end
  109. -- Collect branch targets of a function.
  110. local function bctargets(func)
  111. local target = {}
  112. for pc=1,1000000000 do
  113. local ins, m = funcbc(func, pc)
  114. if not ins then break end
  115. if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
  116. end
  117. return target
  118. end
  119. -- Dump bytecode instructions of a function.
  120. local function bcdump(func, out, all)
  121. if not out then out = stdout end
  122. local fi = funcinfo(func)
  123. if all and fi.children then
  124. for n=-1,-1000000000,-1 do
  125. local k = funck(func, n)
  126. if not k then break end
  127. if type(k) == "proto" then bcdump(k, out, true) end
  128. end
  129. end
  130. out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
  131. local target = bctargets(func)
  132. for pc=1,1000000000 do
  133. local s = bcline(func, pc, target[pc] and "=>")
  134. if not s then break end
  135. out:write(s)
  136. end
  137. out:write("\n")
  138. out:flush()
  139. end
  140. ------------------------------------------------------------------------------
  141. -- Active flag and output file handle.
  142. local active, out
  143. -- List handler.
  144. local function h_list(func)
  145. return bcdump(func, out)
  146. end
  147. -- Detach list handler.
  148. local function bclistoff()
  149. if active then
  150. active = false
  151. jit.attach(h_list)
  152. if out and out ~= stdout and out ~= stderr then out:close() end
  153. out = nil
  154. end
  155. end
  156. -- Open the output file and attach list handler.
  157. local function bcliston(outfile)
  158. if active then bclistoff() end
  159. if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
  160. if outfile then
  161. out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
  162. else
  163. out = stderr
  164. end
  165. jit.attach(h_list, "bc")
  166. active = true
  167. end
  168. -- Public module functions.
  169. return {
  170. line = bcline,
  171. dump = bcdump,
  172. targets = bctargets,
  173. on = bcliston,
  174. off = bclistoff,
  175. start = bcliston -- For -j command line option.
  176. }