root/src/cmd/5c/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. garg1
  7. nodconst
  8. nod32const
  9. nodfconst
  10. nodreg
  11. regret
  12. tmpreg
  13. regalloc
  14. regialloc
  15. regfree
  16. regsalloc
  17. regaalloc1
  18. regaalloc
  19. regind
  20. raddr
  21. naddr
  22. fop
  23. gmovm
  24. gmove
  25. gmover
  26. gins
  27. gopcode
  28. samaddr
  29. gbranch
  30. patch
  31. gpseudo
  32. gpcdata
  33. gprefetch
  34. sconst
  35. sval
  36. exreg

// Inferno utils/5c/txt.c
// http://code.google.com/p/inferno-os/source/browse/utils/5c/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 = '5';
char *thestring = "arm";

LinkArch        *thelinkarch = &linkarm;

void
linkarchinit(void)
{
}

void
ginit(void)
{
        Type *t;

        exregoffset = REGEXT;
        exfregoffset = FREGEXT;
        listinit();
        nstring = 0;
        mnstring = 0;
        nrathole = 0;
        pc = 0;
        breakpc = -1;
        continpc = -1;
        cases = C;
        lastp = P;
        tfield = types[TLONG];

        zprog.link = P;
        zprog.as = AGOK;
        zprog.reg = NREG;
        zprog.from.type = D_NONE;
        zprog.from.name = D_NONE;
        zprog.from.reg = NREG;
        zprog.to = zprog.from;
        zprog.scond = 0xE;

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

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

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

        com64init();

        memset(reg, 0, sizeof(reg));
}

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

        for(i=0; i<NREG; i++)
                if(reg[i])
                        diag(Z, "reg %d left allocated", i);
        for(i=NREG; i<NREG+NFREG; i++)
                if(reg[i])
                        diag(Z, "freg %d left allocated", i-NREG);
        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;
        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;
}

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(typesuv[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 && typechlp[n->type->etype]) {
                regaalloc1(tn1, n);
                if(n->complex >= FNX) {
                        cgen(*fnxp, tn1);
                        (*fnxp)++;
                } else
                        cgen(n, tn1);
                return;
        }
        regalloc(tn1, n, Z);
        if(n->complex >= FNX) {
                cgen(*fnxp, tn1);
                (*fnxp)++;
        } else
                cgen(n, tn1);
        regaalloc(tn2, n);
        gopcode(OAS, tn1, Z, tn2);
        regfree(tn1);
}

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

Node*
nod32const(vlong v)
{
        constnode.vconst = v & MASK(32);
        return &constnode;
}

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

void
nodreg(Node *n, Node *nn, int reg)
{
        *n = regnode;
        n->reg = reg;
        n->type = nn->type;
        n->lineno = nn->lineno;
}

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

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

int
tmpreg(void)
{
        int i;

        for(i=REGRET+1; i<NREG; i++)
                if(reg[i] == 0)
                        return i;
        diag(Z, "out of fixed registers");
        return 0;
}

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 TIND:
                if(o != Z && o->op == OREGISTER) {
                        i = o->reg;
                        if(i >= 0 && i < NREG)
                                goto out;
                }
                for(i=REGRET+1; i<=REGEXT-2; i++)
                        if(reg[i] == 0)
                                goto out;
                diag(tn, "out of fixed registers");
                goto err;

        case TFLOAT:
        case TDOUBLE:
        case TVLONG:
                if(o != Z && o->op == OREGISTER) {
                        i = o->reg;
                        if(i >= NREG && i < NREG+NFREG)
                                goto out;
                }
                for(i=NREG; i<NREG+NFREG; i++)
                        if(reg[i] == 0)
                                goto out;
                diag(tn, "out of float registers");
                goto err;
        }
        diag(tn, "unknown type in regalloc: %T", tn->type);
err:
        nodreg(n, tn, 0);
        return;
out:
        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: %d", 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[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 + SZ_LONG;
        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
raddr(Node *n, Prog *p)
{
        Addr a;

        naddr(n, &a);
        if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
                a.type = D_REG;
                a.reg = 0;
        }
        if(a.type != D_REG && a.type != D_FREG) {
                if(n)
                        diag(n, "bad in raddr: %O", n->op);
                else
                        diag(n, "bad in raddr: <null>");
                p->reg = NREG;
        } else
                p->reg = a.reg;
}

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", n->op);
                break;

        case OREGISTER:
                a->type = D_REG;
                a->sym = nil;
                a->reg = n->reg;
                if(a->reg >= NREG) {
                        a->type = D_FREG;
                        a->reg -= NREG;
                }
                break;

        case OIND:
                naddr(n->left, a);
                if(a->type == D_REG) {
                        a->type = D_OREG;
                        break;
                }
                if(a->type == D_CONST) {
                        a->type = D_OREG;
                        break;
                }
                goto bad;

        case OINDREG:
                a->type = D_OREG;
                a->sym = nil;
                a->offset = n->xoffset;
                a->reg = n->reg;
                break;

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

        case OCONST:
                a->sym = nil;
                a->reg = NREG;
                if(typefd[n->type->etype]) {
                        a->type = D_FCONST;
                        a->u.dval = n->fconst;
                } else {
                        a->type = D_CONST;
                        a->offset = n->vconst;
                }
                break;

        case OADDR:
                naddr(n->left, a);
                if(a->type == D_OREG) {
                        a->type = D_CONST;
                        break;
                }
                goto bad;

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

        }
}

void
fop(int as, int f1, int f2, Node *t)
{
        Node nod1, nod2, nod3;

        nodreg(&nod1, t, NREG+f1);
        nodreg(&nod2, t, NREG+f2);
        regalloc(&nod3, t, t);
        gopcode(as, &nod1, &nod2, &nod3);
        gmove(&nod3, t);
        regfree(&nod3);
}

void
gmovm(Node *f, Node *t, int w)
{
        gins(AMOVM, f, t);
        p->scond |= C_UBIT;
        if(w)
                p->scond |= C_WBIT;
}

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

        ft = f->type->etype;
        tt = t->type->etype;

        if(ft == TDOUBLE && f->op == OCONST) {
        }
        if(ft == TFLOAT && f->op == OCONST) {
        }

        /*
         * a load --
         * put it into a register then
         * worry what to do with it.
         */
        if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
                switch(ft) {
                default:
                        a = AMOVW;
                        break;
                case TFLOAT:
                        a = AMOVF;
                        break;
                case TDOUBLE:
                        a = AMOVD;
                        break;
                case TCHAR:
                        a = AMOVBS;
                        break;
                case TUCHAR:
                        a = AMOVBU;
                        break;
                case TSHORT:
                        a = AMOVHS;
                        break;
                case TUSHORT:
                        a = AMOVHU;
                        break;
                }
                if(typechlp[ft] && typeilp[tt])
                        regalloc(&nod, t, t);
                else
                        regalloc(&nod, f, t);
                gins(a, f, &nod);
                gmove(&nod, t);
                regfree(&nod);
                return;
        }

        /*
         * a store --
         * put it into a register then
         * store it.
         */
        if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
                switch(tt) {
                default:
                        a = AMOVW;
                        break;
                case TUCHAR:
                        a = AMOVBU;
                        break;
                case TCHAR:
                        a = AMOVBS;
                        break;
                case TUSHORT:
                        a = AMOVHU;
                        break;
                case TSHORT:
                        a = AMOVHS;
                        break;
                case TFLOAT:
                        a = AMOVF;
                        break;
                case TVLONG:
                case TDOUBLE:
                        a = AMOVD;
                        break;
                }
                if(ft == tt)
                        regalloc(&nod, t, f);
                else
                        regalloc(&nod, t, Z);
                gmove(f, &nod);
                gins(a, &nod, t);
                regfree(&nod);
                return;
        }

        /*
         * type x type cross table
         */
        a = AGOK;
        switch(ft) {
        case TDOUBLE:
        case TVLONG:
        case TFLOAT:
                switch(tt) {
                case TDOUBLE:
                case TVLONG:
                        a = AMOVD;
                        if(ft == TFLOAT)
                                a = AMOVFD;
                        break;
                case TFLOAT:
                        a = AMOVDF;
                        if(ft == TFLOAT)
                                a = AMOVF;
                        break;
                case TINT:
                case TUINT:
                case TLONG:
                case TULONG:
                case TIND:
                        a = AMOVDW;
                        if(ft == TFLOAT)
                                a = AMOVFW;
                        break;
                case TSHORT:
                case TUSHORT:
                case TCHAR:
                case TUCHAR:
                        a = AMOVDW;
                        if(ft == TFLOAT)
                                a = AMOVFW;
                        break;
                }
                break;
        case TUINT:
        case TULONG:
                if(tt == TFLOAT || tt == TDOUBLE) {
                        // ugly and probably longer than necessary,
                        // but vfp has a single instruction for this,
                        // so hopefully it won't last long.
                        //
                        //      tmp = f
                        //      tmp1 = tmp & 0x80000000
                        //      tmp ^= tmp1
                        //      t = float(int32(tmp))
                        //      if(tmp1)
                        //              t += 2147483648.
                        //
                        regalloc(&nod, f, Z);
                        regalloc(&nod1, f, Z);
                        gins(AMOVW, f, &nod);
                        gins(AMOVW, &nod, &nod1);
                        gins(AAND, nodconst(0x80000000), &nod1);
                        gins(AEOR, &nod1, &nod);
                        if(tt == TFLOAT)
                                gins(AMOVWF, &nod, t);
                        else
                                gins(AMOVWD, &nod, t);
                        gins(ACMP, nodconst(0), Z);
                        raddr(&nod1, p);
                        gins(ABEQ, Z, Z);
                        regfree(&nod);
                        regfree(&nod1);
                        p1 = p;
                        regalloc(&nod, t, Z);
                        gins(AMOVF, nodfconst(2147483648.), &nod);
                        gins(AADDF, &nod, t);
                        regfree(&nod);
                        patch(p1, pc);
                        return;
                }
                // fall through
        
        case TINT:
        case TLONG:
        case TIND:
                switch(tt) {
                case TDOUBLE:
                        gins(AMOVWD, f, t);
                        return;
                case TFLOAT:
                        gins(AMOVWF, f, t);
                        return;
                case TINT:
                case TUINT:
                case TLONG:
                case TULONG:
                case TIND:
                case TSHORT:
                case TUSHORT:
                case TCHAR:
                case TUCHAR:
                        a = AMOVW;
                        break;
                }
                break;
        case TSHORT:
                switch(tt) {
                case TDOUBLE:
                        regalloc(&nod, f, Z);
                        gins(AMOVHS, f, &nod);
                        gins(AMOVWD, &nod, t);
                        regfree(&nod);
                        return;
                case TFLOAT:
                        regalloc(&nod, f, Z);
                        gins(AMOVHS, f, &nod);
                        gins(AMOVWF, &nod, t);
                        regfree(&nod);
                        return;
                case TUINT:
                case TINT:
                case TULONG:
                case TLONG:
                case TIND:
                        a = AMOVHS;
                        break;
                case TSHORT:
                case TUSHORT:
                case TCHAR:
                case TUCHAR:
                        a = AMOVW;
                        break;
                }
                break;
        case TUSHORT:
                switch(tt) {
                case TDOUBLE:
                        regalloc(&nod, f, Z);
                        gins(AMOVHU, f, &nod);
                        gins(AMOVWD, &nod, t);
                        regfree(&nod);
                        return;
                case TFLOAT:
                        regalloc(&nod, f, Z);
                        gins(AMOVHU, f, &nod);
                        gins(AMOVWF, &nod, t);
                        regfree(&nod);
                        return;
                case TINT:
                case TUINT:
                case TLONG:
                case TULONG:
                case TIND:
                        a = AMOVHU;
                        break;
                case TSHORT:
                case TUSHORT:
                case TCHAR:
                case TUCHAR:
                        a = AMOVW;
                        break;
                }
                break;
        case TCHAR:
                switch(tt) {
                case TDOUBLE:
                        regalloc(&nod, f, Z);
                        gins(AMOVBS, f, &nod);
                        gins(AMOVWD, &nod, t);
                        regfree(&nod);
                        return;
                case TFLOAT:
                        regalloc(&nod, f, Z);
                        gins(AMOVBS, f, &nod);
                        gins(AMOVWF, &nod, t);
                        regfree(&nod);
                        return;
                case TINT:
                case TUINT:
                case TLONG:
                case TULONG:
                case TIND:
                case TSHORT:
                case TUSHORT:
                        a = AMOVBS;
                        break;
                case TCHAR:
                case TUCHAR:
                        a = AMOVW;
                        break;
                }
                break;
        case TUCHAR:
                switch(tt) {
                case TDOUBLE:
                        regalloc(&nod, f, Z);
                        gins(AMOVBU, f, &nod);
                        gins(AMOVWD, &nod, t);
                        regfree(&nod);
                        return;
                case TFLOAT:
                        regalloc(&nod, f, Z);
                        gins(AMOVBU, f, &nod);
                        gins(AMOVWF, &nod, t);
                        regfree(&nod);
                        return;
                case TINT:
                case TUINT:
                case TLONG:
                case TULONG:
                case TIND:
                case TSHORT:
                case TUSHORT:
                        a = AMOVBU;
                        break;
                case TCHAR:
                case TUCHAR:
                        a = AMOVW;
                        break;
                }
                break;
        }
        if(a == AGOK)
                diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
        if(a == AMOVW || a == AMOVF || a == AMOVD)
        if(samaddr(f, t))
                return;
        gins(a, f, t);
}

void
gmover(Node *f, Node *t)
{
        int ft, tt, a;

        ft = f->type->etype;
        tt = t->type->etype;
        a = AGOK;
        if(typechlp[ft] && typechlp[tt] && ewidth[ft] >= ewidth[tt]){
                switch(tt){
                case TSHORT:
                        a = AMOVHS;
                        break;
                case TUSHORT:
                        a = AMOVHU;
                        break;
                case TCHAR:
                        a = AMOVBS;
                        break;
                case TUCHAR:
                        a = AMOVBU;
                        break;
                }
        }
        if(a == AGOK)
                gmove(f, t);
        else
                gins(a, f, t);
}

void
gins(int a, Node *f, Node *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, Node *f1, Node *f2, Node *t)
{
        int a, et;
        Addr ta;

        et = TLONG;
        if(f1 != Z && f1->type != T)
                et = f1->type->etype;
        a = AGOK;
        switch(o) {
        case OAS:
                gmove(f1, t);
                return;

        case OASADD:
        case OADD:
                a = AADD;
                if(et == TFLOAT)
                        a = AADDF;
                else
                if(et == TDOUBLE || et == TVLONG)
                        a = AADDD;
                break;

        case OASSUB:
        case OSUB:
                if(f2 && f2->op == OCONST) {
                        Node *t = f1;
                        f1 = f2;
                        f2 = t;
                        a = ARSB;
                } else
                        a = ASUB;
                if(et == TFLOAT)
                        a = ASUBF;
                else
                if(et == TDOUBLE || et == TVLONG)
                        a = ASUBD;
                break;

        case OASOR:
        case OOR:
                a = AORR;
                break;

        case OASAND:
        case OAND:
                a = AAND;
                break;

        case OASXOR:
        case OXOR:
                a = AEOR;
                break;

        case OASLSHR:
        case OLSHR:
                a = ASRL;
                break;

        case OASASHR:
        case OASHR:
                a = ASRA;
                break;

        case OASASHL:
        case OASHL:
                a = ASLL;
                break;

        case OFUNC:
                a = ABL;
                break;

        case OASMUL:
        case OMUL:
                a = AMUL;
                if(et == TFLOAT)
                        a = AMULF;
                else
                if(et == TDOUBLE || et == TVLONG)
                        a = AMULD;
                break;

        case OASDIV:
        case ODIV:
                a = ADIV;
                if(et == TFLOAT)
                        a = ADIVF;
                else
                if(et == TDOUBLE || et == TVLONG)
                        a = ADIVD;
                break;

        case OASMOD:
        case OMOD:
                a = AMOD;
                break;

        case OASLMUL:
        case OLMUL:
                a = AMULU;
                break;

        case OASLMOD:
        case OLMOD:
                a = AMODU;
                break;

        case OASLDIV:
        case OLDIV:
                a = ADIVU;
                break;

        case OCASE:
        case OEQ:
        case ONE:
        case OLT:
        case OLE:
        case OGE:
        case OGT:
        case OLO:
        case OLS:
        case OHS:
        case OHI:
                a = ACMP;
                if(et == TFLOAT)
                        a = ACMPF;
                else
                if(et == TDOUBLE || et == TVLONG)
                        a = ACMPD;
                nextpc();
                p->as = a;
                naddr(f1, &p->from);
                if(a == ACMP && f1->op == OCONST && p->from.offset < 0) {
                        p->as = ACMN;
                        p->from.offset = -p->from.offset;
                }
                raddr(f2, p);
                switch(o) {
                case OEQ:
                        a = ABEQ;
                        break;
                case ONE:
                        a = ABNE;
                        break;
                case OLT:
                        a = ABLT;
                        break;
                case OLE:
                        a = ABLE;
                        break;
                case OGE:
                        a = ABGE;
                        break;
                case OGT:
                        a = ABGT;
                        break;
                case OLO:
                        a = ABLO;
                        break;
                case OLS:
                        a = ABLS;
                        break;
                case OHS:
                        a = ABHS;
                        break;
                case OHI:
                        a = ABHI;
                        break;
                case OCASE:
                        nextpc();
                        p->as = ACASE;
                        p->scond = 0x9;
                        naddr(f2, &p->from);
                        a = ABHI;
                        break;
                }
                f1 = Z;
                f2 = Z;
                break;
        }
        if(a == AGOK)
                diag(Z, "bad in gopcode %O", o);
        nextpc();
        p->as = a;
        if(f1 != Z)
                naddr(f1, &p->from);
        if(f2 != Z) {
                naddr(f2, &ta);
                p->reg = ta.reg;
        }
        if(t != Z)
                naddr(t, &p->to);
        if(debug['g'])
                print("%P\n", p);
}

int
samaddr(Node *f, Node *t)
{

        if(f->op != t->op)
                return 0;
        switch(f->op) {

        case OREGISTER:
                if(f->reg != t->reg)
                        break;
                return 1;
        }
        return 0;
}

void
gbranch(int o)
{
        int a;

        a = AGOK;
        switch(o) {
        case ORETURN:
                a = ARET;
                break;
        case OGOTO:
                a = AB;
                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;
}

void
gpseudo(int a, Sym *s, Node *n)
{
        nextpc();
        p->as = a;
        p->from.type = D_OREG;
        p->from.sym = linksym(s);
        p->from.name = D_EXTERN;

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

        if(s->class == CSTATIC)
                p->from.name = 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(APLD, &n1, Z);
        regfree(&n1);
}

int
sconst(Node *n)
{
        vlong vv;

        if(n->op == OCONST) {
                if(!typefd[n->type->etype]) {
                        vv = n->vconst;
                        if(vv >= (vlong)(-32766) && vv < (vlong)32766)
                                return 1;
                        /*
                         * should be specialised for constant values which will
                         * fit in different instructionsl; for now, let 5l
                         * sort it out
                         */
                        return 1;
                }
        }
        return 0;
}

int
sval(int32 v)
{
        int i;

        for(i=0; i<16; i++) {
                if((v & ~0xff) == 0)
                        return 1;
                if((~v & ~0xff) == 0)
                        return 1;
                v = (v<<2) | ((uint32)v>>30);
        }
        return 0;
}

int32
exreg(Type *t)
{
        int32 o;

        if(typechlp[t->etype]) {
                if(exregoffset <= REGEXT-4)
                        return 0;
                o = exregoffset;
                exregoffset--;
                return o;
        }
        if(typefd[t->etype]) {
                if(exfregoffset <= NFREG-1)
                        return 0;
                o = exfregoffset + NREG;
                exfregoffset--;
                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] */
};

int32   ncast[NTYPE] =
{
        0,                              /* [TXXX] */
        BCHAR|BUCHAR,                   /* [TCHAR] */
        BCHAR|BUCHAR,                   /* [TUCHAR] */
        BSHORT|BUSHORT,                 /* [TSHORT] */
        BSHORT|BUSHORT,                 /* [TUSHORT] */
        BINT|BUINT|BLONG|BULONG|BIND,   /* [TINT] */
        BINT|BUINT|BLONG|BULONG|BIND,   /* [TUINT] */
        BINT|BUINT|BLONG|BULONG|BIND,   /* [TLONG] */
        BINT|BUINT|BLONG|BULONG|BIND,   /* [TULONG] */
        BVLONG|BUVLONG,                 /* [TVLONG] */
        BVLONG|BUVLONG,                 /* [TUVLONG] */
        BFLOAT,                         /* [TFLOAT] */
        BDOUBLE,                        /* [TDOUBLE] */
        BLONG|BULONG|BIND,              /* [TIND] */
        0,                              /* [TFUNC] */
        0,                              /* [TARRAY] */
        0,                              /* [TVOID] */
        BSTRUCT,                        /* [TSTRUCT] */
        BUNION,                         /* [TUNION] */
        0,                              /* [TENUM] */
};

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