aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/LuaJIT-1.1.7/jit/dumphints.lua
blob: 4a6467614fc301567c4639bfa64d8621b0b028d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
----------------------------------------------------------------------------
-- LuaJIT hints dumper module.
--
-- Copyright (C) 2005-2011 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See luajit.h for full copyright notice.
----------------------------------------------------------------------------
-- Activate this module to dump the bytecode and the hints from
-- the optimizer for all functions to be compiled.
--
-- Try: luajit -O -j dumphints -e 'return 1'
--
-- Default output is to stderr. To redirect output to a file,
-- pass a filename as an argument or set the environment variable
-- "LUAJIT_DUMPHINTSFILE".
-- Note: The file is overwritten each time you run luajit.
--
-- TODO: Find a way to be more selective on what to dump.
------------------------------------------------------------------------------

-- Priority for compiler pipeline. Should run before backend (positive)
-- and should be even because we only catch successful compiles.
local PRIORITY = 10

-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 10107, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local type, pairs, format = type, pairs, string.format
local bytecode, const = jutil.bytecode, jutil.const
local hints, fhints = jutil.hints, jutil.fhints
local stdout, stderr = io.stdout, io.stderr

-- Turn compilation off for the whole module. LuaJIT would do that anyway.
jit.off(true, true)

-- Separator line.
local sepline = "-------------------------------"


-- Pretty-print a constant.
local function conststr(func, idx)
  local k = const(func, idx)
  if k == nil then return "nil"
  elseif k == true then return "true"
  elseif k == false then return "false"
  elseif type(k) == "string" then
    if #k > 10 then return format('"%.10s"~', k)
    else return '"'..k..'"' end
  else return k.."" end
end

-- Pretty-print a bytecode instruction.
local function bytecodeline(func, pc, flag)
  local op, a, b, c = bytecode(func, pc)
  if not op then return end
  if op == "JMP" then
    return format("\n%04d %s JMP   => %04d", pc, flag, pc+1+b)
  end
  if op == "FORLOOP" or op == "FORPREP" then
    return format("\n%04d %s %-9s %3d => %04d", pc, flag, op, a, pc+1+b)
  end
  local s = format("\n%04d %s %-9s %3d %4s %4s",
		   pc, flag, op, a, b or "", c or "")
  if b and b < 0 then s = s.." ; "..conststr(func, b) end
  if c and c < 0 then s = s.." ; "..conststr(func, c) end
  return s
end

-- Precompute inverse hints table.
local invhints = {}
for k,v in pairs(hints) do invhints[v] = k end

-- The inverse resolver for inline functions is loaded on demand.
local getname

-- Helper functions to pretty-print hints.
local function typehint(h, v, st, pc)
  if st[pc+hints.INLINE] then return "" end
  local tp = type(v)
  if tp == "function" then
    tp = debug.getinfo(v, "S").what
  elseif tp == "number" and v % 1 == 0 then
    tp = "integer"
  elseif v == false then
    tp = "mixed"
  end
  return " #"..h.."("..tp..")"
end

local hintprint = {
  COMBINE = function(h, v, st, pc)
    if v == false then return "" end -- Dead instructions are already marked.
  end,
  TYPE = typehint,
  TYPEKEY = typehint,
  INLINE = function(h, v, st, pc)
    if not getname then getname = require("jit.opt_inline").getname end
    return " #INLINE("..getname(st[pc+hints.TYPE], v)..")"
  end,
}

-- Generate range string from table: pc[-pc] [,...]
local function rangestring(t)
  local s = ""
  for i,range in ipairs(t) do
    if i ~= 1 then s = s.."," end
    local pc = range % 65536
    range = (range - pc) / 65536
    s = s..pc
    if range ~= 0 then s = s..(-(pc+range)) end
  end
  return s
end

-- Dump instructions and hints for a (to be compiled) function.
local function dumphints(st, out)
  if not out then out = stderr end
  local func = st.func
  local COMBINE = hints.COMBINE

  -- Need to recompute branch targets (not part of hints).
  local target = {}
  for pc=1,1e6 do
    local op, a, b, c = bytecode(func, pc)
    if not op then break end
    if op == "JMP" or op == "FORLOOP" then
      local t = pc+1+b
      if st[pc+COMBINE] ~= false then target[t] = true end
    elseif op == "LOADBOOL" and c ~= 0 then
      target[pc+2] = true
    end
  end

  -- Map hints to bytecode instructions.
  local hintstr = {}
  for k,v in pairs(st) do
    -- CHECK: must match hint shift in ljit_hints.h:JIT_H2NUM().
    if type(k) == "number" and k >= 65536 then
      local pc = k % 65536
      if pc > 0 then
	k = k - pc
	local h = invhints[k] or (k/65536)
	local hp = hintprint[h]
	local s = hp and hp(h, v, st, pc) or (" #"..h)
	local hs = hintstr[pc]
	hintstr[pc] = hs and (hs..s) or s
      end
    end
  end

  -- Write header.
  local info = debug.getinfo(func, "S")
  out:write(sepline, " ", info.source, ":", info.linedefined)

  -- Write function hints.
  for k,v in pairs(fhints) do
    if st[v] then out:write("\n#", k) end
  end

  -- Write instruction hints and bytecode.
  local function dumprange(firstpc, lastpc)
    for pc=firstpc,lastpc do
      local prefix = "  "
      if st[pc+COMBINE] == false then prefix = "**"
      elseif target[pc] then prefix = "=>" end
      local line = bytecodeline(func, pc, prefix)
      if not line then break end
      local h = hintstr[pc]
      if h then
	out:write(format("%-40s %s", line, h))
      else
	out:write(line)
      end
    end
  end

  -- Handle deoptimization range table.
  local t = st.deopt
  if t then
    out:write("  DEOPT=", rangestring(t))
    for i,range in ipairs(t) do
      if i ~= 1 then out:write("\n----") end
      local pc = range % 65536
      range = (range - pc) / 65536
      dumprange(pc, pc+range)
    end
  else
    dumprange(1, 1000000)
  end

  -- Write footer.
  out:write("\n", sepline, "\n")
  out:flush()
end


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

-- Dump hints handler for compiler pipeline.
local function h_dumphints(st)
  local ok, err = pcall(dumphints, st, out)
  if not ok then
    stderr:write("\nERROR: jit.dumphints disabled: ", err, "\n")
    jit.attach(h_dumphints) -- Better turn ourselves off after a failure.
    if out and out ~= stdout then out:close() end
    out = nil
    active = nil
  end
end

-- Detach dump handler from compiler pipeline.
local function dumphintsoff()
  if active then
    active = false
    jit.attach(h_dumphints)
    if out and out ~= stdout then out:close() end
    out = nil
  end
end

-- Open the output file and attach dump handler to compiler pipeline.
local function dumphintson(filename)
  if active then dumphintsoff() end
  local outfile = filename or os.getenv("LUAJIT_DUMPHINTSFILE")
  out = outfile and (outfile == "-" and stdout or assert(io.open(outfile, "w")))
  jit.attach(h_dumphints, PRIORITY)
  active = true
end


-- Public module functions.
module(...)

dump = dumphints
on = dumphintson
off = dumphintsoff
start = dumphintson -- For -j command line option.