181 lines
3.9 KiB
C
181 lines
3.9 KiB
C
/*
|
|
* QBE Backend — in-process QBE IR → assembly compilation.
|
|
*
|
|
* Wraps QBE as a library: feeds IR text via fmemopen(), captures
|
|
* assembly output via open_memstream(), returns it as a JS string.
|
|
* No subprocess, no temp files for IR, no external qbe binary needed.
|
|
*/
|
|
|
|
#include "cell.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
/* QBE headers */
|
|
#include "all.h"
|
|
#include "config.h"
|
|
|
|
/* QBE globals (declared extern in all.h) */
|
|
Target T;
|
|
char debug['Z'+1] = {0};
|
|
|
|
extern Target T_amd64_sysv;
|
|
extern Target T_amd64_apple;
|
|
extern Target T_amd64_win;
|
|
extern Target T_arm64;
|
|
extern Target T_arm64_apple;
|
|
extern Target T_rv64;
|
|
|
|
/* Captured output stream — set before calling parse() */
|
|
static FILE *qbe_outf;
|
|
|
|
static void qbe_data(Dat *d) {
|
|
emitdat(d, qbe_outf);
|
|
if (d->type == DEnd) {
|
|
fputs("/* end data */\n\n", qbe_outf);
|
|
freeall();
|
|
}
|
|
}
|
|
|
|
static void qbe_func(Fn *fn) {
|
|
uint n;
|
|
|
|
T.abi0(fn);
|
|
fillcfg(fn);
|
|
filluse(fn);
|
|
promote(fn);
|
|
filluse(fn);
|
|
ssa(fn);
|
|
filluse(fn);
|
|
ssacheck(fn);
|
|
fillalias(fn);
|
|
loadopt(fn);
|
|
filluse(fn);
|
|
fillalias(fn);
|
|
coalesce(fn);
|
|
filluse(fn);
|
|
filldom(fn);
|
|
ssacheck(fn);
|
|
gvn(fn);
|
|
fillcfg(fn);
|
|
simplcfg(fn);
|
|
filluse(fn);
|
|
filldom(fn);
|
|
gcm(fn);
|
|
filluse(fn);
|
|
ssacheck(fn);
|
|
if (T.cansel) {
|
|
ifconvert(fn);
|
|
fillcfg(fn);
|
|
filluse(fn);
|
|
filldom(fn);
|
|
ssacheck(fn);
|
|
}
|
|
T.abi1(fn);
|
|
simpl(fn);
|
|
fillcfg(fn);
|
|
filluse(fn);
|
|
T.isel(fn);
|
|
fillcfg(fn);
|
|
filllive(fn);
|
|
fillloop(fn);
|
|
fillcost(fn);
|
|
spill(fn);
|
|
rega(fn);
|
|
fillcfg(fn);
|
|
simpljmp(fn);
|
|
fillcfg(fn);
|
|
assert(fn->rpo[0] == fn->start);
|
|
for (n = 0;; n++)
|
|
if (n == fn->nblk - 1) {
|
|
fn->rpo[n]->link = 0;
|
|
break;
|
|
} else
|
|
fn->rpo[n]->link = fn->rpo[n+1];
|
|
T.emitfn(fn, qbe_outf);
|
|
fprintf(qbe_outf, "/* end function %s */\n\n", fn->name);
|
|
freeall();
|
|
}
|
|
|
|
static void qbe_dbgfile(char *fn) {
|
|
emitdbgfile(fn, qbe_outf);
|
|
}
|
|
|
|
/*
|
|
* js_os_qbe(ctx, self, argc, argv)
|
|
*
|
|
* Takes a single string argument (QBE IR text).
|
|
* Returns the compiled assembly as a string.
|
|
*/
|
|
JSValue js_os_qbe(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
if (argc < 1)
|
|
return JS_RaiseDisrupt(js, "os.qbe requires an IR string argument");
|
|
|
|
const char *ir = JS_ToCString(js, argv[0]);
|
|
if (!ir)
|
|
return JS_EXCEPTION;
|
|
|
|
size_t ir_len = strlen(ir);
|
|
|
|
/* Select target for host platform */
|
|
#if defined(__APPLE__) && defined(__aarch64__)
|
|
T = T_arm64_apple;
|
|
#elif defined(__APPLE__) && defined(__x86_64__)
|
|
T = T_amd64_apple;
|
|
#elif defined(_WIN32) && defined(__x86_64__)
|
|
T = T_amd64_win;
|
|
#elif defined(__x86_64__)
|
|
T = T_amd64_sysv;
|
|
#elif defined(__aarch64__)
|
|
T = T_arm64;
|
|
#elif defined(__riscv) && __riscv_xlen == 64
|
|
T = T_rv64;
|
|
#else
|
|
T = Deftgt;
|
|
#endif
|
|
|
|
memset(debug, 0, sizeof(debug));
|
|
|
|
/* Open IR string as input FILE */
|
|
FILE *inf = fmemopen((void *)ir, ir_len, "r");
|
|
if (!inf) {
|
|
JS_FreeCString(js, ir);
|
|
return JS_RaiseDisrupt(js, "os.qbe: fmemopen failed");
|
|
}
|
|
|
|
/* Open output memory stream */
|
|
char *out_buf = NULL;
|
|
size_t out_len = 0;
|
|
qbe_outf = open_memstream(&out_buf, &out_len);
|
|
if (!qbe_outf) {
|
|
fclose(inf);
|
|
JS_FreeCString(js, ir);
|
|
return JS_RaiseDisrupt(js, "os.qbe: open_memstream failed");
|
|
}
|
|
|
|
/* Run the QBE pipeline */
|
|
struct timespec t0, t1;
|
|
clock_gettime(CLOCK_MONOTONIC, &t0);
|
|
parse(inf, "<ir>", qbe_dbgfile, qbe_data, qbe_func);
|
|
fclose(inf);
|
|
|
|
/* Finalize (emit assembler directives) */
|
|
T.emitfin(qbe_outf);
|
|
fflush(qbe_outf);
|
|
fclose(qbe_outf);
|
|
qbe_outf = NULL;
|
|
clock_gettime(CLOCK_MONOTONIC, &t1);
|
|
double ms = (t1.tv_sec - t0.tv_sec) * 1000.0 +
|
|
(t1.tv_nsec - t0.tv_nsec) / 1e6;
|
|
fprintf(stderr, "[qbe_backend] os.qbe: %.1fms (%zu bytes IL -> %zu bytes asm)\n",
|
|
ms, ir_len, out_len);
|
|
|
|
JS_FreeCString(js, ir);
|
|
|
|
/* Return assembly text */
|
|
JSValue result = JS_NewStringLen(js, out_buf, out_len);
|
|
free(out_buf);
|
|
return result;
|
|
}
|