#include "gc.h"

static  int     resvreg[nelem(reg)];

void
ginit(void)
{
       int i;
       Type *t;

       thechar = '6';
       thestring = "amd64";
       exregoffset = REGEXT;
       exfregoffset = FREGEXT;
       listinit();
       nstring = 0;
       mnstring = 0;
       nrathole = 0;
       pc = 0;
       breakpc = -1;
       continpc = -1;
       cases = C;
       firstp = P;
       lastp = P;
       tfield = types[TINT];

       typeword = typechlvp;
       typeswitch = typechlv;
       typecmplx = typesu;

       /* TO DO */
       memmove(typechlpv, typechlp, sizeof(typechlpv));
       typechlpv[TVLONG] = 1;
       typechlpv[TUVLONG] = 1;

       zprog.link = P;
       zprog.as = AGOK;
       zprog.from.type = D_NONE;
       zprog.from.index = D_NONE;
       zprog.from.scale = 0;
       zprog.to = zprog.from;

       lregnode.op = OREGISTER;
       lregnode.class = CEXREG;
       lregnode.reg = REGTMP;
       lregnode.complex = 0;
       lregnode.addable = 11;
       lregnode.type = types[TLONG];

       qregnode = lregnode;
       qregnode.type = types[TVLONG];

       constnode.op = OCONST;
       constnode.class = CXXX;
       constnode.complex = 0;
       constnode.addable = 20;
       constnode.type = types[TLONG];

       vconstnode = constnode;
       vconstnode.type = types[TVLONG];

       fconstnode.op = OCONST;
       fconstnode.class = CXXX;
       fconstnode.complex = 0;
       fconstnode.addable = 20;
       fconstnode.type = types[TDOUBLE];

       nodsafe = new(ONAME, Z, Z);
       nodsafe->sym = slookup(".safe");
       nodsafe->type = types[TINT];
       nodsafe->etype = types[TINT]->etype;
       nodsafe->class = CAUTO;
       complex(nodsafe);

       t = typ(TARRAY, types[TCHAR]);
       symrathole = slookup(".rathole");
       symrathole->class = CGLOBL;
       symrathole->type = t;

       nodrat = new(ONAME, Z, Z);
       nodrat->sym = symrathole;
       nodrat->type = types[TIND];
       nodrat->etype = TVOID;
       nodrat->class = CGLOBL;
       complex(nodrat);
       nodrat->type = t;

       nodret = new(ONAME, Z, Z);
       nodret->sym = slookup(".ret");
       nodret->type = types[TIND];
       nodret->etype = TIND;
       nodret->class = CPARAM;
       nodret = new(OIND, nodret, Z);
       complex(nodret);

       if(0)
               com64init();

       memset(reg, 0, sizeof(reg));
       for(i=0; i<nelem(reg); i++) {
               reg[i] = 1;
               if(i >= D_AX && i <= D_R15 && i != D_SP)
                       reg[i] = 0;
               if(i >= D_X0 && i <= D_X7)
                       reg[i] = 0;
       }
       /* keep two external registers */
       reg[REGEXT] = 1;
       reg[REGEXT-1] = 1;
       memmove(resvreg, reg, sizeof(resvreg));
}

void
gclean(void)
{
       int i;
       Sym *s;

       reg[D_SP]--;
       for(i=D_AX; i<=D_R15; i++)
               if(reg[i] && !resvreg[i])
                       diag(Z, "reg %R left allocated", i);
       for(i=D_X0; i<=D_X7; i++)
               if(reg[i] && !resvreg[i])
                       diag(Z, "reg %R left allocated", i);
       while(mnstring)
               outstring("", 1L);
       symstring->type->width = nstring;
       symrathole->type->width = nrathole;
       for(i=0; i<NHASH; i++)
       for(s = hash[i]; s != S; s = s->link) {
               if(s->type == T)
                       continue;
               if(s->type->width == 0)
                       continue;
               if(s->class != CGLOBL && s->class != CSTATIC)
                       continue;
               if(s->type == types[TENUM])
                       continue;
               gpseudo(AGLOBL, s, nodconst(s->type->width));
       }
       nextpc();
       p->as = AEND;
       outcode();
}

void
nextpc(void)
{

       p = alloc(sizeof(*p));
       *p = zprog;
       p->lineno = nearln;
       pc++;
       if(firstp == P) {
               firstp = p;
               lastp = p;
               return;
       }
       lastp->link = p;
       lastp = p;
}

void
gargs(Node *n, Node *tn1, Node *tn2)
{
       long regs;
       Node fnxargs[20], *fnxp;

       regs = cursafe;

       fnxp = fnxargs;
       garg1(n, tn1, tn2, 0, &fnxp);   /* compile fns to temps */

       curarg = 0;
       fnxp = fnxargs;
       garg1(n, tn1, tn2, 1, &fnxp);   /* compile normal args and temps */

       cursafe = regs;
}

int
nareg(void)
{
       int i, n;

       n = 0;
       for(i=D_AX; i<=D_R15; i++)
               if(reg[i] == 0 && !resvreg[i])
                       n++;
       return n;
}

void
garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
{
       Node nod;

       if(n == Z)
               return;
       if(n->op == OLIST) {
               garg1(n->left, tn1, tn2, f, fnxp);
               garg1(n->right, tn1, tn2, f, fnxp);
               return;
       }
       if(f == 0) {
               if(n->complex >= FNX) {
                       regsalloc(*fnxp, n);
                       nod = znode;
                       nod.op = OAS;
                       nod.left = *fnxp;
                       nod.right = n;
                       nod.type = n->type;
                       cgen(&nod, Z);
                       (*fnxp)++;
               }
               return;
       }
       if(typesu[n->type->etype]) {
               regaalloc(tn2, n);
               if(n->complex >= FNX) {
                       sugen(*fnxp, tn2, n->type->width);
                       (*fnxp)++;
               } else
                       sugen(n, tn2, n->type->width);
               return;
       }
       if(REGARG && curarg == 0 && typechlpv[n->type->etype]) {
               regaalloc1(tn1, n);
               if(n->complex >= FNX) {
                       cgen(*fnxp, tn1);
                       (*fnxp)++;
               } else
                       cgen(n, tn1);
               return;
       }
       if(vconst(n) == 0) {
               regaalloc(tn2, n);
               gmove(n, tn2);
               return;
       }
       regalloc(tn1, n, Z);
       if(n->complex >= FNX) {
               cgen(*fnxp, tn1);
               (*fnxp)++;
       } else
               cgen(n, tn1);
       regaalloc(tn2, n);
       gmove(tn1, tn2);
       regfree(tn1);
}

Node*
nodgconst(vlong v, Type *t)
{
       if(!typev[t->etype])
               return nodconst((long)v);
       vconstnode.vconst = v;
       return &vconstnode;
}

Node*
nodconst(long v)
{
       constnode.vconst = v;
       return &constnode;
}

Node*
nodfconst(double d)
{
       fconstnode.fconst = d;
       return &fconstnode;
}

int
isreg(Node *n, int r)
{

       if(n->op == OREGISTER)
               if(n->reg == r)
                       return 1;
       return 0;
}

int
nodreg(Node *n, Node *nn, int r)
{
       int et;

       *n = qregnode;
       n->reg = r;
       if(nn != Z){
               et = nn->type->etype;
               if(!typefd[et] && nn->type->width <= SZ_LONG && 0)
                       n->type = typeu[et]? types[TUINT]: types[TINT];
               else
                       n->type = nn->type;
//print("nodreg %s [%s]\n", tnames[et], tnames[n->type->etype]);
               n->lineno = nn->lineno;
       }
       if(reg[r] == 0)
               return 0;
       if(nn != Z) {
               if(nn->op == OREGISTER)
               if(nn->reg == r)
                       return 0;
       }
       return 1;
}

void
regret(Node *n, Node *nn)
{
       int r;

       r = REGRET;
       if(typefd[nn->type->etype])
               r = FREGRET;
       nodreg(n, nn, r);
       reg[r]++;
}

void
regalloc(Node *n, Node *tn, Node *o)
{
       int i;

       switch(tn->type->etype) {
       case TCHAR:
       case TUCHAR:
       case TSHORT:
       case TUSHORT:
       case TINT:
       case TUINT:
       case TLONG:
       case TULONG:
       case TVLONG:
       case TUVLONG:
       case TIND:
               if(o != Z && o->op == OREGISTER) {
                       i = o->reg;
                       if(i >= D_AX && i <= D_R15)
                               goto out;
               }
               for(i=D_AX; i<=D_R15; i++){
                       i ^= 7;
                       if(reg[i] == 0 && !resvreg[i])
                               goto out;
                       i ^= 7;
               }
               diag(tn, "out of fixed registers");
               goto err;

       case TFLOAT:
       case TDOUBLE:
               if(o != Z && o->op == OREGISTER) {
                       i = o->reg;
                       if(i >= D_X0 && i <= D_X7)
                               goto out;
               }
               for(i=D_X0; i<=D_X7; i++)
                       if(reg[i] == 0 && !resvreg[i])
                               goto out;
               diag(tn, "out of float registers");
               goto out;
       }
       diag(tn, "unknown type in regalloc: %T", tn->type);
err:
       i = 0;
out:
       if(i)
               reg[i]++;
       nodreg(n, tn, i);
}

void
regialloc(Node *n, Node *tn, Node *o)
{
       Node nod;

       nod = *tn;
       nod.type = types[TIND];
       regalloc(n, &nod, o);
}

void
regfree(Node *n)
{
       int i;

       i = 0;
       if(n->op != OREGISTER && n->op != OINDREG)
               goto err;
       i = n->reg;
       if(i < 0 || i >= sizeof(reg))
               goto err;
       if(reg[i] <= 0)
               goto err;
       reg[i]--;
       return;
err:
       diag(n, "error in regfree: %R", i);
}

void
regsalloc(Node *n, Node *nn)
{
       cursafe = align(cursafe, nn->type, Aaut3);
       maxargsafe = maxround(maxargsafe, cursafe+curarg);
       *n = *nodsafe;
       n->xoffset = -(stkoff + cursafe);
       n->type = nn->type;
       n->etype = nn->type->etype;
       n->lineno = nn->lineno;
}

void
regaalloc1(Node *n, Node *nn)
{
       nodreg(n, nn, REGARG);
       reg[REGARG]++;
       curarg = align(curarg, nn->type, Aarg1);
       curarg = align(curarg, nn->type, Aarg2);
       maxargsafe = maxround(maxargsafe, cursafe+curarg);
}

void
regaalloc(Node *n, Node *nn)
{
       curarg = align(curarg, nn->type, Aarg1);
       *n = *nn;
       n->op = OINDREG;
       n->reg = REGSP;
       n->xoffset = curarg;
       n->complex = 0;
       n->addable = 20;
       curarg = align(curarg, nn->type, Aarg2);
       maxargsafe = maxround(maxargsafe, cursafe+curarg);
}

void
regind(Node *n, Node *nn)
{

       if(n->op != OREGISTER) {
               diag(n, "regind not OREGISTER");
               return;
       }
       n->op = OINDREG;
       n->type = nn->type;
}

void
naddr(Node *n, Adr *a)
{
       long v;

       a->type = D_NONE;
       if(n == Z)
               return;
       switch(n->op) {
       default:
       bad:
               diag(n, "bad in naddr: %O %D", n->op, a);
               break;

       case OREGISTER:
               a->type = n->reg;
               a->sym = S;
               break;


       case OIND:
               naddr(n->left, a);
               if(a->type >= D_AX && a->type <= D_R15)
                       a->type += D_INDIR;
               else
               if(a->type == D_CONST)
                       a->type = D_NONE+D_INDIR;
               else
               if(a->type == D_ADDR) {
                       a->type = a->index;
                       a->index = D_NONE;
               } else
                       goto bad;
               break;

       case OINDEX:
               a->type = idx.ptr;
               if(n->left->op == OADDR || n->left->op == OCONST)
                       naddr(n->left, a);
               if(a->type >= D_AX && a->type <= D_R15)
                       a->type += D_INDIR;
               else
               if(a->type == D_CONST)
                       a->type = D_NONE+D_INDIR;
               else
               if(a->type == D_ADDR) {
                       a->type = a->index;
                       a->index = D_NONE;
               } else
                       goto bad;
               a->index = idx.reg;
               a->scale = n->scale;
               a->offset += n->xoffset;
               break;

       case OINDREG:
               a->type = n->reg+D_INDIR;
               a->sym = S;
               a->offset = n->xoffset;
               break;

       case ONAME:
               a->etype = n->etype;
               a->type = D_STATIC;
               a->sym = n->sym;
               a->offset = n->xoffset;
               if(n->class == CSTATIC)
                       break;
               if(n->class == CEXTERN || n->class == CGLOBL) {
                       a->type = D_EXTERN;
                       break;
               }
               if(n->class == CAUTO) {
                       a->type = D_AUTO;
                       break;
               }
               if(n->class == CPARAM) {
                       a->type = D_PARAM;
                       break;
               }
               goto bad;

       case OCONST:
               if(typefd[n->type->etype]) {
                       a->type = D_FCONST;
                       a->dval = n->fconst;
                       break;
               }
               a->sym = S;
               a->type = D_CONST;
               if(typev[n->type->etype] || n->type->etype == TIND)
                       a->offset = n->vconst;
               else
                       a->offset = convvtox(n->vconst, typeu[n->type->etype]? TULONG: TLONG);
               break;

       case OADDR:
               naddr(n->left, a);
               if(a->type >= D_INDIR) {
                       a->type -= D_INDIR;
                       break;
               }
               if(a->type == D_EXTERN || a->type == D_STATIC ||
                  a->type == D_AUTO || a->type == D_PARAM)
                       if(a->index == D_NONE) {
                               a->index = a->type;
                               a->type = D_ADDR;
                               break;
                       }
               goto bad;

       case OADD:
               if(n->right->op == OCONST) {
                       v = n->right->vconst;
                       naddr(n->left, a);
               } else
               if(n->left->op == OCONST) {
                       v = n->left->vconst;
                       naddr(n->right, a);
               } else
                       goto bad;
               a->offset += v;
               break;

       }
}

void
gcmp(int op, Node *n, vlong val)
{
       Node *cn, nod;

       cn = nodgconst(val, n->type);
       if(!immconst(cn)){
               regalloc(&nod, n, Z);
               gmove(cn, &nod);
               gopcode(op, n->type, n, &nod);
               regfree(&nod);
       }else
               gopcode(op, n->type, n, cn);
}

#define CASE(a,b)       ((a<<8)|(b<<0))

void
gmove(Node *f, Node *t)
{
       int ft, tt, t64, a;
       Node nod, nod1, nod2, nod3;
       Prog *p1, *p2;

       ft = f->type->etype;
       tt = t->type->etype;
       t64 = tt == TVLONG || tt == TUVLONG || tt == TIND;
       if(debug['M'])
               print("gop: %O %O[%s],%O[%s]\n", OAS,
                       f->op, tnames[ft], t->op, tnames[tt]);
       if(typefd[ft] && f->op == OCONST) {
               /* TO DO: pick up special constants, possibly preloaded */
               if(f->fconst == 0.0){
                       regalloc(&nod, t, t);
                       gins(AXORPD, &nod, &nod);
                       gmove(&nod, t);
                       regfree(&nod);
                       return;
               }
       }
/*
* load
*/
       if(f->op == ONAME || f->op == OINDREG ||
          f->op == OIND || f->op == OINDEX)
       switch(ft) {
       case TCHAR:
               a = AMOVBLSX;
               if(t64)
                       a = AMOVBQSX;
               goto ld;
       case TUCHAR:
               a = AMOVBLZX;
               if(t64)
                       a = AMOVBQZX;
               goto ld;
       case TSHORT:
               a = AMOVWLSX;
               if(t64)
                       a = AMOVWQSX;
               goto ld;
       case TUSHORT:
               a = AMOVWLZX;
               if(t64)
                       a = AMOVWQZX;
               goto ld;
       case TINT:
       case TLONG:
               if(typefd[tt]) {
                       regalloc(&nod, t, t);
                       if(tt == TDOUBLE)
                               a = ACVTSL2SD;
                       else
                               a = ACVTSL2SS;
                       gins(a, f, &nod);
                       gmove(&nod, t);
                       regfree(&nod);
                       return;
               }
               a = AMOVL;
               if(t64)
                       a = AMOVLQSX;
               goto ld;
       case TUINT:
       case TULONG:
               a = AMOVL;
               if(t64)
                       a = AMOVLQZX;   /* could probably use plain MOVL */
               goto ld;
       case TVLONG:
               if(typefd[tt]) {
                       regalloc(&nod, t, t);
                       if(tt == TDOUBLE)
                               a = ACVTSQ2SD;
                       else
                               a = ACVTSQ2SS;
                       gins(a, f, &nod);
                       gmove(&nod, t);
                       regfree(&nod);
                       return;
               }
       case TUVLONG:
       case TIND:
               a = AMOVQ;
       ld:
               regalloc(&nod, f, t);
               gins(a, f, &nod);
               gmove(&nod, t);
               regfree(&nod);
               return;

       case TFLOAT:
               a = AMOVSS;
               goto ld;
       case TDOUBLE:
               a = AMOVSD;
               goto ld;
       }

/*
* store
*/
       if(t->op == ONAME || t->op == OINDREG ||
          t->op == OIND || t->op == OINDEX)
       switch(tt) {
       case TCHAR:
       case TUCHAR:
               a = AMOVB;      goto st;
       case TSHORT:
       case TUSHORT:
               a = AMOVW;      goto st;
       case TINT:
       case TUINT:
       case TLONG:
       case TULONG:
               a = AMOVL;      goto st;
       case TVLONG:
       case TUVLONG:
       case TIND:
               a = AMOVQ;      goto st;

       st:
               if(f->op == OCONST) {
                       gins(a, f, t);
                       return;
               }
       fst:
               regalloc(&nod, t, f);
               gmove(f, &nod);
               gins(a, &nod, t);
               regfree(&nod);
               return;

       case TFLOAT:
               a = AMOVSS;
               goto fst;
       case TDOUBLE:
               a = AMOVSD;
               goto fst;
       }

/*
* convert
*/
       switch(CASE(ft,tt)) {
       default:
/*
* integer to integer
********
               a = AGOK;       break;

       case CASE(      TCHAR,  TCHAR):
       case CASE(      TUCHAR, TCHAR):
       case CASE(      TSHORT, TCHAR):
       case CASE(      TUSHORT,TCHAR):
       case CASE(      TINT,   TCHAR):
       case CASE(      TUINT,  TCHAR):
       case CASE(      TLONG,  TCHAR):
       case CASE(      TULONG, TCHAR):

       case CASE(      TCHAR,  TUCHAR):
       case CASE(      TUCHAR, TUCHAR):
       case CASE(      TSHORT, TUCHAR):
       case CASE(      TUSHORT,TUCHAR):
       case CASE(      TINT,   TUCHAR):
       case CASE(      TUINT,  TUCHAR):
       case CASE(      TLONG,  TUCHAR):
       case CASE(      TULONG, TUCHAR):

       case CASE(      TSHORT, TSHORT):
       case CASE(      TUSHORT,TSHORT):
       case CASE(      TINT,   TSHORT):
       case CASE(      TUINT,  TSHORT):
       case CASE(      TLONG,  TSHORT):
       case CASE(      TULONG, TSHORT):

       case CASE(      TSHORT, TUSHORT):
       case CASE(      TUSHORT,TUSHORT):
       case CASE(      TINT,   TUSHORT):
       case CASE(      TUINT,  TUSHORT):
       case CASE(      TLONG,  TUSHORT):
       case CASE(      TULONG, TUSHORT):

       case CASE(      TINT,   TINT):
       case CASE(      TUINT,  TINT):
       case CASE(      TLONG,  TINT):
       case CASE(      TULONG, TINT)::

       case CASE(      TINT,   TUINT):
       case CASE(      TUINT,  TUINT):
       case CASE(      TLONG,  TUINT):
       case CASE(      TULONG, TUINT):
*****/
               a = AMOVL;
               break;

       case CASE(      TINT,   TIND):
       case CASE(      TINT,   TVLONG):
       case CASE(      TINT,   TUVLONG):
       case CASE(      TLONG,  TIND):
       case CASE(      TLONG,  TVLONG):
       case CASE(      TLONG,  TUVLONG):
               a = AMOVLQSX;
               if(f->op == OCONST) {
                       f->vconst &= (uvlong)0xffffffffU;
                       if(f->vconst & 0x80000000)
                               f->vconst |= (vlong)0xffffffff << 32;
                       a = AMOVQ;
               }
               break;

       case CASE(      TUINT,  TIND):
       case CASE(      TUINT,  TVLONG):
       case CASE(      TUINT,  TUVLONG):
       case CASE(      TULONG, TVLONG):
       case CASE(      TULONG, TUVLONG):
       case CASE(      TULONG, TIND):
               a = AMOVLQZX;
               if(f->op == OCONST) {
                       f->vconst &= (uvlong)0xffffffffU;
                       a = AMOVQ;
               }
               break;

       case CASE(      TIND,   TCHAR):
       case CASE(      TIND,   TUCHAR):
       case CASE(      TIND,   TSHORT):
       case CASE(      TIND,   TUSHORT):
       case CASE(      TIND,   TINT):
       case CASE(      TIND,   TUINT):
       case CASE(      TIND,   TLONG):
       case CASE(      TIND,   TULONG):
       case CASE(      TVLONG, TCHAR):
       case CASE(      TVLONG, TUCHAR):
       case CASE(      TVLONG, TSHORT):
       case CASE(      TVLONG, TUSHORT):
       case CASE(      TVLONG, TINT):
       case CASE(      TVLONG, TUINT):
       case CASE(      TVLONG, TLONG):
       case CASE(      TVLONG, TULONG):
       case CASE(      TUVLONG,        TCHAR):
       case CASE(      TUVLONG,        TUCHAR):
       case CASE(      TUVLONG,        TSHORT):
       case CASE(      TUVLONG,        TUSHORT):
       case CASE(      TUVLONG,        TINT):
       case CASE(      TUVLONG,        TUINT):
       case CASE(      TUVLONG,        TLONG):
       case CASE(      TUVLONG,        TULONG):
               a = AMOVQL;
               if(f->op == OCONST) {
                       f->vconst &= 0xffffffffU;
                       a = AMOVL;
               }
               break;

       case CASE(      TIND,   TIND):
       case CASE(      TIND,   TVLONG):
       case CASE(      TIND,   TUVLONG):
       case CASE(      TVLONG, TIND):
       case CASE(      TVLONG, TVLONG):
       case CASE(      TVLONG, TUVLONG):
       case CASE(      TUVLONG,        TIND):
       case CASE(      TUVLONG,        TVLONG):
       case CASE(      TUVLONG,        TUVLONG):
               a = AMOVQ;
               break;

       case CASE(      TSHORT, TINT):
       case CASE(      TSHORT, TUINT):
       case CASE(      TSHORT, TLONG):
       case CASE(      TSHORT, TULONG):
               a = AMOVWLSX;
               if(f->op == OCONST) {
                       f->vconst &= 0xffff;
                       if(f->vconst & 0x8000)
                               f->vconst |= 0xffff0000;
                       a = AMOVL;
               }
               break;

       case CASE(      TSHORT, TVLONG):
       case CASE(      TSHORT, TUVLONG):
       case CASE(      TSHORT, TIND):
               a = AMOVWQSX;
               if(f->op == OCONST) {
                       f->vconst &= 0xffff;
                       if(f->vconst & 0x8000){
                               f->vconst |= 0xffff0000;
                               f->vconst |= (vlong)~0 << 32;
                       }
                       a = AMOVL;
               }
               break;

       case CASE(      TUSHORT,TINT):
       case CASE(      TUSHORT,TUINT):
       case CASE(      TUSHORT,TLONG):
       case CASE(      TUSHORT,TULONG):
               a = AMOVWLZX;
               if(f->op == OCONST) {
                       f->vconst &= 0xffff;
                       a = AMOVL;
               }
               break;

       case CASE(      TUSHORT,TVLONG):
       case CASE(      TUSHORT,TUVLONG):
       case CASE(      TUSHORT,TIND):
               a = AMOVWQZX;
               if(f->op == OCONST) {
                       f->vconst &= 0xffff;
                       a = AMOVL;      /* MOVL also zero-extends to 64 bits */
               }
               break;

       case CASE(      TCHAR,  TSHORT):
       case CASE(      TCHAR,  TUSHORT):
       case CASE(      TCHAR,  TINT):
       case CASE(      TCHAR,  TUINT):
       case CASE(      TCHAR,  TLONG):
       case CASE(      TCHAR,  TULONG):
               a = AMOVBLSX;
               if(f->op == OCONST) {
                       f->vconst &= 0xff;
                       if(f->vconst & 0x80)
                               f->vconst |= 0xffffff00;
                       a = AMOVL;
               }
               break;

       case CASE(      TCHAR,  TVLONG):
       case CASE(      TCHAR,  TUVLONG):
       case CASE(      TCHAR,  TIND):
               a = AMOVBQSX;
               if(f->op == OCONST) {
                       f->vconst &= 0xff;
                       if(f->vconst & 0x80){
                               f->vconst |= 0xffffff00;
                               f->vconst |= (vlong)~0 << 32;
                       }
                       a = AMOVQ;
               }
               break;

       case CASE(      TUCHAR, TSHORT):
       case CASE(      TUCHAR, TUSHORT):
       case CASE(      TUCHAR, TINT):
       case CASE(      TUCHAR, TUINT):
       case CASE(      TUCHAR, TLONG):
       case CASE(      TUCHAR, TULONG):
               a = AMOVBLZX;
               if(f->op == OCONST) {
                       f->vconst &= 0xff;
                       a = AMOVL;
               }
               break;

       case CASE(      TUCHAR, TVLONG):
       case CASE(      TUCHAR, TUVLONG):
       case CASE(      TUCHAR, TIND):
               a = AMOVBQZX;
               if(f->op == OCONST) {
                       f->vconst &= 0xff;
                       a = AMOVL;      /* zero-extends to 64-bits */
               }
               break;

/*
* float to fix
*/
       case CASE(      TFLOAT, TCHAR):
       case CASE(      TFLOAT, TUCHAR):
       case CASE(      TFLOAT, TSHORT):
       case CASE(      TFLOAT, TUSHORT):
       case CASE(      TFLOAT, TINT):
       case CASE(      TFLOAT, TUINT):
       case CASE(      TFLOAT, TLONG):
       case CASE(      TFLOAT, TULONG):
       case CASE(      TFLOAT, TVLONG):
       case CASE(      TFLOAT, TUVLONG):
       case CASE(      TFLOAT, TIND):

       case CASE(      TDOUBLE,TCHAR):
       case CASE(      TDOUBLE,TUCHAR):
       case CASE(      TDOUBLE,TSHORT):
       case CASE(      TDOUBLE,TUSHORT):
       case CASE(      TDOUBLE,TINT):
       case CASE(      TDOUBLE,TUINT):
       case CASE(      TDOUBLE,TLONG):
       case CASE(      TDOUBLE,TULONG):
       case CASE(      TDOUBLE,TVLONG):
       case CASE(      TDOUBLE,TUVLONG):
       case CASE(      TDOUBLE,TIND):
               regalloc(&nod, t, Z);
               if(ewidth[tt] == SZ_VLONG || typeu[tt] && ewidth[tt] == SZ_INT){
                       if(ft == TFLOAT)
                               a = ACVTTSS2SQ;
                       else
                               a = ACVTTSD2SQ;
               }else{
                       if(ft == TFLOAT)
                               a = ACVTTSS2SL;
                       else
                               a = ACVTTSD2SL;
               }
               gins(a, f, &nod);
               gmove(&nod, t);
               regfree(&nod);
               return;

/*
* ulong to float
*/
       case CASE(      TUVLONG,        TDOUBLE):
       case CASE(      TUVLONG,        TFLOAT):
               a = ACVTSQ2SS;
               if(tt == TDOUBLE)
                       a = ACVTSQ2SD;
               regalloc(&nod, f, f);
               gmove(f, &nod);
               regalloc(&nod1, t, t);
               gins(ACMPQ, &nod, nodconst(0));
               gins(AJLT, Z, Z);
               p1 = p;
               gins(a, &nod, &nod1);
               gins(AJMP, Z, Z);
               p2 = p;
               patch(p1, pc);
               regalloc(&nod2, f, Z);
               regalloc(&nod3, f, Z);
               gmove(&nod, &nod2);
               gins(ASHRQ, nodconst(1), &nod2);
               gmove(&nod, &nod3);
               gins(AANDL, nodconst(1), &nod3);
               gins(AORQ, &nod3, &nod2);
               gins(a, &nod2, &nod1);
               gins(tt == TDOUBLE? AADDSD: AADDSS, &nod1, &nod1);
               regfree(&nod2);
               regfree(&nod3);
               patch(p2, pc);
               regfree(&nod);
               regfree(&nod1);
               return;

       case CASE(      TULONG, TDOUBLE):
       case CASE(      TUINT,  TDOUBLE):
       case CASE(      TULONG, TFLOAT):
       case CASE(      TUINT,  TFLOAT):
               a = ACVTSQ2SS;
               if(tt == TDOUBLE)
                       a = ACVTSQ2SD;
               regalloc(&nod, f, f);
               gins(AMOVLQZX, f, &nod);
               regalloc(&nod1, t, t);
               gins(a, &nod, &nod1);
               gmove(&nod1, t);
               regfree(&nod);
               regfree(&nod1);
               return;

/*
* fix to float
*/
       case CASE(      TCHAR,  TFLOAT):
       case CASE(      TUCHAR, TFLOAT):
       case CASE(      TSHORT, TFLOAT):
       case CASE(      TUSHORT,TFLOAT):
       case CASE(      TINT,   TFLOAT):
       case CASE(      TLONG,  TFLOAT):
       case CASE(      TVLONG, TFLOAT):
       case CASE(      TIND,   TFLOAT):

       case CASE(      TCHAR,  TDOUBLE):
       case CASE(      TUCHAR, TDOUBLE):
       case CASE(      TSHORT, TDOUBLE):
       case CASE(      TUSHORT,TDOUBLE):
       case CASE(      TINT,   TDOUBLE):
       case CASE(      TLONG,  TDOUBLE):
       case CASE(      TVLONG, TDOUBLE):
       case CASE(      TIND,   TDOUBLE):
               regalloc(&nod, t, t);
               if(ewidth[ft] == SZ_VLONG){
                       if(tt == TFLOAT)
                               a = ACVTSQ2SS;
                       else
                               a = ACVTSQ2SD;
               }else{
                       if(tt == TFLOAT)
                               a = ACVTSL2SS;
                       else
                               a = ACVTSL2SD;
               }
               gins(a, f, &nod);
               gmove(&nod, t);
               regfree(&nod);
               return;

/*
* float to float
*/
       case CASE(      TFLOAT, TFLOAT):
               a = AMOVSS;
               break;
       case CASE(      TDOUBLE,TFLOAT):
               a = ACVTSD2SS;
               break;
       case CASE(      TFLOAT, TDOUBLE):
               a = ACVTSS2SD;
               break;
       case CASE(      TDOUBLE,TDOUBLE):
               a = AMOVSD;
               break;
       }
       if(a == AMOVQ || a == AMOVSD || a == AMOVSS || a == AMOVL && ewidth[ft] == ewidth[tt])  /* TO DO: check AMOVL */
       if(samaddr(f, t))
               return;
       gins(a, f, t);
}

static int
regused(Node *n, int r)
{
       if(n == nil)
               return 0;
       if(isreg(n, r))
               return 1;
       return regused(n->left, r) || regused(n->right, r);
}

void
doindex(Node *n, Node *o)
{
       Node nod, nod1;
       long v;

if(debug['Y'])
prtree(n, "index");

if(n->left->complex >= FNX)
print("botch in doindex\n");

       if(n->right->op == OREGISTER)
               o = n->right;
       else if(o == Z || o->op != OREGISTER || regused(n, o->reg))
               o = Z;
       regalloc(&nod, &qregnode, o);
       v = constnode.vconst;
       cgen(n->right, &nod);
       idx.ptr = D_NONE;
       if(n->left->op == OCONST)
               idx.ptr = D_CONST;
       else if(n->left->op == OREGISTER)
               idx.ptr = n->left->reg;
       else if(n->left->op != OADDR) {
               reg[D_BP]++;    // cant be used as a base
               reg[D_R13]++;
               regalloc(&nod1, &qregnode, Z);
               cgen(n->left, &nod1);
               idx.ptr = nod1.reg;
               regfree(&nod1);
               reg[D_BP]--;
               reg[D_R13]--;
       }
       idx.reg = nod.reg;
       regfree(&nod);
       constnode.vconst = v;
}

void
gins(int a, Node *f, Node *t)
{

       if(f != Z && f->op == OINDEX)
               doindex(f, a == AMOVL || a == ALEAL
                       || a == AMOVQ || a == ALEAQ
                       || a == AMOVBLSX || a == AMOVBLZX
                       || a == AMOVBQSX || a == AMOVBQZX
                       || a == AMOVWLSX || a == AMOVWLZX
                       || a == AMOVWQSX || a == AMOVWQZX ? t : Z);
       if(t != Z && t->op == OINDEX)
               doindex(t, Z);
       nextpc();
       p->as = a;
       if(f != Z)
               naddr(f, &p->from);
       if(t != Z)
               naddr(t, &p->to);
       if(debug['g'])
               print("%P\n", p);
}

void
gopcode(int o, Type *ty, Node *f, Node *t)
{
       int a, et;

       et = TLONG;
       if(ty != T)
               et = ty->etype;
       if(debug['M']) {
               if(f != Z && f->type != T)
                       print("gop: %O %O[%s],", o, f->op, tnames[et]);
               else
                       print("gop: %O Z,", o);
               if(t != Z && t->type != T)
                       print("%O[%s]\n", t->op, tnames[t->type->etype]);
               else
                       print("Z\n");
       }
       a = AGOK;
       switch(o) {
       case OCOM:
               a = ANOTL;
               if(et == TCHAR || et == TUCHAR)
                       a = ANOTB;
               if(et == TSHORT || et == TUSHORT)
                       a = ANOTW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ANOTQ;
               break;

       case ONEG:
               a = ANEGL;
               if(et == TCHAR || et == TUCHAR)
                       a = ANEGB;
               if(et == TSHORT || et == TUSHORT)
                       a = ANEGW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ANEGQ;
               break;

       case OADDR:
               a = ALEAQ;
               break;

       case OASADD:
       case OADD:
               a = AADDL;
               if(et == TCHAR || et == TUCHAR)
                       a = AADDB;
               if(et == TSHORT || et == TUSHORT)
                       a = AADDW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AADDQ;
               if(et == TFLOAT)
                       a = AADDSS;
               if(et == TDOUBLE)
                       a = AADDSD;
               break;

       case OASSUB:
       case OSUB:
               a = ASUBL;
               if(et == TCHAR || et == TUCHAR)
                       a = ASUBB;
               if(et == TSHORT || et == TUSHORT)
                       a = ASUBW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ASUBQ;
               if(et == TFLOAT)
                       a = ASUBSS;
               if(et == TDOUBLE)
                       a = ASUBSD;
               break;

       case OASOR:
       case OOR:
               a = AORL;
               if(et == TCHAR || et == TUCHAR)
                       a = AORB;
               if(et == TSHORT || et == TUSHORT)
                       a = AORW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AORQ;
               break;

       case OASAND:
       case OAND:
               a = AANDL;
               if(et == TCHAR || et == TUCHAR)
                       a = AANDB;
               if(et == TSHORT || et == TUSHORT)
                       a = AANDW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AANDQ;
               break;

       case OASXOR:
       case OXOR:
               a = AXORL;
               if(et == TCHAR || et == TUCHAR)
                       a = AXORB;
               if(et == TSHORT || et == TUSHORT)
                       a = AXORW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AXORQ;
               break;

       case OASLSHR:
       case OLSHR:
               a = ASHRL;
               if(et == TCHAR || et == TUCHAR)
                       a = ASHRB;
               if(et == TSHORT || et == TUSHORT)
                       a = ASHRW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ASHRQ;
               break;

       case OASASHR:
       case OASHR:
               a = ASARL;
               if(et == TCHAR || et == TUCHAR)
                       a = ASARB;
               if(et == TSHORT || et == TUSHORT)
                       a = ASARW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ASARQ;
               break;

       case OASASHL:
       case OASHL:
               a = ASALL;
               if(et == TCHAR || et == TUCHAR)
                       a = ASALB;
               if(et == TSHORT || et == TUSHORT)
                       a = ASALW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ASALQ;
               break;

       case OROL:
               a = AROLL;
               if(et == TCHAR || et == TUCHAR)
                       a = AROLB;
               if(et == TSHORT || et == TUSHORT)
                       a = AROLW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AROLQ;
               break;

       case OFUNC:
               a = ACALL;
               break;

       case OASMUL:
       case OMUL:
               if(f->op == OREGISTER && t != Z && isreg(t, D_AX) && reg[D_DX] == 0)
                       t = Z;
               a = AIMULL;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AIMULQ;
               if(et == TFLOAT)
                       a = AMULSS;
               if(et == TDOUBLE)
                       a = AMULSD;
               break;

       case OASMOD:
       case OMOD:
       case OASDIV:
       case ODIV:
               a = AIDIVL;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AIDIVQ;
               if(et == TFLOAT)
                       a = ADIVSS;
               if(et == TDOUBLE)
                       a = ADIVSD;
               break;

       case OASLMUL:
       case OLMUL:
               a = AMULL;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = AMULQ;
               break;

       case OASLMOD:
       case OLMOD:
       case OASLDIV:
       case OLDIV:
               a = ADIVL;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ADIVQ;
               break;

       case OEQ:
       case ONE:
       case OLT:
       case OLE:
       case OGE:
       case OGT:
       case OLO:
       case OLS:
       case OHS:
       case OHI:
               a = ACMPL;
               if(et == TCHAR || et == TUCHAR)
                       a = ACMPB;
               if(et == TSHORT || et == TUSHORT)
                       a = ACMPW;
               if(et == TVLONG || et == TUVLONG || et == TIND)
                       a = ACMPQ;
               if(et == TFLOAT)
                       a = AUCOMISS;
               if(et == TDOUBLE)
                       a = AUCOMISD;
               gins(a, f, t);
               switch(o) {
               case OEQ:       a = AJEQ; break;
               case ONE:       a = AJNE; break;
               case OLT:       a = AJLT; break;
               case OLE:       a = AJLE; break;
               case OGE:       a = AJGE; break;
               case OGT:       a = AJGT; break;
               case OLO:       a = AJCS; break;
               case OLS:       a = AJLS; break;
               case OHS:       a = AJCC; break;
               case OHI:       a = AJHI; break;
               }
               gins(a, Z, Z);
               return;
       }
       if(a == AGOK)
               diag(Z, "bad in gopcode %O", o);
       gins(a, f, t);
}

int
samaddr(Node *f, Node *t)
{
       return f->op == OREGISTER && t->op == OREGISTER && f->reg == t->reg;
}

void
gbranch(int o)
{
       int a;

       a = AGOK;
       switch(o) {
       case ORETURN:
               a = ARET;
               break;
       case OGOTO:
               a = AJMP;
               break;
       }
       nextpc();
       if(a == AGOK) {
               diag(Z, "bad in gbranch %O",  o);
               nextpc();
       }
       p->as = a;
}

void
patch(Prog *op, long pc)
{

       op->to.offset = pc;
       op->to.type = D_BRANCH;
}

void
gpseudo(int a, Sym *s, Node *n)
{

       nextpc();
       p->as = a;
       p->from.type = D_EXTERN;
       p->from.sym = s;
       p->from.scale = (profileflg ? 0 : NOPROF);
       if(s->class == CSTATIC)
               p->from.type = D_STATIC;
       naddr(n, &p->to);
       if(a == ADATA || a == AGLOBL)
               pc--;
}

int
sconst(Node *n)
{
       long v;

       if(n->op == OCONST && !typefd[n->type->etype]) {
               v = n->vconst;
               if(v >= -32766L && v < 32766L)
                       return 1;
       }
       return 0;
}

long
exreg(Type *t)
{
       long o;

       if(typechlpv[t->etype]) {
               if(exregoffset <= REGEXT-4)
                       return 0;
               o = exregoffset;
               exregoffset--;
               return o;
       }
       return 0;
}

schar   ewidth[NTYPE] =
{
       -1,             /*[TXXX]*/
       SZ_CHAR,        /*[TCHAR]*/
       SZ_CHAR,        /*[TUCHAR]*/
       SZ_SHORT,       /*[TSHORT]*/
       SZ_SHORT,       /*[TUSHORT]*/
       SZ_INT,         /*[TINT]*/
       SZ_INT,         /*[TUINT]*/
       SZ_LONG,        /*[TLONG]*/
       SZ_LONG,        /*[TULONG]*/
       SZ_VLONG,       /*[TVLONG]*/
       SZ_VLONG,       /*[TUVLONG]*/
       SZ_FLOAT,       /*[TFLOAT]*/
       SZ_DOUBLE,      /*[TDOUBLE]*/
       SZ_IND,         /*[TIND]*/
       0,              /*[TFUNC]*/
       -1,             /*[TARRAY]*/
       0,              /*[TVOID]*/
       -1,             /*[TSTRUCT]*/
       -1,             /*[TUNION]*/
       SZ_INT,         /*[TENUM]*/
};
long    ncast[NTYPE] =
{
       0,                              /*[TXXX]*/
       BCHAR|BUCHAR,                   /*[TCHAR]*/
       BCHAR|BUCHAR,                   /*[TUCHAR]*/
       BSHORT|BUSHORT,                 /*[TSHORT]*/
       BSHORT|BUSHORT,                 /*[TUSHORT]*/
       BINT|BUINT|BLONG|BULONG,        /*[TINT]*/
       BINT|BUINT|BLONG|BULONG,        /*[TUINT]*/
       BINT|BUINT|BLONG|BULONG,        /*[TLONG]*/
       BINT|BUINT|BLONG|BULONG,        /*[TULONG]*/
       BVLONG|BUVLONG|BIND,                    /*[TVLONG]*/
       BVLONG|BUVLONG|BIND,                    /*[TUVLONG]*/
       BFLOAT,                         /*[TFLOAT]*/
       BDOUBLE,                        /*[TDOUBLE]*/
       BVLONG|BUVLONG|BIND,            /*[TIND]*/
       0,                              /*[TFUNC]*/
       0,                              /*[TARRAY]*/
       0,                              /*[TVOID]*/
       BSTRUCT,                        /*[TSTRUCT]*/
       BUNION,                         /*[TUNION]*/
       0,                              /*[TENUM]*/
};