shithub: mc

ref: 4931139165780ca410fe5ab41cac6d1af0af059e
dir: /6/ra.c/

View raw version
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
#include <string.h>

#include "parse.h"
#include "opt.h"
#include "asm.h"

#define Sizetbits (CHAR_BIT*sizeof(size_t)) /* used in graph reprs */

typedef struct Usemap Usemap;
struct Usemap {
    int l[Maxarg + 1]; /* location of arg used in instruction's arg list */
    int r[Maxarg + 1]; /* list of registers used implicitly by instruction */
};

static void printedge(FILE *fd, char *msg, size_t a, size_t b);

/* tables of uses/defs by instruction */
Usemap usetab[] = {
#define Use(...) {__VA_ARGS__}
#define Insn(i, fmt, use, def) use,
#include "insns.def"
#undef Insn
#undef Use
};

Usemap deftab[] = {
#define Def(...) {__VA_ARGS__}
#define Insn(i, fmt, use, def) def,
#include "insns.def"
#undef Insn
#undef Def
};

/* A map of which registers interfere */
Reg regmap[][Nmode] = {
    [0]  = {Rnone, Ral, Rax, Reax, Rrax},
    [1]  = {Rnone, Rcl, Rcx, Recx, Rrcx},
    [2]  = {Rnone, Rdl, Rdx, Redx, Rrdx},
    [3]  = {Rnone, Rbl, Rbx, Rebx, Rrbx},
    [4]  = {Rnone, Rsil, Rsi, Resi, Rrsi},
    [5]  = {Rnone, Rdil, Rdi, Redi, Rrdi},
    [6]  = {Rnone, R8b, R8w, R8d, R8},
    [7]  = {Rnone, R9b, R9w, R9d, R9},
    [8]  = {Rnone, R10b, R10w, R10d, R10},
    [9]  = {Rnone, R11b, R11w, R11d, R11},
    [10]  = {Rnone, R12b, R12w, R12d, R12},
    [11]  = {Rnone, R13b, R13w, R13d, R13},
    [12]  = {Rnone, R14b, R14w, R14d, R14},
    [13]  = {Rnone, R15b, R15w, R15d, R15},
};

/* Which regmap entry a register maps to */
int colourmap[Nreg] = {
    /* byte */
    [Ral] = 0,
    [Rcl] = 1,
    [Rdl] = 2,
    [Rbl] = 3,
    [Rsil] = 4,
    [Rdil] = 5,
    [R8b] = 6,
    [R9b] = 7,
    [R10b] = 8,
    [R11b] = 9,
    [R12b] = 10,
    [R13b] = 11,
    [R14b] = 12,
    [R15b] = 13,

    /* word */
    [Rax] = 0,
    [Rcx] = 1,
    [Rdx] = 2,
    [Rbx] = 3,
    [Rsi] = 4,
    [Rdi] = 5,
    [R8w] = 6,
    [R9w] = 7,
    [R10w] = 8,
    [R11w] = 9,
    [R12w] = 10,
    [R13w] = 11,
    [R14w] = 12,
    [R15w] = 13,

    /* dword */
    [Reax] = 0,
    [Recx] = 1,
    [Redx] = 2,
    [Rebx] = 3,
    [Resi] = 4,
    [Redi] = 5,
    [R8d] = 6,
    [R9d] = 7,
    [R10d] = 8,
    [R11d] = 9,
    [R12d] = 10,
    [R13d] = 11,
    [R14d] = 12,
    [R15d] = 13,

    /* qword */
    [Rrax] = 0,
    [Rrcx] = 1,
    [Rrdx] = 2,
    [Rrbx] = 3,
    [Rrsi] = 4,
    [Rrdi] = 5,
    [R8] = 6,
    [R9] = 7,
    [R10] = 8,
    [R11] = 9,
    [R12] = 10,
    [R13] = 11,
    [R14] = 12,
    [R15] = 13,
};

/* %esp, %ebp are not in the allocatable pool */
static int isfixreg(Loc *l)
{
    if (l->reg.colour == Resp)
        return 1;
    if (l->reg.colour == Rebp)
        return 1;
    return 0;
}

static size_t uses(Insn *insn, long *u)
{
    size_t i, j;
    int k;
    Loc *m;

    j = 0;
    /* Add all the registers used and defined. Duplicates
     * in this list are fine, since they're being added to
     * a set anyways */
    for (i = 0; i < Maxarg; i++) {
        if (!usetab[insn->op].l[i])
            break;
        k = usetab[insn->op].l[i] - 1;
        /* non-registers are handled later */
        if (insn->args[k]->type == Locreg)
            if (!isfixreg(insn->args[k]))
                u[j++] = insn->args[k]->reg.id;
    }
    /* some insns don't reflect their defs in the args.
     * These are explictly listed in the insn description */
    for (i = 0; i < Maxarg; i++) {
        if (!usetab[insn->op].r[i])
            break;
        /* not a leak; physical registers get memoized */
        u[j++] = locphysreg(usetab[insn->op].r[i])->reg.id;
    }
    /* If the registers are in an address calculation,
     * they're used no matter what. */
    for (i = 0; i < insn->nargs; i++) {
        m = insn->args[i];
        if (m->type != Locmem && m->type != Locmeml)
            continue;
        if (m->mem.base)
            if (!isfixreg(m->mem.base))
                u[j++] = m->mem.base->reg.id;
        if (m->mem.idx)
            if (!isfixreg(m->mem.base))
                u[j++] = m->mem.idx->reg.id;
    }
    return j;
}

static size_t defs(Insn *insn, long *d)
{
    size_t i, j;
    int k;

    j = 0;
    /* Add all the registers dsed and defined. Duplicates
     * in this list are fine, since they're being added to
     * a set anyways */
    for (i = 0; i < Maxarg; i++) {
        if (!deftab[insn->op].l[i])
            break;
        k = deftab[insn->op].l[i] - 1;
        if (insn->args[k]->type == Locreg)
            if (!isfixreg(insn->args[k]))
                d[j++] = insn->args[k]->reg.id;
    }
    /* some insns don't reflect their defs in the args.
     * These are explictly listed in the insn description */
    for (i = 0; i < Maxarg; i++) {
        if (!deftab[insn->op].r[i])
            break;
        /* not a leak; physical registers get memoized */
        d[j++] = locphysreg(deftab[insn->op].r[i])->reg.id;
    }
    return j;
}

/* The uses and defs for an entire BB. */
static void udcalc(Asmbb *bb)
{
    /* up to 2 registers per memloc, so
     * 2*Maxarg is the maximum number of
     * uses or defs we can see */
    long   u[2*Maxarg], d[2*Maxarg];
    size_t nu, nd;
    size_t i, j;

    bb->use = bsclear(bb->use);
    bb->def = bsclear(bb->def);
    for (i = 0; i < bb->ni; i++) {
        nu = uses(bb->il[i], u);
        nd = defs(bb->il[i], d);
        for (j = 0; j < nu; j++)
            if (!bshas(bb->def, u[j]))
                bsput(bb->use, u[j]);
        for (j = 0; j < nd; j++)
            bsput(bb->def, d[j]);
    }
}

static void liveness(Isel *s)
{
    Bitset *old;
    Asmbb **bb;
    size_t nbb;
    size_t i, j;
    int changed;

    bb = s->bb;
    nbb = s->nbb;
    for (i = 0; i < nbb; i++) {
        udcalc(s->bb[i]);
        bb[i]->livein = bsclear(bb[i]->livein);
        bb[i]->liveout = bsclear(bb[i]->liveout);
    }

    changed = 1;
    while (changed) {
        changed = 0;
        for (i = 0; i < nbb; i++) {
            old = bsdup(bb[i]->liveout);
            /* liveout[b] = U(s in succ) livein[s] */
            for (j = 0; bsiter(bb[i]->succ, &j); j++)
                bsunion(bb[i]->liveout, bb[j]->livein);
            /* livein[b] = use[b] U (out[b] \ def[b]) */
            bb[i]->livein = bsclear(bb[i]->livein);
            bsunion(bb[i]->livein, bb[i]->liveout);
            bsdiff(bb[i]->livein, bb[i]->def);
            bsunion(bb[i]->livein, bb[i]->use);
            if (!changed)
                changed = !bseq(old, bb[i]->liveout);
        }
    }
}

/* we're only interested in register->register moves */
static int ismove(Insn *i)
{
    if (i->op != Imov)
        return 0;
    return i->args[0]->type == Locreg && i->args[1]->type == Locreg;
}

static int gbhasedge(Isel *s, size_t u, size_t v)
{
    size_t i;
    i = (maxregid * v) + u;
    return (s->gbits[i/Sizetbits] & (1ULL <<(i % Sizetbits))) != 0;
}

static void gbputedge(Isel *s, size_t u, size_t v)
{
    size_t i, j;

    i = (maxregid * u) + v;
    j = (maxregid * v) + u;
    s->gbits[i/Sizetbits] |= 1ULL <<(i % Sizetbits);
    s->gbits[j/Sizetbits] |= 1ULL <<(j % Sizetbits);
    assert(gbhasedge(s, u, v) && gbhasedge(s, v, u));
}

static void addedge(Isel *s, size_t u, size_t v)
{
    if (u == v || gbhasedge(s, u, v))
        return;
    gbputedge(s, u, v);
    gbputedge(s, v, u);
    if (!bshas(s->prepainted, u)) {
        bsput(s->gadj[u], v);
        s->degree[u]++;
    }
    if (!bshas(s->prepainted, v)) {
        bsput(s->gadj[v], u);
        s->degree[v]++;
    }
}

static void setup(Isel *s)
{
    Bitset **gadj;
    size_t gchunks;
    size_t i;

    free(s->gbits);
    gchunks = (maxregid*maxregid)/Sizetbits + 1;
    s->gbits = zalloc(gchunks*sizeof(size_t));
    /* fresh adj list repr. try to avoid reallocating all the bitsets */
    gadj = zalloc(maxregid * sizeof(Bitset*));
    if (s->gadj)
        for (i = 0; i < maxregid; i++)
            gadj[i] = bsclear(s->gadj[i]);
    else
        for (i = 0; i < maxregid; i++)
            gadj[i] = mkbs();
    free(s->gadj);
    s->gadj = gadj;

    s->spilled = bsclear(s->spilled);
    s->coalesced = bsclear(s->coalesced);
    /*
    s->wlspill = bsclear(s->wlspill);
    s->wlfreeze = bsclear(s->wlfreeze);
    s->wlsimp = bsclear(s->wlsimp);
    */

    s->aliasmap = zalloc(maxregid * sizeof(size_t));
    s->degree = zalloc(maxregid * sizeof(int));
    s->rmoves = zalloc(maxregid * sizeof(Loc **));
    s->nrmoves = zalloc(maxregid * sizeof(size_t));
}

static void build(Isel *s)
{
    long u[2*Maxarg], d[2*Maxarg];
    size_t nu, nd;
    size_t i, k;
    ssize_t j;
    Bitset *live;
    Asmbb **bb;
    size_t nbb;
    Insn *insn;
    size_t l;

    /* set up convenience vars */
    bb = s->bb;
    nbb = s->nbb;

    for (i = 0; i < nbb; i++) {
        live = bsdup(bb[i]->liveout);
        for (j = bb[i]->ni - 1; j >= 0; j--) {
            insn = bb[i]->il[j];
            nu = uses(insn, u);
            nd = defs(insn, d);

            /* moves get special treatment, since we don't want spurious
             * edges between the src and dest */
            if (ismove(insn)) {
                /* live \= uses(i) */
                for (k = 0; k < nu; k++)
                    bsdel(live, u[k]);

                for (k = 0; k < nu; k++)
                    lappend(&s->rmoves[u[k]], &s->nrmoves[u[k]], insn);
                for (k = 0; k < nd; k++)
                    lappend(&s->rmoves[d[k]], &s->nrmoves[d[k]], insn);
                lappend(&s->wlmove, &s->nwlmove, insn);
            }
            /* live = live U def(i) */
            for (k = 0; k < nd; k++)
                bsput(live, d[k]);

            for (k = 0; k < nd; k++)
                for (l = 0; bsiter(live, &l); l++)
                    addedge(s, d[k], l);
            /* live = use(i) U (live \ def(i)) */
            for (k = 0; k < nd; k++)
                bsdel(live, d[k]);
            for (k = 0; k < nu; k++)
                bsput(live, u[k]);
        }
    }
}

static int adjiter(Isel *s, regid n, regid *m)
{
    size_t i, r;

    for (r = *m; bsiter(s->gadj[n], &r); r++) {
        for (i = 0; i < s->nselstk; i++)
            if (r == s->selstk[i]->reg.id)
                goto next;
        if (bshas(s->coalesced, r))
            goto next;
        assert(r < maxregid);
        *m = r;
        return 1;
next:
        continue;
    }
    return 0;
}

static size_t nodemoves(Isel *s, regid n, Insn ***pil)
{
    size_t i, j;
    size_t count;

    /* FIXME: inefficient. Do I care? */
    count = 0;
    if (pil)
        *pil = NULL;
    for (i = 0; i < s->nrmoves[n]; i++) {
        for (j = 0; j < s->nmactive; j++) {
            if (s->mactive[j] == s->rmoves[n][i]) {
                if (pil)
                    lappend(pil, &count, s->rmoves[n][i]);
                continue;
            }
        }
        for (j = 0; j < s->nwlmove; j++) {
            if (s->wlmove[j] == s->rmoves[n][i]) {
                if (pil)
                    lappend(pil, &count, s->rmoves[n][i]);
                continue;
            }
        }
    }
    return count;
}

static int moverelated(Isel *s, regid n)
{
    return nodemoves(s, n, NULL) != 0;
}

static void mkworklist(Isel *s)
{
    size_t i;

    for (i = 0; i < maxregid; i++) {
        if (bshas(s->prepainted, i))
            continue;
        else if (s->degree[i] >= K)
            lappend(&s->wlspill, &s->nwlspill, locmap[i]);
        else if (moverelated(s, i))
            lappend(&s->wlfreeze, &s->nwlfreeze, locmap[i]);
        else
            lappend(&s->wlsimp, &s->nwlsimp, locmap[i]);
    }
}

static void enablemove(Isel *s, regid n)
{
    size_t i, j;
    Insn **il;
    size_t ni;

    ni = nodemoves(s, n, &il);
    for (i = 0; i < ni; i++) {
        for (j = 0; j < s->nmactive; j++) {
            if (il[i] == s->mactive[j]) {
                ldel(&s->mactive, &s->nmactive, j);
                lappend(&s->wlmove, &s->nwlmove, il[i]);
            }
        }
    }
}

static void decdegree(Isel *s, regid n)
{
    int d;
    regid m;

    assert(n < maxregid);
    d = s->degree[n];
    s->degree[n]--;

    if (d == K) {
        enablemove(s, n);
        for (m = 0; adjiter(s, n, &m); m++)
            enablemove(s, n);
    }
}

static void simp(Isel *s)
{
    Loc *l;
    regid m;

    l = lpop(&s->wlsimp, &s->nwlsimp);
    lappend(&s->selstk, &s->nselstk, l);
    for (m = 0; adjiter(s, l->reg.id, &m); m++)
        decdegree(s, m);
}

static regid getalias(Isel *s, regid id)
{
    while (1) {
        if (!s->aliasmap[id])
            break;
        id = s->aliasmap[id]->reg.id;
    };
    return id;
}

static void wladd(Isel *s, regid u)
{
    size_t i;

    if (bshas(s->prepainted, u))
        return;
    if (moverelated(s, u))
        return;
    if (s->degree[u] >= K)
        return;
    for (i = 0; i < s->nwlfreeze; i++)
        if (s->wlfreeze[i]->reg.id == u)
            ldel(&s->wlfreeze, &s->nwlfreeze, i);
    lappend(&s->wlsimp, &s->nwlsimp, locmap[u]);
}

static int conservative(Isel *s, regid u, regid v)
{
    int k;
    regid n;

    k = 0;
    for (n = 0; adjiter(s, u, &n); n++)
        if (s->degree[n] >= K)
            k++;
    for (n = 0; adjiter(s, v, &n); n++)
        if (s->degree[n] >= K)
            k++;
    return k < K;
}

/* FIXME: is this actually correct? */
static int ok(Isel *s, regid t, regid r)
{
    if (s->degree[t] >= K)
        return 0;
    if (!bshas(s->prepainted, t))
        return 0;
    if (!gbhasedge(s, t, r))
        return 0;
    return 1;
}

static int combinable(Isel *s, regid u, regid v)
{
    regid t;

    /* Regs of different modes can't be combined as things stand.
     * In principle they should be combinable, but it confused the
     * whole mode dance. */
    if (locmap[u]->mode != locmap[v]->mode)
        return 0;
    /* if u isn't prepainted, can we conservatively coalesce? */
    if (!bshas(s->prepainted, u) && conservative(s, u, v))
        return 1;

    /* if it is, are the adjacent nodes ok to combine with this? */
    for (t = 0; adjiter(s, u, &t); t++)
        if (!ok(s, t, u))
            return 0;
    return 1;
}

static int wlhas(Loc **wl, size_t nwl, regid v, size_t *idx)
{
    size_t i;

    for (i = 0; i < nwl; i++) {
        if (wl[i]->reg.id == v) { 
            *idx = i;
            return 1;
        }
    }
    return 0;
}

static void combine(Isel *s, regid u, regid v)
{
    regid t;
    size_t idx;
    size_t i, j;
    int has;

    if (debugopt['r'])
        printedge(stdout, "combining:", u, v);
    if (wlhas(s->wlfreeze, s->nwlfreeze, v, &idx))
        ldel(&s->wlfreeze, &s->nwlfreeze, idx);
    else if (wlhas(s->wlspill, s->nwlspill, v, &idx))
        ldel(&s->wlspill, &s->nwlspill, idx);
    bsput(s->coalesced, v);
    s->aliasmap[v] = locmap[u];

    /* nodemoves[u] = nodemoves[u] U nodemoves[v] */
    for (i = 0; i < s->nrmoves[v]; i++) {
        has = 0;
        for (j = 0; j < s->nrmoves[u]; j++) {
            if (s->rmoves[v][i] == s->rmoves[u][j]) {
                has = 1;
                break;
            }
        }
        if (!has)
            lappend(&s->rmoves[u], &s->nrmoves[u], s->rmoves[v][i]);
    }

    for (t = 0; adjiter(s, v, &t); t++) {
        if (debugopt['r'])
            printedge(stdout, "combine-putedge:", v, t);
        addedge(s, t, u);
        decdegree(s, t);
    }
    if (s->degree[u] >= K && wlhas(s->wlfreeze, s->nwlfreeze, u, &idx)) {
        ldel(&s->wlfreeze, &s->nwlfreeze, idx);
        lappend(&s->wlspill, &s->nwlspill, locmap[u]);
    }
}

static void coalesce(Isel *s)
{
    Insn *m;
    regid u, v, tmp;

    m = lpop(&s->wlmove, &s->nwlmove);
    /* FIXME: src, dst? dst, src? Does it matter? */
    u = getalias(s, m->args[0]->reg.id);
    v = getalias(s, m->args[1]->reg.id);

    if (bshas(s->prepainted, v)) {
        tmp = u;
        u = v;
        v = tmp;
    }

    if (u == v) {
        lappend(&s->mcoalesced, &s->nmcoalesced, m);
        wladd(s, u);
        wladd(s, v);
    } else if (bshas(s->prepainted, v) || gbhasedge(s, u, v)) {
        lappend(&s->mconstrained, &s->nmconstrained, m);
        wladd(s, u);
        wladd(s, v);
    } else if (combinable(s, u, v)) {
        lappend(&s->mcoalesced, &s->nmcoalesced, m);
        combine(s, u, v);
        wladd(s, u);
    } else {
        lappend(&s->mactive, &s->nmactive, m);
    }
}

static int mldel(Insn ***ml, size_t *nml, Insn *m)
{
    size_t i;
    for (i = 0; i < *nml; i++) {
        if (m == (*ml)[i]) {
            ldel(ml, nml, i);
            return 1;
        }
    }
    return 0;
}

static void freezemoves(Isel *s, Loc *u)
{
    size_t i;
    Insn **ml;
    Insn *m;
    size_t nml;
    size_t idx;
    Loc *v;
    
    nml = nodemoves(s, u->reg.id, &ml);
    for (i = 0; i < nml; i++) {
        m = ml[i];
        if (m->args[0] == u)
            v = m->args[1];
        else if (m->args[1] == u)
            v = m->args[0];
        else
            continue;

        if (!mldel(&s->mactive, &s->nmactive, m))
            mldel(&s->wlmove, &s->nwlmove, m);
        lappend(&s->mfrozen, &s->nmfrozen, m);
        if (!nodemoves(s, v->reg.id, NULL) && s->degree[v->reg.id] < K) {
            if (!wlhas(s->wlfreeze, s->nwlfreeze, v->reg.id, &idx))
                die("Reg %zd not in freeze wl\n", v->reg.id);
            ldel(&s->wlfreeze, &s->nwlfreeze, idx);
            lappend(&s->wlsimp, &s->nwlsimp, v);
        }

    }
    lfree(&ml, &nml);
}

static void freeze(Isel *s)
{
    Loc *l;

    l = lpop(&s->wlfreeze, &s->nwlfreeze);
    lappend(&s->wlsimp, &s->nwlsimp, l);
    freezemoves(s, l);
}

static void selspill(Isel *s)
{
    size_t i;
    Loc *m;

    /* FIXME: pick a better heuristic for spilling */
    m = NULL;
    for (i = 0; i < s->nwlspill; i++) {
        if (!bshas(s->spilled, s->wlspill[i]->reg.id)) {
            m = s->wlspill[i];
            ldel(&s->wlspill, &s->nwlspill, i);
            break;
        }
    }
    assert(m != NULL);
    lappend(&s->wlsimp, &s->nwlsimp, m);
    freezemoves(s, m);
}

static int paint(Isel *s)
{
    int taken[K + 2]; /* esp, ebp aren't "real colours" */
    Loc *n, *w;
    regid l;
    size_t i;
    int spilled;
    int found;

    spilled = 0;
    while (s->nselstk) {
        bzero(taken, K*sizeof(int));
        n = lpop(&s->selstk, &s->nselstk);

        for (l = 0; bsiter(s->gadj[n->reg.id], &l); l++) {
            if (debugopt['r'] > 1)
                printedge(stdout, "paint-edge:", n->reg.id, l);
            w = locmap[getalias(s, l)];
            if (w->reg.colour)
                taken[colourmap[w->reg.colour]] = 1;
        }

        found = 0;
        for (i = 0; i < K; i++) {
            if (!taken[i]) {
                if (debugopt['r']) {
                    fprintf(stdout, "\tselecting ");
                    locprint(stdout, n, 'x');
                    fprintf(stdout, " = %s\n", regnames[regmap[i][n->mode]]);
                }
                n->reg.colour = regmap[i][n->mode];
                found = 1;
                break;
            }
        }
        if (!found) {
            spilled = 1;
            bsput(s->spilled, n->reg.id);
        }
    }
    for (l = 0; bsiter(s->coalesced, &l); l++) {
        n = locmap[getalias(s, l)];
        locmap[l]->reg.colour = n->reg.colour;
    }
    return spilled;
}

static void rewrite(Isel *s)
{
    die("Rewrite spills!");
}

void regalloc(Isel *s)
{
    size_t i;
    int spilled;

    s->prepainted = mkbs();
    for (i = 0; i < maxregid; i++)
        if (locmap[i]->reg.colour)
            bsput(s->prepainted, i);
    do {
        setup(s);
        liveness(s);
        build(s);
        mkworklist(s);
        if (debugopt['r'])
            dumpasm(s, stdout);
        do {
            if (s->nwlsimp)
                simp(s);
            else if (s->nwlmove)
                coalesce(s);
            else if (s->nwlfreeze)
                freeze(s);
            else if (s->nwlspill)
                selspill(s);
        } while (s->nwlsimp || s->nwlmove || s->nwlfreeze || s->nwlspill);
        spilled = paint(s);
        if (spilled)
            rewrite(s);
    } while (spilled);

}

static void setprint(FILE *fd, Bitset *s)
{
    char *sep;
    size_t i;

    sep = "";
    for (i = 0; i < bsmax(s); i++) {
        if (bshas(s, i)) {
            fprintf(fd, "%s%zd", sep, i);
            sep = ",";
        }
    }
    fprintf(fd, "\n");
}

static void locsetprint(FILE *fd, Bitset *s)
{
    char *sep;
    size_t i;

    sep = "";
    for (i = 0; i < bsmax(s); i++) {
        if (bshas(s, i)) {
            fprintf(fd, "%s", sep);
            locprint(fd, locmap[i], 'x');
            sep = ",";
        }
    }
    fprintf(fd, "\n");
}

static void printedge(FILE *fd, char *msg, size_t a, size_t b)
{
    fprintf(fd, "\t%s ", msg);
    locprint(fd, locmap[a], 'x');
    fprintf(fd, " -- ");
    locprint(fd, locmap[b], 'x');
    fprintf(fd, "\n");
}

void dumpasm(Isel *s, FILE *fd)
{
    size_t i, j;
    char *sep;
    Asmbb *bb;

    fprintf(fd, "IGRAPH ----- \n");
    for (i = 0; i < maxregid; i++) {
        for (j = i; j < maxregid; j++) {
            if (gbhasedge(s, i, j))
                printedge(stdout, "", i, j);
        }
    }
    fprintf(fd, "ASM -------- \n");
    for (j = 0; j < s->nbb; j++) {
        bb = s->bb[j];
        fprintf(fd, "\n");
        fprintf(fd, "Bb: %d labels=(", bb->id);
        sep = "";
        for (i = 0; i < bb->nlbls; i++) {;
            fprintf(fd, "%s%s", bb->lbls[i], sep);
            sep = ",";
        }
        fprintf(fd, ")\n");

        fprintf(fd, "Pred: ");
        setprint(fd, bb->pred);
        fprintf(fd, "Succ: ");
        setprint(fd, bb->succ);

        fprintf(fd, "Use: ");
        locsetprint(fd, bb->use);
        fprintf(fd, "Def: ");
        locsetprint(fd, bb->def);
        fprintf(fd, "Livein:  ");
        locsetprint(fd, bb->livein);
        fprintf(fd, "Liveout: ");
        locsetprint(fd, bb->liveout);
        for (i = 0; i < bb->ni; i++)
            iprintf(fd, bb->il[i]);
    }
    fprintf(fd, "ENDASM -------- \n");
}