#include "libqbe.h"
#include <getopt.h>
#include <stdio.h>

Target T;

char debug['Z'+1] = {
    ['P'] = 0, /* parsing */
    ['M'] = 0, /* memory optimization */
    ['N'] = 0, /* ssa construction */
    ['C'] = 0, /* copy elimination */
    ['F'] = 0, /* constant folding */
    ['A'] = 0, /* abi lowering */
    ['I'] = 0, /* instruction selection */
    ['L'] = 0, /* liveness */
    ['S'] = 0, /* spilling */
    ['R'] = 0, /* reg. allocation */
};

static FILE *outf;
static int dbg;

static void
data(Dat *d)
{
    if (dbg)
        return;
    emitdat(d, outf);
    if (d->type == DEnd) {
        fputs("/* end data */\n\n", outf);
        freeall();
    }
}

static void
func(Fn *fn)
{
    uint n;

    if (dbg)
        fprintf(stderr, "**** Function %s ****", fn->name);
    if (debug['P']) {
        fprintf(stderr, "\n> After parsing:\n");
        printfn(fn, stderr);
    }
    T.abi0(fn);
    fillrpo(fn);
    fillpreds(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);
    ssacheck(fn);
    copy(fn);
    filluse(fn);
    fold(fn);
    T.abi1(fn);
    simpl(fn);
    fillpreds(fn);
    filluse(fn);
    T.isel(fn);
    fillrpo(fn);
    filllive(fn);
    fillloop(fn);
    fillcost(fn);
    spill(fn);
    rega(fn);
    fillrpo(fn);
    simpljmp(fn);
    fillpreds(fn);
    fillrpo(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];
    if (!dbg) {
        T.emitfn(fn, outf);
        fprintf(outf, "/* end function %s */\n\n", fn->name);
    } else
        fprintf(stderr, "\n");
    freeall();
}

static void
dbgfile(char *fn)
{
    emitdbgfile(fn, outf);
}

const char* 
qbe_gen(Target target, const char* ssa)
{
    T = target;
    FILE *inf = tmpfile();
    fputs(ssa, inf);
    rewind(inf);
    outf = tmpfile();
    char* f = "-";

    parse(inf, f, dbgfile, data, func);
    fclose(inf);

    if (!dbg)
        T.emitfin(outf);

    fseek(outf, 0, SEEK_END);
    long size = ftell(outf);
    fseek(outf, 0, SEEK_SET);

    char* buf = malloc(size + 1);
    fread(buf, size, 1, outf);
    buf[size] = '\0';

    fclose(outf);

    return buf;
}

void
qbe_emit(const char* filename, const char* assembly) {
    char cc[255];
    snprintf(cc, 255, "cc -x assembler -o %s -", filename);

    FILE* cc_pipe = popen(cc, "w");
    if(fputs(assembly, cc_pipe) == EOF) {
        perror("fputs");
    }
    pclose(cc_pipe);
}

void
qbe_gen_and_emit(Target target, const char* filename, const char* ssa) {
    qbe_emit(filename, qbe_gen(target, ssa));
}