mirror of
https://github.com/LuaJIT/LuaJIT.git
synced 2025-02-08 07:34:07 +00:00
458 lines
12 KiB
C
458 lines
12 KiB
C
/*
|
|
** LuaJIT VM builder: library definition compiler.
|
|
** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h
|
|
*/
|
|
|
|
#include "buildvm.h"
|
|
#include "lj_obj.h"
|
|
#include "lj_bc.h"
|
|
#include "lj_lib.h"
|
|
#include "buildvm_libbc.h"
|
|
|
|
/* Context for library definitions. */
|
|
static uint8_t obuf[8192];
|
|
static uint8_t *optr;
|
|
static char modname[80];
|
|
static size_t modnamelen;
|
|
static char funcname[80];
|
|
static int modstate, regfunc;
|
|
static int ffid, recffid, ffasmfunc;
|
|
|
|
enum {
|
|
REGFUNC_OK,
|
|
REGFUNC_NOREG,
|
|
REGFUNC_NOREGUV
|
|
};
|
|
|
|
static void libdef_name(const char *p, int kind)
|
|
{
|
|
size_t n = strlen(p);
|
|
if (kind != LIBINIT_STRING) {
|
|
if (n > modnamelen && p[modnamelen] == '_' &&
|
|
!strncmp(p, modname, modnamelen)) {
|
|
p += modnamelen+1;
|
|
n -= modnamelen+1;
|
|
}
|
|
}
|
|
if (n > LIBINIT_MAXSTR) {
|
|
fprintf(stderr, "Error: string too long: '%s'\n", p);
|
|
exit(1);
|
|
}
|
|
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
*optr++ = (uint8_t)(n | kind);
|
|
memcpy(optr, p, n);
|
|
optr += n;
|
|
}
|
|
|
|
static void libdef_endmodule(BuildCtx *ctx)
|
|
{
|
|
if (modstate != 0) {
|
|
char line[80];
|
|
const uint8_t *p;
|
|
int n;
|
|
if (modstate == 1)
|
|
fprintf(ctx->fp, " (lua_CFunction)0");
|
|
fprintf(ctx->fp, "\n};\n");
|
|
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n",
|
|
LABEL_PREFIX_LIBINIT, modname);
|
|
line[0] = '\0';
|
|
for (n = 0, p = obuf; p < optr; p++) {
|
|
n += sprintf(line+n, "%d,", *p);
|
|
if (n >= 75) {
|
|
fprintf(ctx->fp, "%s\n", line);
|
|
n = 0;
|
|
line[0] = '\0';
|
|
}
|
|
}
|
|
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END);
|
|
}
|
|
}
|
|
|
|
static void libdef_module(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
UNUSED(arg);
|
|
if (ctx->mode == BUILD_libdef) {
|
|
libdef_endmodule(ctx);
|
|
optr = obuf;
|
|
*optr++ = (uint8_t)ffid;
|
|
*optr++ = (uint8_t)ffasmfunc;
|
|
*optr++ = 0; /* Hash table size. */
|
|
modstate = 1;
|
|
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
|
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
|
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n",
|
|
LABEL_PREFIX_LIBCF, p);
|
|
}
|
|
modnamelen = strlen(p);
|
|
if (modnamelen > sizeof(modname)-1) {
|
|
fprintf(stderr, "Error: module name too long: '%s'\n", p);
|
|
exit(1);
|
|
}
|
|
strcpy(modname, p);
|
|
}
|
|
|
|
static int find_ffofs(BuildCtx *ctx, const char *name)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ctx->nglob; i++) {
|
|
const char *gl = ctx->globnames[i];
|
|
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) {
|
|
return (int)((uint8_t *)ctx->glob[i] - ctx->code);
|
|
}
|
|
}
|
|
fprintf(stderr, "Error: undefined fast function %s%s\n",
|
|
LABEL_PREFIX_FF, name);
|
|
exit(1);
|
|
}
|
|
|
|
static void libdef_func(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
if (arg != LIBINIT_CF)
|
|
ffasmfunc++;
|
|
if (ctx->mode == BUILD_libdef) {
|
|
if (modstate == 0) {
|
|
fprintf(stderr, "Error: no module for function definition %s\n", p);
|
|
exit(1);
|
|
}
|
|
if (regfunc == REGFUNC_NOREG) {
|
|
if (optr+1 > obuf+sizeof(obuf)) {
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
*optr++ = LIBINIT_FFID;
|
|
} else {
|
|
if (arg != LIBINIT_ASM_) {
|
|
if (modstate != 1) fprintf(ctx->fp, ",\n");
|
|
modstate = 2;
|
|
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
|
|
}
|
|
if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */
|
|
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
|
|
}
|
|
} else if (ctx->mode == BUILD_ffdef) {
|
|
fprintf(ctx->fp, "FFDEF(%s)\n", p);
|
|
} else if (ctx->mode == BUILD_recdef) {
|
|
if (strlen(p) > sizeof(funcname)-1) {
|
|
fprintf(stderr, "Error: function name too long: '%s'\n", p);
|
|
exit(1);
|
|
}
|
|
strcpy(funcname, p);
|
|
} else if (ctx->mode == BUILD_vmdef) {
|
|
int i;
|
|
for (i = 1; p[i] && modname[i-1]; i++)
|
|
if (p[i] == '_') p[i] = '.';
|
|
fprintf(ctx->fp, "\"%s\",\n", p);
|
|
} else if (ctx->mode == BUILD_bcdef) {
|
|
if (arg != LIBINIT_CF)
|
|
fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p));
|
|
}
|
|
ffid++;
|
|
regfunc = REGFUNC_OK;
|
|
}
|
|
|
|
static uint8_t *libdef_uleb128(uint8_t *p, uint32_t *vv)
|
|
{
|
|
uint32_t v = *p++;
|
|
if (v >= 0x80) {
|
|
int sh = 0; v &= 0x7f;
|
|
do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80);
|
|
}
|
|
*vv = v;
|
|
return p;
|
|
}
|
|
|
|
static void libdef_fixupbc(uint8_t *p)
|
|
{
|
|
uint32_t i, sizebc;
|
|
p += 4;
|
|
p = libdef_uleb128(p, &sizebc);
|
|
p = libdef_uleb128(p, &sizebc);
|
|
p = libdef_uleb128(p, &sizebc);
|
|
for (i = 0; i < sizebc; i++, p += 4) {
|
|
uint8_t op = p[libbc_endian ? 3 : 0];
|
|
uint8_t ra = p[libbc_endian ? 2 : 1];
|
|
uint8_t rc = p[libbc_endian ? 1 : 2];
|
|
uint8_t rb = p[libbc_endian ? 0 : 3];
|
|
if (!LJ_DUALNUM && op == BC_ISTYPE && rc == ~LJ_TNUMX+1) {
|
|
op = BC_ISNUM; rc++;
|
|
}
|
|
p[LJ_ENDIAN_SELECT(0, 3)] = op;
|
|
p[LJ_ENDIAN_SELECT(1, 2)] = ra;
|
|
p[LJ_ENDIAN_SELECT(2, 1)] = rc;
|
|
p[LJ_ENDIAN_SELECT(3, 0)] = rb;
|
|
}
|
|
}
|
|
|
|
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);
|
|
libdef_fixupbc(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;
|
|
uint32_t n;
|
|
for (n = 2; *p; n++) {
|
|
if (strcmp(p, name) == 0)
|
|
return n;
|
|
p += strlen(p)+1;
|
|
}
|
|
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) {
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
strcpy(p, name);
|
|
return n;
|
|
}
|
|
|
|
static void libdef_rec(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
UNUSED(arg);
|
|
if (ctx->mode == BUILD_recdef) {
|
|
char *q;
|
|
uint32_t n;
|
|
for (; recffid+1 < ffid; recffid++)
|
|
fprintf(ctx->fp, ",\n0");
|
|
recffid = ffid;
|
|
if (*p == '.') p = funcname;
|
|
q = strchr(p, ' ');
|
|
if (q) *q++ = '\0';
|
|
n = find_rec(p);
|
|
if (q)
|
|
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q);
|
|
else
|
|
fprintf(ctx->fp, ",\n0x%02x00", n);
|
|
}
|
|
}
|
|
|
|
static void memcpy_endian(void *dst, void *src, size_t n)
|
|
{
|
|
union { uint8_t b; uint32_t u; } host_endian;
|
|
host_endian.u = 1;
|
|
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) {
|
|
memcpy(dst, src, n);
|
|
} else {
|
|
size_t i;
|
|
for (i = 0; i < n; i++)
|
|
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1];
|
|
}
|
|
}
|
|
|
|
static void libdef_push(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
UNUSED(arg);
|
|
if (ctx->mode == BUILD_libdef) {
|
|
int len = (int)strlen(p);
|
|
if (*p == '"') {
|
|
if (len > 1 && p[len-1] == '"') {
|
|
p[len-1] = '\0';
|
|
libdef_name(p+1, LIBINIT_STRING);
|
|
return;
|
|
}
|
|
} else if (*p >= '0' && *p <= '9') {
|
|
char *ep;
|
|
double d = strtod(p, &ep);
|
|
if (*ep == '\0') {
|
|
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) {
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
*optr++ = LIBINIT_NUMBER;
|
|
memcpy_endian(optr, &d, sizeof(double));
|
|
optr += sizeof(double);
|
|
return;
|
|
}
|
|
} else if (!strcmp(p, "lastcl")) {
|
|
if (optr+1 > obuf+sizeof(obuf)) {
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
*optr++ = LIBINIT_LASTCL;
|
|
return;
|
|
} else if (len > 4 && !strncmp(p, "top-", 4)) {
|
|
if (optr+2 > obuf+sizeof(obuf)) {
|
|
fprintf(stderr, "Error: output buffer overflow\n");
|
|
exit(1);
|
|
}
|
|
*optr++ = LIBINIT_COPY;
|
|
*optr++ = (uint8_t)atoi(p+4);
|
|
return;
|
|
}
|
|
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static void libdef_set(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
UNUSED(arg);
|
|
if (ctx->mode == BUILD_libdef) {
|
|
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */
|
|
libdef_name(p, LIBINIT_STRING);
|
|
*optr++ = LIBINIT_SET;
|
|
obuf[2]++; /* Bump hash table size. */
|
|
}
|
|
}
|
|
|
|
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg)
|
|
{
|
|
UNUSED(ctx); UNUSED(p);
|
|
regfunc = arg;
|
|
}
|
|
|
|
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg);
|
|
|
|
typedef struct LibDefHandler {
|
|
const char *suffix;
|
|
const char *stop;
|
|
const LibDefFunc func;
|
|
const int arg;
|
|
} LibDefHandler;
|
|
|
|
static const LibDefHandler libdef_handlers[] = {
|
|
{ "MODULE_", " \t\r\n", libdef_module, 0 },
|
|
{ "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 },
|
|
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV },
|
|
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG },
|
|
{ NULL, NULL, (LibDefFunc)0, 0 }
|
|
};
|
|
|
|
/* Emit C source code for library function definitions. */
|
|
void emit_lib(BuildCtx *ctx)
|
|
{
|
|
const char *fname;
|
|
|
|
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
|
|
ctx->mode == BUILD_recdef)
|
|
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
|
else if (ctx->mode == BUILD_vmdef)
|
|
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
|
|
if (ctx->mode == BUILD_recdef)
|
|
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
|
|
recffid = ffid = FF_C+1;
|
|
ffasmfunc = 0;
|
|
|
|
while ((fname = *ctx->args++)) {
|
|
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
|
FILE *fp;
|
|
if (fname[0] == '-' && fname[1] == '\0') {
|
|
fp = stdin;
|
|
} else {
|
|
fp = fopen(fname, "r");
|
|
if (!fp) {
|
|
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
|
fname, strerror(errno));
|
|
exit(1);
|
|
}
|
|
}
|
|
modstate = 0;
|
|
regfunc = REGFUNC_OK;
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
char *p;
|
|
/* Simplistic pre-processor. Only handles top-level #if/#endif. */
|
|
if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
|
int ok = 1;
|
|
if (!strcmp(buf, "#if LJ_52\n"))
|
|
ok = LJ_52;
|
|
else if (!strcmp(buf, "#if LJ_HASJIT\n"))
|
|
ok = LJ_HASJIT;
|
|
else if (!strcmp(buf, "#if LJ_HASFFI\n"))
|
|
ok = LJ_HASFFI;
|
|
if (!ok) {
|
|
int lvl = 1;
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
|
if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') {
|
|
if (--lvl == 0) break;
|
|
} else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
|
lvl++;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
|
|
const LibDefHandler *ldh;
|
|
p += sizeof(LIBDEF_PREFIX)-1;
|
|
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
|
|
size_t n, len = strlen(ldh->suffix);
|
|
if (!strncmp(p, ldh->suffix, len)) {
|
|
p += len;
|
|
n = ldh->stop ? strcspn(p, ldh->stop) : 0;
|
|
if (!p[n]) break;
|
|
p[n] = '\0';
|
|
ldh->func(ctx, p, ldh->arg);
|
|
p += n+1;
|
|
break;
|
|
}
|
|
}
|
|
if (ldh->suffix == NULL) {
|
|
buf[strlen(buf)-1] = '\0';
|
|
fprintf(stderr, "Error: unknown library definition tag %s%s\n",
|
|
LIBDEF_PREFIX, p);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
fclose(fp);
|
|
if (ctx->mode == BUILD_libdef) {
|
|
libdef_endmodule(ctx);
|
|
}
|
|
}
|
|
|
|
if (ctx->mode == BUILD_ffdef) {
|
|
fprintf(ctx->fp, "\n#undef FFDEF\n\n");
|
|
fprintf(ctx->fp,
|
|
"#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n",
|
|
ffasmfunc);
|
|
} else if (ctx->mode == BUILD_vmdef) {
|
|
fprintf(ctx->fp, "},\n\n");
|
|
} else if (ctx->mode == BUILD_bcdef) {
|
|
int i;
|
|
fprintf(ctx->fp, "\n};\n\n");
|
|
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n");
|
|
fprintf(ctx->fp, "BCDEF(BCMODE)\n");
|
|
for (i = ffasmfunc-1; i > 0; i--)
|
|
fprintf(ctx->fp, "BCMODE_FF,\n");
|
|
fprintf(ctx->fp, "BCMODE_FF\n};\n\n");
|
|
} else if (ctx->mode == BUILD_recdef) {
|
|
char *p = (char *)obuf;
|
|
fprintf(ctx->fp, "\n};\n\n");
|
|
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
|
|
"recff_nyi,\n"
|
|
"recff_c");
|
|
while (*p) {
|
|
fprintf(ctx->fp, ",\nrecff_%s", p);
|
|
p += strlen(p)+1;
|
|
}
|
|
fprintf(ctx->fp, "\n};\n\n");
|
|
}
|
|
}
|
|
|