Subversion Repositories planix.SVN

Rev

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

#include "gc.h"

void
zeroregm(Node *n)
{
        gins(AMOVL, nodconst(0), n);
}

/* do we need to load the address of a vlong? */
int
vaddr(Node *n, int a)
{
        switch(n->op) {
        case ONAME:
                if(a)
                        return 1;
                return !(n->class == CEXTERN || n->class == CGLOBL || n->class == CSTATIC);

        case OCONST:
        case OREGISTER:
        case OINDREG:
                return 1;
        }
        return 0;
}

long
hi64v(Node *n)
{
        if(align(0, types[TCHAR], Aarg1))       /* isbigendian */
                return (long)(n->vconst) & ~0L;
        else
                return (long)((uvlong)n->vconst>>32) & ~0L;
}

long
lo64v(Node *n)
{
        if(align(0, types[TCHAR], Aarg1))       /* isbigendian */
                return (long)((uvlong)n->vconst>>32) & ~0L;
        else
                return (long)(n->vconst) & ~0L;
}

Node *
hi64(Node *n)
{
        return nodconst(hi64v(n));
}

Node *
lo64(Node *n)
{
        return nodconst(lo64v(n));
}

static Node *
anonreg(void)
{
        Node *n;

        n = new(OREGISTER, Z, Z);
        n->reg = D_NONE;
        n->type = types[TLONG];
        return n;
}

static Node *
regpair(Node *n, Node *t)
{
        Node *r;

        if(n != Z && n->op == OREGPAIR)
                return n;
        r = new(OREGPAIR, anonreg(), anonreg());
        if(n != Z)
                r->type = n->type;
        else
                r->type = t->type;
        return r;
}

static void
evacaxdx(Node *r)
{
        Node nod1, nod2;

        if(r->reg == D_AX || r->reg == D_DX) {
                reg[D_AX]++;
                reg[D_DX]++;
                /*
                 * this is just an optim that should
                 * check for spill
                 */
                r->type = types[TULONG];
                regalloc(&nod1, r, Z);
                nodreg(&nod2, Z, r->reg);
                gins(AMOVL, &nod2, &nod1);
                regfree(r);
                r->reg = nod1.reg;
                reg[D_AX]--;
                reg[D_DX]--;
        }
}

/* lazy instantiation of register pair */
static int
instpair(Node *n, Node *l)
{
        int r;

        r = 0;
        if(n->left->reg == D_NONE) {
                if(l != Z) {
                        n->left->reg = l->reg;
                        r = 1;
                }
                else
                        regalloc(n->left, n->left, Z);
        }
        if(n->right->reg == D_NONE)
                regalloc(n->right, n->right, Z);
        return r;
}

static void
zapreg(Node *n)
{
        if(n->reg != D_NONE) {
                //prtree(n, "zapreg");
                regfree(n);
                n->reg = D_NONE;
        }
}

static void
freepair(Node *n)
{
        regfree(n->left);
        regfree(n->right);
}

/* n is not OREGPAIR, nn is */
void
loadpair(Node *n, Node *nn)
{
        Node nod;

        instpair(nn, Z);
        if(n->op == OCONST) {
                gins(AMOVL, lo64(n), nn->left);
                n->xoffset += SZ_LONG;
                gins(AMOVL, hi64(n), nn->right);
                n->xoffset -= SZ_LONG;
                return;
        }
        if(!vaddr(n, 0)) {
                /* steal the right register for the laddr */
                nod = regnode;
                nod.reg = nn->right->reg;
                lcgen(n, &nod);
                n = &nod;
                regind(n, n);
                n->xoffset = 0;
        }
        gins(AMOVL, n, nn->left);
        n->xoffset += SZ_LONG;
        gins(AMOVL, n, nn->right);
        n->xoffset -= SZ_LONG;
}

/* n is OREGPAIR, nn is not */
static void
storepair(Node *n, Node *nn, int f)
{
        Node nod;

        if(!vaddr(nn, 0)) {
                reglcgen(&nod, nn, Z);
                nn = &nod;
        }
        gins(AMOVL, n->left, nn);
        nn->xoffset += SZ_LONG;
        gins(AMOVL, n->right, nn);
        nn->xoffset -= SZ_LONG;
        if(nn == &nod)
                regfree(&nod);
        if(f)
                freepair(n);
}

enum
{
/* 4 only, see WW */
        WNONE   = 0,
        WCONST,
        WADDR,
        WHARD,
};

static int
whatof(Node *n, int a)
{
        if(n->op == OCONST)
                return WCONST;
        return !vaddr(n, a) ? WHARD : WADDR;
}

/* can upgrade an extern to addr for AND */
static int
reduxv(Node *n)
{
        return lo64v(n) == 0 || hi64v(n) == 0;
}

int
cond(int op)
{
        switch(op) {
        case OANDAND:
        case OOROR:
        case ONOT:
                return 1;

        case OEQ:
        case ONE:
        case OLE:
        case OLT:
        case OGE:
        case OGT:
        case OHI:
        case OHS:
        case OLO:
        case OLS:
                return 1;
        }
        return 0;
}

/*
 * for a func operand call it and then return
 * the safe node
 */
static Node *
vfunc(Node *n, Node *nn)
{
        Node *t;

        if(n->op != OFUNC)
                return n;
        t = new(0, Z, Z);
        if(nn == Z || nn == nodret)
                nn = n;
        regsalloc(t, nn);
        sugen(n, t, 8);
        return t;
}

/* try to steal a reg */
static int
getreg(Node **np, Node *t, int r)
{
        Node *n, *p;

        n = *np;
        if(n->reg == r) {
                p = new(0, Z, Z);
                regalloc(p, n, Z);
                gins(AMOVL, n, p);
                *t = *n;
                *np = p;
                return 1;
        }
        return 0;
}

static Node *
snarfreg(Node *n, Node *t, int r, Node *d, Node *c)
{
        if(n == Z || n->op != OREGPAIR || (!getreg(&n->left, t, r) && !getreg(&n->right, t, r))) {
                if(nodreg(t, Z, r)) {
                        regalloc(c, d, Z);
                        gins(AMOVL, t, c);
                        reg[r]++;
                        return c;
                }
                reg[r]++;
        }
        return Z;
}

enum
{
        Vstart  = OEND,

        Vgo,
        Vamv,
        Vmv,
        Vzero,
        Vop,
        Vopx,
        Vins,
        Vins0,
        Vinsl,
        Vinsr,
        Vinsla,
        Vinsra,
        Vinsx,
        Vmul,
        Vshll,
        VT,
        VF,
        V_l_lo_f,
        V_l_hi_f,
        V_l_lo_t,
        V_l_hi_t,
        V_l_lo_u,
        V_l_hi_u,
        V_r_lo_f,
        V_r_hi_f,
        V_r_lo_t,
        V_r_hi_t,
        V_r_lo_u,
        V_r_hi_u,
        Vspazz,
        Vend,

        V_T0,
        V_T1,
        V_F0,
        V_F1,

        V_a0,
        V_a1,
        V_f0,
        V_f1,

        V_p0,
        V_p1,
        V_p2,
        V_p3,
        V_p4,

        V_s0,
        V_s1,
        V_s2,
        V_s3,
        V_s4,

        C00,
        C01,
        C31,
        C32,

        O_l_lo,
        O_l_hi,
        O_r_lo,
        O_r_hi,
        O_t_lo,
        O_t_hi,
        O_l,
        O_r,
        O_l_rp,
        O_r_rp,
        O_t_rp,
        O_r0,
        O_r1,
        O_Zop,

        O_a0,
        O_a1,

        V_C0,
        V_C1,

        V_S0,
        V_S1,

        VOPS    = 5,
        VLEN    = 5,
        VARGS   = 2,

        S00     = 0,
        Sc0,
        Sc1,
        Sc2,
        Sac3,
        Sac4,
        S10,

        SAgen   = 0,
        SAclo,
        SAc32,
        SAchi,
        SAdgen,
        SAdclo,
        SAdc32,
        SAdchi,

        B0c     = 0,
        Bca,
        Bac,

        T0i     = 0,
        Tii,

        Bop0    = 0,
        Bop1,
};

/*
 * _testv:
 *      CMPL    lo,$0
 *      JNE     true
 *      CMPL    hi,$0
 *      JNE     true
 *      GOTO    false
 * false:
 *      GOTO    code
 * true:
 *      GOTO    patchme
 * code:
 */

static uchar    testi[][VLEN] =
{
        {Vop, ONE, O_l_lo, C00},
        {V_s0, Vop, ONE, O_l_hi, C00},
        {V_s1, Vgo, V_s2, Vgo, V_s3},
        {VF, V_p0, V_p1, VT, V_p2},
        {Vgo, V_p3},
        {VT, V_p0, V_p1, VF, V_p2},
        {Vend},
};

/* shift left general case */
static uchar    shll00[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vinsl, ASHLL, O_r, O_l_rp},
        {Vins, ASHLL, O_r, O_l_lo, Vgo},
        {V_p0, V_s0},
        {Vins, ASHLL, O_r, O_l_lo},
        {Vins, AMOVL, O_l_lo, O_l_hi},
        {Vzero, O_l_lo, V_p0, Vend},
};

/* shift left rp, const < 32 */
static uchar    shllc0[][VLEN] =
{
        {Vinsl, ASHLL, O_r, O_l_rp},
        {Vshll, O_r, O_l_lo, Vend},
};

/* shift left rp, const == 32 */
static uchar    shllc1[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_l_hi},
        {Vzero, O_l_lo, Vend},
};

/* shift left rp, const > 32 */
static uchar    shllc2[][VLEN] =
{
        {Vshll, O_r, O_l_lo},
        {Vins, AMOVL, O_l_lo, O_l_hi},
        {Vzero, O_l_lo, Vend},
};

/* shift left addr, const == 32 */
static uchar    shllac3[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {Vzero, O_t_lo, Vend},
};

/* shift left addr, const > 32 */
static uchar    shllac4[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {Vshll, O_r, O_t_hi},
        {Vzero, O_t_lo, Vend},
};

/* shift left of constant */
static uchar    shll10[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsl, ASHLL, O_r, O_t_rp},
        {Vins, ASHLL, O_r, O_t_lo, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {V_l_lo_t, Vins, ASHLL, O_r, O_t_hi},
        {Vzero, O_t_lo, V_p0, Vend},
};

static uchar    (*shlltab[])[VLEN] =
{
        shll00,
        shllc0,
        shllc1,
        shllc2,
        shllac3,
        shllac4,
        shll10,
};

/* shift right general case */
static uchar    shrl00[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vinsr, ASHRL, O_r, O_l_rp},
        {Vins, O_a0, O_r, O_l_hi, Vgo},
        {V_p0, V_s0},
        {Vins, O_a0, O_r, O_l_hi},
        {Vins, AMOVL, O_l_hi, O_l_lo},
        {V_T1, Vzero, O_l_hi},
        {V_F1, Vins, ASARL, C31, O_l_hi},
        {V_p0, Vend},
};

/* shift right rp, const < 32 */
static uchar    shrlc0[][VLEN] =
{
        {Vinsr, ASHRL, O_r, O_l_rp},
        {Vins, O_a0, O_r, O_l_hi, Vend},
};

/* shift right rp, const == 32 */
static uchar    shrlc1[][VLEN] =
{
        {Vins, AMOVL, O_l_hi, O_l_lo},
        {V_T1, Vzero, O_l_hi},
        {V_F1, Vins, ASARL, C31, O_l_hi},
        {Vend},
};

/* shift right rp, const > 32 */
static uchar    shrlc2[][VLEN] =
{
        {Vins, O_a0, O_r, O_l_hi},
        {Vins, AMOVL, O_l_hi, O_l_lo},
        {V_T1, Vzero, O_l_hi},
        {V_F1, Vins, ASARL, C31, O_l_hi},
        {Vend},
};

/* shift right addr, const == 32 */
static uchar    shrlac3[][VLEN] =
{
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {V_T1, Vzero, O_t_hi},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {Vend},
};

/* shift right addr, const > 32 */
static uchar    shrlac4[][VLEN] =
{
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {Vins, O_a0, O_r, O_t_lo},
        {V_T1, Vzero, O_t_hi},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {Vend},
};

/* shift right of constant */
static uchar    shrl10[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsr, ASHRL, O_r, O_t_rp},
        {Vins, O_a0, O_r, O_t_hi, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {V_l_hi_t, Vins, O_a0, O_r, O_t_lo},
        {V_l_hi_u, V_S1},
        {V_T1, Vzero, O_t_hi, V_p0},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {Vend},
};

static uchar    (*shrltab[])[VLEN] =
{
        shrl00,
        shrlc0,
        shrlc1,
        shrlc2,
        shrlac3,
        shrlac4,
        shrl10,
};

/* shift asop left general case */
static uchar    asshllgen[][VLEN] =
{
        {V_a0, V_a1},
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_r0},
        {Vins, AMOVL, O_l_hi, O_r1},
        {Vinsla, ASHLL, O_r, O_r0},
        {Vins, ASHLL, O_r, O_r0},
        {Vins, AMOVL, O_r1, O_l_hi},
        {Vins, AMOVL, O_r0, O_l_lo, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vzero, O_l_lo},
        {Vins, ASHLL, O_r, O_r0},
        {Vins, AMOVL, O_r0, O_l_hi, V_p0},
        {V_f0, V_f1, Vend},
};

/* shift asop left, const < 32 */
static uchar    asshllclo[][VLEN] =
{
        {V_a0, V_a1},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vins, AMOVL, O_l_hi, O_r1},
        {Vinsla, ASHLL, O_r, O_r0},
        {Vshll, O_r, O_r0},
        {Vins, AMOVL, O_r1, O_l_hi},
        {Vins, AMOVL, O_r0, O_l_lo},
        {V_f0, V_f1, Vend},
};

/* shift asop left, const == 32 */
static uchar    asshllc32[][VLEN] =
{
        {V_a0},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vzero, O_l_lo},
        {Vins, AMOVL, O_r0, O_l_hi},
        {V_f0, Vend},
};

/* shift asop left, const > 32 */
static uchar    asshllchi[][VLEN] =
{
        {V_a0},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vzero, O_l_lo},
        {Vshll, O_r, O_r0},
        {Vins, AMOVL, O_r0, O_l_hi},
        {V_f0, Vend},
};

/* shift asop dest left general case */
static uchar    asdshllgen[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsl, ASHLL, O_r, O_t_rp},
        {Vins, ASHLL, O_r, O_t_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {Vzero, O_l_lo},
        {Vins, ASHLL, O_r, O_t_hi},
        {Vzero, O_t_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
        {Vend},
};

/* shift asop dest left, const < 32 */
static uchar    asdshllclo[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsl, ASHLL, O_r, O_t_rp},
        {Vshll, O_r, O_t_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vend},
};

/* shift asop dest left, const == 32 */
static uchar    asdshllc32[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {Vzero, O_t_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vend},
};

/* shift asop dest, const > 32 */
static uchar    asdshllchi[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_hi},
        {Vzero, O_t_lo},
        {Vshll, O_r, O_t_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vend},
};

static uchar    (*asshlltab[])[VLEN] =
{
        asshllgen,
        asshllclo,
        asshllc32,
        asshllchi,
        asdshllgen,
        asdshllclo,
        asdshllc32,
        asdshllchi,
};

/* shift asop right general case */
static uchar    asshrlgen[][VLEN] =
{
        {V_a0, V_a1},
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_r0},
        {Vins, AMOVL, O_l_hi, O_r1},
        {Vinsra, ASHRL, O_r, O_r0},
        {Vinsx, Bop0, O_r, O_r1},
        {Vins, AMOVL, O_r0, O_l_lo},
        {Vins, AMOVL, O_r1, O_l_hi, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_hi, O_r0},
        {Vinsx, Bop0, O_r, O_r0},
        {V_T1, Vzero, O_l_hi},
        {Vins, AMOVL, O_r0, O_l_lo},
        {V_F1, Vins, ASARL, C31, O_r0},
        {V_F1, Vins, AMOVL, O_r0, O_l_hi},
        {V_p0, V_f0, V_f1, Vend},
};

/* shift asop right, const < 32 */
static uchar    asshrlclo[][VLEN] =
{
        {V_a0, V_a1},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vins, AMOVL, O_l_hi, O_r1},
        {Vinsra, ASHRL, O_r, O_r0},
        {Vinsx, Bop0, O_r, O_r1},
        {Vins, AMOVL, O_r0, O_l_lo},
        {Vins, AMOVL, O_r1, O_l_hi},
        {V_f0, V_f1, Vend},
};

/* shift asop right, const == 32 */
static uchar    asshrlc32[][VLEN] =
{
        {V_a0},
        {Vins, AMOVL, O_l_hi, O_r0},
        {V_T1, Vzero, O_l_hi},
        {Vins, AMOVL, O_r0, O_l_lo},
        {V_F1, Vins, ASARL, C31, O_r0},
        {V_F1, Vins, AMOVL, O_r0, O_l_hi},
        {V_f0, Vend},
};

/* shift asop right, const > 32 */
static uchar    asshrlchi[][VLEN] =
{
        {V_a0},
        {Vins, AMOVL, O_l_hi, O_r0},
        {V_T1, Vzero, O_l_hi},
        {Vinsx, Bop0, O_r, O_r0},
        {Vins, AMOVL, O_r0, O_l_lo},
        {V_F1, Vins, ASARL, C31, O_r0},
        {V_F1, Vins, AMOVL, O_r0, O_l_hi},
        {V_f0, Vend},
};

/* shift asop dest right general case */
static uchar    asdshrlgen[][VLEN] =
{
        {Vop, OGE, O_r, C32},
        {V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsr, ASHRL, O_r, O_t_rp},
        {Vinsx, Bop0, O_r, O_t_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi, Vgo},
        {V_p0, V_s0},
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {V_T1, Vzero, O_t_hi},
        {Vinsx, Bop0, O_r, O_t_lo},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
        {Vend},
};

/* shift asop dest right, const < 32 */
static uchar    asdshrlclo[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsr, ASHRL, O_r, O_t_rp},
        {Vinsx, Bop0, O_r, O_t_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vend},
};

/* shift asop dest right, const == 32 */
static uchar    asdshrlc32[][VLEN] =
{
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {V_T1, Vzero, O_t_hi},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi},
        {Vend},
};

/* shift asop dest, const > 32 */
static uchar    asdshrlchi[][VLEN] =
{
        {Vins, AMOVL, O_l_hi, O_t_lo},
        {V_T1, Vzero, O_t_hi},
        {Vinsx, Bop0, O_r, O_t_lo},
        {V_T1, Vins, AMOVL, O_t_hi, O_l_hi},
        {V_T1, Vins, AMOVL, O_t_lo, O_l_lo},
        {V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
        {V_F1, Vins, ASARL, C31, O_t_hi},
        {V_F1, Vins, AMOVL, O_t_lo, O_l_lo},
        {V_F1, Vins, AMOVL, O_t_hi, O_l_hi},
        {Vend},
};

static uchar    (*asshrltab[])[VLEN] =
{
        asshrlgen,
        asshrlclo,
        asshrlc32,
        asshrlchi,
        asdshrlgen,
        asdshrlclo,
        asdshrlc32,
        asdshrlchi,
};

static uchar    shrlargs[]      = { ASHRL, 1 };
static uchar    sarlargs[]      = { ASARL, 0 };

/* ++ -- */
static uchar    incdec[][VLEN] =
{
        {Vinsx, Bop0, C01, O_l_lo},
        {Vinsx, Bop1, C00, O_l_hi, Vend},
};

/* ++ -- *p */
static uchar    incdecpre[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsx, Bop0, C01, O_t_lo},
        {Vinsx, Bop1, C00, O_t_hi},
        {Vins, AMOVL, O_t_lo, O_l_lo},
        {Vins, AMOVL, O_t_hi, O_l_hi, Vend},
};

/* *p ++ -- */
static uchar    incdecpost[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsx, Bop0, C01, O_l_lo},
        {Vinsx, Bop1, C00, O_l_hi, Vend},
};

/* binop rp, rp */
static uchar    binop00[][VLEN] =
{
        {Vinsx, Bop0, O_r_lo, O_l_lo},
        {Vinsx, Bop1, O_r_hi, O_l_hi, Vend},
        {Vend},
};

/* binop rp, addr */
static uchar    binoptmp[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_r_lo, O_r0},
        {Vinsx, Bop0, O_r0, O_l_lo},
        {Vins, AMOVL, O_r_hi, O_r0},
        {Vinsx, Bop1, O_r0, O_l_hi},
        {V_f0, Vend},
};

/* binop t = *a op *b */
static uchar    binop11[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vinsx, Bop0, O_r_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsx, Bop1, O_r_hi, O_t_hi, Vend},
};

/* binop t = rp +- c */
static uchar    add0c[][VLEN] =
{
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
        {V_r_lo_f, Vamv, Bop0, Bop1},
        {Vinsx, Bop1, O_r_hi, O_l_hi},
        {Vend},
};

/* binop t = rp & c */
static uchar    and0c[][VLEN] =
{
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
        {V_r_lo_f, Vins, AMOVL, C00, O_l_lo},
        {V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
        {V_r_hi_f, Vins, AMOVL, C00, O_l_hi},
        {Vend},
};

/* binop t = rp | c */
static uchar    or0c[][VLEN] =
{
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
        {V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
        {Vend},
};

/* binop t = c - rp */
static uchar    sub10[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_l_lo, O_r0},
        {Vinsx, Bop0, O_r_lo, O_r0},
        {Vins, AMOVL, O_l_hi, O_r_lo},
        {Vinsx, Bop1, O_r_hi, O_r_lo},
        {Vspazz, V_f0, Vend},
};

/* binop t = c + *b */
static uchar    addca[][VLEN] =
{
        {Vins, AMOVL, O_r_lo, O_t_lo},
        {V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
        {V_l_lo_f, Vamv, Bop0, Bop1},
        {Vins, AMOVL, O_r_hi, O_t_hi},
        {Vinsx, Bop1, O_l_hi, O_t_hi},
        {Vend},
};

/* binop t = c & *b */
static uchar    andca[][VLEN] =
{
        {V_l_lo_t, Vins, AMOVL, O_r_lo, O_t_lo},
        {V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
        {V_l_lo_f, Vzero, O_t_lo},
        {V_l_hi_t, Vins, AMOVL, O_r_hi, O_t_hi},
        {V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
        {V_l_hi_f, Vzero, O_t_hi},
        {Vend},
};

/* binop t = c | *b */
static uchar    orca[][VLEN] =
{
        {Vins, AMOVL, O_r_lo, O_t_lo},
        {V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_r_hi, O_t_hi},
        {V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
        {Vend},
};

/* binop t = c - *b */
static uchar    subca[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsx, Bop0, O_r_lo, O_t_lo},
        {Vinsx, Bop1, O_r_hi, O_t_hi},
        {Vend},
};

/* binop t = *a +- c */
static uchar    addac[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
        {V_r_lo_f, Vamv, Bop0, Bop1},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {Vinsx, Bop1, O_r_hi, O_t_hi},
        {Vend},
};

/* binop t = *a | c */
static uchar    orac[][VLEN] =
{
        {Vins, AMOVL, O_l_lo, O_t_lo},
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
        {Vins, AMOVL, O_l_hi, O_t_hi},
        {V_r_hi_t, Vinsx, Bop1, O_r_hi, O_t_hi},
        {Vend},
};

/* binop t = *a & c */
static uchar    andac[][VLEN] =
{
        {V_r_lo_t, Vins, AMOVL, O_l_lo, O_t_lo},
        {V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
        {V_r_lo_f, Vzero, O_t_lo},
        {V_r_hi_t, Vins, AMOVL, O_l_hi, O_t_hi},
        {V_r_hi_t, Vinsx, Bop0, O_r_hi, O_t_hi},
        {V_r_hi_f, Vzero, O_t_hi},
        {Vend},
};

static uchar    ADDargs[]       = { AADDL, AADCL };
static uchar    ANDargs[]       = { AANDL, AANDL };
static uchar    ORargs[]        = { AORL, AORL };
static uchar    SUBargs[]       = { ASUBL, ASBBL };
static uchar    XORargs[]       = { AXORL, AXORL };

static uchar    (*ADDtab[])[VLEN] =
{
        add0c, addca, addac,
};

static uchar    (*ANDtab[])[VLEN] =
{
        and0c, andca, andac,
};

static uchar    (*ORtab[])[VLEN] =
{
        or0c, orca, orac,
};

static uchar    (*SUBtab[])[VLEN] =
{
        add0c, subca, addac,
};

/* mul of const32 */
static uchar    mulc32[][VLEN] =
{
        {V_a0, Vop, ONE, O_l_hi, C00},
        {V_s0, Vins, AMOVL, O_r_lo, O_r0},
        {Vins, AMULL, O_r0, O_Zop},
        {Vgo, V_p0, V_s0},
        {Vins, AMOVL, O_l_hi, O_r0},
        {Vmul, O_r_lo, O_r0},
        {Vins, AMOVL, O_r_lo, O_l_hi},
        {Vins, AMULL, O_l_hi, O_Zop},
        {Vins, AADDL, O_r0, O_l_hi},
        {V_f0, V_p0, Vend},
};

/* mul of const64 */
static uchar    mulc64[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_r_hi, O_r0},
        {Vop, OOR, O_l_hi, O_r0},
        {Vop, ONE, O_r0, C00},
        {V_s0, Vins, AMOVL, O_r_lo, O_r0},
        {Vins, AMULL, O_r0, O_Zop},
        {Vgo, V_p0, V_s0},
        {Vmul, O_r_lo, O_l_hi},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vmul, O_r_hi, O_r0},
        {Vins, AADDL, O_l_hi, O_r0},
        {Vins, AMOVL, O_r_lo, O_l_hi},
        {Vins, AMULL, O_l_hi, O_Zop},
        {Vins, AADDL, O_r0, O_l_hi},
        {V_f0, V_p0, Vend},
};

/* mul general */
static uchar    mull[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_r_hi, O_r0},
        {Vop, OOR, O_l_hi, O_r0},
        {Vop, ONE, O_r0, C00},
        {V_s0, Vins, AMOVL, O_r_lo, O_r0},
        {Vins, AMULL, O_r0, O_Zop},
        {Vgo, V_p0, V_s0},
        {Vins, AIMULL, O_r_lo, O_l_hi},
        {Vins, AMOVL, O_l_lo, O_r0},
        {Vins, AIMULL, O_r_hi, O_r0},
        {Vins, AADDL, O_l_hi, O_r0},
        {Vins, AMOVL, O_r_lo, O_l_hi},
        {Vins, AMULL, O_l_hi, O_Zop},
        {Vins, AADDL, O_r0, O_l_hi},
        {V_f0, V_p0, Vend},
};

/* cast rp l to rp t */
static uchar    castrp[][VLEN] =
{
        {Vmv, O_l, O_t_lo},
        {VT, Vins, AMOVL, O_t_lo, O_t_hi},
        {VT, Vins, ASARL, C31, O_t_hi},
        {VF, Vzero, O_t_hi},
        {Vend},
};

/* cast rp l to addr t */
static uchar    castrpa[][VLEN] =
{
        {VT, V_a0, Vmv, O_l, O_r0},
        {VT, Vins, AMOVL, O_r0, O_t_lo},
        {VT, Vins, ASARL, C31, O_r0},
        {VT, Vins, AMOVL, O_r0, O_t_hi},
        {VT, V_f0},
        {VF, Vmv, O_l, O_t_lo},
        {VF, Vzero, O_t_hi},
        {Vend},
};

static uchar    netab0i[][VLEN] =
{
        {Vop, ONE, O_l_lo, O_r_lo},
        {V_s0, Vop, ONE, O_l_hi, O_r_hi},
        {V_s1, Vgo, V_s2, Vgo, V_s3},
        {VF, V_p0, V_p1, VT, V_p2},
        {Vgo, V_p3},
        {VT, V_p0, V_p1, VF, V_p2},
        {Vend},
};

static uchar    netabii[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_l_lo, O_r0},
        {Vop, ONE, O_r0, O_r_lo},
        {V_s0, Vins, AMOVL, O_l_hi, O_r0},
        {Vop, ONE, O_r0, O_r_hi},
        {V_s1, Vgo, V_s2, Vgo, V_s3},
        {VF, V_p0, V_p1, VT, V_p2},
        {Vgo, V_p3},
        {VT, V_p0, V_p1, VF, V_p2},
        {V_f0, Vend},
};

static uchar    cmptab0i[][VLEN] =
{
        {Vopx, Bop0, O_l_hi, O_r_hi},
        {V_s0, Vins0, AJNE},
        {V_s1, Vopx, Bop1, O_l_lo, O_r_lo},
        {V_s2, Vgo, V_s3, Vgo, V_s4},
        {VT, V_p1, V_p3},
        {VF, V_p0, V_p2},
        {Vgo, V_p4},
        {VT, V_p0, V_p2},
        {VF, V_p1, V_p3},
        {Vend},
};

static uchar    cmptabii[][VLEN] =
{
        {V_a0, Vins, AMOVL, O_l_hi, O_r0},
        {Vopx, Bop0, O_r0, O_r_hi},
        {V_s0, Vins0, AJNE},
        {V_s1, Vins, AMOVL, O_l_lo, O_r0},
        {Vopx, Bop1, O_r0, O_r_lo},
        {V_s2, Vgo, V_s3, Vgo, V_s4},
        {VT, V_p1, V_p3},
        {VF, V_p0, V_p2},
        {Vgo, V_p4},
        {VT, V_p0, V_p2},
        {VF, V_p1, V_p3},
        {V_f0, Vend},
};

static uchar    (*NEtab[])[VLEN] =
{
        netab0i, netabii,
};

static uchar    (*cmptab[])[VLEN] =
{
        cmptab0i, cmptabii,
};

static uchar    GEargs[]        = { OGT, OHS };
static uchar    GTargs[]        = { OGT, OHI };
static uchar    HIargs[]        = { OHI, OHI };
static uchar    HSargs[]        = { OHI, OHS };

/* Big Generator */
static void
biggen(Node *l, Node *r, Node *t, int true, uchar code[][VLEN], uchar *a)
{
        int i, j, g, oc, op, lo, ro, to, xo, *xp;
        Type *lt;
        Prog *pr[VOPS];
        Node *ot, *tl, *tr, tmps[2];
        uchar *c, (*cp)[VLEN], args[VARGS];

        if(a != nil)
                memmove(args, a, VARGS);
//print("biggen %d %d %d\n", args[0], args[1], args[2]);
//if(l) prtree(l, "l");
//if(r) prtree(r, "r");
//if(t) prtree(t, "t");
        lo = ro = to = 0;
        cp = code;

        for (;;) {
                c = *cp++;
                g = 1;
                i = 0;
//print("code %d %d %d %d %d\n", c[0], c[1], c[2], c[3], c[4]);
                for(;;) {
                        switch(op = c[i]) {
                        case Vgo:
                                if(g)
                                        gbranch(OGOTO);
                                i++;
                                break;

                        case Vamv:
                                i += 3;
                                if(i > VLEN) {
                                        diag(l, "bad Vop");
                                        return;
                                }
                                if(g)
                                        args[c[i - 1]] = args[c[i - 2]];
                                break;

                        case Vzero:
                                i += 2;
                                if(i > VLEN) {
                                        diag(l, "bad Vop");
                                        return;
                                }
                                j = i - 1;
                                goto op;

                        case Vspazz:    // nasty hack to save a reg in SUB
//print("spazz\n");
                                if(g) {
//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
                                        ot = r->right;
                                        r->right = r->left;
                                        tl = new(0, Z, Z);
                                        *tl = tmps[0];
                                        r->left = tl;
                                        tmps[0] = *ot;
//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
                                }
                                i++;
                                break;

                        case Vmv:
                        case Vmul:
                        case Vshll:
                                i += 3;
                                if(i > VLEN) {
                                        diag(l, "bad Vop");
                                        return;
                                }
                                j = i - 2;
                                goto op;

                        case Vins0:
                                i += 2;
                                if(i > VLEN) {
                                        diag(l, "bad Vop");
                                        return;
                                }
                                gins(c[i - 1], Z, Z);
                                break;

                        case Vop:
                        case Vopx:
                        case Vins:
                        case Vinsl:
                        case Vinsr:
                        case Vinsla:
                        case Vinsra:
                        case Vinsx:
                                i += 4;
                                if(i > VLEN) {
                                        diag(l, "bad Vop");
                                        return;
                                }
                                j = i - 2;
                                goto op;

                        op:
                                if(!g)
                                        break;
                                tl = Z;
                                tr = Z;
                                for(; j < i; j++) {
                                        switch(c[j]) {
                                        case C00:
                                                ot = nodconst(0);
                                                break;
                                        case C01:
                                                ot = nodconst(1);
                                                break;
                                        case C31:
                                                ot = nodconst(31);
                                                break;
                                        case C32:
                                                ot = nodconst(32);
                                                break;

                                        case O_l:
                                        case O_l_lo:
                                                ot = l; xp = &lo; xo = 0;
                                                goto op0;
                                        case O_l_hi:
                                                ot = l; xp = &lo; xo = SZ_LONG;
                                                goto op0;
                                        case O_r:
                                        case O_r_lo:
                                                ot = r; xp = &ro; xo = 0;
                                                goto op0;
                                        case O_r_hi:
                                                ot = r; xp = &ro; xo = SZ_LONG;
                                                goto op0;
                                        case O_t_lo:
                                                ot = t; xp = &to; xo = 0;
                                                goto op0;
                                        case O_t_hi:
                                                ot = t; xp = &to; xo = SZ_LONG;
                                                goto op0;
                                        case O_l_rp:
                                                ot = l;
                                                break;
                                        case O_r_rp:
                                                ot = r;
                                                break;
                                        case O_t_rp:
                                                ot = t;
                                                break;
                                        case O_r0:
                                        case O_r1:
                                                ot = &tmps[c[j] - O_r0];
                                                break;
                                        case O_Zop:
                                                ot = Z;
                                                break;

                                        op0:
                                                switch(ot->op) {
                                                case OCONST:
                                                        if(xo)
                                                                ot = hi64(ot);
                                                        else
                                                                ot = lo64(ot);
                                                        break;
                                                case OREGPAIR:
                                                        if(xo)
                                                                ot = ot->right;
                                                        else
                                                                ot = ot->left;
                                                        break;
                                                case OREGISTER:
                                                        break;
                                                default:
                                                        if(xo != *xp) {
                                                                ot->xoffset += xo - *xp;
                                                                *xp = xo;
                                                        }
                                                }
                                                break;
                                        
                                        default:
                                                diag(l, "bad V_lop");
                                                return;
                                        }
                                        if(tl == nil)
                                                tl = ot;
                                        else
                                                tr = ot;
                                }
                                if(op == Vzero) {
                                        zeroregm(tl);
                                        break;
                                }
                                oc = c[i - 3];
                                if(op == Vinsx || op == Vopx) {
//print("%d -> %d\n", oc, args[oc]);
                                        oc = args[oc];
                                }
                                else {
                                        switch(oc) {
                                        case O_a0:
                                        case O_a1:
                                                oc = args[oc - O_a0];
                                                break;
                                        }
                                }
                                switch(op) {
                                case Vmul:
                                        mulgen(tr->type, tl, tr);
                                        break;
                                case Vmv:
                                        gmove(tl, tr);
                                        break;
                                case Vshll:
                                        shiftit(tr->type, tl, tr);
                                        break;
                                case Vop:
                                case Vopx:
                                        gopcode(oc, types[TULONG], tl, tr);
                                        break;
                                case Vins:
                                case Vinsx:
                                        gins(oc, tl, tr);
                                        break;
                                case Vinsl:
                                        gins(oc, tl, tr->right);
                                        p->from.index = tr->left->reg;
                                        break;
                                case Vinsr:
                                        gins(oc, tl, tr->left);
                                        p->from.index = tr->right->reg;
                                        break;
                                case Vinsla:
                                        gins(oc, tl, tr + 1);
                                        p->from.index = tr->reg;
                                        break;
                                case Vinsra:
                                        gins(oc, tl, tr);
                                        p->from.index = (tr + 1)->reg;
                                        break;
                                }
                                break;

                        case VT:
                                g = true;
                                i++;
                                break;
                        case VF:
                                g = !true;
                                i++;
                                break;

                        case V_T0: case V_T1:
                                g = args[op - V_T0];
                                i++;
                                break;

                        case V_F0: case V_F1:
                                g = !args[op - V_F0];
                                i++;
                                break;

                        case V_C0: case V_C1:
                                if(g)
                                        args[op - V_C0] = 0;
                                i++;
                                break;

                        case V_S0: case V_S1:
                                if(g)
                                        args[op - V_S0] = 1;
                                i++;
                                break;

                        case V_l_lo_f:
                                g = lo64v(l) == 0;
                                i++;
                                break;
                        case V_l_hi_f:
                                g = hi64v(l) == 0;
                                i++;
                                break;
                        case V_l_lo_t:
                                g = lo64v(l) != 0;
                                i++;
                                break;
                        case V_l_hi_t:
                                g = hi64v(l) != 0;
                                i++;
                                break;
                        case V_l_lo_u:
                                g = lo64v(l) >= 0;
                                i++;
                                break;
                        case V_l_hi_u:
                                g = hi64v(l) >= 0;
                                i++;
                                break;
                        case V_r_lo_f:
                                g = lo64v(r) == 0;
                                i++;
                                break;
                        case V_r_hi_f:
                                g = hi64v(r) == 0;
                                i++;
                                break;
                        case V_r_lo_t:
                                g = lo64v(r) != 0;
                                i++;
                                break;
                        case V_r_hi_t:
                                g = hi64v(r) != 0;
                                i++;
                                break;
                        case V_r_lo_u:
                                g = lo64v(r) >= 0;
                                i++;
                                break;
                        case V_r_hi_u:
                                g = hi64v(r) >= 0;
                                i++;
                                break;

                        case Vend:
                                goto out;

                        case V_a0: case V_a1:
                                if(g) {
                                        lt = l->type;
                                        l->type = types[TULONG];
                                        regalloc(&tmps[op - V_a0], l, Z);
                                        l->type = lt;
                                }
                                i++;
                                break;

                        case V_f0: case V_f1:
                                if(g)
                                        regfree(&tmps[op - V_f0]);
                                i++;
                                break;

                        case V_p0: case V_p1: case V_p2: case V_p3: case V_p4:
                                if(g)
                                        patch(pr[op - V_p0], pc);
                                i++;
                                break;

                        case V_s0: case V_s1: case V_s2: case V_s3: case V_s4:
                                if(g)
                                        pr[op - V_s0] = p;
                                i++;
                                break;

                        default:
                                diag(l, "bad biggen: %d", op);
                                return;
                        }
                        if(i == VLEN || c[i] == 0)
                                break;
                }
        }
out:
        if(lo)
                l->xoffset -= lo;
        if(ro)
                r->xoffset -= ro;
        if(to)
                t->xoffset -= to;
}

int
cgen64(Node *n, Node *nn)
{
        Type *dt;
        uchar *args, (*cp)[VLEN], (**optab)[VLEN];
        int li, ri, lri, dr, si, m, op, sh, cmp, true;
        Node *c, *d, *l, *r, *t, *s, nod1, nod2, nod3, nod4, nod5;

        if(debug['g']) {
                prtree(nn, "cgen64 lhs");
                prtree(n, "cgen64");
                print("AX = %d\n", reg[D_AX]);
        }
        cmp = 0;
        sh = 0;

        switch(n->op) {
        case ONEG:
                d = regpair(nn, n);
                sugen(n->left, d, 8);
                gins(ANOTL, Z, d->right);
                gins(ANEGL, Z, d->left);
                gins(ASBBL, nodconst(-1), d->right);
                break;

        case OCOM:
                if(!vaddr(n->left, 0) || !vaddr(nn, 0))
                        d = regpair(nn, n);
                else
                        return 0;
                sugen(n->left, d, 8);
                gins(ANOTL, Z, d->left);
                gins(ANOTL, Z, d->right);
                break;

        case OADD:
                optab = ADDtab;
                args = ADDargs;
                goto twoop;
        case OAND:
                optab = ANDtab;
                args = ANDargs;
                goto twoop;
        case OOR:
                optab = ORtab;
                args = ORargs;
                goto twoop;
        case OSUB:
                optab = SUBtab;
                args = SUBargs;
                goto twoop;
        case OXOR:
                optab = ORtab;
                args = XORargs;
                goto twoop;
        case OASHL:
                sh = 1;
                args = nil;
                optab = shlltab;
                goto twoop;
        case OLSHR:
                sh = 1;
                args = shrlargs;
                optab = shrltab;
                goto twoop;
        case OASHR:
                sh = 1;
                args = sarlargs;
                optab = shrltab;
                goto twoop;
        case OEQ:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case ONE:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OLE:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OLT:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OGE:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OGT:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OHI:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OHS:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OLO:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;
        case OLS:
                cmp = 1;
                args = nil;
                optab = nil;
                goto twoop;

twoop:
                dr = nn != Z && nn->op == OREGPAIR;
                l = vfunc(n->left, nn);
                if(sh)
                        r = n->right;
                else
                        r = vfunc(n->right, nn);

                li = l->op == ONAME || l->op == OINDREG || l->op == OCONST;
                ri = r->op == ONAME || r->op == OINDREG || r->op == OCONST;

#define IMM(l, r)       ((l) | ((r) << 1))

                lri = IMM(li, ri);

                /* find out what is so easy about some operands */
                if(li)
                        li = whatof(l, sh | cmp);
                if(ri)
                        ri = whatof(r, cmp);

                if(sh)
                        goto shift;

                if(cmp)
                        goto cmp;

                /* evaluate hard subexps, stealing nn if possible. */
                switch(lri) {
                case IMM(0, 0):
                bin00:
                        if(l->complex > r->complex) {
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, n);
                                sugen(l, t, 8);
                                l = t;
                                t = regpair(Z, n);
                                sugen(r, t, 8);
                                r = t;
                        }
                        else {
                                t = regpair(Z, n);
                                sugen(r, t, 8);
                                r = t;
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, n);
                                sugen(l, t, 8);
                                l = t;
                        }
                        break;
                case IMM(0, 1):
                        if(dr)
                                t = nn;
                        else
                                t = regpair(Z, n);
                        sugen(l, t, 8);
                        l = t;
                        break;
                case IMM(1, 0):
                        if(n->op == OSUB && l->op == OCONST && hi64v(l) == 0) {
                                lri = IMM(0, 0);
                                goto bin00;
                        }
                        if(dr)
                                t = nn;
                        else
                                t = regpair(Z, n);
                        sugen(r, t, 8);
                        r = t;
                        break;
                case IMM(1, 1):
                        break;
                }

#define WW(l, r)        ((l) | ((r) << 2))
                d = Z;
                dt = nn->type;
                nn->type = types[TLONG];

                switch(lri) {
                case IMM(0, 0):
                        biggen(l, r, Z, 0, binop00, args);
                        break;
                case IMM(0, 1):
                        switch(ri) {
                        case WNONE:
                                diag(r, "bad whatof\n");
                                break;
                        case WCONST:
                                biggen(l, r, Z, 0, optab[B0c], args);
                                break;
                        case WHARD:
                                reglcgen(&nod2, r, Z);
                                r = &nod2;
                                /* fall thru */
                        case WADDR:
                                biggen(l, r, Z, 0, binoptmp, args);
                                if(ri == WHARD)
                                        regfree(r);
                                break;
                        }
                        break;
                case IMM(1, 0):
                        if(n->op == OSUB) {
                                switch(li) {
                                case WNONE:
                                        diag(l, "bad whatof\n");
                                        break;
                                case WHARD:
                                        reglcgen(&nod2, l, Z);
                                        l = &nod2;
                                        /* fall thru */
                                case WADDR:
                                case WCONST:
                                        biggen(l, r, Z, 0, sub10, args);
                                        break;
                                }
                                if(li == WHARD)
                                        regfree(l);
                        }
                        else {
                                switch(li) {
                                case WNONE:
                                        diag(l, "bad whatof\n");
                                        break;
                                case WCONST:
                                        biggen(r, l, Z, 0, optab[B0c], args);
                                        break;
                                case WHARD:
                                        reglcgen(&nod2, l, Z);
                                        l = &nod2;
                                        /* fall thru */
                                case WADDR:
                                        biggen(r, l, Z, 0, binoptmp, args);
                                        if(li == WHARD)
                                                regfree(l);
                                        break;
                                }
                        }
                        break;
                case IMM(1, 1):
                        switch(WW(li, ri)) {
                        case WW(WCONST, WHARD):
                                if(r->op == ONAME && n->op == OAND && reduxv(l))
                                        ri = WADDR;
                                break;
                        case WW(WHARD, WCONST):
                                if(l->op == ONAME && n->op == OAND && reduxv(r))
                                        li = WADDR;
                                break;
                        }
                        if(li == WHARD) {
                                reglcgen(&nod3, l, Z);
                                l = &nod3;
                        }
                        if(ri == WHARD) {
                                reglcgen(&nod2, r, Z);
                                r = &nod2;
                        }
                        d = regpair(nn, n);
                        instpair(d, Z);
                        switch(WW(li, ri)) {
                        case WW(WCONST, WADDR):
                        case WW(WCONST, WHARD):
                                biggen(l, r, d, 0, optab[Bca], args);
                                break;

                        case WW(WADDR, WCONST):
                        case WW(WHARD, WCONST):
                                biggen(l, r, d, 0, optab[Bac], args);
                                break;

                        case WW(WADDR, WADDR):
                        case WW(WADDR, WHARD):
                        case WW(WHARD, WADDR):
                        case WW(WHARD, WHARD):
                                biggen(l, r, d, 0, binop11, args);
                                break;

                        default:
                                diag(r, "bad whatof pair %d %d\n", li, ri);
                                break;
                        }
                        if(li == WHARD)
                                regfree(l);
                        if(ri == WHARD)
                                regfree(r);
                        break;
                }

                nn->type = dt;

                if(d != Z)
                        goto finished;

                switch(lri) {
                case IMM(0, 0):
                        freepair(r);
                        /* fall thru */;
                case IMM(0, 1):
                        if(!dr)
                                storepair(l, nn, 1);
                        break;
                case IMM(1, 0):
                        if(!dr)
                                storepair(r, nn, 1);
                        break;
                case IMM(1, 1):
                        break;
                }
                return 1;

        shift:
                c = Z;

                /* evaluate hard subexps, stealing nn if possible. */
                /* must also secure CX.  not as many optims as binop. */
                switch(lri) {
                case IMM(0, 0):
                imm00:
                        if(l->complex + 1 > r->complex) {
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, l);
                                sugen(l, t, 8);
                                l = t;
                                t = &nod1;
                                c = snarfreg(l, t, D_CX, r, &nod2);
                                cgen(r, t);
                                r = t;
                        }
                        else {
                                t = &nod1;
                                c = snarfreg(nn, t, D_CX, r, &nod2);
                                cgen(r, t);
                                r = t;
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, l);
                                sugen(l, t, 8);
                                l = t;
                        }
                        break;
                case IMM(0, 1):
                imm01:
                        if(ri != WCONST) {
                                lri = IMM(0, 0);
                                goto imm00;
                        }
                        if(dr)
                                t = nn;
                        else
                                t = regpair(Z, n);
                        sugen(l, t, 8);
                        l = t;
                        break;
                case IMM(1, 0):
                imm10:
                        if(li != WCONST) {
                                lri = IMM(0, 0);
                                goto imm00;
                        }
                        t = &nod1;
                        c = snarfreg(nn, t, D_CX, r, &nod2);
                        cgen(r, t);
                        r = t;
                        break;
                case IMM(1, 1):
                        if(ri != WCONST) {
                                lri = IMM(1, 0);
                                goto imm10;
                        }
                        if(li == WHARD) {
                                lri = IMM(0, 1);
                                goto imm01;
                        }
                        break;
                }

                d = Z;

                switch(lri) {
                case IMM(0, 0):
                        biggen(l, r, Z, 0, optab[S00], args);
                        break;
                case IMM(0, 1):
                        switch(ri) {
                        case WNONE:
                        case WADDR:
                        case WHARD:
                                diag(r, "bad whatof\n");
                                break;
                        case WCONST:
                                m = r->vconst & 63;
                                s = nodconst(m);
                                if(m < 32)
                                        cp = optab[Sc0];
                                else if(m == 32)
                                        cp = optab[Sc1];
                                else
                                        cp = optab[Sc2];
                                biggen(l, s, Z, 0, cp, args);
                                break;
                        }
                        break;
                case IMM(1, 0):
                        /* left is const */
                        d = regpair(nn, n);
                        instpair(d, Z);
                        biggen(l, r, d, 0, optab[S10], args);
                        regfree(r);
                        break;
                case IMM(1, 1):
                        d = regpair(nn, n);
                        instpair(d, Z);
                        switch(WW(li, ri)) {
                        case WW(WADDR, WCONST):
                                m = r->vconst & 63;
                                s = nodconst(m);
                                if(m < 32) {
                                        loadpair(l, d);
                                        l = d;
                                        cp = optab[Sc0];
                                }
                                else if(m == 32)
                                        cp = optab[Sac3];
                                else
                                        cp = optab[Sac4];
                                biggen(l, s, d, 0, cp, args);
                                break;

                        default:
                                diag(r, "bad whatof pair %d %d\n", li, ri);
                                break;
                        }
                        break;
                }

                if(c != Z) {
                        gins(AMOVL, c, r);
                        regfree(c);
                }

                if(d != Z)
                        goto finished;

                switch(lri) {
                case IMM(0, 0):
                        regfree(r);
                        /* fall thru */
                case IMM(0, 1):
                        if(!dr)
                                storepair(l, nn, 1);
                        break;
                case IMM(1, 0):
                        regfree(r);
                        break;
                case IMM(1, 1):
                        break;
                }
                return 1;

        cmp:
                op = n->op;
                /* evaluate hard subexps */
                switch(lri) {
                case IMM(0, 0):
                        if(l->complex > r->complex) {
                                t = regpair(Z, l);
                                sugen(l, t, 8);
                                l = t;
                                t = regpair(Z, r);
                                sugen(r, t, 8);
                                r = t;
                        }
                        else {
                                t = regpair(Z, r);
                                sugen(r, t, 8);
                                r = t;
                                t = regpair(Z, l);
                                sugen(l, t, 8);
                                l = t;
                        }
                        break;
                case IMM(1, 0):
                        t = r;
                        r = l;
                        l = t;
                        ri = li;
                        op = invrel[relindex(op)];
                        /* fall thru */
                case IMM(0, 1):
                        t = regpair(Z, l);
                        sugen(l, t, 8);
                        l = t;
                        break;
                case IMM(1, 1):
                        break;
                }

                true = 1;
                optab = cmptab;
                switch(op) {
                case OEQ:
                        optab = NEtab;
                        true = 0;
                        break;
                case ONE:
                        optab = NEtab;
                        break;
                case OLE:
                        args = GTargs;
                        true = 0;
                        break;
                case OGT:
                        args = GTargs;
                        break;
                case OLS:
                        args = HIargs;
                        true = 0;
                        break;
                case OHI:
                        args = HIargs;
                        break;
                case OLT:
                        args = GEargs;
                        true = 0;
                        break;
                case OGE:
                        args = GEargs;
                        break;
                case OLO:
                        args = HSargs;
                        true = 0;
                        break;
                case OHS:
                        args = HSargs;
                        break;
                default:
                        diag(n, "bad cmp\n");
                        SET(optab);
                }

                switch(lri) {
                case IMM(0, 0):
                        biggen(l, r, Z, true, optab[T0i], args);
                        break;
                case IMM(0, 1):
                case IMM(1, 0):
                        switch(ri) {
                        case WNONE:
                                diag(l, "bad whatof\n");
                                break;
                        case WCONST:
                                biggen(l, r, Z, true, optab[T0i], args);
                                break;
                        case WHARD:
                                reglcgen(&nod2, r, Z);
                                r = &nod2;
                                /* fall thru */
                        case WADDR:
                                biggen(l, r, Z, true, optab[T0i], args);
                                if(ri == WHARD)
                                        regfree(r);
                                break;
                        }
                        break;
                case IMM(1, 1):
                        if(li == WHARD) {
                                reglcgen(&nod3, l, Z);
                                l = &nod3;
                        }
                        if(ri == WHARD) {
                                reglcgen(&nod2, r, Z);
                                r = &nod2;
                        }
                        biggen(l, r, Z, true, optab[Tii], args);
                        if(li == WHARD)
                                regfree(l);
                        if(ri == WHARD)
                                regfree(r);
                        break;
                }

                switch(lri) {
                case IMM(0, 0):
                        freepair(r);
                        /* fall thru */;
                case IMM(0, 1):
                case IMM(1, 0):
                        freepair(l);
                        break;
                case IMM(1, 1):
                        break;
                }
                return 1;

        case OASMUL:
        case OASLMUL:
                m = 0;
                goto mulop;

        case OMUL:
        case OLMUL:
                m = 1;
                goto mulop;

        mulop:
                dr = nn != Z && nn->op == OREGPAIR;
                l = vfunc(n->left, nn);
                r = vfunc(n->right, nn);
                if(r->op != OCONST) {
                        if(l->complex > r->complex) {
                                if(m) {
                                        t = l;
                                        l = r;
                                        r = t;
                                }
                                else if(!vaddr(l, 1)) {
                                        reglcgen(&nod5, l, Z);
                                        l = &nod5;
                                        evacaxdx(l);
                                }
                        }
                        t = regpair(Z, n);
                        sugen(r, t, 8);
                        r = t;
                        evacaxdx(r->left);
                        evacaxdx(r->right);
                        if(l->complex <= r->complex && !m && !vaddr(l, 1)) {
                                reglcgen(&nod5, l, Z);
                                l = &nod5;
                                evacaxdx(l);
                        }
                }
                if(dr)
                        t = nn;
                else
                        t = regpair(Z, n);
                //print("dr=%d ", dr); prtree(t, "t");
                c = Z;
                d = Z;
                if(!nodreg(&nod1, t->left, D_AX)) {
                        if(t->left->reg != D_AX){
                                t->left->reg = D_AX;
                                reg[D_AX]++;
                        }else if(reg[D_AX] == 0)
                                fatal(Z, "vlong mul AX botch");
                }
                if(!nodreg(&nod2, t->right, D_DX)) {
                        if(t->right->reg != D_DX){
                                t->right->reg = D_DX;
                                reg[D_DX]++;
                        }else if(reg[D_DX] == 0)
                                fatal(Z, "vlong mul DX botch");
                }
                //prtree(t, "t1"); print("reg/ax = %d reg/dx = %d\n", reg[D_AX], reg[D_DX]);
                if(m)
                        sugen(l, t, 8);
                else
                        loadpair(l, t);
                //prtree(t, "t2"); print("reg/ax = %d reg/dx = %d\n", reg[D_AX], reg[D_DX]);
                if(t->left->reg != D_AX) {
                        c = &nod3;
                        regsalloc(c, t->left);
                        gmove(&nod1, c);
                        gmove(t->left, &nod1);
                        zapreg(t->left);
                }
                //print("reg/ax = %d reg/dx = %d\n", reg[D_AX], reg[D_DX]);
                if(t->right->reg != D_DX) {
                        d = &nod4;
                        regsalloc(d, t->right);
                        gmove(&nod2, d);
                        if(t->right->reg == D_AX && c != nil){
                                /* need previous value of AX in DX */
                                gmove(c, &nod2);
                        }else
                                gmove(t->right, &nod2);
                        zapreg(t->right);
                }
                if(c != Z || d != Z) {
                        s = regpair(Z, n);
                        s->left = &nod1;
                        s->right = &nod2;
                }
                else
                        s = t;
                reg[D_AX]++;    /* don't allow biggen to allocate AX or DX (smashed by MUL) as temp */
                reg[D_DX]++;
                if(r->op == OCONST) {
                        if(hi64v(r) == 0)
                                biggen(s, r, Z, 0, mulc32, nil);
                        else
                                biggen(s, r, Z, 0, mulc64, nil);
                }
                else
                        biggen(s, r, Z, 0, mull, nil);
                instpair(t, Z);
                reg[D_AX]--;
                reg[D_DX]--;
                if(c != Z) {
                        gmove(&nod1, t->left);
                        gmove(&nod3, &nod1);
                }
                if(d != Z) {
                        gmove(&nod2, t->right);
                        gmove(&nod4, &nod2);
                }

                if(r->op == OREGPAIR)
                        freepair(r);
                if(!m)
                        storepair(t, l, 0);
                if(l == &nod5)
                        regfree(l);

                if(!dr) {
                        if(nn != Z)
                                storepair(t, nn, 1);
                        else
                                freepair(t);
                }
                return 1;

        case OASADD:
                args = ADDargs;
                goto vasop;
        case OASAND:
                args = ANDargs;
                goto vasop;
        case OASOR:
                args = ORargs;
                goto vasop;
        case OASSUB:
                args = SUBargs;
                goto vasop;
        case OASXOR:
                args = XORargs;
                goto vasop;

        vasop:
                l = n->left;
                r = n->right;
                dr = nn != Z && nn->op == OREGPAIR;
                m = 0;
                if(l->complex > r->complex) {
                        if(!vaddr(l, 1)) {
                                reglcgen(&nod1, l, Z);
                                l = &nod1;
                        }
                        if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, r);
                                sugen(r, t, 8);
                                r = t;
                                m = 1;
                        }
                }
                else {
                        if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
                                if(dr)
                                        t = nn;
                                else
                                        t = regpair(Z, r);
                                sugen(r, t, 8);
                                r = t;
                                m = 1;
                        }
                        if(!vaddr(l, 1)) {
                                reglcgen(&nod1, l, Z);
                                l = &nod1;
                        }
                }
                if(nn != Z) {
                        if(n->op == OASSUB)
                                biggen(l, r, Z, 0, sub10, args);
                        else
                                biggen(r, l, Z, 0, binoptmp, args);
                        storepair(r, l, 0);
                }
                else {
                        if(m)
                                biggen(l, r, Z, 0, binop00, args);
                        else
                                biggen(l, r, Z, 0, binoptmp, args);
                }
                if(l == &nod1)
                        regfree(&nod1);
                if(m) {
                        if(nn == Z)
                                freepair(r);
                        else if(!dr)
                                storepair(r, nn, 1);
                }
                return 1;

        case OASASHL:
                args = nil;
                optab = asshlltab;
                goto assh;
        case OASLSHR:
                args = shrlargs;
                optab = asshrltab;
                goto assh;
        case OASASHR:
                args = sarlargs;
                optab = asshrltab;
                goto assh;

        assh:
                c = Z;
                l = n->left;
                r = n->right;
                if(r->op == OCONST) {
                        m = r->vconst & 63;
                        if(m < 32)
                                m = SAclo;
                        else if(m == 32)
                                m = SAc32;
                        else
                                m = SAchi;
                }
                else
                        m = SAgen;
                if(l->complex > r->complex) {
                        if(!vaddr(l, 0)) {
                                reglcgen(&nod1, l, Z);
                                l = &nod1;
                        }
                        if(m == SAgen) {
                                t = &nod2;
                                if(l->reg == D_CX) {
                                        regalloc(t, r, Z);
                                        gmove(l, t);
                                        l->reg = t->reg;
                                        t->reg = D_CX;
                                }
                                else
                                        c = snarfreg(nn, t, D_CX, r, &nod3);
                                cgen(r, t);
                                r = t;
                        }
                }
                else {
                        if(m == SAgen) {
                                t = &nod2;
                                c = snarfreg(nn, t, D_CX, r, &nod3);
                                cgen(r, t);
                                r = t;
                        }
                        if(!vaddr(l, 0)) {
                                reglcgen(&nod1, l, Z);
                                l = &nod1;
                        }
                }

                if(nn != Z) {
                        m += SAdgen - SAgen;
                        d = regpair(nn, n);
                        instpair(d, Z);
                        biggen(l, r, d, 0, optab[m], args);
                        if(l == &nod1) {
                                regfree(&nod1);
                                l = Z;
                        }
                        if(r == &nod2 && c == Z) {
                                regfree(&nod2);
                                r = Z;
                        }
                        if(d != nn)
                                storepair(d, nn, 1);
                }
                else
                        biggen(l, r, Z, 0, optab[m], args);

                if(c != Z) {
                        gins(AMOVL, c, r);
                        regfree(c);
                }
                if(l == &nod1)
                        regfree(&nod1);
                if(r == &nod2)
                        regfree(&nod2);
                return 1;

        case OPOSTINC:
                args = ADDargs;
                cp = incdecpost;
                goto vinc;
        case OPOSTDEC:
                args = SUBargs;
                cp = incdecpost;
                goto vinc;
        case OPREINC:
                args = ADDargs;
                cp = incdecpre;
                goto vinc;
        case OPREDEC:
                args = SUBargs;
                cp = incdecpre;
                goto vinc;

        vinc:
                l = n->left;
                if(!vaddr(l, 1)) {
                        reglcgen(&nod1, l, Z);
                        l = &nod1;
                }
                
                if(nn != Z) {
                        d = regpair(nn, n);
                        instpair(d, Z);
                        biggen(l, Z, d, 0, cp, args);
                        if(l == &nod1) {
                                regfree(&nod1);
                                l = Z;
                        }
                        if(d != nn)
                                storepair(d, nn, 1);
                }
                else
                        biggen(l, Z, Z, 0, incdec, args);

                if(l == &nod1)
                        regfree(&nod1);
                return 1;

        case OCAST:
                l = n->left;
                if(typev[l->type->etype]) {
                        if(!vaddr(l, 1)) {
                                if(l->complex + 1 > nn->complex) {
                                        d = regpair(Z, l);
                                        sugen(l, d, 8);
                                        if(!vaddr(nn, 1)) {
                                                reglcgen(&nod1, nn, Z);
                                                r = &nod1;
                                        }
                                        else
                                                r = nn;
                                }
                                else {
                                        if(!vaddr(nn, 1)) {
                                                reglcgen(&nod1, nn, Z);
                                                r = &nod1;
                                        }
                                        else
                                                r = nn;
                                        d = regpair(Z, l);
                                        sugen(l, d, 8);
                                }
//                              d->left->type = r->type;
                                d->left->type = types[TLONG];
                                gmove(d->left, r);
                                freepair(d);
                        }
                        else {
                                if(nn->op != OREGISTER && !vaddr(nn, 1)) {
                                        reglcgen(&nod1, nn, Z);
                                        r = &nod1;
                                }
                                else
                                        r = nn;
//                              l->type = r->type;
                                l->type = types[TLONG];
                                gmove(l, r);
                        }
                        if(r != nn)
                                regfree(r);
                }
                else {
                        if(typeu[l->type->etype] || cond(l->op))
                                si = TUNSIGNED;
                        else
                                si = TSIGNED;
                        regalloc(&nod1, l, Z);
                        cgen(l, &nod1);
                        if(nn->op == OREGPAIR) {
                                m = instpair(nn, &nod1);
                                biggen(&nod1, Z, nn, si == TSIGNED, castrp, nil);
                        }
                        else {
                                m = 0;
                                if(!vaddr(nn, si != TSIGNED)) {
                                        dt = nn->type;
                                        nn->type = types[TLONG];
                                        reglcgen(&nod2, nn, Z);
                                        nn->type = dt;
                                        nn = &nod2;
                                }
                                dt = nn->type;
                                nn->type = types[TLONG];
                                biggen(&nod1, Z, nn, si == TSIGNED, castrpa, nil);
                                nn->type = dt;
                                if(nn == &nod2)
                                        regfree(&nod2);
                        }
                        if(!m)
                                regfree(&nod1);
                }
                return 1;

        default:
                if(n->op == OREGPAIR) {
                        storepair(n, nn, 1);
                        return 1;
                }
                if(nn->op == OREGPAIR) {
                        loadpair(n, nn);
                        return 1;
                }
                return 0;
        }
finished:
        if(d != nn)
                storepair(d, nn, 1);
        return 1;
}

void
testv(Node *n, int true)
{
        Type *t;
        Node *nn, nod, *b;

        if(machcap(Z)) {
                b = &nod;
                b->op = true ? ONE : OEQ;
                b->left = n;
                b->right = new(0, Z, Z);
                *b->right = *nodconst(0);
                b->right->type = n->type;
                b->type = types[TLONG];
                cgen64(b, Z);
                return;
        }

        switch(n->op) {
        case OINDREG:
        case ONAME:
                biggen(n, Z, Z, true, testi, nil);
                break;

        default:
                n = vfunc(n, n);
                if(n->addable >= INDEXED) {
                        t = n->type;
                        n->type = types[TLONG];
                        reglcgen(&nod, n, Z);
                        n->type = t;
                        n = &nod;
                        biggen(n, Z, Z, true, testi, nil);
                        if(n == &nod)
                                regfree(n);
                }
                else {
                        nn = regpair(Z, n);
                        sugen(n, nn, 8);
                        biggen(nn, Z, Z, true, testi, nil);
                        freepair(nn);
                }
        }
}