Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This is the reference Lua interpreter with the Yk JIT retrofitted.

This is experimental!


## Build

GNU make is required.
Expand All @@ -15,15 +16,16 @@ export YK_BUILD_TYPE=<debug|release>
make -j "$(nproc)"
```

To build with hot location debug support, define the `YK_HL_DEBUG` preprocessor
macro. You can do this when you invoke `make` like this:
To build with debug string support for both `HotLocation`s and per Lua
`Instruction` in a trace, define the `YKLUA_DEBUG_STRS` preprocessor macro:

```shell
make -j "$(nproc)" MYCFLAGS=-DYK_HL_DEBUG
make -j "$(nproc)" MYCFLAGS=-DYKLUA_DEBUG_STRS
```

With an undefined value, or a value of `1`, `YK_HL_DEBUG` will put full path
names into the debug output. To output only leaf names, set `-DYK_HL_DEBUG=2`.
With an unspecified value, or a value of `1`, `YKLUA_DEBUG_STRS` will put full path
names into the debug output. To output only leaf names, set `-DYKLUA_DEBUG_STRS=2`.


## Run

Expand Down
12 changes: 12 additions & 0 deletions src/lfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,20 @@ Proto *luaF_newproto (lua_State *L) {
f->source = NULL;
#ifdef USE_YK
f->yklocs = NULL;
#ifdef YKLUA_DEBUG_STRS
f->instdebugstrs = NULL;
#endif
f->sizeyklocs = 0;
f->proto_version = global_proto_version;
#endif
return f;
}


#ifdef USE_YK
#include <stdlib.h>
#endif

void luaF_freeproto (lua_State *L, Proto *f) {
#ifdef USE_YK
global_proto_version++;
Expand All @@ -285,6 +292,11 @@ void luaF_freeproto (lua_State *L, Proto *f) {
luaM_freearray(L, f->upvalues, f->sizeupvalues);
#ifdef USE_YK
luaM_freearray(L, f->yklocs, f->sizeyklocs);
#ifdef YKLUA_DEBUG_STRS
for (int i = 0; i < f->sizeyklocs; i++)
free(f->instdebugstrs[i]);
luaM_freearray(L, f->instdebugstrs, f->sizeyklocs);
#endif
#endif
luaM_free(L, f);
}
Expand Down
5 changes: 4 additions & 1 deletion src/lobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,10 @@ typedef struct Proto {
Instruction *code; /* opcodes */
#ifdef USE_YK
YkLocation *yklocs; /* One 'YkLocation' per instruction in 'code' */
int sizeyklocs; /* size of 'yklocs' */
#ifdef YKLUA_DEBUG_STRS
char **instdebugstrs; /* One `char *` per instruction in `code` */
#endif
int sizeyklocs; /* size of 'yklocs' and (if present) `instDebugStr` */
uint64_t proto_version; /* What 'Proto Version' was this created under? */
#endif
struct Proto **p; /* functions defined inside the function */
Expand Down
184 changes: 75 additions & 109 deletions src/lparser.c
Original file line number Diff line number Diff line change
Expand Up @@ -760,136 +760,102 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
}

#ifdef USE_YK
/* Read line `n` out of the file at `filename` and put it in `line` */
bool read_source_line(char line[], size_t len, const char *filename, int n) {
/* FIXME: not optimised in any way */
FILE *f = fopen(filename, "r");
if (f == NULL) {
return false;
}
for (; n != 0; n--) {
if (fgets(line, len, f) == NULL) {
return false;
}
}
if (fclose(f) == EOF) {
return false;
}
return true;
}


#define MAX_READ_SOURCELINE 128
bool luaG_format_hl_debug_str(char *filename, int line_number, char *into, size_t into_len) {
if (filename == NULL) {
/* We know nothing */
return false;
}
#if YK_HL_DEBUG == 2
char *leaf = strrchr(filename, '/');
if (leaf && strlen(leaf) > 0)
leaf++;
else
leaf = filename;
#else
char *leaf = filename;
#endif
if (line_number != -1) {
/* We know the filename and the line number */
int off = snprintf(into, into_len, "%s:%d: ", leaf, line_number);
if (off == -1) {
return false;
}
char srcl[MAX_READ_SOURCELINE];
bool ok = read_source_line(srcl, MAX_READ_SOURCELINE, filename, line_number);
if (!ok) {
return false;
}
char *srcl_strip = srcl;
/* strip leading whitespace */
for (; *srcl_strip == ' ' && *srcl_strip != '\t'; srcl_strip++);
/* strip trailing if present */
for (char *p = srcl_strip; *p != '\0'; p++) {
if ((*p == '\n') || (*p == '\r')) {
*p = '\0';
break;
}
}
if (snprintf(into + off, into_len - off, "%s", srcl_strip) == -1) {
return false;
}
} else {
/* We know the filename, but not the line number */
if (snprintf(into, into_len, "%s:?: ?", leaf) == -1) {
return false;
}
}
return true;
}

#include <libgen.h>
#include "lopnames.h"

/*
* Get a Lua-level source line for use in a hot location debug string.
*
* Returns `true` if we could get any debug info.
*/
bool luaG_get_hl_debug_str(Proto *p, int pc, char *into, size_t into_len) {
char *filename = NULL;
int line_number = -1;

if (p->source != NULL) {
filename = getstr(p->source);
if (strlen(filename) > 0 && filename[0] == '@') {
filename++; /* strip the leading '@' */
line_number = luaG_getfuncline(p, pc);
char *luaG_ykdebug_str(Proto *f, int pc) {
char tmp[256];
Instruction i = f->code[pc];
const char *opcode = opnames[GET_OPCODE(i)];
if (f->source == NULL) {
if (snprintf(tmp, sizeof(tmp), "<unknown>:<unknown>:%s", opcode) < 0)
goto err;
} else {
char source[128];
if (snprintf(source, sizeof(source), "%.*s", (int) tsslen(f->source), getstr(f->source)) < 0)
goto err;
if (source[0] == '@') {
char *path = source + 1;
#if YKLUA_DEBUG_STRS == 2
char *leaf = basename(path);
#else
char *leaf = path;
#endif
int line_number = luaG_getfuncline(f, pc);
if (line_number != -1) {
if (snprintf(tmp, sizeof(tmp), "%s:%d: %s", leaf, line_number, opcode) < 0) {
goto err;
}
} else {
if (snprintf(tmp, sizeof(tmp), "%s:<unknown>: %s", leaf, opcode) < 0)
goto err;
}
} else {
if (snprintf(tmp, sizeof(tmp), "%s:<unknown>: %s", source, opcode) < 0)
goto err;
}
}
return luaG_format_hl_debug_str(filename, line_number, into, into_len);
}
char *dstr;
dstr = malloc(strlen(tmp) + 1);
lua_assert(dstr != NULL);
strcpy(dstr, tmp);
return dstr;


static void add_yk_location(Proto *f, int pc) {
YkLocation loc = yk_location_new();
#if defined(YK_HL_DEBUG)
#define HL_DEBUG_STR_MAX 128
/* Add hot location debug info, if possible */
char dstr[HL_DEBUG_STR_MAX];
if (luaG_get_hl_debug_str(f, pc, dstr, HL_DEBUG_STR_MAX)) {
yk_location_set_debug_str(&loc, dstr);
}
#endif
f->yklocs[pc] = loc;
err:
dstr = malloc(1);
lua_assert(dstr != NULL);
dstr[0] = '\0';
return dstr;
}


/*
* Identify loops in the function and insert yk locations there.
* Set the `Proto` `f` up for yk.
*
* This should only be called at a time when the bytecode instructions have
* been properly finalised.
* This must only be called after bytecode instructions have been fully
* finalised.
*/
void assign_yklocs(lua_State *L, Proto *f, int num_insts) {
void ykifyCode(lua_State *L, Proto *f, int num_insts) {
f->yklocs = luaM_newvectorchecked(L, num_insts, YkLocation);
f->sizeyklocs = num_insts;
#ifdef YKLUA_DEBUG_STRS
f->instdebugstrs = luaM_newvectorchecked(L, num_insts, char *);
#endif
lua_assert(num_insts > 0);
add_yk_location(f, 0);
for (int pc = 1; pc < num_insts; pc++) {
Instruction i = f->code[pc];
for (int pc = 0; pc < num_insts; pc++) {
f->yklocs[pc] = yk_location_null();
Instruction i = f->code[pc];
#ifdef YKLUA_DEBUG_STRS
char *dstr = luaG_ykdebug_str(f, pc);
f->instdebugstrs[pc] = dstr;
#endif
/*
* The computations for finding the start of loops is derived from
* `PrintCode()` in `luac.c`. Note that we have to deduct one because luac
* prints bytecode pcs starting from 1.
*
* The assertions below check that inserting a null location will never
* overwrite a non-null location in a later iteration of this loop.
* The computation for finding the start of loops is derived from
* `PrintCode()` in `luac.c`.
*/
if ((GET_OPCODE(i) == OP_JMP) && (GETARG_sJ(i) < 0)) {
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)) {
lua_assert(GETARG_sJ(i) + pc + 2 - 1 < pc);
add_yk_location(f, GETARG_sJ(i) + pc + 2 - 1);
loc_pc = GETARG_sJ(i) + pc + 2 - 1;
} else if (GET_OPCODE(i) == OP_FORLOOP) {
lua_assert(pc - GETARG_Bx(i) + 2 - 1 < pc);
add_yk_location(f, pc - GETARG_Bx(i) + 2 - 1);
loc_pc = pc - GETARG_Bx(i) + 2 - 1;
} else {
continue;
}
YkLocation loc = yk_location_new();
#if YKLUA_DEBUG_STRS
char *loc_dstr = luaG_ykdebug_str(f, loc_pc);
yk_location_set_debug_str(&loc, loc_dstr);
free(loc_dstr);
#endif
f->yklocs[loc_pc] = loc;
}
}
#endif
Expand All @@ -914,7 +880,7 @@ static void close_func (LexState *ls) {
luaC_checkGC(L);

#ifdef USE_YK
assign_yklocs(L, f, fs->pc);
ykifyCode(L, f, fs->pc);
#endif
}

Expand Down
2 changes: 1 addition & 1 deletion src/lparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ LUAI_FUNC int luaY_nvarstack (FuncState *fs);
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar);
#ifdef USE_YK
LUAI_FUNC void assign_yklocs(lua_State *L, Proto *f, int num_insts);
LUAI_FUNC void ykifyCode(lua_State *L, Proto *f, int num_insts);
#endif


Expand Down
2 changes: 1 addition & 1 deletion src/lundump.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ static void loadCode (LoadState *S, Proto *f) {
loadVector(S, f->code, n);
#ifdef USE_YK
/* FIXME: Ideally we'd persist the locations across a dump+undump too */
assign_yklocs(S->L, f, n);
ykifyCode(S->L, f, n);
#endif
}

Expand Down
3 changes: 3 additions & 0 deletions src/lvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
/* main loop of interpreter */
for (;;) {
Instruction i; /* instruction being executed */
#ifdef YKLUA_DEBUG_STRS
yk_debug_str(cl->p->instdebugstrs[cast_int(pc - cl->p->code)]);
#endif
vmfetch();
#ifdef USE_YK
YkLocation *ykloc = &cl->p->yklocs[pcRel(pc, cl->p)];
Expand Down