diff --git a/src/ldo.c b/src/ldo.c index 2a0017c..ca775e5 100644 --- a/src/ldo.c +++ b/src/ldo.c @@ -562,6 +562,18 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, setnilvalue(s2v(func + narg1)); /* complete missing arguments */ ci->top.p = func + 1 + fsize; /* top for new function */ lua_assert(ci->top.p <= L->stack_last.p); +#ifdef USE_YK + // If this is a recursive call and we don't yet have a yk_location, + // create one now. + if (p->called && yk_location_is_null(p->yklocs[0])) { + p->yklocs[0] = yk_location_new(); +#if YKLUA_DEBUG_STRS + yk_location_set_debug_str(&p->yklocs[0], p->instdebugstrs[0]); +#endif + } else if (!p->called) { + p->called = true; + } +#endif ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus |= CIST_TAIL; L->top.p = func + narg1; /* set top */ @@ -606,6 +618,18 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { for (; narg < nfixparams; narg++) setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ lua_assert(ci->top.p <= L->stack_last.p); +#ifdef USE_YK + // If this is a recursive call and we don't yet have a yk_location, + // create one now. + if (p->called && yk_location_is_null(p->yklocs[0])) { + p->yklocs[0] = yk_location_new(); +#if YKLUA_DEBUG_STRS + yk_location_set_debug_str(&p->yklocs[0], p->instdebugstrs[0]); +#endif + } else if (!p->called) { + p->called = true; + } +#endif return ci; } default: { /* not a function */ diff --git a/src/lfunc.c b/src/lfunc.c index bfc8bd1..2906b3d 100644 --- a/src/lfunc.c +++ b/src/lfunc.c @@ -264,6 +264,7 @@ Proto *luaF_newproto (lua_State *L) { f->lastlinedefined = 0; f->source = NULL; #ifdef USE_YK + f->called = false; f->yklocs = NULL; #ifdef YKLUA_DEBUG_STRS f->instdebugstrs = NULL; diff --git a/src/lobject.h b/src/lobject.h index 89da758..0b58945 100644 --- a/src/lobject.h +++ b/src/lobject.h @@ -569,6 +569,13 @@ typedef struct Proto { TValue *k; /* constants used by the function */ Instruction *code; /* opcodes */ #ifdef USE_YK + /* Used to detect recursive function calls. When a function is + * called this is set to `true` and when we return it is set to `false`. This + * works because a recursive function call must detect the `true` case before + * the bit is flipped. In other words, `called` being `false` does not mean + * "this isn't a recursive call", but if it's `true` it definitely is a + * recursive call. */ + bool called; YkLocation *yklocs; /* One 'YkLocation' per instruction in 'code' */ #ifdef YKLUA_DEBUG_STRS char **instdebugstrs; /* One `char *` per instruction in `code` */ diff --git a/src/lparser.c b/src/lparser.c index c6f4ff0..7514d70 100644 --- a/src/lparser.c +++ b/src/lparser.c @@ -832,15 +832,18 @@ void ykifyCode(lua_State *L, Proto *f, int num_insts) { char *dstr = luaG_ykdebug_str(f, pc); f->instdebugstrs[pc] = dstr; #endif + if (pc == 0) { + // We only insert locations for the start of functions when we + // dynamically detect that a function is definitely recursive. See + // luaD_precall. + continue; + } /* * The computation for finding the start of loops is derived from * `PrintCode()` in `luac.c`. */ int loc_pc; - if (pc == 0) { - // We always add a ykloc to the first instruction. - loc_pc = 0; - } else if ((GET_OPCODE(i) == OP_JMP) && (GETARG_sJ(i) < 0)) { + if ((GET_OPCODE(i) == OP_JMP) && (GETARG_sJ(i) < 0)) { lua_assert(GETARG_sJ(i) + pc + 2 - 1 < pc); loc_pc = GETARG_sJ(i) + pc + 2 - 1; } else if (GET_OPCODE(i) == OP_FORLOOP) { diff --git a/src/lvm.c b/src/lvm.c index 382a66f..73eb04d 100644 --- a/src/lvm.c +++ b/src/lvm.c @@ -1853,6 +1853,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } } ret: /* return from a Lua function */ +#ifdef USE_YK + cl->p->called = false; +#endif if (ci->callstatus & CIST_FRESH) return; /* end this frame */ else {