Subversion Repositories planix.SVN

Rev

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

#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++)
                        if(reg[i] == 0 && !resvreg[i])
                                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 && !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:
                a = AMOVQ;
                goto ld;
        case TIND:
                a = AMOVQ;

        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(      TIND,   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(      TIND,   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(      TIND,   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(      TIND,   TUSHORT):

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

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

        case CASE(      TUINT,  TIND):
        case CASE(      TVLONG, TUINT):
        case CASE(      TVLONG, TULONG):
        case CASE(      TUVLONG, TUINT):
        case CASE(      TUVLONG, TULONG):
 *****/
                a = AMOVL;
                break;

        case CASE(      TVLONG, TCHAR):
        case    CASE(   TVLONG, TSHORT):
        case CASE(      TVLONG, TINT):
        case CASE(      TVLONG, TLONG):
        case CASE(      TUVLONG, TCHAR):
        case    CASE(   TUVLONG, TSHORT):
        case CASE(      TUVLONG, TINT):
        case CASE(      TUVLONG, TLONG):
        case CASE(      TINT,   TVLONG):
        case CASE(      TINT,   TUVLONG):
        case CASE(      TLONG,  TVLONG):
        case CASE(      TINT,   TIND):
        case CASE(      TLONG,  TIND):
                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 = AMOVL;      /* same effect as AMOVLQZX */
                if(f->op == OCONST) {
                        f->vconst &= (uvlong)0xffffffffU;
                        a = AMOVQ;
                }
                break;

        case CASE(      TIND,   TVLONG):
        case CASE(      TVLONG, TVLONG):
        case CASE(      TUVLONG,        TVLONG):
        case CASE(      TVLONG, TUVLONG):
        case CASE(      TUVLONG,        TUVLONG):
        case CASE(      TIND,   TUVLONG):
        case CASE(      TVLONG, TIND):
        case CASE(      TUVLONG,        TIND):
        case CASE(      TIND,   TIND):
                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);
}

void
doindex(Node *n)
{
        Node nod, nod1;
        long 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]++;    // cant 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(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 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]*/
};