From d61ea6c5eeef4343e40c0225f22ad39c994f84a9 Mon Sep 17 00:00:00 2001 From: William Wilgus Date: Mon, 5 Aug 2019 00:03:08 -0500 Subject: lua LCD (Lua Compact Debug) patch LCD developed 9/2015 by Terry Ellison We've already discarded the ldebug module from lua it only makes sense to discard the debug info as well adds 1.5 K to the binary saves 8 Kb on the base state once scripts start getting called i've seen 10-50Kb savings but it all depends on what exactly you are running Change-Id: Ibb74f344df1c4c96380ec6c98b010a810e9ae9cc --- apps/plugins/lua/lbaselib.c | 74 +++++++++++++++++++++++++++++++ apps/plugins/lua/lcode.c | 103 ++++++++++++++++++++++++++++++++++++++++++++ apps/plugins/lua/ldebug.c | 99 +++++++++++++++++++++++++++++++++++++++++- apps/plugins/lua/ldebug.h | 19 +++++++- apps/plugins/lua/ldump.c | 9 ++++ apps/plugins/lua/lfunc.c | 13 +++++- apps/plugins/lua/lgc.c | 8 +++- apps/plugins/lua/lobject.h | 4 ++ apps/plugins/lua/lparser.c | 32 +++++++++++++- apps/plugins/lua/lparser.h | 5 +++ apps/plugins/lua/lua.h | 3 ++ apps/plugins/lua/luaconf.h | 1 + apps/plugins/lua/lundump.c | 9 ++++ 13 files changed, 373 insertions(+), 6 deletions(-) diff --git a/apps/plugins/lua/lbaselib.c b/apps/plugins/lua/lbaselib.c index ad94984191..cb7b29a92a 100644 --- a/apps/plugins/lua/lbaselib.c +++ b/apps/plugins/lua/lbaselib.c @@ -19,6 +19,13 @@ #include "lauxlib.h" #include "lualib.h" +#ifdef LUA_OPTIMIZE_DEBUG_USER +#include "lobject.h" +#include "lstate.h" +#include "ldebug.h" +#include "lfunc.h" +#endif + @@ -302,6 +309,70 @@ static int luaB_loadfile (lua_State *L) { } +#ifdef LUA_OPTIMIZE_DEBUG_USER +/* stripdebug([level[, function]]). + * level: 1 don't discard debug + * 2 discard Local and Upvalue debug info + * 3 discard Local, Upvalue and lineno debug info. + * function: Function to be stripped as per setfenv except 0 not permitted. + * If no arguments then the current default setting is returned. + * If function is omitted, this is the default setting for future compiles + * The function returns an estimated integer count of the bytes stripped. + */ +static int luaB_stripdebug (lua_State *L) { + int level; + + if (L->top == L->base) { + lua_pushlightuserdata(L, &luaG_stripdebug); + lua_gettable(L, LUA_REGISTRYINDEX); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushinteger(L, LUA_OPTIMIZE_DEBUG); + } + return 1; + } + + level = luaL_checkint(L, 1); + if ((level <= 0) || (level > 3)) luaL_argerror(L, 1, "must in range 1-3"); + + if (L->top == L->base + 1) { + /* Store the default level in the registry if no function parameter */ + lua_pushlightuserdata(L, &luaG_stripdebug); + lua_pushinteger(L, level); + lua_settable(L, LUA_REGISTRYINDEX); + lua_settop(L,0); + return 0; + } + + if (level == 1) { + lua_settop(L,0); + lua_pushinteger(L, 0); + return 1; + } + + if (!lua_isfunction(L, 2)) { + int scope = luaL_checkint(L, 2); + if (scope > 0) { + /* if the function parameter is a +ve integer then climb to find function */ + lua_Debug ar; + lua_pop(L, 1); /* pop level as getinfo will replace it by the function */ + if (lua_getstack(L, scope, &ar)) { + lua_getinfo(L, "f", &ar); + } + } + } + + if(!lua_isfunction(L, 2) || lua_iscfunction(L, -1)) luaL_argerror(L, 2, "must be a Lua Function"); + // lua_lock(L); + Proto *f = clvalue(L->base + 1)->l.p; + // lua_unlock(L); + lua_settop(L,0); + lua_pushinteger(L, luaG_stripdebug(L, f, level, 16)); + return 1; +} +#endif + + /* ** Reader for generic `load' function: `lua_load' uses the ** stack for internal stuff, so the reader cannot change the @@ -482,6 +553,9 @@ static const luaL_Reg base_funcs[] = { {"select", luaB_select}, {"setfenv", luaB_setfenv}, {"setmetatable", luaB_setmetatable}, +#ifdef LUA_OPTIMIZE_DEBUG_USER + {"stripdebug", luaB_stripdebug}, +#endif {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, {"type", luaB_type}, diff --git a/apps/plugins/lua/lcode.c b/apps/plugins/lua/lcode.c index 07f9cbe4c4..18102d460e 100644 --- a/apps/plugins/lua/lcode.c +++ b/apps/plugins/lua/lcode.c @@ -784,8 +784,95 @@ void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { } +#ifdef LUA_OPTIMIZE_DEBUG +/* + * Attempted to write to last (null terminator) byte of lineinfo, so need + * to grow the lineinfo vector and extend the fill bytes + */ +static unsigned char *growLineInfo(FuncState *fs) { + Proto *f = fs->f; + int start = (f->sizelineinfo > 0 ? f->sizelineinfo - 1 : 0); + + unsigned char *p; + + lua_assert(f->packedlineinfo==NULL || f->packedlineinfo[start] == '\0'); + + /* using the macro results in a redundant if test, but what the hell */ + luaM_growvector(fs->L, f->packedlineinfo, f->sizelineinfo, f->sizelineinfo, + unsigned char, MAX_INT, "code size overflow"); + + p = &f->packedlineinfo[start]; + memset(p, INFO_FILL_BYTE, f->sizelineinfo - start); + f->packedlineinfo[f->sizelineinfo - 1] = '\0'; + return p; +} + + +static void generateInfoDeltaLine(FuncState *fs, int line) { + /* Handle first time through when lineinfo points is NULL */ + unsigned char mask = INFO_DELTA_MASK; + unsigned char *p = fs->f->packedlineinfo ? lineInfoTop(fs) + 1 : growLineInfo(fs); +#define addDLbyte(v) if (*p=='\0') p = growLineInfo(fs); *p++ = (v); + int delta = line - fs->lastline - 1; + if (delta != 0) { + if (delta < 0) { + delta = -delta; + mask |= INFO_SIGN_MASK; + } + delta -= 1; + mask |= (delta & INFO_DELTA_6BITS); + delta >>= 6; + addDLbyte(mask); + + while (delta > 0) { + mask = INFO_DELTA_MASK | (delta & INFO_DELTA_7BITS); + delta >>= 7; + addDLbyte(mask); + } + } + addDLbyte(1); + fs->lastline = line; + fs->lastlineOffset = p - fs->f->packedlineinfo - 1; +#undef addDLbyte +} +#endif + + void luaK_fixline (FuncState *fs, int line) { +#ifdef LUA_OPTIMIZE_DEBUG + /* The fixup line can be the same as existing one and in this case there's nothing to do */ + if (line != fs->lastline) { + /* first remove the current line reference */ + unsigned char *p = lineInfoTop(fs); + lua_assert(*p < 127); + if (*p >1) { + (*p)--; /* this is simply decrementing the last count a multi-PC line */ + } else { + /* it's a bit more complicated if it's the 1st instruction on the line */ + int delta = 0; + unsigned char code; + /* this logic handles [1snnnnnnn [1nnnnnnn]*]? */ + *p-- = INFO_FILL_BYTE; + /* work backwards over the coded delta computing the delta */ + while ((code=*p) & INFO_DELTA_MASK) { + *p-- = INFO_FILL_BYTE; + if (*p & INFO_DELTA_MASK) { + delta += ((code & INFO_DELTA_7BITS)<<7); + } else { + delta += (code & INFO_DELTA_6BITS) + 1; + if (code & INFO_SIGN_MASK) delta = -delta; + } + } + /* and reposition the FuncState lastline pointers at the previous instruction count */ + fs->lastline-= delta + 1; + fs->lastlineOffset = p - fs->f->packedlineinfo; + } + /* Then add the new line reference */ + generateInfoDeltaLine(fs, line); + } +#else fs->f->lineinfo[fs->pc - 1] = line; +#endif } @@ -797,9 +884,25 @@ static int luaK_code (FuncState *fs, Instruction i, int line) { MAX_INT, "code size overflow"); f->code[fs->pc] = i; /* save corresponding line information */ +#ifdef LUA_OPTIMIZE_DEBUG + /* note that frst time fs->lastline==0 through, so the else branch is taken */ + if (fs->pc == fs->lineinfoLastPC+1) { + if (line == fs->lastline && f->packedlineinfo[fs->lastlineOffset] < INFO_MAX_LINECNT) { + f->packedlineinfo[fs->lastlineOffset]++; + } else { + generateInfoDeltaLine(fs, line); + } + } else { + /* The last instruction is occasionally overwritten as part of branch optimisation*/ + lua_assert(fs->pc == fs->lineinfoLastPC); /* panic if its anything other than this !! */ + luaK_fixline(fs,line); + } + fs->lineinfoLastPC = fs->pc; +#else luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, MAX_INT, "code size overflow"); f->lineinfo[fs->pc] = line; +#endif return fs->pc++; } diff --git a/apps/plugins/lua/ldebug.c b/apps/plugins/lua/ldebug.c index 50ad3d3803..446f7d1a45 100644 --- a/apps/plugins/lua/ldebug.c +++ b/apps/plugins/lua/ldebug.c @@ -180,16 +180,111 @@ static void collectvalidlines (lua_State *L, Closure *f) { } else { Table *t = luaH_new(L, 0, 0); +#ifdef LUA_OPTIMIZE_DEBUG + int line = 0; + unsigned char *p = f->l.p->packedlineinfo; + if (p) { + for (; *p && *p != INFO_FILL_BYTE; ) { + if (*p & INFO_DELTA_MASK) { /* line delta */ + int delta = *p & INFO_DELTA_6BITS; + unsigned char sign = *p++ & INFO_SIGN_MASK; + int shift; + for (shift = 6; *p & INFO_DELTA_MASK; p++, shift += 7) { + delta += (*p & INFO_DELTA_7BITS)<l.p->lineinfo; int i; for (i=0; il.p->sizelineinfo; i++) setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); - sethvalue(L, L->top, t); +#endif + sethvalue(L, L->top, t); } incr_top(L); } +#ifdef LUA_OPTIMIZE_DEBUG +/* + * This may seem expensive but this is only accessed frequently in traceexec + * and the while loop will be executed roughly half the number of non-blank + * source lines in the Lua function and these tend to be short. + */ +LUAI_FUNC int luaG_getline (const Proto *f, int pc) { + int line = 0, thispc = 0, nextpc; + unsigned char *p; + + for (p = f->packedlineinfo; *p && *p != INFO_FILL_BYTE;) { + if (*p & INFO_DELTA_MASK) { /* line delta */ + int delta = *p & INFO_DELTA_6BITS; + unsigned char sign = *p++ & INFO_SIGN_MASK; + int shift; + for (shift = 6; *p & INFO_DELTA_MASK; p++, shift += 7) { + delta += (*p & INFO_DELTA_7BITS)<sizelineinfo; + f->packedlineinfo = luaM_freearray(L, f->packedlineinfo, f->sizelineinfo, unsigned char); + case 2: + len += f->sizelocvars * (sizeof(struct LocVar) + sizeof(dummy->tsv) + sizeof(struct LocVar *)); + f->locvars = luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); + f->upvalues = luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); + len += f->sizelocvars * (sizeof(struct LocVar) + sizeof(dummy->tsv) + sizeof(struct LocVar *)) + + f->sizeupvalues * (sizeof(dummy->tsv) + sizeof(TString *)); + f->sizelocvars = 0; + f->sizeupvalues = 0; + case 1: + default: + break; + } + return len; +} + + +/* This is a recursive function so it's stack size has been kept to a minimum! */ +LUAI_FUNC int luaG_stripdebug (lua_State *L, Proto *f, int level, int recv){ + int len = 0, i; +#ifndef LUA_OPTIMIZE_DEBUG_USER /* gcc doesn't realize level can't be changed */ + level = LUA_OPTIMIZE_DEBUG; +#endif + if (recv > 0 && f->sizep != 0) { + /* recv limits recursion depth */ + for(i=0;isizep;i++) len += luaG_stripdebug(L, f->p[i], level, --recv); + } + len += stripdebug (L, f, level); + return len; +} +#endif + + static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; @@ -279,7 +374,9 @@ static int precheck (const Proto *pt) { check(!(pt->is_vararg & VARARG_NEEDSARG) || (pt->is_vararg & VARARG_HASARG)); check(pt->sizeupvalues <= pt->nups); +#ifndef LUA_OPTIMIZE_DEBUG check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); +#endif check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); return 1; } diff --git a/apps/plugins/lua/ldebug.h b/apps/plugins/lua/ldebug.h index 22226b4096..7aa9f459d5 100644 --- a/apps/plugins/lua/ldebug.h +++ b/apps/plugins/lua/ldebug.h @@ -13,7 +13,19 @@ #define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) -#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) +#ifdef LUA_OPTIMIZE_DEBUG +# include "lvm.h" +# define getline(f,pc) (((f)->packedlineinfo) ? luaG_getline((f), pc) : 0) +# define INFO_FILL_BYTE 0x7F +# define INFO_DELTA_MASK 0x80 +# define INFO_SIGN_MASK 0x40 +# define INFO_DELTA_6BITS 0x3F +# define INFO_DELTA_7BITS 0x7F +# define INFO_MAX_LINECNT 126 +# define lineInfoTop(fs) ((fs)->f->packedlineinfo + (fs)->lastlineOffset) +#else +# define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) +#endif #define resethookcount(L) (L->hookcount = L->basehookcount) @@ -30,4 +42,9 @@ LUAI_FUNC void luaG_errormsg (lua_State *L); LUAI_FUNC int luaG_checkcode (const Proto *pt); LUAI_FUNC int luaG_checkopenop (Instruction i); +#ifdef LUA_OPTIMIZE_DEBUG +LUAI_FUNC int luaG_getline (const Proto *f, int pc); +LUAI_FUNC int luaG_stripdebug (lua_State *L, Proto *f, int level, int recv); +#endif + #endif diff --git a/apps/plugins/lua/ldump.c b/apps/plugins/lua/ldump.c index 9afba60a37..be7dce3497 100644 --- a/apps/plugins/lua/ldump.c +++ b/apps/plugins/lua/ldump.c @@ -113,8 +113,17 @@ static void DumpConstants(const Proto* f, DumpState* D) static void DumpDebug(const Proto* f, DumpState* D) { int i,n; +#ifdef LUA_OPTIMIZE_DEBUG + n = (D->strip || f->packedlineinfo == NULL) ? 0: f->sizelineinfo; + DumpInt(n,D); + if (n) + { + DumpBlock(f->packedlineinfo, n, D); + } +#else n= (D->strip) ? 0 : f->sizelineinfo; DumpVector(f->lineinfo,n,sizeof(int),D); +#endif n= (D->strip) ? 0 : f->sizelocvars; DumpInt(n,D); for (i=0; inumparams = 0; f->is_vararg = 0; f->maxstacksize = 0; - f->lineinfo = NULL; f->sizelocvars = 0; f->locvars = NULL; f->linedefined = 0; f->lastlinedefined = 0; f->source = NULL; +#ifdef LUA_OPTIMIZE_DEBUG + f->packedlineinfo = NULL; +#else + f->lineinfo = NULL; + +#endif return f; } @@ -142,7 +147,13 @@ void luaF_freeproto (lua_State *L, Proto *f) { luaM_freearray(L, f->code, f->sizecode, Instruction); luaM_freearray(L, f->p, f->sizep, Proto *); luaM_freearray(L, f->k, f->sizek, TValue); +#ifdef LUA_OPTIMIZE_DEBUG + if (f->packedlineinfo) { + luaM_freearray(L, f->packedlineinfo, f->sizelineinfo, unsigned char); + } +#else luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); +#endif luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); luaM_free(L, f); diff --git a/apps/plugins/lua/lgc.c b/apps/plugins/lua/lgc.c index 17b64a103b..2a4d95aba8 100644 --- a/apps/plugins/lua/lgc.c +++ b/apps/plugins/lua/lgc.c @@ -318,9 +318,13 @@ static l_mem propagatemark (global_State *g) { return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto *) * p->sizep + sizeof(TValue) * p->sizek + - sizeof(int) * p->sizelineinfo + sizeof(LocVar) * p->sizelocvars + - sizeof(TString *) * p->sizeupvalues; + sizeof(TString *) * p->sizeupvalues + +#ifdef LUA_OPTIMIZE_DEBUG + p->sizelineinfo; +#else + sizeof(int) * p->sizelineinfo; +#endif } default: lua_assert(0); return 0; } diff --git a/apps/plugins/lua/lobject.h b/apps/plugins/lua/lobject.h index 028ff0355b..83651c2c47 100644 --- a/apps/plugins/lua/lobject.h +++ b/apps/plugins/lua/lobject.h @@ -240,7 +240,11 @@ typedef struct Proto { TValue *k; /* constants used by the function */ Instruction *code; struct Proto **p; /* functions defined inside the function */ +#ifdef LUA_OPTIMIZE_DEBUG + unsigned char *packedlineinfo; +#else int *lineinfo; /* map from opcodes to source lines */ +#endif struct LocVar *locvars; /* information about local variables */ TString **upvalues; /* upvalue names */ TString *source; diff --git a/apps/plugins/lua/lparser.c b/apps/plugins/lua/lparser.c index d002e96b86..bb08c51c0b 100644 --- a/apps/plugins/lua/lparser.c +++ b/apps/plugins/lua/lparser.c @@ -344,6 +344,11 @@ static void open_func (LexState *ls, FuncState *fs) { fs->bl = NULL; f->source = ls->source; f->maxstacksize = 2; /* registers 0/1 are always valid */ +#ifdef LUA_OPTIMIZE_DEBUG + fs->lastline = 0; + fs->lastlineOffset = 0; + fs->lineinfoLastPC = -1; +#endif fs->h = luaH_new(L, 0, 0); /* anchor table of constants and prototype (to avoid being collected) */ sethvalue2s(L, L->top, fs->h); @@ -361,8 +366,14 @@ static void close_func (LexState *ls) { luaK_ret(fs, 0, 0); /* final return */ luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); f->sizecode = fs->pc; +#ifdef LUA_OPTIMIZE_DEBUG + f->packedlineinfo[fs->lastlineOffset+1]=0; + luaM_reallocvector(L, f->packedlineinfo, f->sizelineinfo, + fs->lastlineOffset+2, unsigned char); +#else luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); f->sizelineinfo = fs->pc; +#endif luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); f->sizek = fs->nk; luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); @@ -379,6 +390,23 @@ static void close_func (LexState *ls) { L->top -= 2; /* remove table and prototype from the stack */ } +#ifdef LUA_OPTIMIZE_DEBUG +static void compile_stripdebug(lua_State *L, Proto *f) { + int level; +#ifdef LUA_OPTIMIZE_DEBUG_USER + lua_pushlightuserdata(L, &luaG_stripdebug); + lua_gettable(L, LUA_REGISTRYINDEX); + level = lua_isnil(L, -1) ? LUA_OPTIMIZE_DEBUG : lua_tointeger(L, -1); + lua_pop(L, 1); +#else + level = LUA_OPTIMIZE_DEBUG; +#endif + + if (level > 1) { + luaG_stripdebug(L, f, level, 16); + } +} +#endif Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { struct LexState lexstate; @@ -395,6 +423,9 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { check(&lexstate, TK_EOS); close_func(&lexstate); L->top--; /* remove 'name' from stack */ +#ifdef LUA_OPTIMIZE_DEBUG + compile_stripdebug(L, funcstate.f); +#endif lua_assert(funcstate.prev == NULL); lua_assert(funcstate.f->nups == 0); lua_assert(lexstate.fs == NULL); @@ -402,7 +433,6 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { } - /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ diff --git a/apps/plugins/lua/lparser.h b/apps/plugins/lua/lparser.h index f9b8e24913..a6af9eb94f 100644 --- a/apps/plugins/lua/lparser.h +++ b/apps/plugins/lua/lparser.h @@ -72,6 +72,11 @@ typedef struct FuncState { lu_byte nactvar; /* number of active local variables */ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ +#ifdef LUA_OPTIMIZE_DEBUG + int lastline; /* only used during compilation for line info */ + int lastlineOffset; /* ditto */ + int lineinfoLastPC; /* ditto */ +#endif } FuncState; diff --git a/apps/plugins/lua/lua.h b/apps/plugins/lua/lua.h index a4b73e743e..74331e924f 100644 --- a/apps/plugins/lua/lua.h +++ b/apps/plugins/lua/lua.h @@ -94,6 +94,9 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); #include LUA_USER_H #endif +#if defined(LUA_OPTIMIZE_DEBUG) && LUA_OPTIMIZE_DEBUG == 0 +#undef LUA_OPTIMIZE_DEBUG +#endif /* type of numbers in Lua */ typedef LUA_NUMBER lua_Number; diff --git a/apps/plugins/lua/luaconf.h b/apps/plugins/lua/luaconf.h index 62a3ea0f6b..f7dd99b19e 100644 --- a/apps/plugins/lua/luaconf.h +++ b/apps/plugins/lua/luaconf.h @@ -818,5 +818,6 @@ extern long rb_pow(long, long); /*else*/ #define LUA_USER_H "lua_user.h" #define LUA_DISABLE_BYTECODE +#define LUA_OPTIMIZE_DEBUG 2 /* Lua Compact Debug -- Terry Ellison 2015 */ #endif diff --git a/apps/plugins/lua/lundump.c b/apps/plugins/lua/lundump.c index 967b45c94b..13f910edd7 100644 --- a/apps/plugins/lua/lundump.c +++ b/apps/plugins/lua/lundump.c @@ -140,9 +140,18 @@ static void LoadDebug(LoadState* S, Proto* f) { int i,n; n=LoadInt(S); +#ifdef LUA_OPTIMIZE_DEBUG + if(n) { + f->packedlineinfo=luaM_newvector(S->L,n,unsigned char); + LoadBlock(S,f->packedlineinfo,n); + } else { + f->packedlineinfo=NULL; + } +#else f->lineinfo=luaM_newvector(S->L,n,int); f->sizelineinfo=n; LoadVector(S,f->lineinfo,n,sizeof(int)); +#endif n=LoadInt(S); f->locvars=luaM_newvector(S->L,n,LocVar); f->sizelocvars=n; -- cgit v1.2.3