diff options
Diffstat (limited to 'src/host/genlibbc.lua')
| -rw-r--r-- | src/host/genlibbc.lua | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/host/genlibbc.lua b/src/host/genlibbc.lua new file mode 100644 index 00000000..3eb1b827 --- /dev/null +++ b/src/host/genlibbc.lua | |||
| @@ -0,0 +1,234 @@ | |||
| 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-2026 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 | table.remove(arg, 1) | ||
| 33 | table.remove(arg, 1) | ||
| 34 | end | ||
| 35 | return outfile | ||
| 36 | end | ||
| 37 | |||
| 38 | local function read_files(names) | ||
| 39 | local src = "" | ||
| 40 | for _,name in ipairs(names) do | ||
| 41 | local fp = assert(io.open(name)) | ||
| 42 | src = src .. fp:read("*a") | ||
| 43 | fp:close() | ||
| 44 | end | ||
| 45 | return src | ||
| 46 | end | ||
| 47 | |||
| 48 | local function transform_lua(code) | ||
| 49 | local fixup = {} | ||
| 50 | local n = -30000 | ||
| 51 | code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var) | ||
| 52 | n = n + 1 | ||
| 53 | fixup[n] = { "CHECK", tp } | ||
| 54 | return format("%s=%d", var, n) | ||
| 55 | end) | ||
| 56 | code = string.gsub(code, "PAIRS%((.-)%)", function(var) | ||
| 57 | fixup.PAIRS = true | ||
| 58 | return format("nil, %s, 0x4dp80", var) | ||
| 59 | end) | ||
| 60 | return "return "..code, fixup | ||
| 61 | end | ||
| 62 | |||
| 63 | local function read_uleb128(p) | ||
| 64 | local v = p[0]; p = p + 1 | ||
| 65 | if v >= 128 then | ||
| 66 | local sh = 7; v = v - 128 | ||
| 67 | repeat | ||
| 68 | local r = p[0] | ||
| 69 | v = v + bit.lshift(bit.band(r, 127), sh) | ||
| 70 | sh = sh + 7 | ||
| 71 | p = p + 1 | ||
| 72 | until r < 128 | ||
| 73 | end | ||
| 74 | return p, v | ||
| 75 | end | ||
| 76 | |||
| 77 | -- ORDER LJ_T | ||
| 78 | local name2itype = { | ||
| 79 | str = 5, func = 9, tab = 12, int = 14, num = 15 | ||
| 80 | } | ||
| 81 | |||
| 82 | local BC, BCN = {}, {} | ||
| 83 | for i=0,#bcnames/6-1 do | ||
| 84 | local name = bcnames:sub(i*6+1, i*6+6):gsub(" ", "") | ||
| 85 | BC[name] = i | ||
| 86 | BCN[i] = name | ||
| 87 | end | ||
| 88 | local xop, xra = isbe and 3 or 0, isbe and 2 or 1 | ||
| 89 | local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3 | ||
| 90 | |||
| 91 | local function fixup_dump(dump, fixup) | ||
| 92 | local buf = ffi.new("uint8_t[?]", #dump+1, dump) | ||
| 93 | local p = buf+5 | ||
| 94 | local n, sizebc | ||
| 95 | p, n = read_uleb128(p) | ||
| 96 | local start = p | ||
| 97 | p = p + 4 | ||
| 98 | p = read_uleb128(p) | ||
| 99 | p = read_uleb128(p) | ||
| 100 | p, sizebc = read_uleb128(p) | ||
| 101 | local startbc = tonumber(p - start) | ||
| 102 | local rawtab = {} | ||
| 103 | for i=0,sizebc-1 do | ||
| 104 | local op = p[xop] | ||
| 105 | if op == BC.KSHORT then | ||
| 106 | local rd = p[xrc] + 256*p[xrb] | ||
| 107 | rd = bit.arshift(bit.lshift(rd, 16), 16) | ||
| 108 | local f = fixup[rd] | ||
| 109 | if f then | ||
| 110 | if f[1] == "CHECK" then | ||
| 111 | local tp = f[2] | ||
| 112 | if tp == "tab" then rawtab[p[xra]] = true end | ||
| 113 | p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE | ||
| 114 | p[xrb] = 0 | ||
| 115 | p[xrc] = name2itype[tp] | ||
| 116 | else | ||
| 117 | error("unhandled fixup type: "..f[1]) | ||
| 118 | end | ||
| 119 | end | ||
| 120 | elseif op == BC.TGETV then | ||
| 121 | if rawtab[p[xrb]] then | ||
| 122 | p[xop] = BC.TGETR | ||
| 123 | end | ||
| 124 | elseif op == BC.TSETV then | ||
| 125 | if rawtab[p[xrb]] then | ||
| 126 | p[xop] = BC.TSETR | ||
| 127 | end | ||
| 128 | elseif op == BC.ITERC then | ||
| 129 | if fixup.PAIRS then | ||
| 130 | p[xop] = BC.ITERN | ||
| 131 | end | ||
| 132 | end | ||
| 133 | p = p + 4 | ||
| 134 | end | ||
| 135 | local ndump = ffi.string(start, n) | ||
| 136 | -- Fixup hi-part of 0x4dp80 to LJ_KEYINDEX. | ||
| 137 | ndump = ndump:gsub("\x80\x80\xcd\xaa\x04", "\xff\xff\xf9\xff\x0f") | ||
| 138 | return { dump = ndump, startbc = startbc, sizebc = sizebc } | ||
| 139 | end | ||
| 140 | |||
| 141 | local function find_defs(src, mode) | ||
| 142 | local defs = {} | ||
| 143 | for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do | ||
| 144 | local tcode, fixup = transform_lua(code) | ||
| 145 | local func = assert(load(tcode, "", mode)) | ||
| 146 | defs[name] = fixup_dump(string.dump(func, mode), fixup) | ||
| 147 | defs[#defs+1] = name | ||
| 148 | end | ||
| 149 | return defs | ||
| 150 | end | ||
| 151 | |||
| 152 | local function gen_header(defs32, defs64) | ||
| 153 | local t = {} | ||
| 154 | local function w(x) t[#t+1] = x end | ||
| 155 | w("/* This is a generated file. DO NOT EDIT! */\n\n") | ||
| 156 | w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n") | ||
| 157 | for j,defs in ipairs{defs64, defs32} do | ||
| 158 | local s, sb = "", "" | ||
| 159 | for i,name in ipairs(defs) do | ||
| 160 | local d = defs[name] | ||
| 161 | s = s .. d.dump | ||
| 162 | sb = sb .. string.char(i) .. ("\0"):rep(d.startbc - 1) | ||
| 163 | .. (isbe and "\0\0\0\255" or "\255\0\0\0"):rep(d.sizebc) | ||
| 164 | .. ("\0"):rep(#d.dump - d.startbc - d.sizebc*4) | ||
| 165 | end | ||
| 166 | if j == 1 then | ||
| 167 | w("static const uint8_t libbc_code[] = {\n#if LJ_FR2\n") | ||
| 168 | else | ||
| 169 | w("\n#else\n") | ||
| 170 | end | ||
| 171 | local n = 0 | ||
| 172 | for i=1,#s do | ||
| 173 | local x = string.byte(s, i) | ||
| 174 | local xb = string.byte(sb, i) | ||
| 175 | if xb == 255 then | ||
| 176 | local name = BCN[x] | ||
| 177 | local m = #name + 4 | ||
| 178 | if n + m > 78 then n = 0; w("\n") end | ||
| 179 | n = n + m | ||
| 180 | w("BC_"); w(name) | ||
| 181 | else | ||
| 182 | local m = x < 10 and 2 or (x < 100 and 3 or 4) | ||
| 183 | if xb == 0 then | ||
| 184 | if n + m > 78 then n = 0; w("\n") end | ||
| 185 | else | ||
| 186 | local name = defs[xb]:gsub("_", ".") | ||
| 187 | if n ~= 0 then w("\n") end | ||
| 188 | w("/* "); w(name); w(" */ ") | ||
| 189 | n = #name + 7 | ||
| 190 | end | ||
| 191 | n = n + m | ||
| 192 | w(x) | ||
| 193 | end | ||
| 194 | w(",") | ||
| 195 | end | ||
| 196 | end | ||
| 197 | w("\n#endif\n0\n};\n\n") | ||
| 198 | w("static const struct { const char *name; int ofs; } libbc_map[] = {\n") | ||
| 199 | local m32, m64 = 0, 0 | ||
| 200 | for i,name in ipairs(defs32) do | ||
| 201 | assert(name == defs64[i]) | ||
| 202 | w('{"'); w(name); w('",'); w(m32) w('},\n') | ||
| 203 | m32 = m32 + #defs32[name].dump | ||
| 204 | m64 = m64 + #defs64[name].dump | ||
| 205 | assert(m32 == m64) | ||
| 206 | end | ||
| 207 | w("{NULL,"); w(m32); w("}\n};\n\n") | ||
| 208 | return table.concat(t) | ||
| 209 | end | ||
| 210 | |||
| 211 | local function write_file(name, data) | ||
| 212 | if name == "-" then | ||
| 213 | assert(io.write(data)) | ||
| 214 | assert(io.flush()) | ||
| 215 | else | ||
| 216 | local fp = io.open(name) | ||
| 217 | if fp then | ||
| 218 | local old = fp:read("*a") | ||
| 219 | fp:close() | ||
| 220 | if data == old then return end | ||
| 221 | end | ||
| 222 | fp = assert(io.open(name, "w")) | ||
| 223 | assert(fp:write(data)) | ||
| 224 | assert(fp:close()) | ||
| 225 | end | ||
| 226 | end | ||
| 227 | |||
| 228 | local outfile = parse_arg(arg) | ||
| 229 | local src = read_files(arg) | ||
| 230 | local defs32 = find_defs(src, "Wdts") | ||
| 231 | local defs64 = find_defs(src, "Xdts") | ||
| 232 | local hdr = gen_header(defs32, defs64) | ||
| 233 | write_file(outfile, hdr) | ||
| 234 | |||
