root/src/cmd/6c/txt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. linkarchinit
  2. ginit
  3. gclean
  4. nextpc
  5. gargs
  6. nareg
  7. garg1
  8. nodgconst
  9. nodconst
  10. nodfconst
  11. isreg
  12. nodreg
  13. regret
  14. regalloc
  15. regialloc
  16. regfree
  17. regsalloc
  18. regaalloc1
  19. regaalloc
  20. regind
  21. naddr
  22. gcmp
  23. gmove
  24. doindex
  25. gins
  26. gopcode
  27. samaddr
  28. gbranch
  29. patch
  30. gpseudo
  31. gpcdata
  32. gprefetch
  33. sconst
  34. exreg

// Inferno utils/6c/txt.c
// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c
//
//      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//      Portions Copyright © 1997-1999 Vita Nuova Limited
//      Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//      Portions Copyright © 2004,2006 Bruce Ellis
//      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//      Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//      Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "gc.h"

int thechar = '6';
char *thestring = "amd64";

LinkArch        *thelinkarch = &linkamd64;

void
linkarchinit(void)
{
        if(strcmp(getgoarch(), "amd64p32") == 0)
                thelinkarch = &linkamd64p32;
}

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

        dodefine("_64BITREG");
        if(ewidth[TIND] == 8)
                dodefine("_64BIT");
        listinit();
        nstring = 0;
        mnstring = 0;
        nrathole = 0;
        pc = 0;
        breakpc = -1;
        continpc = -1;
        cases = C;
        lastp = P;
        tfield = types[TINT];

        typeword = typechlvp;
        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();

        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;
        }
        if(nacl) {
                reg[D_BP] = 1;
                reg[D_R15] = 1;
        }
}

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

        reg[D_SP]--;
        if(nacl) {
                reg[D_BP]--;
                reg[D_R15]--;
        }
        for(i=D_AX; i<=D_R15; i++)
                if(reg[i])
                        diag(Z, "reg %R left allocated", i);
        for(i=D_X0; i<=D_X7; i++)
                if(reg[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)
{
        Plist *pl;

        p = alloc(sizeof(*p));
        *p = zprog;
        p->lineno = nearln;
        p->pc = pc;
        pc++;
        if(lastp == nil) {
                pl = linknewplist(ctxt);
                pl->firstpc = p;
        } else
                lastp->link = p;
        lastp = p;
}

void
gargs(Node *n, Node *tn1, Node *tn2)
{
        int32 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)
                        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 >= 0 && 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((int32)v);
        vconstnode.vconst = v;
        return &vconstnode;
}

Node*
nodconst(int32 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++)
                        if(reg[i] == 0)
                                goto out;
                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)
                                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 >= nelem(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, nil);
        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)
{
        if(REGARG < 0) {
                fatal(n, "regaalloc1 and REGARG<0");
                return;
        }
        nodreg(n, nn, REGARG);
        reg[(uchar)REGARG]++;
        curarg = align(curarg, nn->type, Aarg1, nil);
        curarg = align(curarg, nn->type, Aarg2, nil);
        maxargsafe = maxround(maxargsafe, cursafe+curarg);
}

void
regaalloc(Node *n, Node *nn)
{
        curarg = align(curarg, nn->type, Aarg1, nil);
        *n = *nn;
        n->op = OINDREG;
        n->reg = REGSP;
        n->xoffset = curarg;
        n->complex = 0;
        n->addable = 20;
        curarg = align(curarg, nn->type, Aarg2, nil);
        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, Addr *a)
{
        int32 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 = nil;
                break;

        case OEXREG:
                a->type = D_INDIR + D_TLS;
                a->offset = n->reg - 1;
                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 = nil;
                a->offset = n->xoffset;
                break;

        case ONAME:
                a->etype = n->etype;
                a->type = D_STATIC;
                a->sym = linksym(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->u.dval = n->fconst;
                        break;
                }
                a->sym = nil;
                a->type = D_CONST;
                if(typev[n->type->etype] || (n->type->etype == TIND && ewidth[TIND] == 8))
                        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;
        if(ewidth[TIND] == 4) {
                if(ft == TIND)
                        ft = TUINT;
                if(tt == TIND)
                        tt = TUINT;
        }
        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(ft == TVLONG || ft == TUVLONG)
        if(f->op == OCONST)
        if(f->vconst > 0x7fffffffLL || f->vconst < -0x7fffffffLL)
        if(t->op != OREGISTER) {
                regalloc(&nod, f, Z);
                gmove(f, &nod);
                gmove(&nod, t);
                regfree(&nod);
                return;
        }

        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:
                a = AMOVQ;
                goto ld;
        case TIND:
                a = AMOVQ;
                if(ewidth[TIND] == 4)
                        a = AMOVL;

        ld:
                regalloc(&nod, f, t);
                nod.type = t64? types[TVLONG]: types[TINT];
                gins(a, f, &nod);
                gmove(&nod, t);
                regfree(&nod);
                return;

        case TFLOAT:
                a = AMOVSS;
                goto fld;
        case TDOUBLE:
                a = AMOVSD;
        fld:
                regalloc(&nod, f, t);
                if(tt != TDOUBLE && tt != TFLOAT){      /* TO DO: why is this here */
                        prtree(f, "odd tree");
                        nod.type = t64? types[TVLONG]: types[TINT];
                }
                gins(a, f, &nod);
                gmove(&nod, t);
                regfree(&nod);
                return;
        }

/*
 * 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 &= (int)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;

/*
 * uvlong 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);
}

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

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

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

        regalloc(&nod, &qregnode, Z);
        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]++;    // can't be used as a base
                regalloc(&nod1, &qregnode, Z);
                cgen(n->left, &nod1);
                idx.ptr = nod1.reg;
                regfree(&nod1);
                reg[D_BP]--;
        }
        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);
        if(t != Z && t->op == OINDEX)
                doindex(t);
        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(et == TIND && ewidth[TIND] == 4)
                et = TUINT;
        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 OROTL:
                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, int32 pc)
{
        op->to.offset = pc;
        op->to.type = D_BRANCH;
        op->to.u.branch = nil;
        op->pcond = nil;
}

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

        nextpc();
        p->as = a;
        p->from.type = D_EXTERN;
        p->from.sym = linksym(s);

        switch(a) {
        case ATEXT:
                p->from.scale = textflag;
                textflag = 0;
                break;
        case AGLOBL:
                p->from.scale = s->dataflag;
                break;
        }

        if(s->class == CSTATIC)
                p->from.type = D_STATIC;
        naddr(n, &p->to);
        if(a == ADATA || a == AGLOBL)
                pc--;
}

void
gpcdata(int index, int value)
{
        Node n1;
        
        n1 = *nodconst(index);
        gins(APCDATA, &n1, nodconst(value));
}

void
gprefetch(Node *n)
{
        Node n1;
        
        regalloc(&n1, n, Z);
        gmove(n, &n1);
        n1.op = OINDREG;
        gins(APREFETCHNTA, &n1, Z);
        regfree(&n1);
}

int
sconst(Node *n)
{
        int32 v;

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

int32
exreg(Type *t)
{
        int32 o;

        if(typechlpv[t->etype]) {
                if(exregoffset >= 64)
                        return 0;
                o = exregoffset;
                exregoffset += ewidth[TIND];
                return o+1;     // +1 to avoid 0 == failure; naddr's case OEXREG will subtract 1.
        }
        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]*/
};
int32   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]*/
};

/* [<][>][^][v][top][bottom][index][help] */