Files
cell/source/qbe_backend.c
2026-02-17 10:23:47 -06:00

173 lines
3.6 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>
/* 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_ThrowTypeError(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_ThrowInternalError(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_ThrowInternalError(js, "os.qbe: open_memstream failed");
}
/* Run the QBE pipeline */
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;
JS_FreeCString(js, ir);
/* Return assembly text */
JSValue result = JS_NewStringLen(js, out_buf, out_len);
free(out_buf);
return result;
}