ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

genlibbc.lua (4966B)


      1 ----------------------------------------------------------------------------
      2 -- Lua script to dump the bytecode of the library functions written in Lua.
      3 -- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT.
      4 ----------------------------------------------------------------------------
      5 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      6 -- Released under the MIT license. See Copyright Notice in luajit.h
      7 ----------------------------------------------------------------------------
      8 
      9 local ffi = require("ffi")
     10 local bit = require("bit")
     11 local vmdef = require("jit.vmdef")
     12 local bcnames = vmdef.bcnames
     13 
     14 local format = string.format
     15 
     16 local isbe = (string.byte(string.dump(function() end), 5) % 2 == 1)
     17 
     18 local function usage(arg)
     19   io.stderr:write("Usage: ", arg and arg[0] or "genlibbc",
     20 		  " [-o buildvm_libbc.h] lib_*.c\n")
     21   os.exit(1)
     22 end
     23 
     24 local function parse_arg(arg)
     25   local outfile = "-"
     26   if not (arg and arg[1]) then
     27     usage(arg)
     28   end
     29   if arg[1] == "-o" then
     30     outfile = arg[2]
     31     if not outfile then usage(arg) end
     32     arg = {table.unpack(arg, 3)}
     33   end
     34   return outfile, arg
     35 end
     36 
     37 local function read_files(names)
     38   local src = ""
     39   for _,name in ipairs(names) do
     40     local fp = assert(io.open(name))
     41     src = src .. fp:read("*a")
     42     fp:close()
     43   end
     44   return src
     45 end
     46 
     47 local function transform_lua(code)
     48   local fixup = {}
     49   local n = -30000
     50   code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var)
     51     n = n + 1
     52     fixup[n] = { "CHECK", tp }
     53     return format("%s=%d", var, n)
     54   end)
     55   code = string.gsub(code, "PAIRS%((.-)%)", function(var)
     56     fixup.PAIRS = true
     57     return format("nil, %s, 0", var)
     58   end)
     59   return "return "..code, fixup
     60 end
     61 
     62 local function read_uleb128(p)
     63   local v = p[0]; p = p + 1
     64   if v >= 128 then
     65     local sh = 7; v = v - 128
     66     repeat
     67       local r = p[0]
     68       v = v + bit.lshift(bit.band(r, 127), sh)
     69       sh = sh + 7
     70       p = p + 1
     71     until r < 128
     72   end
     73   return p, v
     74 end
     75 
     76 -- ORDER LJ_T
     77 local name2itype = {
     78   str = 5, func = 9, tab = 12, int = 14, num = 15
     79 }
     80 
     81 local BC = {}
     82 for i=0,#bcnames/6-1 do
     83   BC[string.gsub(string.sub(bcnames, i*6+1, i*6+6), " ", "")] = i
     84 end
     85 local xop, xra = isbe and 3 or 0, isbe and 2 or 1
     86 local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3
     87 
     88 local function fixup_dump(dump, fixup)
     89   local buf = ffi.new("uint8_t[?]", #dump+1, dump)
     90   local p = buf+5
     91   local n, sizebc
     92   p, n = read_uleb128(p)
     93   local start = p
     94   p = p + 4
     95   p = read_uleb128(p)
     96   p = read_uleb128(p)
     97   p, sizebc = read_uleb128(p)
     98   local rawtab = {}
     99   for i=0,sizebc-1 do
    100     local op = p[xop]
    101     if op == BC.KSHORT then
    102       local rd = p[xrc] + 256*p[xrb]
    103       rd = bit.arshift(bit.lshift(rd, 16), 16)
    104       local f = fixup[rd]
    105       if f then
    106 	if f[1] == "CHECK" then
    107 	  local tp = f[2]
    108 	  if tp == "tab" then rawtab[p[xra]] = true end
    109 	  p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE
    110 	  p[xrb] = 0
    111 	  p[xrc] = name2itype[tp]
    112 	else
    113 	  error("unhandled fixup type: "..f[1])
    114 	end
    115       end
    116     elseif op == BC.TGETV then
    117       if rawtab[p[xrb]] then
    118 	p[xop] = BC.TGETR
    119       end
    120     elseif op == BC.TSETV then
    121       if rawtab[p[xrb]] then
    122 	p[xop] = BC.TSETR
    123       end
    124     elseif op == BC.ITERC then
    125       if fixup.PAIRS then
    126 	p[xop] = BC.ITERN
    127       end
    128     end
    129     p = p + 4
    130   end
    131   return ffi.string(start, n)
    132 end
    133 
    134 local function find_defs(src)
    135   local defs = {}
    136   for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do
    137     local env = {}
    138     local tcode, fixup = transform_lua(code)
    139     local func = assert(load(tcode, "", nil, env))()
    140     defs[name] = fixup_dump(string.dump(func, true), fixup)
    141     defs[#defs+1] = name
    142   end
    143   return defs
    144 end
    145 
    146 local function gen_header(defs)
    147   local t = {}
    148   local function w(x) t[#t+1] = x end
    149   w("/* This is a generated file. DO NOT EDIT! */\n\n")
    150   w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n")
    151   local s = ""
    152   for _,name in ipairs(defs) do
    153     s = s .. defs[name]
    154   end
    155   w("static const uint8_t libbc_code[] = {\n")
    156   local n = 0
    157   for i=1,#s do
    158     local x = string.byte(s, i)
    159     w(x); w(",")
    160     n = n + (x < 10 and 2 or (x < 100 and 3 or 4))
    161     if n >= 75 then n = 0; w("\n") end
    162   end
    163   w("0\n};\n\n")
    164   w("static const struct { const char *name; int ofs; } libbc_map[] = {\n")
    165   local m = 0
    166   for _,name in ipairs(defs) do
    167     w('{"'); w(name); w('",'); w(m) w('},\n')
    168     m = m + #defs[name]
    169   end
    170   w("{NULL,"); w(m); w("}\n};\n\n")
    171   return table.concat(t)
    172 end
    173 
    174 local function write_file(name, data)
    175   if name == "-" then
    176     assert(io.write(data))
    177     assert(io.flush())
    178   else
    179     local fp = io.open(name)
    180     if fp then
    181       local old = fp:read("*a")
    182       fp:close()
    183       if data == old then return end
    184     end
    185     fp = assert(io.open(name, "w"))
    186     assert(fp:write(data))
    187     assert(fp:close())
    188   end
    189 end
    190 
    191 local outfile
    192 outfile, arg = parse_arg(arg)
    193 local src = read_files(arg)
    194 local defs = find_defs(src)
    195 local hdr = gen_header(defs)
    196 write_file(outfile, hdr)
    197