qbe in native build
This commit is contained in:
172
source/qbe_backend.c
Normal file
172
source/qbe_backend.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user