/* * 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 #include #include /* 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, "", 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; }