summaryrefslogtreecommitdiff
path: root/src/host/genminilua.lua
blob: e8e86c538961f343dfbd441f155e62a1106813c8 (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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
----------------------------------------------------------------------------
-- Lua script to generate a customized, minified version of Lua.
-- The resulting 'minilua' is used for the build process of LuaJIT.
----------------------------------------------------------------------------
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------

local sub, match, gsub = string.sub, string.match, string.gsub

local LUA_VERSION = "5.1.5"
local LUA_SOURCE

local function usage()
  io.stderr:write("Usage: ", arg and arg[0] or "genminilua",
		  " lua-", LUA_VERSION, "-source-dir\n")
  os.exit(1)
end

local function find_sources()
  LUA_SOURCE = arg and arg[1]
  if not LUA_SOURCE then usage() end
  if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end
  local fp = io.open(LUA_SOURCE .. "lua.h")
  if not fp then
    LUA_SOURCE = LUA_SOURCE.."src/"
    fp = io.open(LUA_SOURCE .. "lua.h")
    if not fp then usage() end
  end
  local all = fp:read("*a")
  fp:close()
  if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then
    io.stderr:write("Error: version mismatch\n")
    usage()
  end
end

local LUA_FILES = {
"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c",
"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c",
"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c",
"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c",
}

local REMOVE_LIB = {}
gsub([[
collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset
select tostring xpcall
foreach foreachi getn maxn setn
popen tmpfile seek setvbuf __tostring
clock date difftime execute getenv rename setlocale time tmpname
dump gfind len reverse
LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME
]], "%S+", function(name)
  REMOVE_LIB[name] = true
end)

local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, }

local CUSTOM_MAIN = [[
typedef unsigned int UB;
static UB barg(lua_State *L,int idx){
union{lua_Number n;U64 b;}bn;
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
return(UB)bn.b;
}
#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1;
static int tobit(lua_State *L){
BRET(barg(L,1))}
static int bnot(lua_State *L){
BRET(~barg(L,1))}
static int band(lua_State *L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
static int bor(lua_State *L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
static int bxor(lua_State *L){
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
static int lshift(lua_State *L){
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
static int rshift(lua_State *L){
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
static int arshift(lua_State *L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
static int rol(lua_State *L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
static int ror(lua_State *L){
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
static int bswap(lua_State *L){
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
static int tohex(lua_State *L){
UB b=barg(L,1);
int n=lua_isnone(L,2)?8:(int)barg(L,2);
const char *hexdigits="0123456789abcdef";
char buf[8];
int i;
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
if(n>8)n=8;
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
lua_pushlstring(L,buf,(size_t)n);
return 1;
}
static const struct luaL_Reg bitlib[] = {
{"tobit",tobit},
{"bnot",bnot},
{"band",band},
{"bor",bor},
{"bxor",bxor},
{"lshift",lshift},
{"rshift",rshift},
{"arshift",arshift},
{"rol",rol},
{"ror",ror},
{"bswap",bswap},
{"tohex",tohex},
{NULL,NULL}
};
int main(int argc, char **argv){
  lua_State *L = luaL_newstate();
  int i;
  luaL_openlibs(L);
  luaL_register(L, "bit", bitlib);
  if (argc < 2) return sizeof(void *);
  lua_createtable(L, 0, 1);
  lua_pushstring(L, argv[1]);
  lua_rawseti(L, -2, 0);
  lua_setglobal(L, "arg");
  if (luaL_loadfile(L, argv[1]))
    goto err;
  for (i = 2; i < argc; i++)
    lua_pushstring(L, argv[i]);
  if (lua_pcall(L, argc - 2, 0, 0)) {
  err:
    fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
    return 1;
  }
  lua_close(L);
  return 0;
}
]]

local function read_sources()
  local t = {}
  for i, name in ipairs(LUA_FILES) do
    local fp = assert(io.open(LUA_SOURCE..name, "r"))
    t[i] = fp:read("*a")
    assert(fp:close())
  end
  t[#t+1] = CUSTOM_MAIN
  return table.concat(t)
end

local includes = {}

local function merge_includes(src)
  return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name)
    if includes[name] then return "" end
    includes[name] = true
    local fp = assert(io.open(LUA_SOURCE..name, "r"))
    local inc = fp:read("*a")
    assert(fp:close())
    inc = gsub(inc, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "")
    inc = gsub(inc, "#endif%s*$", "")
    return merge_includes(inc)
  end)
end

local function get_license(src)
  return match(src, "/%*+\n%* Copyright %(.-%*/\n")
end

local function fold_lines(src)
  return gsub(src, "\\\n", " ")
end

local strings = {}

local function save_str(str)
  local n = #strings+1
  strings[n] = str
  return "\1"..n.."\2"
end

local function save_strings(src)
  src = gsub(src, '"[^"\n]*"', save_str)
  return gsub(src, "'[^'\n]*'", save_str)
end

local function restore_strings(src)
  return gsub(src, "\1(%d+)\2", function(numstr)
    return strings[tonumber(numstr)]
  end)
end

local function def_istrue(def)
  return def == "INT_MAX > 2147483640L" or
	 def == "LUAI_BITSINT >= 32" or
	 def == "SIZE_Bx < LUAI_BITSINT-1" or
	 def == "cast" or
	 def == "defined(LUA_CORE)" or
	 def == "MINSTRTABSIZE" or
	 def == "LUA_MINBUFFER" or
	 def == "HARDSTACKTESTS" or
	 def == "UNUSED"
end

local head, defs = {[[
#ifdef _MSC_VER
typedef unsigned __int64 U64;
#else
typedef unsigned long long U64;
#endif
int _CRT_glob = 0;
]]}, {}

local function preprocess(src)
  local t = { match(src, "^(.-)#") }
  local lvl, on, oldon = 0, true, {}
  for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do
    if pp == "if" or pp == "ifdef" or pp == "ifndef" then
      lvl = lvl + 1
      oldon[lvl] = on
      on = def_istrue(def)
    elseif pp == "else" then
      if oldon[lvl] then
	if on == false then on = true else on = false end
      end
    elseif pp == "elif" then
      if oldon[lvl] then
	on = def_istrue(def)
      end
    elseif pp == "endif" then
      on = oldon[lvl]
      lvl = lvl - 1
    elseif on then
      if pp == "include" then
	if not head[def] and not REMOVE_EXTINC[def] then
	  head[def] = true
	  head[#head+1] = "#include "..def.."\n"
	end
      elseif pp == "define" then
	local k, sp, v = match(def, "([%w_]+)(%s*)(.*)")
	if k and not (sp == "" and sub(v, 1, 1) == "(") then
	  defs[k] = gsub(v, "%a[%w_]*", function(tok)
	    return defs[tok] or tok
	  end)
	else
	  t[#t+1] = "#define "..def.."\n"
	end
      elseif pp ~= "undef" then
	error("unexpected directive: "..pp.." "..def)
      end
    end
    if on then t[#t+1] = txt end
  end
  return gsub(table.concat(t), "%a[%w_]*", function(tok)
    return defs[tok] or tok
  end)
end

local function merge_header(src, license)
  local hdr = string.format([[
/* This is a heavily customized and minimized copy of Lua %s. */
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
]], LUA_VERSION)
  return hdr..license..table.concat(head)..src
end

local function strip_unused1(src)
  return gsub(src, '(  {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func)
    return REMOVE_LIB[func] and "" or line
  end)
end

local function strip_unused2(src)
  return gsub(src, "Symbolic Execution.-}=", "")
end

local function strip_unused3(src)
  src = gsub(src, "extern", "static")
  src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(")
  src = gsub(src, "#define lua_assert[^\n]*\n", "")
  src = gsub(src, "lua_assert%b();?", "")
  src = gsub(src, "default:\n}", "default:;\n}")
  src = gsub(src, "lua_lock%b();", "")
  src = gsub(src, "lua_unlock%b();", "")
  src = gsub(src, "luai_threadyield%b();", "")
  src = gsub(src, "luai_userstateopen%b();", "{}")
  src = gsub(src, "luai_userstate%w+%b();", "")
  src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser")
  src = gsub(src, "trydecpoint%(ls,seminfo%)",
		  "luaX_lexerror(ls,\"malformed number\",TK_NUMBER)")
  src = gsub(src, "int c=luaZ_lookahead%b();", "")
  src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;",
		  "return 1;")
  src = gsub(src, "getfuncname%b():", "NULL:")
  src = gsub(src, "getobjname%b():", "NULL:")
  src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "")
  src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "")
  src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "")
  src = gsub(src, "(twoto%b()%()", "%1(size_t)")
  src = gsub(src, "i<sizenode", "i<(int)sizenode")
  src = gsub(src, "cast%(unsigned int,key%-1%)", "cast(unsigned int,key)-1")
  return gsub(src, "\n\n+", "\n")
end

local function strip_comments(src)
  return gsub(src, "/%*.-%*/", " ")
end

local function strip_whitespace(src)
  src = gsub(src, "^%s+", "")
  src = gsub(src, "%s*\n%s*", "\n")
  src = gsub(src, "[ \t]+", " ")
  src = gsub(src, "(%W) ", "%1")
  return gsub(src, " (%W)", "%1")
end

local function rename_tokens1(src)
  src = gsub(src, "getline", "getline_")
  src = gsub(src, "struct ([%w_]+)", "ZX%1")
  return gsub(src, "union ([%w_]+)", "ZY%1")
end

local function rename_tokens2(src)
  src = gsub(src, "ZX([%w_]+)", "struct %1")
  return gsub(src, "ZY([%w_]+)", "union %1")
end

local function fix_bugs_and_warnings(src)
 src = gsub(src, "(luaD_checkstack%(L,p%->maxstacksize)%)", "%1+p->numparams)")
 src = gsub(src, "if%(sep==%-1%)(return'%[';)\nelse (luaX_lexerror%b();)", "if (sep!=-1)%2\n%1")
 return gsub(src, "(default:{\nNode%*n=mainposition)", "/*fallthrough*/\n%1")
end

local function func_gather(src)
  local nodes, list = {}, {}
  local pos, len = 1, #src
  while pos < len do
    local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos)
    if d then
      local n = #list+1
      list[n] = d
      nodes[w] = n
    else
      local s
      d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos)
      if not d then
	d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos)
	if not d then d = match(src, "^[^\n]*\n", pos) end
      end
      if s == "{" then
	d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
	if sub(d, -2) == "{\n" then
	  d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
	end
      end
      local k, v = nil, d
      if w == "typedef" then
	if match(d, "^typedef enum") then
	  head[#head+1] = d
	else
	  k = match(d, "([%w_]+);\n$")
	  if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end
	end
      elseif w == "enum" then
	head[#head+1] = v
      elseif w ~= nil then
	k = match(d, "^[^\n]-([%w_]+)[(%[=]")
	if k then
	  if w ~= "static" and k ~= "main" then v = "static "..d end
	else
	  k = w
	end
      end
      if w and k then
	local o = nodes[k]
	if o then nodes["*"..k] = o end
	local n = #list+1
	list[n] = v
	nodes[k] = n
      end
    end
    pos = pos + #d
  end
  return nodes, list
end

local function func_visit(nodes, list, used, n)
  local i = nodes[n]
  for m in string.gmatch(list[i], "[%w_]+") do
    if nodes[m] then
      local j = used[m]
      if not j then
	used[m] = i
	func_visit(nodes, list, used, m)
      elseif i < j then
	used[m] = i
      end
    end
  end
end

local function func_collect(src)
  local nodes, list = func_gather(src)
  local used = {}
  func_visit(nodes, list, used, "main")
  for n,i in pairs(nodes) do
    local j = used[n]
    if j and j < i then used["*"..n] = j end
  end
  for n,i in pairs(nodes) do
    if not used[n] then list[i] = "" end
  end
  return table.concat(list)
end

find_sources()
local src = read_sources()
src = merge_includes(src)
local license = get_license(src)
src = fold_lines(src)
src = strip_unused1(src)
src = save_strings(src)
src = strip_unused2(src)
src = strip_comments(src)
src = preprocess(src)
src = strip_whitespace(src)
src = strip_unused3(src)
src = rename_tokens1(src)
src = func_collect(src)
src = rename_tokens2(src)
src = restore_strings(src)
src = fix_bugs_and_warnings(src)
src = merge_header(src, license)
io.write(src)