diff --git a/src/Makefile b/src/Makefile index 4ea8c85e..f3631a0d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -558,6 +558,10 @@ amalg: clean: $(HOST_RM) $(ALL_RM) +libbc: + ./$(LUAJIT_T) host/genlibbc.lua $(LJLIB_C) >host/buildvm_libbc.h + $(MAKE) all + depend: @for file in $(ALL_HDRGEN); do \ test -f $$file || touch $$file; \ @@ -572,7 +576,7 @@ depend: test -s $$file || $(HOST_RM) $$file; \ done -.PHONY: default all amalg clean depend +.PHONY: default all amalg clean libbc depend ############################################################################## # Rules for generated files. diff --git a/src/Makefile.dep b/src/Makefile.dep index 5d91723a..54004f7c 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -124,7 +124,8 @@ lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_state.h lj_lex.h lj_parse.h lj_char.h lj_strscan.h lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \ lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \ - lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_lib.h + lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_lex.h lj_bcdump.h \ + lj_lib.h lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_func.h lj_frame.h \ lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h @@ -220,7 +221,8 @@ host/buildvm_asm.o: host/buildvm_asm.c host/buildvm.h lj_def.h lua.h luaconf.h \ host/buildvm_fold.o: host/buildvm_fold.c host/buildvm.h lj_def.h lua.h \ luaconf.h lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_ir.h lj_obj.h host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \ - lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_lib.h lj_obj.h + lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_lib.h lj_obj.h \ + host/buildvm_libbc.h host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \ luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h host/minilua.o: host/minilua.c diff --git a/src/host/buildvm_lib.c b/src/host/buildvm_lib.c index 40141dfb..182ab90f 100644 --- a/src/host/buildvm_lib.c +++ b/src/host/buildvm_lib.c @@ -6,6 +6,7 @@ #include "buildvm.h" #include "lj_obj.h" #include "lj_lib.h" +#include "buildvm_libbc.h" /* Context for library definitions. */ static uint8_t obuf[8192]; @@ -151,6 +152,55 @@ static void libdef_func(BuildCtx *ctx, char *p, int arg) regfunc = REGFUNC_OK; } +static uint32_t libdef_uleb128(uint8_t **pp) +{ + uint8_t *p = *pp; + uint32_t v = *p++; + if (v >= 0x80) { + int sh = 0; v &= 0x7f; + do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80); + } + *pp = p; + return v; +} + +static void libdef_swapbc(uint8_t *p) +{ + uint32_t i, sizebc; + p += 4; + libdef_uleb128(&p); + libdef_uleb128(&p); + sizebc = libdef_uleb128(&p); + for (i = 0; i < sizebc; i++, p += 4) { + uint8_t t = p[0]; p[0] = p[3]; p[3] = t; + t = p[1]; p[1] = p[2]; p[2] = t; + } +} + +static void libdef_lua(BuildCtx *ctx, char *p, int arg) +{ + UNUSED(arg); + if (ctx->mode == BUILD_libdef) { + int i; + for (i = 0; libbc_map[i].name != NULL; i++) { + if (!strcmp(libbc_map[i].name, p)) { + int ofs = libbc_map[i].ofs; + int len = libbc_map[i+1].ofs - ofs; + obuf[2]++; /* Bump hash table size. */ + *optr++ = LIBINIT_LUA; + libdef_name(p, 0); + memcpy(optr, libbc_code + ofs, len); + if (libbc_endian != LJ_BE) + libdef_swapbc(optr); + optr += len; + return; + } + } + fprintf(stderr, "Error: missing libbc definition for %s\n", p); + exit(1); + } +} + static uint32_t find_rec(char *name) { char *p = (char *)obuf; @@ -277,6 +327,7 @@ static const LibDefHandler libdef_handlers[] = { { "CF(", ")", libdef_func, LIBINIT_CF }, { "ASM(", ")", libdef_func, LIBINIT_ASM }, { "ASM_(", ")", libdef_func, LIBINIT_ASM_ }, + { "LUA(", ")", libdef_lua, 0 }, { "REC(", ")", libdef_rec, 0 }, { "PUSH(", ")", libdef_push, 0 }, { "SET(", ")", libdef_set, 0 }, diff --git a/src/host/buildvm_libbc.h b/src/host/buildvm_libbc.h new file mode 100644 index 00000000..d2d83ea6 --- /dev/null +++ b/src/host/buildvm_libbc.h @@ -0,0 +1,12 @@ +/* This is a generated file. DO NOT EDIT! */ + +static const int libbc_endian = 0; + +static const uint8_t libbc_code[] = { +0 +}; + +static const struct { const char *name; int ofs; } libbc_map[] = { +{NULL,0} +}; + diff --git a/src/host/genlibbc.lua b/src/host/genlibbc.lua new file mode 100644 index 00000000..b0dbf17a --- /dev/null +++ b/src/host/genlibbc.lua @@ -0,0 +1,68 @@ +---------------------------------------------------------------------------- +-- Lua script to dump the bytecode of the library functions written in Lua. +-- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT. +---------------------------------------------------------------------------- +-- Copyright (C) 2005-2013 Mike Pall. All rights reserved. +-- Released under the MIT license. See Copyright Notice in luajit.h +---------------------------------------------------------------------------- + +local function usage() + io.stderr:write("Usage: ", arg and arg[0] or "genlibbc", " lib_*.c\n") + os.exit(1) +end + +local function read_source() + if not (arg and arg[1]) then usage() end + local src = "" + for _,name in ipairs(arg) do + local fp = assert(io.open(name)) + src = src .. fp:read("*a") + fp:close() + end + return src +end + +local function find_defs(src) + local defs = {} + for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do + local env = {} + local func = assert(load("return "..code, "", nil, env))() + local d = string.dump(func, true) + local ofs = 6 + while string.byte(d, ofs) > 127 do ofs = ofs + 1 end + defs[name] = string.sub(d, ofs+1, -2) + defs[#defs+1] = name + end + return defs +end + +local function write_defs(fp, defs) + fp:write("/* This is a generated file. DO NOT EDIT! */\n\n") + fp:write("static const int libbc_endian = ", + string.byte(string.dump(function() end), 5) % 2, ";\n\n") + local s = "" + for _,name in ipairs(defs) do + s = s .. defs[name] + end + fp:write("static const uint8_t libbc_code[] = {\n") + local n = 0 + for i=1,#s do + local x = string.byte(s, i) + fp:write(x, ",") + n = n + (x < 10 and 2 or (x < 100 and 3 or 4)) + if n >= 75 then n = 0; fp:write("\n") end + end + fp:write("0\n};\n\n") + fp:write("static const struct { const char *name; int ofs; } libbc_map[] = {\n") + local m = 0 + for _,name in ipairs(defs) do + fp:write('{"', name, '",', m, '},\n') + m = m + #defs[name] + end + fp:write("{NULL,", m, "}\n};\n\n") + fp:flush() +end + +local src = read_source() +local defs = find_defs(src) +write_defs(io.stdout, defs) diff --git a/src/lj_bcdump.h b/src/lj_bcdump.h index e660156d..c1ed54e7 100644 --- a/src/lj_bcdump.h +++ b/src/lj_bcdump.h @@ -61,6 +61,7 @@ enum { LJ_FUNC int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data, int strip); +LJ_FUNC GCproto *lj_bcread_proto(LexState *ls); LJ_FUNC GCproto *lj_bcread(LexState *ls); #endif diff --git a/src/lj_bcread.c b/src/lj_bcread.c index 2b5ba855..7a8c08f5 100644 --- a/src/lj_bcread.c +++ b/src/lj_bcread.c @@ -326,25 +326,13 @@ static void bcread_uv(LexState *ls, GCproto *pt, MSize sizeuv) } /* Read a prototype. */ -static GCproto *bcread_proto(LexState *ls) +GCproto *lj_bcread_proto(LexState *ls) { GCproto *pt; MSize framesize, numparams, flags, sizeuv, sizekgc, sizekn, sizebc, sizept; MSize ofsk, ofsuv, ofsdbg; MSize sizedbg = 0; BCLine firstline = 0, numline = 0; - MSize len, startn; - - /* Read length. */ - if (ls->n > 0 && ls->p[0] == 0) { /* Shortcut EOF. */ - ls->n--; ls->p++; - return NULL; - } - bcread_want(ls, 5); - len = bcread_uleb128(ls); - if (!len) return NULL; /* EOF */ - bcread_need(ls, len); - startn = ls->n; /* Read prototype header. */ flags = bcread_byte(ls); @@ -413,9 +401,6 @@ static GCproto *bcread_proto(LexState *ls) setmref(pt->uvinfo, NULL); setmref(pt->varinfo, NULL); } - - if (len != startn - ls->n) - bcread_error(ls, LJ_ERR_BCBAD); return pt; } @@ -462,8 +447,21 @@ GCproto *lj_bcread(LexState *ls) if (!bcread_header(ls)) bcread_error(ls, LJ_ERR_BCFMT); for (;;) { /* Process all prototypes in the bytecode dump. */ - GCproto *pt = bcread_proto(ls); - if (!pt) break; + GCproto *pt; + MSize len, startn; + /* Read length. */ + if (ls->n > 0 && ls->p[0] == 0) { /* Shortcut EOF. */ + ls->n--; ls->p++; + break; + } + bcread_want(ls, 5); + len = bcread_uleb128(ls); + if (!len) break; /* EOF */ + bcread_need(ls, len); + startn = ls->n; + pt = lj_bcread_proto(ls); + if (len != startn - ls->n) + bcread_error(ls, LJ_ERR_BCBAD); setprotoV(L, L->top, pt); incr_top(L); } diff --git a/src/lj_debug.c b/src/lj_debug.c index be7fb2b1..ec56b7d2 100644 --- a/src/lj_debug.c +++ b/src/lj_debug.c @@ -321,7 +321,7 @@ const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name) /* -- Source code locations ----------------------------------------------- */ /* Generate shortened source name. */ -void lj_debug_shortname(char *out, GCstr *str) +void lj_debug_shortname(char *out, GCstr *str, BCLine line) { const char *src = strdata(str); if (*src == '=') { @@ -335,11 +335,11 @@ void lj_debug_shortname(char *out, GCstr *str) *out++ = '.'; *out++ = '.'; *out++ = '.'; } strcpy(out, src); - } else { /* Output [string "string"]. */ + } else { /* Output [string "string"] or [builtin:name]. */ size_t len; /* Length, up to first control char. */ for (len = 0; len < LUA_IDSIZE-12; len++) if (((const unsigned char *)src)[len] < ' ') break; - strcpy(out, "[string \""); out += 9; + strcpy(out, line == ~(BCLine)0 ? "[builtin:" : "[string \""); out += 9; if (src[len] != '\0') { /* Must truncate? */ if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15; strncpy(out, src, len); out += len; @@ -347,7 +347,7 @@ void lj_debug_shortname(char *out, GCstr *str) } else { strcpy(out, src); out += len; } - strcpy(out, "\"]"); + strcpy(out, line == ~(BCLine)0 ? "]" : "\"]"); } } @@ -360,8 +360,9 @@ void lj_debug_addloc(lua_State *L, const char *msg, if (isluafunc(fn)) { BCLine line = debug_frameline(L, fn, nextframe); if (line >= 0) { + GCproto *pt = funcproto(fn); char buf[LUA_IDSIZE]; - lj_debug_shortname(buf, proto_chunkname(funcproto(fn))); + lj_debug_shortname(buf, proto_chunkname(pt), pt->firstline); lj_str_pushf(L, "%s:%d: %s", buf, line, msg); return; } @@ -377,7 +378,9 @@ void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc) const char *s = strdata(name); MSize i, len = name->len; BCLine line = lj_debug_line(pt, pc); - if (*s == '@') { + if (pt->firstline == ~(BCLine)0) { + lj_str_pushf(L, "builtin:%s", s); + } else if (*s == '@') { s++; len--; for (i = len; i > 0; i--) if (s[i] == '/' || s[i] == '\\') { @@ -453,7 +456,7 @@ int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, int ext) BCLine firstline = pt->firstline; GCstr *name = proto_chunkname(pt); ar->source = strdata(name); - lj_debug_shortname(ar->short_src, name); + lj_debug_shortname(ar->short_src, name, pt->firstline); ar->linedefined = (int)firstline; ar->lastlinedefined = (int)(firstline + pt->numline); ar->what = firstline ? "Lua" : "main"; diff --git a/src/lj_debug.h b/src/lj_debug.h index 7cf57de7..4144b47e 100644 --- a/src/lj_debug.h +++ b/src/lj_debug.h @@ -34,7 +34,7 @@ LJ_FUNC const char *lj_debug_slotname(GCproto *pt, const BCIns *pc, BCReg slot, const char **name); LJ_FUNC const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name); -LJ_FUNC void lj_debug_shortname(char *out, GCstr *str); +LJ_FUNC void lj_debug_shortname(char *out, GCstr *str, BCLine line); LJ_FUNC void lj_debug_addloc(lua_State *L, const char *msg, cTValue *frame, cTValue *nextframe); LJ_FUNC void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc); diff --git a/src/lj_err.c b/src/lj_err.c index 4a33a233..e0fb7167 100644 --- a/src/lj_err.c +++ b/src/lj_err.c @@ -587,7 +587,7 @@ LJ_NOINLINE void lj_err_lex(lua_State *L, GCstr *src, const char *tok, { char buff[LUA_IDSIZE]; const char *msg; - lj_debug_shortname(buff, src); + lj_debug_shortname(buff, src, line); msg = lj_str_pushvf(L, err2msg(em), argp); msg = lj_str_pushf(L, "%s:%d: %s", buff, line, msg); if (tok) diff --git a/src/lj_lib.c b/src/lj_lib.c index 331eaa6a..be3ee004 100644 --- a/src/lj_lib.c +++ b/src/lj_lib.c @@ -18,6 +18,8 @@ #include "lj_dispatch.h" #include "lj_vm.h" #include "lj_strscan.h" +#include "lj_lex.h" +#include "lj_bcdump.h" #include "lj_lib.h" /* -- Library initialization ---------------------------------------------- */ @@ -43,6 +45,28 @@ static GCtab *lib_create_table(lua_State *L, const char *libname, int hsize) return tabV(L->top-1); } +static const uint8_t *lib_read_lfunc(lua_State *L, const uint8_t *p, GCtab *tab) +{ + int len = *p++; + GCstr *name = lj_str_new(L, (const char *)p, len); + LexState ls; + GCproto *pt; + GCfunc *fn; + memset(&ls, 0, sizeof(ls)); + ls.L = L; + ls.p = (const char *)(p+len); + ls.n = ~(MSize)0; + ls.current = -1; + ls.level = (BCDUMP_F_STRIP|(LJ_BE*BCDUMP_F_BE)); + ls.chunkname = name; + pt = lj_bcread_proto(&ls); + pt->firstline = ~(BCLine)0; + fn = lj_func_newL_empty(L, pt, tabref(L->env)); + /* NOBARRIER: See below for common barrier. */ + setfuncV(L, lj_tab_setstr(L, tab, name), fn); + return (const uint8_t *)ls.p; +} + void lj_lib_register(lua_State *L, const char *libname, const uint8_t *p, const lua_CFunction *cf) { @@ -87,6 +111,9 @@ void lj_lib_register(lua_State *L, const char *libname, ofn = fn; } else { switch (tag | len) { + case LIBINIT_LUA: + p = lib_read_lfunc(L, p, tab); + break; case LIBINIT_SET: L->top -= 2; if (tvisstr(L->top+1) && strV(L->top+1)->len == 0) diff --git a/src/lj_lib.h b/src/lj_lib.h index 2fe6d2a8..05f90de5 100644 --- a/src/lj_lib.h +++ b/src/lj_lib.h @@ -77,6 +77,7 @@ static LJ_AINLINE void lj_lib_pushcc(lua_State *L, lua_CFunction f, #define LJLIB_CF(name) static int lj_cf_##name(lua_State *L) #define LJLIB_ASM(name) static int lj_ffh_##name(lua_State *L) #define LJLIB_ASM_(name) +#define LJLIB_LUA(name) #define LJLIB_SET(name) #define LJLIB_PUSH(arg) #define LJLIB_REC(handler) @@ -96,7 +97,8 @@ LJ_FUNC void lj_lib_register(lua_State *L, const char *libname, #define LIBINIT_ASM 0x40 #define LIBINIT_ASM_ 0x80 #define LIBINIT_STRING 0xc0 -#define LIBINIT_MAXSTR 0x39 +#define LIBINIT_MAXSTR 0x38 +#define LIBINIT_LUA 0xf9 #define LIBINIT_SET 0xfa #define LIBINIT_NUMBER 0xfb #define LIBINIT_COPY 0xfc