#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;

       if(nn != Z && nn->complex >= FNX){
               reglcgen(&nod1, nn, Z);
               cgen(n, &nod1);
               regfree(&nod1);
               return 1;
       }

       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) && l->op != OINDEX) {   // theres no 64 bit indexing
                               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);
               }
       }
}