root/src/cmd/5g/gsubr.c

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

DEFINITIONS

This source file includes following definitions.
  1. clearp
  2. prog
  3. dumpdata
  4. gbranch
  5. patch
  6. unpatch
  7. newplist
  8. gused
  9. gjmp
  10. ggloblnod
  11. gargsize
  12. ggloblsym
  13. gtrack
  14. isfat
  15. afunclit
  16. ginit
  17. gclean
  18. anyregalloc
  19. regalloc
  20. regfree
  21. nodreg
  22. nodindreg
  23. nodarg
  24. ncon
  25. ismem
  26. split64
  27. splitclean
  28. gmove
  29. samaddr
  30. gins
  31. raddr
  32. gcmp
  33. gshift
  34. gregshift
  35. naddr
  36. optoas
  37. sudoclean
  38. dotaddable
  39. sudoaddable

// Derived from 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 <u.h>
#include <libc.h>
#include "gg.h"
#include "../../pkg/runtime/funcdata.h"

// TODO(rsc): Can make this bigger if we move
// the text segment up higher in 5l for all GOOS.
// At the same time, can raise StackBig in ../../pkg/runtime/stack.h.
long unmappedzero = 4096;

void
clearp(Prog *p)
{
        p->as = AEND;
        p->reg = NREG;
        p->scond = C_SCOND_NONE;
        p->from.type = D_NONE;
        p->from.name = D_NONE;
        p->from.reg = NREG;
        p->to.type = D_NONE;
        p->to.name = D_NONE;
        p->to.reg = NREG;
        p->pc = pcloc;
        pcloc++;
}

static int ddumped;
static Prog *dfirst;
static Prog *dpc;

/*
 * generate and return proc with p->as = as,
 * linked into program.  pc is next instruction.
 */
Prog*
prog(int as)
{
        Prog *p;

        if(as == ADATA || as == AGLOBL) {
                if(ddumped)
                        fatal("already dumped data");
                if(dpc == nil) {
                        dpc = mal(sizeof(*dpc));
                        dfirst = dpc;
                }
                p = dpc;
                dpc = mal(sizeof(*dpc));
                p->link = dpc;
                p->reg = 0;  // used for flags
        } else {
                p = pc;
                pc = mal(sizeof(*pc));
                clearp(pc);
                p->link = pc;
        }

        if(lineno == 0) {
                if(debug['K'])
                        warn("prog: line 0");
        }

        p->as = as;
        p->lineno = lineno;
        return p;
}

void
dumpdata(void)
{
        ddumped = 1;
        if(dfirst == nil)
                return;
        newplist();
        *pc = *dfirst;
        pc = dpc;
        clearp(pc);
}

/*
 * generate a branch.
 * t is ignored.
 * likely values are for branch prediction:
 *      -1 unlikely
 *      0 no opinion
 *      +1 likely
 */
Prog*
gbranch(int as, Type *t, int likely)
{
        Prog *p;

        USED(t);
        USED(likely);  // TODO: record this for linker

        p = prog(as);
        p->to.type = D_BRANCH;
        p->to.u.branch = P;
        return p;
}

/*
 * patch previous branch to jump to to.
 */
void
patch(Prog *p, Prog *to)
{
        if(p->to.type != D_BRANCH)
                fatal("patch: not a branch");
        p->to.u.branch = to;
        p->to.offset = to->pc;
}

Prog*
unpatch(Prog *p)
{
        Prog *q;

        if(p->to.type != D_BRANCH)
                fatal("unpatch: not a branch");
        q = p->to.u.branch;
        p->to.u.branch = P;
        p->to.offset = 0;
        return q;
}

/*
 * start a new Prog list.
 */
Plist*
newplist(void)
{
        Plist *pl;

        pl = linknewplist(ctxt);

        pc = mal(sizeof(*pc));
        clearp(pc);
        pl->firstpc = pc;

        return pl;
}

void
gused(Node *n)
{
        gins(ANOP, n, N);       // used
}

Prog*
gjmp(Prog *to)
{
        Prog *p;

        p = gbranch(AB, T, 0);
        if(to != P)
                patch(p, to);
        return p;
}

void
ggloblnod(Node *nam)
{
        Prog *p;

        p = gins(AGLOBL, nam, N);
        p->lineno = nam->lineno;
        p->from.sym->gotype = linksym(ngotype(nam));
        p->to.sym = nil;
        p->to.type = D_CONST;
        p->to.offset = nam->type->width;
        if(nam->readonly)
                p->reg = RODATA;
        if(nam->type != T && !haspointers(nam->type))
                p->reg |= NOPTR;
}

void
gargsize(int32 size)
{
        Node n1, n2;
        
        nodconst(&n1, types[TINT32], PCDATA_ArgSize);
        nodconst(&n2, types[TINT32], size);
        gins(APCDATA, &n1, &n2);
}

void
ggloblsym(Sym *s, int32 width, int dupok, int rodata)
{
        Prog *p;

        p = gins(AGLOBL, N, N);
        p->from.type = D_OREG;
        p->from.name = D_EXTERN;
        p->from.sym = linksym(s);
        p->to.type = D_CONST;
        p->to.name = D_NONE;
        p->to.offset = width;
        if(dupok)
                p->reg |= DUPOK;
        if(rodata)
                p->reg |= RODATA;
}

void
gtrack(Sym *s)
{
        Prog *p;
        
        p = gins(AUSEFIELD, N, N);
        p->from.type = D_OREG;
        p->from.name = D_EXTERN;
        p->from.sym = linksym(s);
}

int
isfat(Type *t)
{
        if(t != T)
        switch(t->etype) {
        case TSTRUCT:
        case TARRAY:
        case TSTRING:
        case TINTER:    // maybe remove later
                return 1;
        }
        return 0;
}

/*
 * naddr of func generates code for address of func.
 * if using opcode that can take address implicitly,
 * call afunclit to fix up the argument.
 * also fix up direct register references to be D_OREG.
 */
void
afunclit(Addr *a, Node *n)
{
        if(a->type == D_CONST && a->name == D_EXTERN || a->type == D_REG) {
                a->type = D_OREG;
                if(n->op == ONAME)
                        a->sym = linksym(n->sym);
        }
}

static  int     resvd[] =
{
        9,     // reserved for m
        10,    // reserved for g
        REGSP, // reserved for SP
};

void
ginit(void)
{
        int i;

        for(i=0; i<nelem(reg); i++)
                reg[i] = 0;
        for(i=0; i<nelem(resvd); i++)
                reg[resvd[i]]++;
}

void
gclean(void)
{
        int i;

        for(i=0; i<nelem(resvd); i++)
                reg[resvd[i]]--;

        for(i=0; i<nelem(reg); i++)
                if(reg[i])
                        yyerror("reg %R left allocated\n", i);
}

int32
anyregalloc(void)
{
        int i, j;

        for(i=0; i<nelem(reg); i++) {
                if(reg[i] == 0)
                        goto ok;
                for(j=0; j<nelem(resvd); j++)
                        if(resvd[j] == i)
                                goto ok;
                return 1;
        ok:;
        }
        return 0;
}

uintptr regpc[REGALLOC_FMAX+1];

/*
 * allocate register of type t, leave in n.
 * if o != N, o is desired fixed register.
 * caller must regfree(n).
 */
void
regalloc(Node *n, Type *t, Node *o)
{
        int i, et, fixfree, floatfree;

        if(0 && debug['r']) {
                fixfree = 0;
                for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
                        if(reg[i] == 0)
                                fixfree++;
                floatfree = 0;
                for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++)
                        if(reg[i] == 0)
                                floatfree++;
                print("regalloc fix %d float %d\n", fixfree, floatfree);
        }

        if(t == T)
                fatal("regalloc: t nil");
        et = simtype[t->etype];
        if(is64(t))
                fatal("regalloc: 64 bit type %T");

        switch(et) {
        case TINT8:
        case TUINT8:
        case TINT16:
        case TUINT16:
        case TINT32:
        case TUINT32:
        case TPTR32:
        case TBOOL:
                if(o != N && o->op == OREGISTER) {
                        i = o->val.u.reg;
                        if(i >= REGALLOC_R0 && i <= REGALLOC_RMAX)
                                goto out;
                }
                for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
                        if(reg[i] == 0) {
                                regpc[i] = (uintptr)getcallerpc(&n);
                                goto out;
                        }
                print("registers allocated at\n");
                for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
                        print("%d %p\n", i, regpc[i]);
                yyerror("out of fixed registers");
                goto err;

        case TFLOAT32:
        case TFLOAT64:
                if(o != N && o->op == OREGISTER) {
                        i = o->val.u.reg;
                        if(i >= REGALLOC_F0 && i <= REGALLOC_FMAX)
                                goto out;
                }
                for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++)
                        if(reg[i] == 0)
                                goto out;
                yyerror("out of floating point registers");
                goto err;

        case TCOMPLEX64:
        case TCOMPLEX128:
                tempname(n, t);
                return;
        }
        yyerror("regalloc: unknown type %T", t);

err:
        nodreg(n, t, 0);
        return;

out:
        reg[i]++;
        nodreg(n, t, i);
}

void
regfree(Node *n)
{
        int i, fixfree, floatfree;

        if(0 && debug['r']) {
                fixfree = 0;
                for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++)
                        if(reg[i] == 0)
                                fixfree++;
                floatfree = 0;
                for(i=REGALLOC_F0; i<=REGALLOC_FMAX; i++)
                        if(reg[i] == 0)
                                floatfree++;
                print("regalloc fix %d float %d\n", fixfree, floatfree);
        }

        if(n->op == ONAME)
                return;
        if(n->op != OREGISTER && n->op != OINDREG)
                fatal("regfree: not a register");
        i = n->val.u.reg;
        if(i == REGSP)
                return;
        if(i < 0 || i >= nelem(reg) || i >= nelem(regpc))
                fatal("regfree: reg out of range");
        if(reg[i] <= 0)
                fatal("regfree: reg %R not allocated", i);
        reg[i]--;
        if(reg[i] == 0)
                regpc[i] = 0;
}

/*
 * initialize n to be register r of type t.
 */
void
nodreg(Node *n, Type *t, int r)
{
        if(t == T)
                fatal("nodreg: t nil");

        memset(n, 0, sizeof(*n));
        n->op = OREGISTER;
        n->addable = 1;
        ullmancalc(n);
        n->val.u.reg = r;
        n->type = t;
}

/*
 * initialize n to be indirect of register r; n is type t.
 */
void
nodindreg(Node *n, Type *t, int r)
{
        nodreg(n, t, r);
        n->op = OINDREG;
}

Node*
nodarg(Type *t, int fp)
{
        Node *n;
        NodeList *l;
        Type *first;
        Iter savet;

        // entire argument struct, not just one arg
        if(t->etype == TSTRUCT && t->funarg) {
                n = nod(ONAME, N, N);
                n->sym = lookup(".args");
                n->type = t;
                first = structfirst(&savet, &t);
                if(first == nil)
                        fatal("nodarg: bad struct");
                if(first->width == BADWIDTH)
                        fatal("nodarg: offset not computed for %T", t);
                n->xoffset = first->width;
                n->addable = 1;
                goto fp;
        }

        if(t->etype != TFIELD)
                fatal("nodarg: not field %T", t);

        if(fp == 1) {
                for(l=curfn->dcl; l; l=l->next) {
                        n = l->n;
                        if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym)
                                return n;
                }
        }

        n = nod(ONAME, N, N);
        n->type = t->type;
        n->sym = t->sym;
        if(t->width == BADWIDTH)
                fatal("nodarg: offset not computed for %T", t);
        n->xoffset = t->width;
        n->addable = 1;
        n->orig = t->nname;

fp:
        // Rewrite argument named _ to __,
        // or else the assignment to _ will be
        // discarded during code generation.
        if(isblank(n))
                n->sym = lookup("__");

        switch(fp) {
        default:
                fatal("nodarg %T %d", t, fp);

        case 0:         // output arg for calling another function
                n->op = OINDREG;
                n->val.u.reg = REGSP;
                n->xoffset += 4;
                break;

        case 1:         // input arg to current function
                n->class = PPARAM;
                break;
        }
        n->typecheck = 1;
        return n;
}

/*
 * return constant i node.
 * overwritten by next call, but useful in calls to gins.
 */
Node*
ncon(uint32 i)
{
        static Node n;

        if(n.type == T)
                nodconst(&n, types[TUINT32], 0);
        mpmovecfix(n.val.u.xval, i);
        return &n;
}

/*
 * Is this node a memory operand?
 */
int
ismem(Node *n)
{
        switch(n->op) {
        case OINDREG:
        case ONAME:
        case OPARAM:
        case OCLOSUREVAR:
                return 1;
        }
        return 0;
}

Node sclean[10];
int nsclean;

/*
 * n is a 64-bit value.  fill in lo and hi to refer to its 32-bit halves.
 */
void
split64(Node *n, Node *lo, Node *hi)
{
        Node n1;
        int64 i;

        if(!is64(n->type))
                fatal("split64 %T", n->type);

        if(nsclean >= nelem(sclean))
                fatal("split64 clean");
        sclean[nsclean].op = OEMPTY;
        nsclean++;
        switch(n->op) {
        default:
                if(!dotaddable(n, &n1)) {
                        igen(n, &n1, N);
                        sclean[nsclean-1] = n1;
                }
                n = &n1;
                goto common;
        case ONAME:
                if(n->class == PPARAMREF) {
                        cgen(n->heapaddr, &n1);
                        sclean[nsclean-1] = n1;
                        // fall through.
                        n = &n1;
                }
                goto common;
        case OINDREG:
        common:
                *lo = *n;
                *hi = *n;
                lo->type = types[TUINT32];
                if(n->type->etype == TINT64)
                        hi->type = types[TINT32];
                else
                        hi->type = types[TUINT32];
                hi->xoffset += 4;
                break;

        case OLITERAL:
                convconst(&n1, n->type, &n->val);
                i = mpgetfix(n1.val.u.xval);
                nodconst(lo, types[TUINT32], (uint32)i);
                i >>= 32;
                if(n->type->etype == TINT64)
                        nodconst(hi, types[TINT32], (int32)i);
                else
                        nodconst(hi, types[TUINT32], (uint32)i);
                break;
        }
}

void
splitclean(void)
{
        if(nsclean <= 0)
                fatal("splitclean");
        nsclean--;
        if(sclean[nsclean].op != OEMPTY)
                regfree(&sclean[nsclean]);
}

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

void
gmove(Node *f, Node *t)
{
        int a, ft, tt, fa, ta;
        Type *cvt;
        Node r1, r2, flo, fhi, tlo, thi, con;
        Prog *p1;

        if(debug['M'])
                print("gmove %N -> %N\n", f, t);

        ft = simsimtype(f->type);
        tt = simsimtype(t->type);
        cvt = t->type;

        if(iscomplex[ft] || iscomplex[tt]) {
                complexmove(f, t);
                return;
        }

        // cannot have two memory operands;
        // except 64-bit, which always copies via registers anyway.
        if(!is64(f->type) && !is64(t->type) && ismem(f) && ismem(t))
                goto hard;

        // convert constant to desired type
        if(f->op == OLITERAL) {
                switch(tt) {
                default:
                        convconst(&con, t->type, &f->val);
                        break;

                case TINT16:
                case TINT8:
                        convconst(&con, types[TINT32], &f->val);
                        regalloc(&r1, con.type, t);
                        gins(AMOVW, &con, &r1);
                        gmove(&r1, t);
                        regfree(&r1);
                        return;

                case TUINT16:
                case TUINT8:
                        convconst(&con, types[TUINT32], &f->val);
                        regalloc(&r1, con.type, t);
                        gins(AMOVW, &con, &r1);
                        gmove(&r1, t);
                        regfree(&r1);
                        return;
                }

                f = &con;
                ft = simsimtype(con.type);

                // constants can't move directly to memory
                if(ismem(t) && !is64(t->type)) goto hard;
        }

        // value -> value copy, only one memory operand.
        // figure out the instruction to use.
        // break out of switch for one-instruction gins.
        // goto rdst for "destination must be register".
        // goto hard for "convert to cvt type first".
        // otherwise handle and return.

        switch(CASE(ft, tt)) {
        default:
                goto fatal;

        /*
         * integer copy and truncate
         */
        case CASE(TINT8, TINT8):        // same size
                if(!ismem(f)) {
                        a = AMOVB;
                        break;
                }
        case CASE(TUINT8, TINT8):
        case CASE(TINT16, TINT8):       // truncate
        case CASE(TUINT16, TINT8):
        case CASE(TINT32, TINT8):
        case CASE(TUINT32, TINT8):
                a = AMOVBS;
                break;

        case CASE(TUINT8, TUINT8):
                if(!ismem(f)) {
                        a = AMOVB;
                        break;
                }
        case CASE(TINT8, TUINT8):
        case CASE(TINT16, TUINT8):
        case CASE(TUINT16, TUINT8):
        case CASE(TINT32, TUINT8):
        case CASE(TUINT32, TUINT8):
                a = AMOVBU;
                break;

        case CASE(TINT64, TINT8):       // truncate low word
        case CASE(TUINT64, TINT8):
                a = AMOVBS;
                goto trunc64;

        case CASE(TINT64, TUINT8):
        case CASE(TUINT64, TUINT8):
                a = AMOVBU;
                goto trunc64;

        case CASE(TINT16, TINT16):      // same size
                if(!ismem(f)) {
                        a = AMOVH;
                        break;
                }
        case CASE(TUINT16, TINT16):
        case CASE(TINT32, TINT16):      // truncate
        case CASE(TUINT32, TINT16):
                a = AMOVHS;
                break;

        case CASE(TUINT16, TUINT16):
                if(!ismem(f)) {
                        a = AMOVH;
                        break;
                }
        case CASE(TINT16, TUINT16):
        case CASE(TINT32, TUINT16):
        case CASE(TUINT32, TUINT16):
                a = AMOVHU;
                break;

        case CASE(TINT64, TINT16):      // truncate low word
        case CASE(TUINT64, TINT16):
                a = AMOVHS;
                goto trunc64;

        case CASE(TINT64, TUINT16):
        case CASE(TUINT64, TUINT16):
                a = AMOVHU;
                goto trunc64;

        case CASE(TINT32, TINT32):      // same size
        case CASE(TINT32, TUINT32):
        case CASE(TUINT32, TINT32):
        case CASE(TUINT32, TUINT32):
                a = AMOVW;
                break;

        case CASE(TINT64, TINT32):      // truncate
        case CASE(TUINT64, TINT32):
        case CASE(TINT64, TUINT32):
        case CASE(TUINT64, TUINT32):
                split64(f, &flo, &fhi);
                regalloc(&r1, t->type, N);
                gins(AMOVW, &flo, &r1);
                gins(AMOVW, &r1, t);
                regfree(&r1);
                splitclean();
                return;

        case CASE(TINT64, TINT64):      // same size
        case CASE(TINT64, TUINT64):
        case CASE(TUINT64, TINT64):
        case CASE(TUINT64, TUINT64):
                split64(f, &flo, &fhi);
                split64(t, &tlo, &thi);
                regalloc(&r1, flo.type, N);
                regalloc(&r2, fhi.type, N);
                gins(AMOVW, &flo, &r1);
                gins(AMOVW, &fhi, &r2);
                gins(AMOVW, &r1, &tlo);
                gins(AMOVW, &r2, &thi);
                regfree(&r1);
                regfree(&r2);
                splitclean();
                splitclean();
                return;

        /*
         * integer up-conversions
         */
        case CASE(TINT8, TINT16):       // sign extend int8
        case CASE(TINT8, TUINT16):
        case CASE(TINT8, TINT32):
        case CASE(TINT8, TUINT32):
                a = AMOVBS;
                goto rdst;
        case CASE(TINT8, TINT64):       // convert via int32
        case CASE(TINT8, TUINT64):
                cvt = types[TINT32];
                goto hard;

        case CASE(TUINT8, TINT16):      // zero extend uint8
        case CASE(TUINT8, TUINT16):
        case CASE(TUINT8, TINT32):
        case CASE(TUINT8, TUINT32):
                a = AMOVBU;
                goto rdst;
        case CASE(TUINT8, TINT64):      // convert via uint32
        case CASE(TUINT8, TUINT64):
                cvt = types[TUINT32];
                goto hard;

        case CASE(TINT16, TINT32):      // sign extend int16
        case CASE(TINT16, TUINT32):
                a = AMOVHS;
                goto rdst;
        case CASE(TINT16, TINT64):      // convert via int32
        case CASE(TINT16, TUINT64):
                cvt = types[TINT32];
                goto hard;

        case CASE(TUINT16, TINT32):     // zero extend uint16
        case CASE(TUINT16, TUINT32):
                a = AMOVHU;
                goto rdst;
        case CASE(TUINT16, TINT64):     // convert via uint32
        case CASE(TUINT16, TUINT64):
                cvt = types[TUINT32];
                goto hard;

        case CASE(TINT32, TINT64):      // sign extend int32
        case CASE(TINT32, TUINT64):
                split64(t, &tlo, &thi);
                regalloc(&r1, tlo.type, N);
                regalloc(&r2, thi.type, N);
                gmove(f, &r1);
                p1 = gins(AMOVW, &r1, &r2);
                p1->from.type = D_SHIFT;
                p1->from.offset = 2 << 5 | 31 << 7 | r1.val.u.reg; // r1->31
                p1->from.reg = NREG;
//print("gmove: %P\n", p1);
                gins(AMOVW, &r1, &tlo);
                gins(AMOVW, &r2, &thi);
                regfree(&r1);
                regfree(&r2);
                splitclean();
                return;

        case CASE(TUINT32, TINT64):     // zero extend uint32
        case CASE(TUINT32, TUINT64):
                split64(t, &tlo, &thi);
                gmove(f, &tlo);
                regalloc(&r1, thi.type, N);
                gins(AMOVW, ncon(0), &r1);
                gins(AMOVW, &r1, &thi);
                regfree(&r1);
                splitclean();
                return;

        /*
        * float to integer
        */
        case CASE(TFLOAT32, TINT8):
        case CASE(TFLOAT32, TUINT8):
        case CASE(TFLOAT32, TINT16):
        case CASE(TFLOAT32, TUINT16):
        case CASE(TFLOAT32, TINT32):
        case CASE(TFLOAT32, TUINT32):
//      case CASE(TFLOAT32, TUINT64):

        case CASE(TFLOAT64, TINT8):
        case CASE(TFLOAT64, TUINT8):
        case CASE(TFLOAT64, TINT16):
        case CASE(TFLOAT64, TUINT16):
        case CASE(TFLOAT64, TINT32):
        case CASE(TFLOAT64, TUINT32):
//      case CASE(TFLOAT64, TUINT64):
                fa = AMOVF;
                a = AMOVFW;
                if(ft == TFLOAT64) {
                        fa = AMOVD;
                        a = AMOVDW;
                }
                ta = AMOVW;
                switch(tt) {
                case TINT8:
                        ta = AMOVBS;
                        break;
                case TUINT8:
                        ta = AMOVBU;
                        break;
                case TINT16:
                        ta = AMOVHS;
                        break;
                case TUINT16:
                        ta = AMOVHU;
                        break;
                }

                regalloc(&r1, types[ft], f);
                regalloc(&r2, types[tt], t);
                gins(fa, f, &r1);       // load to fpu
                p1 = gins(a, &r1, &r1); // convert to w
                switch(tt) {
                case TUINT8:
                case TUINT16:
                case TUINT32:
                        p1->scond |= C_UBIT;
                }
                gins(AMOVW, &r1, &r2);  // copy to cpu
                gins(ta, &r2, t);       // store
                regfree(&r1);
                regfree(&r2);
                return;

        /*
         * integer to float
         */
        case CASE(TINT8, TFLOAT32):
        case CASE(TUINT8, TFLOAT32):
        case CASE(TINT16, TFLOAT32):
        case CASE(TUINT16, TFLOAT32):
        case CASE(TINT32, TFLOAT32):
        case CASE(TUINT32, TFLOAT32):
        case CASE(TINT8, TFLOAT64):
        case CASE(TUINT8, TFLOAT64):
        case CASE(TINT16, TFLOAT64):
        case CASE(TUINT16, TFLOAT64):
        case CASE(TINT32, TFLOAT64):
        case CASE(TUINT32, TFLOAT64):
                fa = AMOVW;
                switch(ft) {
                case TINT8:
                        fa = AMOVBS;
                        break;
                case TUINT8:
                        fa = AMOVBU;
                        break;
                case TINT16:
                        fa = AMOVHS;
                        break;
                case TUINT16:
                        fa = AMOVHU;
                        break;
                }
                a = AMOVWF;
                ta = AMOVF;
                if(tt == TFLOAT64) {
                        a = AMOVWD;
                        ta = AMOVD;
                }
                regalloc(&r1, types[ft], f);
                regalloc(&r2, types[tt], t);
                gins(fa, f, &r1);       // load to cpu
                gins(AMOVW, &r1, &r2);  // copy to fpu
                p1 = gins(a, &r2, &r2); // convert
                switch(ft) {
                case TUINT8:
                case TUINT16:
                case TUINT32:
                        p1->scond |= C_UBIT;
                }
                gins(ta, &r2, t);       // store
                regfree(&r1);
                regfree(&r2);
                return;

        case CASE(TUINT64, TFLOAT32):
        case CASE(TUINT64, TFLOAT64):
                fatal("gmove UINT64, TFLOAT not implemented");
                return;


        /*
         * float to float
         */
        case CASE(TFLOAT32, TFLOAT32):
                a = AMOVF;
                break;

        case CASE(TFLOAT64, TFLOAT64):
                a = AMOVD;
                break;

        case CASE(TFLOAT32, TFLOAT64):
                regalloc(&r1, types[TFLOAT64], t);
                gins(AMOVF, f, &r1);
                gins(AMOVFD, &r1, &r1);
                gins(AMOVD, &r1, t);
                regfree(&r1);
                return;

        case CASE(TFLOAT64, TFLOAT32):
                regalloc(&r1, types[TFLOAT64], t);
                gins(AMOVD, f, &r1);
                gins(AMOVDF, &r1, &r1);
                gins(AMOVF, &r1, t);
                regfree(&r1);
                return;
        }

        gins(a, f, t);
        return;

rdst:
        // TODO(kaib): we almost always require a register dest anyway, this can probably be
        // removed.
        // requires register destination
        regalloc(&r1, t->type, t);
        gins(a, f, &r1);
        gmove(&r1, t);
        regfree(&r1);
        return;

hard:
        // requires register intermediate
        regalloc(&r1, cvt, t);
        gmove(f, &r1);
        gmove(&r1, t);
        regfree(&r1);
        return;

trunc64:
        // truncate 64 bit integer
        split64(f, &flo, &fhi);
        regalloc(&r1, t->type, N);
        gins(a, &flo, &r1);
        gins(a, &r1, t);
        regfree(&r1);
        splitclean();
        return;

fatal:
        // should not happen
        fatal("gmove %N -> %N", f, t);
}

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

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

        switch(f->op) {
        case OREGISTER:
                if(f->val.u.reg != t->val.u.reg)
                        break;
                return 1;
        }
        return 0;
}

/*
 * generate one instruction:
 *      as f, t
 */
Prog*
gins(int as, Node *f, Node *t)
{
//      Node nod;
//      int32 v;
        Prog *p;
        Addr af, at;

        if(f != N && f->op == OINDEX) {
                fatal("gins OINDEX not implemented");
//              regalloc(&nod, &regnode, Z);
//              v = constnode.vconst;
//              cgen(f->right, &nod);
//              constnode.vconst = v;
//              idx.reg = nod.reg;
//              regfree(&nod);
        }
        if(t != N && t->op == OINDEX) {
                fatal("gins OINDEX not implemented");
//              regalloc(&nod, &regnode, Z);
//              v = constnode.vconst;
//              cgen(t->right, &nod);
//              constnode.vconst = v;
//              idx.reg = nod.reg;
//              regfree(&nod);
        }

        memset(&af, 0, sizeof af);
        memset(&at, 0, sizeof at);
        if(f != N)
                naddr(f, &af, 1);
        if(t != N)
                naddr(t, &at, 1);
        p = prog(as);
        if(f != N)
                p->from = af;
        if(t != N)
                p->to = at;
        if(debug['g'])
                print("%P\n", p);
        return p;
}

/*
 * insert n into reg slot of p
 */
void
raddr(Node *n, Prog *p)
{
        Addr a;

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

/* generate a comparison
TODO(kaib): one of the args can actually be a small constant. relax the constraint and fix call sites.
 */
Prog*
gcmp(int as, Node *lhs, Node *rhs)
{
        Prog *p;

        if(lhs->op != OREGISTER)
                fatal("bad operands to gcmp: %O %O", lhs->op, rhs->op);

        p = gins(as, rhs, N);
        raddr(lhs, p);
        return p;
}

/* generate a constant shift
 * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal.
*/
Prog*
gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs)
{
        Prog *p;

        if(sval <= 0 || sval > 32)
                fatal("bad shift value: %d", sval);

        sval = sval&0x1f;

        p = gins(as, N, rhs);
        p->from.type = D_SHIFT;
        p->from.offset = stype | sval<<7 | lhs->val.u.reg;
        return p;
}

/* generate a register shift
*/
Prog *
gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs)
{
        Prog *p;
        p = gins(as, N, rhs);
        p->from.type = D_SHIFT;
        p->from.offset = stype | reg->val.u.reg << 8 | 1<<4 | lhs->val.u.reg;
        return p;
}

/*
 * generate code to compute n;
 * make a refer to result.
 */
void
naddr(Node *n, Addr *a, int canemitcode)
{
        Sym *s;

        a->type = D_NONE;
        a->name = D_NONE;
        a->reg = NREG;
        a->gotype = nil;
        a->node = N;
        a->etype = 0;
        if(n == N)
                return;

        if(n->type != T && n->type->etype != TIDEAL) {
                dowidth(n->type);
                a->width = n->type->width;
        }

        switch(n->op) {
        default:
                fatal("naddr: bad %O %D", n->op, a);
                break;

        case OREGISTER:
                if(n->val.u.reg <= REGALLOC_RMAX) {
                        a->type = D_REG;
                        a->reg = n->val.u.reg;
                } else {
                        a->type = D_FREG;
                        a->reg = n->val.u.reg - REGALLOC_F0;
                }
                a->sym = nil;
                break;

        case OINDEX:
        case OIND:
                fatal("naddr: OINDEX");
//              naddr(n->left, a);
//              if(a->type >= D_AX && a->type <= D_DI)
//                      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;
//              if(n->op == OINDEX) {
//                      a->index = idx.reg;
//                      a->scale = n->scale;
//              }
//              break;

        case OINDREG:
                a->type = D_OREG;
                a->reg = n->val.u.reg;
                a->sym = linksym(n->sym);
                a->offset = n->xoffset;
                break;

        case OPARAM:
                // n->left is PHEAP ONAME for stack parameter.
                // compute address of actual parameter on stack.
                a->etype = simtype[n->left->type->etype];
                a->width = n->left->type->width;
                a->offset = n->xoffset;
                a->sym = linksym(n->left->sym);
                a->type = D_OREG;
                a->name = D_PARAM;
                a->node = n->left->orig;
                break;
        
        case OCLOSUREVAR:
                if(!curfn->needctxt)
                        fatal("closurevar without needctxt");
                a->type = D_OREG;
                a->reg = 7;
                a->offset = n->xoffset;
                a->sym = nil;
                break;          

        case OCFUNC:
                naddr(n->left, a, canemitcode);
                a->sym = linksym(n->left->sym);
                break;

        case ONAME:
                a->etype = 0;
                a->width = 0;
                a->reg = NREG;
                if(n->type != T) {
                        a->etype = simtype[n->type->etype];
                        a->width = n->type->width;
                }
                a->offset = n->xoffset;
                s = n->sym;
                a->node = n->orig;
                //if(a->node >= (Node*)&n)
                //      fatal("stack node");
                if(s == S)
                        s = lookup(".noname");
                if(n->method) {
                        if(n->type != T)
                        if(n->type->sym != S)
                        if(n->type->sym->pkg != nil)
                                s = pkglookup(s->name, n->type->sym->pkg);
                }

                a->type = D_OREG;
                switch(n->class) {
                default:
                        fatal("naddr: ONAME class %S %d\n", n->sym, n->class);
                case PEXTERN:
                        a->name = D_EXTERN;
                        break;
                case PAUTO:
                        a->name = D_AUTO;
                        break;
                case PPARAM:
                case PPARAMOUT:
                        a->name = D_PARAM;
                        break;
                case PFUNC:
                        a->name = D_EXTERN;
                        a->type = D_CONST;
                        s = funcsym(s);
                        break;
                }
                a->sym = linksym(s);
                break;

        case OLITERAL:
                switch(n->val.ctype) {
                default:
                        fatal("naddr: const %lT", n->type);
                        break;
                case CTFLT:
                        a->type = D_FCONST;
                        a->u.dval = mpgetflt(n->val.u.fval);
                        break;
                case CTINT:
                case CTRUNE:
                        a->sym = nil;
                        a->type = D_CONST;
                        a->offset = mpgetfix(n->val.u.xval);
                        break;
                case CTSTR:
                        datagostring(n->val.u.sval, a);
                        break;
                case CTBOOL:
                        a->sym = nil;
                        a->type = D_CONST;
                        a->offset = n->val.u.bval;
                        break;
                case CTNIL:
                        a->sym = nil;
                        a->type = D_CONST;
                        a->offset = 0;
                        break;
                }
                break;

        case OITAB:
                // itable of interface value
                naddr(n->left, a, canemitcode);
                a->etype = TINT32;
                if(a->type == D_CONST && a->offset == 0)
                        break;  // len(nil)
                break;

        case OSPTR:
                // pointer in a string or slice
                naddr(n->left, a, canemitcode);
                if(a->type == D_CONST && a->offset == 0)
                        break;  // ptr(nil)
                a->etype = simtype[tptr];
                a->offset += Array_array;
                a->width = widthptr;
                break;

        case OLEN:
                // len of string or slice
                naddr(n->left, a, canemitcode);
                a->etype = TINT32;
                if(a->type == D_CONST && a->offset == 0)
                        break;  // len(nil)
                a->offset += Array_nel;
                break;

        case OCAP:
                // cap of string or slice
                naddr(n->left, a, canemitcode);
                a->etype = TINT32;
                if(a->type == D_CONST && a->offset == 0)
                        break;  // cap(nil)
                a->offset += Array_cap;
                break;

        case OADDR:
                naddr(n->left, a, canemitcode);
                a->etype = tptr;
                switch(a->type) {
                case D_OREG:
                        a->type = D_CONST;
                        break;

                case D_REG:
                case D_CONST:
                        break;
                
                default:
                        fatal("naddr: OADDR %d\n", a->type);
                }
        }
        
        if(a->width < 0)
                fatal("naddr: bad width for %N -> %D", n, a);
}

/*
 * return Axxx for Oxxx on type t.
 */
int
optoas(int op, Type *t)
{
        int a;

        if(t == T)
                fatal("optoas: t is nil");

        a = AGOK;
        switch(CASE(op, simtype[t->etype])) {
        default:
                fatal("optoas: no entry %O-%T etype %T simtype %T", op, t, types[t->etype], types[simtype[t->etype]]);
                break;

/*      case CASE(OADDR, TPTR32):
                a = ALEAL;
                break;

        case CASE(OADDR, TPTR64):
                a = ALEAQ;
                break;
*/
        // TODO(kaib): make sure the conditional branches work on all edge cases
        case CASE(OEQ, TBOOL):
        case CASE(OEQ, TINT8):
        case CASE(OEQ, TUINT8):
        case CASE(OEQ, TINT16):
        case CASE(OEQ, TUINT16):
        case CASE(OEQ, TINT32):
        case CASE(OEQ, TUINT32):
        case CASE(OEQ, TINT64):
        case CASE(OEQ, TUINT64):
        case CASE(OEQ, TPTR32):
        case CASE(OEQ, TPTR64):
        case CASE(OEQ, TFLOAT32):
        case CASE(OEQ, TFLOAT64):
                a = ABEQ;
                break;

        case CASE(ONE, TBOOL):
        case CASE(ONE, TINT8):
        case CASE(ONE, TUINT8):
        case CASE(ONE, TINT16):
        case CASE(ONE, TUINT16):
        case CASE(ONE, TINT32):
        case CASE(ONE, TUINT32):
        case CASE(ONE, TINT64):
        case CASE(ONE, TUINT64):
        case CASE(ONE, TPTR32):
        case CASE(ONE, TPTR64):
        case CASE(ONE, TFLOAT32):
        case CASE(ONE, TFLOAT64):
                a = ABNE;
                break;

        case CASE(OLT, TINT8):
        case CASE(OLT, TINT16):
        case CASE(OLT, TINT32):
        case CASE(OLT, TINT64):
        case CASE(OLT, TFLOAT32):
        case CASE(OLT, TFLOAT64):
                a = ABLT;
                break;

        case CASE(OLT, TUINT8):
        case CASE(OLT, TUINT16):
        case CASE(OLT, TUINT32):
        case CASE(OLT, TUINT64):
                a = ABLO;
                break;

        case CASE(OLE, TINT8):
        case CASE(OLE, TINT16):
        case CASE(OLE, TINT32):
        case CASE(OLE, TINT64):
        case CASE(OLE, TFLOAT32):
        case CASE(OLE, TFLOAT64):
                a = ABLE;
                break;

        case CASE(OLE, TUINT8):
        case CASE(OLE, TUINT16):
        case CASE(OLE, TUINT32):
        case CASE(OLE, TUINT64):
                a = ABLS;
                break;

        case CASE(OGT, TINT8):
        case CASE(OGT, TINT16):
        case CASE(OGT, TINT32):
        case CASE(OGT, TINT64):
        case CASE(OGT, TFLOAT32):
        case CASE(OGT, TFLOAT64):
                a = ABGT;
                break;

        case CASE(OGT, TUINT8):
        case CASE(OGT, TUINT16):
        case CASE(OGT, TUINT32):
        case CASE(OGT, TUINT64):
                a = ABHI;
                break;

        case CASE(OGE, TINT8):
        case CASE(OGE, TINT16):
        case CASE(OGE, TINT32):
        case CASE(OGE, TINT64):
        case CASE(OGE, TFLOAT32):
        case CASE(OGE, TFLOAT64):
                a = ABGE;
                break;

        case CASE(OGE, TUINT8):
        case CASE(OGE, TUINT16):
        case CASE(OGE, TUINT32):
        case CASE(OGE, TUINT64):
                a = ABHS;
                break;

        case CASE(OCMP, TBOOL):
        case CASE(OCMP, TINT8):
        case CASE(OCMP, TUINT8):
        case CASE(OCMP, TINT16):
        case CASE(OCMP, TUINT16):
        case CASE(OCMP, TINT32):
        case CASE(OCMP, TUINT32):
        case CASE(OCMP, TPTR32):
                a = ACMP;
                break;

        case CASE(OCMP, TFLOAT32):
                a = ACMPF;
                break;

        case CASE(OCMP, TFLOAT64):
                a = ACMPD;
                break;

        case CASE(OAS, TBOOL):
                a = AMOVB;
                break;

        case CASE(OAS, TINT8):
                a = AMOVBS;
                break;

        case CASE(OAS, TUINT8):
                a = AMOVBU;
                break;

        case CASE(OAS, TINT16):
                a = AMOVHS;
                break;

        case CASE(OAS, TUINT16):
                a = AMOVHU;
                break;

        case CASE(OAS, TINT32):
        case CASE(OAS, TUINT32):
        case CASE(OAS, TPTR32):
                a = AMOVW;
                break;

        case CASE(OAS, TFLOAT32):
                a = AMOVF;
                break;

        case CASE(OAS, TFLOAT64):
                a = AMOVD;
                break;

        case CASE(OADD, TINT8):
        case CASE(OADD, TUINT8):
        case CASE(OADD, TINT16):
        case CASE(OADD, TUINT16):
        case CASE(OADD, TINT32):
        case CASE(OADD, TUINT32):
        case CASE(OADD, TPTR32):
        case CASE(OADDPTR, TPTR32):
                a = AADD;
                break;

        case CASE(OADD, TFLOAT32):
                a = AADDF;
                break;

        case CASE(OADD, TFLOAT64):
                a = AADDD;
                break;

        case CASE(OSUB, TINT8):
        case CASE(OSUB, TUINT8):
        case CASE(OSUB, TINT16):
        case CASE(OSUB, TUINT16):
        case CASE(OSUB, TINT32):
        case CASE(OSUB, TUINT32):
        case CASE(OSUB, TPTR32):
                a = ASUB;
                break;

        case CASE(OSUB, TFLOAT32):
                a = ASUBF;
                break;

        case CASE(OSUB, TFLOAT64):
                a = ASUBD;
                break;

        case CASE(OMINUS, TINT8):
        case CASE(OMINUS, TUINT8):
        case CASE(OMINUS, TINT16):
        case CASE(OMINUS, TUINT16):
        case CASE(OMINUS, TINT32):
        case CASE(OMINUS, TUINT32):
        case CASE(OMINUS, TPTR32):
                a = ARSB;
                break;

        case CASE(OAND, TINT8):
        case CASE(OAND, TUINT8):
        case CASE(OAND, TINT16):
        case CASE(OAND, TUINT16):
        case CASE(OAND, TINT32):
        case CASE(OAND, TUINT32):
        case CASE(OAND, TPTR32):
                a = AAND;
                break;

        case CASE(OOR, TINT8):
        case CASE(OOR, TUINT8):
        case CASE(OOR, TINT16):
        case CASE(OOR, TUINT16):
        case CASE(OOR, TINT32):
        case CASE(OOR, TUINT32):
        case CASE(OOR, TPTR32):
                a = AORR;
                break;

        case CASE(OXOR, TINT8):
        case CASE(OXOR, TUINT8):
        case CASE(OXOR, TINT16):
        case CASE(OXOR, TUINT16):
        case CASE(OXOR, TINT32):
        case CASE(OXOR, TUINT32):
        case CASE(OXOR, TPTR32):
                a = AEOR;
                break;

        case CASE(OLSH, TINT8):
        case CASE(OLSH, TUINT8):
        case CASE(OLSH, TINT16):
        case CASE(OLSH, TUINT16):
        case CASE(OLSH, TINT32):
        case CASE(OLSH, TUINT32):
        case CASE(OLSH, TPTR32):
                a = ASLL;
                break;

        case CASE(ORSH, TUINT8):
        case CASE(ORSH, TUINT16):
        case CASE(ORSH, TUINT32):
        case CASE(ORSH, TPTR32):
                a = ASRL;
                break;

        case CASE(ORSH, TINT8):
        case CASE(ORSH, TINT16):
        case CASE(ORSH, TINT32):
                a = ASRA;
                break;

        case CASE(OMUL, TUINT8):
        case CASE(OMUL, TUINT16):
        case CASE(OMUL, TUINT32):
        case CASE(OMUL, TPTR32):
                a = AMULU;
                break;

        case CASE(OMUL, TINT8):
        case CASE(OMUL, TINT16):
        case CASE(OMUL, TINT32):
                a = AMUL;
                break;

        case CASE(OMUL, TFLOAT32):
                a = AMULF;
                break;

        case CASE(OMUL, TFLOAT64):
                a = AMULD;
                break;

        case CASE(ODIV, TUINT8):
        case CASE(ODIV, TUINT16):
        case CASE(ODIV, TUINT32):
        case CASE(ODIV, TPTR32):
                a = ADIVU;
                break;

        case CASE(ODIV, TINT8):
        case CASE(ODIV, TINT16):
        case CASE(ODIV, TINT32):
                a = ADIV;
                break;

        case CASE(OMOD, TUINT8):
        case CASE(OMOD, TUINT16):
        case CASE(OMOD, TUINT32):
        case CASE(OMOD, TPTR32):
                a = AMODU;
                break;

        case CASE(OMOD, TINT8):
        case CASE(OMOD, TINT16):
        case CASE(OMOD, TINT32):
                a = AMOD;
                break;

//      case CASE(OEXTEND, TINT16):
//              a = ACWD;
//              break;

//      case CASE(OEXTEND, TINT32):
//              a = ACDQ;
//              break;

//      case CASE(OEXTEND, TINT64):
//              a = ACQO;
//              break;

        case CASE(ODIV, TFLOAT32):
                a = ADIVF;
                break;

        case CASE(ODIV, TFLOAT64):
                a = ADIVD;
                break;

        }
        return a;
}

enum
{
        ODynam  = 1<<0,
        OPtrto  = 1<<1,
};

static  Node    clean[20];
static  int     cleani = 0;

void
sudoclean(void)
{
        if(clean[cleani-1].op != OEMPTY)
                regfree(&clean[cleani-1]);
        if(clean[cleani-2].op != OEMPTY)
                regfree(&clean[cleani-2]);
        cleani -= 2;
}

int
dotaddable(Node *n, Node *n1)
{
        int o;
        int64 oary[10];
        Node *nn;

        if(n->op != ODOT)
                return 0;

        o = dotoffset(n, oary, &nn);
        if(nn != N && nn->addable && o == 1 && oary[0] >= 0) {
                *n1 = *nn;
                n1->type = n->type;
                n1->xoffset += oary[0];
                return 1;
        }
        return 0;
}

/*
 * generate code to compute address of n,
 * a reference to a (perhaps nested) field inside
 * an array or struct.
 * return 0 on failure, 1 on success.
 * on success, leaves usable address in a.
 *
 * caller is responsible for calling sudoclean
 * after successful sudoaddable,
 * to release the register used for a.
 */
int
sudoaddable(int as, Node *n, Addr *a, int *w)
{
        int o, i;
        int64 oary[10];
        int64 v;
        Node n1, n2, n3, n4, *nn, *l, *r;
        Node *reg, *reg1;
        Prog *p1, *p2;
        Type *t;

        if(n->type == T)
                return 0;

        switch(n->op) {
        case OLITERAL:
                if(!isconst(n, CTINT))
                        break;
                v = mpgetfix(n->val.u.xval);
                if(v >= 32000 || v <= -32000)
                        break;
                goto lit;

        case ODOT:
        case ODOTPTR:
                cleani += 2;
                reg = &clean[cleani-1];
                reg1 = &clean[cleani-2];
                reg->op = OEMPTY;
                reg1->op = OEMPTY;
                goto odot;

        case OINDEX:
                return 0;
                // disabled: OINDEX case is now covered by agenr
                // for a more suitable register allocation pattern.
                if(n->left->type->etype == TSTRING)
                        return 0;
                cleani += 2;
                reg = &clean[cleani-1];
                reg1 = &clean[cleani-2];
                reg->op = OEMPTY;
                reg1->op = OEMPTY;
                goto oindex;
        }
        return 0;

lit:
        switch(as) {
        default:
                return 0;
        case AADD: case ASUB: case AAND: case AORR: case AEOR:
        case AMOVB: case AMOVBS: case AMOVBU:
        case AMOVH: case AMOVHS: case AMOVHU:
        case AMOVW:
                break;
        }

        cleani += 2;
        reg = &clean[cleani-1];
        reg1 = &clean[cleani-2];
        reg->op = OEMPTY;
        reg1->op = OEMPTY;
        naddr(n, a, 1);
        goto yes;

odot:
        o = dotoffset(n, oary, &nn);
        if(nn == N)
                goto no;

        if(nn->addable && o == 1 && oary[0] >= 0) {
                // directly addressable set of DOTs
                n1 = *nn;
                n1.type = n->type;
                n1.xoffset += oary[0];
                naddr(&n1, a, 1);
                goto yes;
        }

        regalloc(reg, types[tptr], N);
        n1 = *reg;
        n1.op = OINDREG;
        if(oary[0] >= 0) {
                agen(nn, reg);
                n1.xoffset = oary[0];
        } else {
                cgen(nn, reg);
                cgen_checknil(reg);
                n1.xoffset = -(oary[0]+1);
        }

        for(i=1; i<o; i++) {
                if(oary[i] >= 0)
                        fatal("can't happen");
                gins(AMOVW, &n1, reg);
                cgen_checknil(reg);
                n1.xoffset = -(oary[i]+1);
        }

        a->type = D_NONE;
        a->name = D_NONE;
        n1.type = n->type;
        naddr(&n1, a, 1);
        goto yes;

oindex:
        l = n->left;
        r = n->right;
        if(l->ullman >= UINF && r->ullman >= UINF)
                goto no;

        // set o to type of array
        o = 0;
        if(isptr[l->type->etype]) {
                o += OPtrto;
                if(l->type->type->etype != TARRAY)
                        fatal("not ptr ary");
                if(l->type->type->bound < 0)
                        o += ODynam;
        } else {
                if(l->type->etype != TARRAY)
                        fatal("not ary");
                if(l->type->bound < 0)
                        o += ODynam;
        }

        *w = n->type->width;
        if(isconst(r, CTINT))
                goto oindex_const;

        switch(*w) {
        default:
                goto no;
        case 1:
        case 2:
        case 4:
        case 8:
                break;
        }

        // load the array (reg)
        if(l->ullman > r->ullman) {
                regalloc(reg, types[tptr], N);
                if(o & OPtrto) {
                        cgen(l, reg);
                        cgen_checknil(reg);
                } else
                        agen(l, reg);
        }

        // load the index (reg1)
        t = types[TUINT32];
        if(issigned[r->type->etype])
                t = types[TINT32];
        regalloc(reg1, t, N);
        regalloc(&n3, types[TINT32], reg1);
        p2 = cgenindex(r, &n3, debug['B'] || n->bounded);
        gmove(&n3, reg1);
        regfree(&n3);

        // load the array (reg)
        if(l->ullman <= r->ullman) {
                regalloc(reg, types[tptr], N);
                if(o & OPtrto) {
                        cgen(l, reg);
                        cgen_checknil(reg);
                } else
                        agen(l, reg);
        }

        // check bounds
        if(!debug['B']) {
                if(o & ODynam) {
                        n2 = *reg;
                        n2.op = OINDREG;
                        n2.type = types[tptr];
                        n2.xoffset = Array_nel;
                } else {
                        if(o & OPtrto)
                                nodconst(&n2, types[TUINT32], l->type->type->bound);
                        else
                                nodconst(&n2, types[TUINT32], l->type->bound);
                }
                regalloc(&n3, n2.type, N);
                cgen(&n2, &n3);
                gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3);
                regfree(&n3);
                p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1);
                if(p2)
                        patch(p2, pc);
                ginscall(panicindex, 0);
                patch(p1, pc);
        }

        if(o & ODynam) {
                n2 = *reg;
                n2.op = OINDREG;
                n2.type = types[tptr];
                n2.xoffset = Array_array;
                gmove(&n2, reg);
        }

        switch(*w) {
        case 1:
                gins(AADD, reg1, reg);
                break;
        case 2:
                gshift(AADD, reg1, SHIFT_LL, 1, reg);
                break;
        case 4:
                gshift(AADD, reg1, SHIFT_LL, 2, reg);
                break;
        case 8:
                gshift(AADD, reg1, SHIFT_LL, 3, reg);
                break;
        }

        naddr(reg1, a, 1);
        a->type = D_OREG;
        a->reg = reg->val.u.reg;
        a->offset = 0;
        goto yes;

oindex_const:
        // index is constant
        // can check statically and
        // can multiply by width statically

        regalloc(reg, types[tptr], N);
        if(o & OPtrto) {
                cgen(l, reg);
                cgen_checknil(reg);
        } else
                agen(l, reg);

        v = mpgetfix(r->val.u.xval);
        if(o & ODynam) {
                if(!debug['B'] && !n->bounded) {
                        n1 = *reg;
                        n1.op = OINDREG;
                        n1.type = types[tptr];
                        n1.xoffset = Array_nel;
                        nodconst(&n2, types[TUINT32], v);
                        regalloc(&n3, types[TUINT32], N);
                        cgen(&n2, &n3);
                        regalloc(&n4, n1.type, N);
                        cgen(&n1, &n4);
                        gcmp(optoas(OCMP, types[TUINT32]), &n4, &n3);
                        regfree(&n4);
                        regfree(&n3);
                        p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1);
                        ginscall(panicindex, 0);
                        patch(p1, pc);
                }

                n1 = *reg;
                n1.op = OINDREG;
                n1.type = types[tptr];
                n1.xoffset = Array_array;
                gmove(&n1, reg);
        }

        n2 = *reg;
        n2.op = OINDREG;
        n2.xoffset = v * (*w);
        a->type = D_NONE;
        a->name = D_NONE;
        naddr(&n2, a, 1);
        goto yes;

yes:
        return 1;

no:
        sudoclean();
        return 0;
}

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