Merge branch 'master' into v2.1
[luajit-2.0.git] / src / jit / dis_mips.lua
blobbdd70d77882accfc798d93ff428c43e285d510aa
1 ----------------------------------------------------------------------------
2 -- LuaJIT MIPS disassembler module.
3 --
4 -- Copyright (C) 2005-2014 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 ------------------------------------------------------------------------------
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
21 ------------------------------------------------------------------------------
22 -- Primary and extended opcode maps
23 ------------------------------------------------------------------------------
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", }
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",
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",
56 local map_bshfl = {
57 shift = 6, mask = 31,
58 [2] = "wsbhDT",
59 [16] = "sebDT",
60 [24] = "sehDT",
63 local map_special3 = {
64 shift = 0, mask = 63,
65 [0] = "extTSAK", [4] = "insTSAL",
66 [32] = map_bshfl,
67 [59] = "rdhwrTD",
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",
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",
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",
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",
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",
164 local map_cop1w = {
165 shift = 0, mask = 63,
166 [32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
169 local map_cop1l = {
170 shift = 0, mask = 63,
171 [32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
174 local map_cop1bc = {
175 shift = 16, mask = 3,
176 [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
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,
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,
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,
228 ------------------------------------------------------------------------------
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",
237 ------------------------------------------------------------------------------
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
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))
254 ctx.pos = pos + 4
257 -- Fallback for unknown opcodes.
258 local function unknown(ctx)
259 return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
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)
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)
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
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._
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
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
361 elseif p == "1" then
362 if last == "ra" then
363 operands[#operands] = nil
365 else
366 assert(false)
368 if x then operands[#operands+1] = x; last = x end
371 return putop(ctx, name, operands)
374 ------------------------------------------------------------------------------
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
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
399 local function create_el(code, addr, out)
400 local ctx = create(code, addr, out)
401 ctx.get = get_le
402 return ctx
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()
410 local function disass_el(code, addr, out)
411 create_el(code, addr, out):disass()
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)
420 -- Public module functions.
421 return {
422 create = create,
423 create_el = create_el,
424 disass = disass,
425 disass_el = disass_el,
426 regname = regname