Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include        "l.h"
#define r0iszero        1

void
span(void)
{
        Prog *p, *q;
        Sym *setext;
        Optab *o;
        int m, bflag;
        long c, otxt;

        if(debug['v'])
                Bprint(&bso, "%5.2f span\n", cputime());
        Bflush(&bso);

        bflag = 0;
        c = INITTEXT;
        otxt = c;
        for(p = firstp; p != P; p = p->link) {
                p->pc = c;
                o = oplook(p);
                m = o->size;
                if(m == 0) {
                        if(p->as == ATEXT) {
                                curtext = p;
                                autosize = p->to.offset + 4;
                                if(p->from3.type == D_CONST) {
                                        if(p->from3.offset & 3)
                                                diag("illegal origin\n%P", p);
                                        if(c > p->from3.offset)
                                                diag("passed origin (#%lux)\n%P", c, p);
                                        else
                                                c = p->from3.offset;
                                        p->pc = c;
                                }
                                if(p->from.sym != S)
                                        p->from.sym->value = c;
                                /* need passes to resolve branches? */
                                if(c-otxt >= (1L<<15))
                                        bflag = c;
                                otxt = c;
                                continue;
                        }
                        if(p->as != ANOP)
                                diag("zero-width instruction\n%P", p);
                        continue;
                }
                c += m;
        }

        /*
         * if any procedure is large enough to
         * generate a large SBRA branch, then
         * generate extra passes putting branches
         * around jmps to fix. this is rare.
         */
        while(bflag) {
                if(debug['v'])
                        Bprint(&bso, "%5.2f span1\n", cputime());
                bflag = 0;
                c = INITTEXT;
                for(p = firstp; p != P; p = p->link) {
                        p->pc = c;
                        o = oplook(p);
                        if((o->type == 16 || o->type == 17) && p->cond) {
                                otxt = p->cond->pc - c;
                                if(otxt < -(1L<<16)+10 || otxt >= (1L<<15)-10) {
                                        q = prg();
                                        q->link = p->link;
                                        p->link = q;
                                        q->as = ABR;
                                        q->to.type = D_BRANCH;
                                        q->cond = p->cond;
                                        p->cond = q;
                                        q = prg();
                                        q->link = p->link;
                                        p->link = q;
                                        q->as = ABR;
                                        q->to.type = D_BRANCH;
                                        q->cond = q->link->link;
                                        addnop(p->link);
                                        addnop(p);
                                        bflag = 1;
                                }
                        }
                        m = o->size;
                        if(m == 0) {
                                if(p->as == ATEXT) {
                                        curtext = p;
                                        autosize = p->to.offset + 4;
                                        if(p->from.sym != S)
                                                p->from.sym->value = c;
                                        continue;
                                }
                                if(p->as != ANOP)
                                        diag("zero-width instruction\n%P", p);
                                continue;
                        }
                        c += m;
                }
        }

        c = rnd(c, 8);

        setext = lookup("etext", 0);
        if(setext != S) {
                setext->value = c;
                textsize = c - INITTEXT;
        }
        if(INITRND)
                INITDAT = rnd(c, INITRND);
        if(debug['v'])
                Bprint(&bso, "tsize = %lux\n", textsize);
        Bflush(&bso);
}
                
void
xdefine(char *p, int t, long v)
{
        Sym *s;

        s = lookup(p, 0);
        if(s->type == 0 || s->type == SXREF) {
                s->type = t;
                s->value = v;
        }
}

long
regoff(Adr *a)
{

        instoffset = 0;
        aclass(a);
        return instoffset;
}

int
aclass(Adr *a)
{
        Sym *s;
        int t;

        switch(a->type) {
        case D_NONE:
                return C_NONE;

        case D_REG:
                return C_REG;

        case D_FREG:
                return C_FREG;

        case D_CREG:
                return C_CREG;

        case D_SPR:
                if(a->offset == D_LR)
                        return C_LR;
                if(a->offset == D_XER)
                        return C_XER;
                if(a->offset == D_CTR)
                        return C_CTR;
                return C_SPR;

        case D_DCR:
                return C_SPR;

        case D_SREG:
                return C_SREG;

        case D_FPSCR:
                return C_FPSCR;

        case D_MSR:
                return C_MSR;

        case D_OREG:
                switch(a->name) {
                case D_EXTERN:
                case D_STATIC:
                        if(a->sym == S)
                                break;
                        t = a->sym->type;
                        if(t == 0 || t == SXREF) {
                                diag("undefined external: %s in %s",
                                        a->sym->name, TNAME);
                                a->sym->type = SDATA;
                        }
                        if(dlm){
                                instoffset = a->sym->value + a->offset;
                                switch(a->sym->type){
                                case STEXT:
                                case SLEAF:
                                case SCONST:
                                case SUNDEF:
                                        break;
                                default:
                                        instoffset += INITDAT;
                                }
                                return C_ADDR;
                        }
                        instoffset = a->sym->value + a->offset - BIG;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SEXT;
                        return C_LEXT;
                case D_AUTO:
                        instoffset = autosize + a->offset;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SAUTO;
                        return C_LAUTO;

                case D_PARAM:
                        instoffset = autosize + a->offset + 4L;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SAUTO;
                        return C_LAUTO;
                case D_NONE:
                        instoffset = a->offset;
                        if(instoffset == 0)
                                return C_ZOREG;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SOREG;
                        return C_LOREG;
                }
                return C_GOK;

        case D_OPT:
                instoffset = a->offset & 31L;
                if(a->name == D_NONE)
                        return C_SCON;
                return C_GOK;

        case D_CONST:
                switch(a->name) {

                case D_NONE:
                        instoffset = a->offset;
                consize:
                        if(instoffset >= 0) {
                                if(r0iszero && instoffset == 0)
                                        return C_ZCON;
                                if(instoffset <= 0x7fff)
                                        return C_SCON;
                                if(instoffset <= 0xffff)
                                        return C_ANDCON;
                                if((instoffset & 0xffff) == 0)
                                        return C_UCON;
                                return C_LCON;
                        }
                        if(instoffset >= -0x8000)
                                return C_ADDCON;
                        if((instoffset & 0xffff) == 0)
                                return C_UCON;
                        return C_LCON;

                case D_EXTERN:
                case D_STATIC:
                        s = a->sym;
                        if(s == S)
                                break;
                        t = s->type;
                        if(t == 0 || t == SXREF) {
                                diag("undefined external: %s in %s",
                                        s->name, TNAME);
                                s->type = SDATA;
                        }
                        if(s->type == STEXT || s->type == SLEAF || s->type == SUNDEF) {
                                instoffset = s->value + a->offset;
                                return C_LCON;
                        }
                        if(s->type == SCONST) {
                                instoffset = s->value + a->offset;
                                if(dlm)
                                        return C_LCON;
                                goto consize;
                        }
                        if(!dlm){
                                instoffset = s->value + a->offset - BIG;
                                if(instoffset >= -BIG && instoffset < BIG && instoffset != 0)
                                        return C_SECON;
                        }
                        instoffset = s->value + a->offset + INITDAT;
                        if(dlm)
                                return C_LCON;
                        /* not sure why this barfs */
                        return C_LCON;
                /*
                        if(instoffset == 0)
                                return C_ZCON;
                        if(instoffset >= -0x8000 && instoffset <= 0xffff)
                                return C_SCON;
                        if((instoffset & 0xffff) == 0)
                                return C_UCON;
                        return C_LCON;
                */

                case D_AUTO:
                        instoffset = autosize + a->offset;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SACON;
                        return C_LACON;

                case D_PARAM:
                        instoffset = autosize + a->offset + 4L;
                        if(instoffset >= -BIG && instoffset < BIG)
                                return C_SACON;
                        return C_LACON;
                }
                return C_GOK;

        case D_BRANCH:
                return C_SBRA;
        }
        return C_GOK;
}

Optab*
oplook(Prog *p)
{
        int a1, a2, a3, a4, r;
        char *c1, *c3, *c4;
        Optab *o, *e;

        a1 = p->optab;
        if(a1)
                return optab+(a1-1);
        a1 = p->from.class;
        if(a1 == 0) {
                a1 = aclass(&p->from) + 1;
                p->from.class = a1;
        }
        a1--;
        a3 = p->from3.class;
        if(a3 == 0) {
                a3 = aclass(&p->from3) + 1;
                p->from3.class = a3;
        }
        a3--;
        a4 = p->to.class;
        if(a4 == 0) {
                a4 = aclass(&p->to) + 1;
                p->to.class = a4;
        }
        a4--;
        a2 = C_NONE;
        if(p->reg != NREG)
                a2 = C_REG;
        r = p->as;
        o = oprange[r].start;
        if(o == 0)
                o = oprange[r].stop; /* just generate an error */
        e = oprange[r].stop;
        c1 = xcmp[a1];
        c3 = xcmp[a3];
        c4 = xcmp[a4];
        for(; o<e; o++)
                if(o->a2 == a2)
                if(c1[o->a1])
                if(c3[o->a3])
                if(c4[o->a4]) {
                        p->optab = (o-optab)+1;
                        return o;
                }
        diag("illegal combination %A %R %R %R %R",
                p->as, a1, a2, a3, a4);
        if(1||!debug['a'])
                prasm(p);
        if(o == 0)
                errorexit();
        return o;
}

int
cmp(int a, int b)
{

        if(a == b)
                return 1;
        switch(a) {
        case C_LCON:
                if(b == C_ZCON || b == C_SCON || b == C_UCON || b == C_ADDCON || b == C_ANDCON)
                        return 1;
                break;
        case C_ADDCON:
                if(b == C_ZCON || b == C_SCON)
                        return 1;
                break;
        case C_ANDCON:
                if(b == C_ZCON || b == C_SCON)
                        return 1;
                break;
        case C_SPR:
                if(b == C_LR || b == C_XER || b == C_CTR)
                        return 1;
                break;
        case C_UCON:
                if(b == C_ZCON)
                        return 1;
                break;
        case C_SCON:
                if(b == C_ZCON)
                        return 1;
                break;
        case C_LACON:
                if(b == C_SACON)
                        return 1;
                break;
        case C_LBRA:
                if(b == C_SBRA)
                        return 1;
                break;
        case C_LEXT:
                if(b == C_SEXT)
                        return 1;
                break;
        case C_LAUTO:
                if(b == C_SAUTO)
                        return 1;
                break;
        case C_REG:
                if(r0iszero && b == C_ZCON)
                        return 1;
                break;
        case C_LOREG:
                if(b == C_ZOREG || b == C_SOREG)
                        return 1;
                break;
        case C_SOREG:
                if(b == C_ZOREG)
                        return 1;
                break;

        case C_ANY:
                return 1;
        }
        return 0;
}

int
ocmp(void *a1, void *a2)
{
        Optab *p1, *p2;
        int n;

        p1 = a1;
        p2 = a2;
        n = p1->as - p2->as;
        if(n)
                return n;
        n = p1->a1 - p2->a1;
        if(n)
                return n;
        n = p1->a2 - p2->a2;
        if(n)
                return n;
        n = p1->a3 - p2->a3;
        if(n)
                return n;
        n = p1->a4 - p2->a4;
        if(n)
                return n;
        return 0;
}

void
buildop(void)
{
        int i, n, r;

        for(i=0; i<C_NCLASS; i++)
                for(n=0; n<C_NCLASS; n++)
                        xcmp[i][n] = cmp(n, i);
        for(n=0; optab[n].as != AXXX; n++)
                ;
        qsort(optab, n, sizeof(optab[0]), ocmp);
        for(i=0; i<n; i++) {
                r = optab[i].as;
                oprange[r].start = optab+i;
                while(optab[i].as == r)
                        i++;
                oprange[r].stop = optab+i;
                i--;
                
                switch(r)
                {
                default:
                        diag("unknown op in build: %A", r);
                        errorexit();
                case ADCBF:     /* unary indexed: op (b+a); op (b) */
                        oprange[ADCBI] = oprange[r];
                        oprange[ADCBST] = oprange[r];
                        oprange[ADCBT] = oprange[r];
                        oprange[ADCBTST] = oprange[r];
                        oprange[ADCBZ] = oprange[r];
                        oprange[AICBI] = oprange[r];
                        break;
                case AECOWX:    /* indexed store: op s,(b+a); op s,(b) */
                        oprange[ASTWCCC] = oprange[r];
                        break;
                case AREM:      /* macro */
                        oprange[AREMCC] = oprange[r];
                        oprange[AREMV] = oprange[r];
                        oprange[AREMVCC] = oprange[r];
                        oprange[AREMU] = oprange[r];
                        oprange[AREMUCC] = oprange[r];
                        oprange[AREMUV] = oprange[r];
                        oprange[AREMUVCC] = oprange[r];
                        break;
                case ADIVW:     /* op Rb[,Ra],Rd */
                        oprange[AMULHW] = oprange[r];
                        oprange[AMULHWCC] = oprange[r];
                        oprange[AMULHWU] = oprange[r];
                        oprange[AMULHWUCC] = oprange[r];
                        oprange[AMULLWCC] = oprange[r];
                        oprange[AMULLWVCC] = oprange[r];
                        oprange[AMULLWV] = oprange[r];
                        oprange[ADIVWCC] = oprange[r];
                        oprange[ADIVWV] = oprange[r];
                        oprange[ADIVWVCC] = oprange[r];
                        oprange[ADIVWU] = oprange[r];
                        oprange[ADIVWUCC] = oprange[r];
                        oprange[ADIVWUV] = oprange[r];
                        oprange[ADIVWUVCC] = oprange[r];
                        oprange[AADDCC] = oprange[r];
                        oprange[AADDCV] = oprange[r];
                        oprange[AADDCVCC] = oprange[r];
                        oprange[AADDV] = oprange[r];
                        oprange[AADDVCC] = oprange[r];
                        oprange[AADDE] = oprange[r];
                        oprange[AADDECC] = oprange[r];
                        oprange[AADDEV] = oprange[r];
                        oprange[AADDEVCC] = oprange[r];
                        oprange[ACRAND] = oprange[r];
                        oprange[ACRANDN] = oprange[r];
                        oprange[ACREQV] = oprange[r];
                        oprange[ACRNAND] = oprange[r];
                        oprange[ACRNOR] = oprange[r];
                        oprange[ACROR] = oprange[r];
                        oprange[ACRORN] = oprange[r];
                        oprange[ACRXOR] = oprange[r];
                        oprange[AMULCHW] = oprange[r];
                        oprange[AMULCHWCC] = oprange[r];
                        oprange[AMULCHWU] = oprange[r];
                        oprange[AMULCHWUCC] = oprange[r];
                        oprange[AMULHHW] = oprange[r];
                        oprange[AMULHHWCC] = oprange[r];
                        oprange[AMULHHWU] = oprange[r];
                        oprange[AMULHHWUCC] = oprange[r];
                        oprange[AMULLHW] = oprange[r];
                        oprange[AMULLHWCC] = oprange[r];
                        oprange[AMULLHWU] = oprange[r];
                        oprange[AMULLHWUCC] = oprange[r];
                        break;
                case AMACCHW:   /* strictly 3 registers */
                        oprange[AMACCHWCC] = oprange[r];
                        oprange[AMACCHWS] = oprange[r];
                        oprange[AMACCHWSCC] = oprange[r];
                        oprange[AMACCHWSU] = oprange[r];
                        oprange[AMACCHWSUCC] = oprange[r];
                        oprange[AMACCHWSUV] = oprange[r];
                        oprange[AMACCHWSUVCC] = oprange[r];
                        oprange[AMACCHWSV] = oprange[r];
                        oprange[AMACCHWSVCC] = oprange[r];
                        oprange[AMACCHWU] = oprange[r];
                        oprange[AMACCHWUCC] = oprange[r];
                        oprange[AMACCHWUV] = oprange[r];
                        oprange[AMACCHWUVCC] = oprange[r];
                        oprange[AMACCHWV] = oprange[r];
                        oprange[AMACCHWVCC] = oprange[r];
                        oprange[AMACHHW] = oprange[r];
                        oprange[AMACHHWCC] = oprange[r];
                        oprange[AMACHHWS] = oprange[r];
                        oprange[AMACHHWSCC] = oprange[r];
                        oprange[AMACHHWSU] = oprange[r];
                        oprange[AMACHHWSUCC] = oprange[r];
                        oprange[AMACHHWSUV] = oprange[r];
                        oprange[AMACHHWSUVCC] = oprange[r];
                        oprange[AMACHHWSV] = oprange[r];
                        oprange[AMACHHWSVCC] = oprange[r];
                        oprange[AMACHHWU] = oprange[r];
                        oprange[AMACHHWUCC] = oprange[r];
                        oprange[AMACHHWUV] = oprange[r];
                        oprange[AMACHHWUVCC] = oprange[r];
                        oprange[AMACHHWV] = oprange[r];
                        oprange[AMACHHWVCC] = oprange[r];
                        oprange[AMACLHW] = oprange[r];
                        oprange[AMACLHWCC] = oprange[r];
                        oprange[AMACLHWS] = oprange[r];
                        oprange[AMACLHWSCC] = oprange[r];
                        oprange[AMACLHWSU] = oprange[r];
                        oprange[AMACLHWSUCC] = oprange[r];
                        oprange[AMACLHWSUV] = oprange[r];
                        oprange[AMACLHWSUVCC] = oprange[r];
                        oprange[AMACLHWSV] = oprange[r];
                        oprange[AMACLHWSVCC] = oprange[r];
                        oprange[AMACLHWU] = oprange[r];
                        oprange[AMACLHWUCC] = oprange[r];
                        oprange[AMACLHWUV] = oprange[r];
                        oprange[AMACLHWUVCC] = oprange[r];
                        oprange[AMACLHWV] = oprange[r];
                        oprange[AMACLHWVCC] = oprange[r];
                        oprange[ANMACCHW] = oprange[r];
                        oprange[ANMACCHWCC] = oprange[r];
                        oprange[ANMACCHWS] = oprange[r];
                        oprange[ANMACCHWSCC] = oprange[r];
                        oprange[ANMACCHWSV] = oprange[r];
                        oprange[ANMACCHWSVCC] = oprange[r];
                        oprange[ANMACCHWV] = oprange[r];
                        oprange[ANMACCHWVCC] = oprange[r];
                        oprange[ANMACHHW] = oprange[r];
                        oprange[ANMACHHWCC] = oprange[r];
                        oprange[ANMACHHWS] = oprange[r];
                        oprange[ANMACHHWSCC] = oprange[r];
                        oprange[ANMACHHWSV] = oprange[r];
                        oprange[ANMACHHWSVCC] = oprange[r];
                        oprange[ANMACHHWV] = oprange[r];
                        oprange[ANMACHHWVCC] = oprange[r];
                        oprange[ANMACLHW] = oprange[r];
                        oprange[ANMACLHWCC] = oprange[r];
                        oprange[ANMACLHWS] = oprange[r];
                        oprange[ANMACLHWSCC] = oprange[r];
                        oprange[ANMACLHWSV] = oprange[r];
                        oprange[ANMACLHWSVCC] = oprange[r];
                        oprange[ANMACLHWV] = oprange[r];
                        oprange[ANMACLHWVCC] = oprange[r];
                        break;
                case AMOVBZ:    /* lbz, stz, rlwm(r/r), lhz, lha, stz, and x variants */
                        oprange[AMOVH] = oprange[r];
                        oprange[AMOVHZ] = oprange[r];
                        break;
                case AMOVBZU:   /* lbz[x]u, stb[x]u, lhz[x]u, lha[x]u, sth[u]x */
                        oprange[AMOVHU] = oprange[r];
                        oprange[AMOVHZU] = oprange[r];
                        oprange[AMOVWU] = oprange[r];
                        oprange[AMOVMW] = oprange[r];
                        break;
                case AAND:      /* logical op Rb,Rs,Ra; no literal */
                        oprange[AANDN] = oprange[r];
                        oprange[AANDNCC] = oprange[r];
                        oprange[AEQV] = oprange[r];
                        oprange[AEQVCC] = oprange[r];
                        oprange[ANAND] = oprange[r];
                        oprange[ANANDCC] = oprange[r];
                        oprange[ANOR] = oprange[r];
                        oprange[ANORCC] = oprange[r];
                        oprange[AORCC] = oprange[r];
                        oprange[AORN] = oprange[r];
                        oprange[AORNCC] = oprange[r];
                        oprange[AXORCC] = oprange[r];
                        break;
                case AADDME:    /* op Ra, Rd */
                        oprange[AADDMECC] = oprange[r];
                        oprange[AADDMEV] = oprange[r];
                        oprange[AADDMEVCC] = oprange[r];
                        oprange[AADDZE] = oprange[r];
                        oprange[AADDZECC] = oprange[r];
                        oprange[AADDZEV] = oprange[r];
                        oprange[AADDZEVCC] = oprange[r];
                        oprange[ASUBME] = oprange[r];
                        oprange[ASUBMECC] = oprange[r];
                        oprange[ASUBMEV] = oprange[r];
                        oprange[ASUBMEVCC] = oprange[r];
                        oprange[ASUBZE] = oprange[r];
                        oprange[ASUBZECC] = oprange[r];
                        oprange[ASUBZEV] = oprange[r];
                        oprange[ASUBZEVCC] = oprange[r];
                        break;
                case AADDC:
                        oprange[AADDCCC] = oprange[r];
                        break;
                case ABEQ:
                        oprange[ABGE] = oprange[r];
                        oprange[ABGT] = oprange[r];
                        oprange[ABLE] = oprange[r];
                        oprange[ABLT] = oprange[r];
                        oprange[ABNE] = oprange[r];
                        oprange[ABVC] = oprange[r];
                        oprange[ABVS] = oprange[r];
                        break;
                case ABR:
                        oprange[ABL] = oprange[r];
                        break;
                case ABC:
                        oprange[ABCL] = oprange[r];
                        break;
                case AEXTSB:    /* op Rs, Ra */
                        oprange[AEXTSBCC] = oprange[r];
                        oprange[AEXTSH] = oprange[r];
                        oprange[AEXTSHCC] = oprange[r];
                        oprange[ACNTLZW] = oprange[r];
                        oprange[ACNTLZWCC] = oprange[r];
                        break;
                case AFABS:     /* fop [s,]d */
                        oprange[AFABSCC] = oprange[r];
                        oprange[AFNABS] = oprange[r];
                        oprange[AFNABSCC] = oprange[r];
                        oprange[AFNEG] = oprange[r];
                        oprange[AFNEGCC] = oprange[r];
                        oprange[AFRSP] = oprange[r];
                        oprange[AFRSPCC] = oprange[r];
                        oprange[AFCTIW] = oprange[r];
                        oprange[AFCTIWCC] = oprange[r];
                        oprange[AFCTIWZ] = oprange[r];
                        oprange[AFCTIWZCC] = oprange[r];
                        oprange[AFRES] = oprange[r];
                        oprange[AFRESCC] = oprange[r];
                        oprange[AFRSQRTE] = oprange[r];
                        oprange[AFRSQRTECC] = oprange[r];
                        oprange[AFSQRT] = oprange[r];
                        oprange[AFSQRTCC] = oprange[r];
                        oprange[AFSQRTS] = oprange[r];
                        oprange[AFSQRTSCC] = oprange[r];
                        oprange[AFPRE] = oprange[r];
                        oprange[AFPRSQRTE] = oprange[r];
                        oprange[AFPABS] = oprange[r];
                        oprange[AFPNEG] = oprange[r];
                        oprange[AFPRSP] = oprange[r];
                        oprange[AFPNABS] = oprange[r];
                        oprange[AFSABS] = oprange[r];
                        oprange[AFSNEG] = oprange[r];
                        oprange[AFSNABS] = oprange[r];
                        oprange[AFPCTIW] = oprange[r];
                        oprange[AFPCTIWZ] = oprange[r];
                        break;
                case AFADD:
                        oprange[AFADDS] = oprange[r];
                        oprange[AFADDCC] = oprange[r];
                        oprange[AFADDSCC] = oprange[r];
                        oprange[AFDIV] = oprange[r];
                        oprange[AFDIVS] = oprange[r];
                        oprange[AFDIVCC] = oprange[r];
                        oprange[AFDIVSCC] = oprange[r];
                        oprange[AFSUB] = oprange[r];
                        oprange[AFSUBS] = oprange[r];
                        oprange[AFSUBCC] = oprange[r];
                        oprange[AFSUBSCC] = oprange[r];
                        oprange[AFPADD] = oprange[r];
                        oprange[AFPSUB] = oprange[r];
                        break;
                case AFMADD:
                        oprange[AFMADDCC] = oprange[r];
                        oprange[AFMADDS] = oprange[r];
                        oprange[AFMADDSCC] = oprange[r];
                        oprange[AFMSUB] = oprange[r];
                        oprange[AFMSUBCC] = oprange[r];
                        oprange[AFMSUBS] = oprange[r];
                        oprange[AFMSUBSCC] = oprange[r];
                        oprange[AFNMADD] = oprange[r];
                        oprange[AFNMADDCC] = oprange[r];
                        oprange[AFNMADDS] = oprange[r];
                        oprange[AFNMADDSCC] = oprange[r];
                        oprange[AFNMSUB] = oprange[r];
                        oprange[AFNMSUBCC] = oprange[r];
                        oprange[AFNMSUBS] = oprange[r];
                        oprange[AFNMSUBSCC] = oprange[r];
                        oprange[AFSEL] = oprange[r];
                        oprange[AFSELCC] = oprange[r];
                        oprange[AFPSEL] = oprange[r];
                        oprange[AFPMADD] = oprange[r];
                        oprange[AFXMADD] = oprange[r];
                        oprange[AFXCPMADD] = oprange[r];
                        oprange[AFXCSMADD] = oprange[r];
                        oprange[AFPNMADD] = oprange[r];
                        oprange[AFXNMADD] = oprange[r];
                        oprange[AFXCPNMADD] = oprange[r];
                        oprange[AFXCSNMADD] = oprange[r];
                        oprange[AFPMSUB] = oprange[r];
                        oprange[AFXMSUB] = oprange[r];
                        oprange[AFXCPMSUB] = oprange[r];
                        oprange[AFXCSMSUB] = oprange[r];
                        oprange[AFPNMSUB] = oprange[r];
                        oprange[AFXNMSUB] = oprange[r];
                        oprange[AFXCPNMSUB] = oprange[r];
                        oprange[AFXCSNMSUB] = oprange[r];
                        oprange[AFXCPNPMA] = oprange[r];
                        oprange[AFXCSNPMA] = oprange[r];
                        oprange[AFXCPNSMA] = oprange[r];
                        oprange[AFXCSNSMA] = oprange[r];
                        oprange[AFXCXNPMA] = oprange[r];
                        oprange[AFXCXNSMA] = oprange[r];
                        oprange[AFXCXMA] = oprange[r];
                        oprange[AFXCXNMS] = oprange[r];
                        break;
                case AFMUL:
                        oprange[AFMULS] = oprange[r];
                        oprange[AFMULCC] = oprange[r];
                        oprange[AFMULSCC] = oprange[r];
                        oprange[AFPMUL] = oprange[r];
                        oprange[AFXMUL] = oprange[r];
                        oprange[AFXPMUL] = oprange[r];
                        oprange[AFXSMUL] = oprange[r];
                        break;
                case AFCMPO:
                        oprange[AFCMPU] = oprange[r];
                        break;
                case AMTFSB0:
                        oprange[AMTFSB0CC] = oprange[r];
                        oprange[AMTFSB1] = oprange[r];
                        oprange[AMTFSB1CC] = oprange[r];
                        break;
                case ANEG:      /* op [Ra,] Rd */
                        oprange[ANEGCC] = oprange[r];
                        oprange[ANEGV] = oprange[r];
                        oprange[ANEGVCC] = oprange[r];
                        break;
                case AOR:       /* or/xor Rb,Rs,Ra; ori/xori $uimm,Rs,Ra; oris/xoris $uimm,Rs,Ra */
                        oprange[AXOR] = oprange[r];
                        break;
                case ASLW:
                        oprange[ASLWCC] = oprange[r];
                        oprange[ASRW] = oprange[r];
                        oprange[ASRWCC] = oprange[r];
                        break;
                case ASRAW:     /* sraw Rb,Rs,Ra; srawi sh,Rs,Ra */
                        oprange[ASRAWCC] = oprange[r];
                        break;
                case ASUB:      /* SUB Ra,Rb,Rd => subf Rd,ra,rb */
                        oprange[ASUB] = oprange[r];
                        oprange[ASUBCC] = oprange[r];
                        oprange[ASUBV] = oprange[r];
                        oprange[ASUBVCC] = oprange[r];
                        oprange[ASUBCCC] = oprange[r];
                        oprange[ASUBCV] = oprange[r];
                        oprange[ASUBCVCC] = oprange[r];
                        oprange[ASUBE] = oprange[r];
                        oprange[ASUBECC] = oprange[r];
                        oprange[ASUBEV] = oprange[r];
                        oprange[ASUBEVCC] = oprange[r];
                        break;
                case ASYNC:
                        oprange[AISYNC] = oprange[r];
                        break;
                case ARLWMI:
                        oprange[ARLWMICC] = oprange[r];
                        oprange[ARLWNM] = oprange[r];
                        oprange[ARLWNMCC] = oprange[r];
                        break;
                case AFMOVD:
                        oprange[AFMOVDCC] = oprange[r];
                        oprange[AFMOVDU] = oprange[r];
                        oprange[AFMOVS] = oprange[r];
                        oprange[AFMOVSU] = oprange[r];
                        break;
                case AECIWX:
                        oprange[ALWAR] = oprange[r];
                        break;
                case ASYSCALL:  /* just the op; flow of control */
                        oprange[ARFI] = oprange[r];
                        oprange[ARFCI] = oprange[r];
                        break;
                case AMOVHBR:
                        oprange[AMOVWBR] = oprange[r];
                        break;
                case AFSMOVS:   /* indexed floating loads and stores (fp2) */
                        oprange[AFSMOVSU] = oprange[r];
                        oprange[AFSMOVDU] = oprange[r];
                        oprange[AFXMOVS] = oprange[r];
                        oprange[AFXMOVSU] = oprange[r];
                        oprange[AFXMOVDU] = oprange[r];
                        oprange[AFPMOVS] = oprange[r];
                        oprange[AFPMOVSU] = oprange[r];
                        oprange[AFPMOVDU] = oprange[r];
                        oprange[AFPMOVIW] = oprange[r];
                        break;
                case AFPMOVD:   /* indexed load/store and moves (fp2) */
                        oprange[AFSMOVD] = oprange[r];
                        oprange[AFXMOVD] = oprange[r];
                        break;
                case AFMOVSPD:  /* move between fp reg sets (fp2) */
                        oprange[AFMOVPSD] = oprange[r];
                        break;
                case AADD:
                case AANDCC:    /* and. Rb,Rs,Ra; andi. $uimm,Rs,Ra; andis. $uimm,Rs,Ra */
                case ACMP:
                case ACMPU:
                case AEIEIO:
                case ALSW:
                case AMOVB:     /* macro: move byte with sign extension */
                case AMOVBU:    /* macro: move byte with sign extension & update */
                case AMOVW:
                case AMOVFL:
                case AMULLW:    /* op $s[,r2],r3; op r1[,r2],r3; no cc/v */
                case ASUBC:     /* op r1,$s,r3; op r1[,r2],r3 */
                case ASTSW:
                case ATLBIE:
                case ATW:
                case AWORD:
                case ANOP:
                case ATEXT:
                        break;
                }
        }
}

enum{
        ABSD = 0,
        ABSU = 1,
        RELD = 2,
        RELU = 3,
};

int modemap[8] = { 0, 1, -1, 2, 3, 4, 5, 6};

typedef struct Reloc Reloc;

struct Reloc
{
        int n;
        int t;
        uchar *m;
        ulong *a;
};

Reloc rels;

static void
grow(Reloc *r)
{
        int t;
        uchar *m, *nm;
        ulong *a, *na;

        t = r->t;
        r->t += 64;
        m = r->m;
        a = r->a;
        r->m = nm = malloc(r->t*sizeof(uchar));
        r->a = na = malloc(r->t*sizeof(ulong));
        memmove(nm, m, t*sizeof(uchar));
        memmove(na, a, t*sizeof(ulong));
        free(m);
        free(a);
}

void
dynreloc(Sym *s, long v, int abs, int split, int sext)
{
        int i, k, n;
        uchar *m;
        ulong *a;
        Reloc *r;

        if(v&3)
                diag("bad relocation address");
        v >>= 2;
        if(s->type == SUNDEF)
                k = abs ? ABSU : RELU;
        else
                k = abs ? ABSD : RELD;
        if(split)
                k += 4;
        if(sext)
                k += 2;
        /* Bprint(&bso, "R %s a=%ld(%lx) %d\n", s->name, a, a, k); */
        k = modemap[k];
        r = &rels;
        n = r->n;
        if(n >= r->t)
                grow(r);
        m = r->m;
        a = r->a;
        for(i = n; i > 0; i--){
                if(v < a[i-1]){ /* happens occasionally for data */
                        m[i] = m[i-1];
                        a[i] = a[i-1];
                }
                else
                        break;
        }
        m[i] = k;
        a[i] = v;
        r->n++;
}

static int
sput(char *s)
{
        char *p;

        p = s;
        while(*s)
                cput(*s++);
        cput(0);
        return s-p+1;
}

void
asmdyn()
{
        int i, n, t, c;
        Sym *s;
        ulong la, ra, *a;
        vlong off;
        uchar *m;
        Reloc *r;

        cflush();
        off = seek(cout, 0, 1);
        lput(0);
        t = 0;
        lput(imports);
        t += 4;
        for(i = 0; i < NHASH; i++)
                for(s = hash[i]; s != S; s = s->link)
                        if(s->type == SUNDEF){
                                lput(s->sig);
                                t += 4;
                                t += sput(s->name);
                        }
        
        la = 0;
        r = &rels;
        n = r->n;
        m = r->m;
        a = r->a;
        lput(n);
        t += 4;
        for(i = 0; i < n; i++){
                ra = *a-la;
                if(*a < la)
                        diag("bad relocation order");
                if(ra < 256)
                        c = 0;
                else if(ra < 65536)
                        c = 1;
                else
                        c = 2;
                cput((c<<6)|*m++);
                t++;
                if(c == 0){
                        cput(ra);
                        t++;
                }
                else if(c == 1){
                        wput(ra);
                        t += 2;
                }
                else{
                        lput(ra);
                        t += 4;
                }
                la = *a++;
        }

        cflush();
        seek(cout, off, 0);
        lput(t);

        if(debug['v']){
                Bprint(&bso, "import table entries = %d\n", imports);
                Bprint(&bso, "export table entries = %d\n", exports);
        }
}