diff options
Diffstat (limited to 'src/host/genlibbc.lua')
-rw-r--r-- | src/host/genlibbc.lua | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/host/genlibbc.lua b/src/host/genlibbc.lua new file mode 100644 index 00000000..3621c3f5 --- /dev/null +++ b/src/host/genlibbc.lua | |||
@@ -0,0 +1,225 @@ | |||
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-2023 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) | ||
142 | local defs = {} | ||
143 | for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do | ||
144 | local env = {} | ||
145 | local tcode, fixup = transform_lua(code) | ||
146 | local func = assert(load(tcode, "", nil, env))() | ||
147 | defs[name] = fixup_dump(string.dump(func, true), fixup) | ||
148 | defs[#defs+1] = name | ||
149 | end | ||
150 | return defs | ||
151 | end | ||
152 | |||
153 | local function gen_header(defs) | ||
154 | local t = {} | ||
155 | local function w(x) t[#t+1] = x end | ||
156 | w("/* This is a generated file. DO NOT EDIT! */\n\n") | ||
157 | w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n") | ||
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 | w("static const uint8_t libbc_code[] = {\n") | ||
167 | local n = 0 | ||
168 | for i=1,#s do | ||
169 | local x = string.byte(s, i) | ||
170 | local xb = string.byte(sb, i) | ||
171 | if xb == 255 then | ||
172 | local name = BCN[x] | ||
173 | local m = #name + 4 | ||
174 | if n + m > 78 then n = 0; w("\n") end | ||
175 | n = n + m | ||
176 | w("BC_"); w(name) | ||
177 | else | ||
178 | local m = x < 10 and 2 or (x < 100 and 3 or 4) | ||
179 | if xb == 0 then | ||
180 | if n + m > 78 then n = 0; w("\n") end | ||
181 | else | ||
182 | local name = defs[xb]:gsub("_", ".") | ||
183 | if n ~= 0 then w("\n") end | ||
184 | w("/* "); w(name); w(" */ ") | ||
185 | n = #name + 7 | ||
186 | end | ||
187 | n = n + m | ||
188 | w(x) | ||
189 | end | ||
190 | w(",") | ||
191 | end | ||
192 | w("\n0\n};\n\n") | ||
193 | w("static const struct { const char *name; int ofs; } libbc_map[] = {\n") | ||
194 | local m = 0 | ||
195 | for _,name in ipairs(defs) do | ||
196 | w('{"'); w(name); w('",'); w(m) w('},\n') | ||
197 | m = m + #defs[name].dump | ||
198 | end | ||
199 | w("{NULL,"); w(m); w("}\n};\n\n") | ||
200 | return table.concat(t) | ||
201 | end | ||
202 | |||
203 | local function write_file(name, data) | ||
204 | if name == "-" then | ||
205 | assert(io.write(data)) | ||
206 | assert(io.flush()) | ||
207 | else | ||
208 | local fp = io.open(name) | ||
209 | if fp then | ||
210 | local old = fp:read("*a") | ||
211 | fp:close() | ||
212 | if data == old then return end | ||
213 | end | ||
214 | fp = assert(io.open(name, "w")) | ||
215 | assert(fp:write(data)) | ||
216 | assert(fp:close()) | ||
217 | end | ||
218 | end | ||
219 | |||
220 | local outfile = parse_arg(arg) | ||
221 | local src = read_files(arg) | ||
222 | local defs = find_defs(src) | ||
223 | local hdr = gen_header(defs) | ||
224 | write_file(outfile, hdr) | ||
225 | |||