summaryrefslogtreecommitdiff
path: root/src/cmd/5g/gsubr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/5g/gsubr.c')
-rw-r--r--src/cmd/5g/gsubr.c2010
1 files changed, 2010 insertions, 0 deletions
diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c
new file mode 100644
index 000000000..ddaf52a88
--- /dev/null
+++ b/src/cmd/5g/gsubr.c
@@ -0,0 +1,2010 @@
+// 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 "gg.h"
+
+// TODO(kaib): Can make this bigger if we move
+// the text segment up higher in 5l for all GOOS.
+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->loc = pcloc;
+ pcloc++;
+}
+
+/*
+ * generate and return proc with p->as = as,
+ * linked into program. pc is next instruction.
+ */
+Prog*
+prog(int as)
+{
+ Prog *p;
+
+ p = pc;
+ pc = mal(sizeof(*pc));
+
+ clearp(pc);
+
+ if(lineno == 0) {
+ if(debug['K'])
+ warn("prog: line 0");
+ }
+
+ p->as = as;
+ p->lineno = lineno;
+ p->link = pc;
+ return p;
+}
+
+/*
+ * generate a branch.
+ * t is ignored.
+ */
+Prog*
+gbranch(int as, Type *t)
+{
+ Prog *p;
+
+ p = prog(as);
+ p->to.type = D_BRANCH;
+ p->to.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.branch = to;
+ p->to.offset = to->loc;
+}
+
+Prog*
+unpatch(Prog *p)
+{
+ Prog *q;
+
+ if(p->to.type != D_BRANCH)
+ fatal("unpatch: not a branch");
+ q = p->to.branch;
+ p->to.branch = P;
+ p->to.offset = 0;
+ return q;
+}
+
+/*
+ * start a new Prog list.
+ */
+Plist*
+newplist(void)
+{
+ Plist *pl;
+
+ pl = mal(sizeof(*pl));
+ if(plist == nil)
+ plist = pl;
+ else
+ plast->link = pl;
+ plast = pl;
+
+ pc = mal(sizeof(*pc));
+ clearp(pc);
+ pl->firstpc = pc;
+
+ return pl;
+}
+
+void
+clearstk(void)
+{
+ Plist *pl;
+ Prog *p, *p1, *p2, *p3;
+ Node dst, end, zero, con;
+
+ if(plast->firstpc->to.offset <= 0)
+ return;
+
+ // reestablish context for inserting code
+ // at beginning of function.
+ pl = plast;
+ p1 = pl->firstpc;
+ p2 = p1->link;
+ pc = mal(sizeof(*pc));
+ clearp(pc);
+ p1->link = pc;
+
+ // zero stack frame
+
+ // MOVW $4(SP), R1
+ nodreg(&dst, types[tptr], 1);
+ p = gins(AMOVW, N, &dst);
+ p->from.type = D_CONST;
+ p->from.reg = REGSP;
+ p->from.offset = 4;
+
+ // MOVW $n(R1), R2
+ nodreg(&end, types[tptr], 2);
+ p = gins(AMOVW, N, &end);
+ p->from.type = D_CONST;
+ p->from.reg = 1;
+ p->from.offset = p1->to.offset;
+
+ // MOVW $0, R3
+ nodreg(&zero, types[TUINT32], 3);
+ nodconst(&con, types[TUINT32], 0);
+ gmove(&con, &zero);
+
+ // L:
+ // MOVW.P R3, 0(R1) +4
+ // CMP R1, R2
+ // BNE L
+ p = gins(AMOVW, &zero, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = 4;
+ p->scond |= C_PBIT;
+ p3 = p;
+ p = gins(ACMP, &dst, N);
+ raddr(&end, p);
+ patch(gbranch(ABNE, T), p3);
+
+ // continue with original code.
+ gins(ANOP, N, N)->link = p2;
+ pc = P;
+}
+
+void
+gused(Node *n)
+{
+ gins(ANOP, n, N); // used
+}
+
+Prog*
+gjmp(Prog *to)
+{
+ Prog *p;
+
+ p = gbranch(AB, T);
+ if(to != P)
+ patch(p, to);
+ return p;
+}
+
+void
+ggloblnod(Node *nam, int32 width)
+{
+ Prog *p;
+
+ p = gins(AGLOBL, nam, N);
+ p->lineno = nam->lineno;
+ p->to.sym = S;
+ p->to.type = D_CONST;
+ p->to.offset = width;
+}
+
+void
+ggloblsym(Sym *s, int32 width, int dupok)
+{
+ Prog *p;
+
+ p = gins(AGLOBL, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->to.type = D_CONST;
+ p->to.name = D_NONE;
+ p->to.offset = width;
+ if(dupok)
+ p->reg = DUPOK;
+}
+
+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)
+{
+ if(a->type == D_CONST && a->name == D_EXTERN || a->type == D_REG) {
+ a->type = D_OREG;
+ }
+}
+
+static int resvd[] =
+{
+ 9, // reserved for m
+ 10, // reserved for g
+};
+
+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;
+}
+
+/*
+ * 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(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)
+ goto out;
+
+ 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(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 && iscomplex[n->type->etype])
+ return;
+ if(n->op != OREGISTER && n->op != OINDREG)
+ fatal("regfree: not a register");
+ i = n->val.u.reg;
+ if(i < 0 || i >= sizeof(reg))
+ fatal("regfree: reg out of range");
+ if(reg[i] <= 0)
+ fatal("regfree: reg not allocated");
+ reg[i]--;
+}
+
+/*
+ * 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;
+ 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);
+
+ 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;
+
+fp:
+ 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:
+ 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);
+
+ sclean[nsclean].op = OEMPTY;
+ if(nsclean >= nelem(sclean))
+ fatal("split64 clean");
+ 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
+ case CASE(TUINT8, TINT8):
+ case CASE(TINT16, TINT8): // truncate
+ case CASE(TUINT16, TINT8):
+ case CASE(TINT32, TINT8):
+ case CASE(TUINT32, TINT8):
+ a = AMOVB;
+ break;
+
+ case CASE(TINT8, TUINT8):
+ case CASE(TUINT8, 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 = AMOVB;
+ goto trunc64;
+
+ case CASE(TINT64, TUINT8):
+ case CASE(TUINT64, TUINT8):
+ a = AMOVBU;
+ goto trunc64;
+
+ case CASE(TINT16, TINT16): // same size
+ case CASE(TUINT16, TINT16):
+ case CASE(TINT32, TINT16): // truncate
+ case CASE(TUINT32, TINT16):
+ a = AMOVH;
+ break;
+
+ case CASE(TINT16, TUINT16):
+ case CASE(TUINT16, TUINT16):
+ case CASE(TINT32, TUINT16):
+ case CASE(TUINT32, TUINT16):
+ a = AMOVHU;
+ break;
+
+ case CASE(TINT64, TINT16): // truncate low word
+ case CASE(TUINT64, TINT16):
+ a = AMOVH;
+ 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 = AMOVB;
+ 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 = AMOVH;
+ 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 = AMOVB;
+ break;
+ case TUINT8:
+ ta = AMOVBU;
+ break;
+ case TINT16:
+ ta = AMOVH;
+ 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 = AMOVB;
+ break;
+ case TUINT8:
+ fa = AMOVBU;
+ break;
+ case TINT16:
+ fa = AMOVH;
+ 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 || rhs->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;
+}
+
+static void
+checkoffset(Addr *a, int canemitcode)
+{
+ Prog *p;
+ Node n1;
+
+ if(a->offset < unmappedzero)
+ return;
+ if(!canemitcode)
+ fatal("checkoffset %#x, cannot emit code", a->offset);
+
+ // cannot rely on unmapped nil page at 0 to catch
+ // reference with large offset. instead, emit explicit
+ // test of 0(reg).
+ regalloc(&n1, types[TUINTPTR], N);
+ p = gins(AMOVW, N, &n1);
+ p->from = *a;
+ p->from.offset = 0;
+ regfree(&n1);
+}
+
+/*
+ * generate code to compute n;
+ * make a refer to result.
+ */
+void
+naddr(Node *n, Addr *a, int canemitcode)
+{
+ a->type = D_NONE;
+ a->name = D_NONE;
+ a->reg = NREG;
+ a->node = N;
+ a->etype = 0;
+ if(n == N)
+ return;
+
+ 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 = S;
+ 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 = n->sym;
+ a->offset = n->xoffset;
+ checkoffset(a, canemitcode);
+ 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 = n->left->sym;
+ a->type = D_OREG;
+ a->name = D_PARAM;
+ 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->pun = n->pun;
+ a->offset = n->xoffset;
+ a->sym = n->sym;
+ if(a->sym == S)
+ a->sym = lookup(".noname");
+ if(n->method) {
+ if(n->type != T)
+ if(n->type->sym != S)
+ if(n->type->sym->pkg != nil)
+ a->sym = pkglookup(a->sym->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;
+ if (n->sym)
+ a->node = n->orig;
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ a->name = D_PARAM;
+ break;
+ case PFUNC:
+ a->name = D_EXTERN;
+ a->type = D_CONST;
+ break;
+ }
+ break;
+
+ case OLITERAL:
+ switch(n->val.ctype) {
+ default:
+ fatal("naddr: const %lT", n->type);
+ break;
+ case CTFLT:
+ a->type = D_FCONST;
+ a->dval = mpgetflt(n->val.u.fval);
+ break;
+ case CTINT:
+ a->sym = S;
+ 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 = S;
+ a->type = D_CONST;
+ a->offset = n->val.u.bval;
+ break;
+ case CTNIL:
+ a->sym = S;
+ a->type = D_CONST;
+ a->offset = 0;
+ break;
+ }
+ 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;
+ if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero)
+ checkoffset(a, canemitcode);
+ 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;
+ if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero)
+ checkoffset(a, canemitcode);
+ 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);
+ }
+ }
+}
+
+/*
+ * 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):
+ case CASE(OAS, TINT8):
+ a = AMOVB;
+ break;
+
+ case CASE(OAS, TUINT8):
+ a = AMOVBU;
+ break;
+
+ case CASE(OAS, TINT16):
+ a = AMOVH;
+ 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):
+ 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(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, 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;
+ int 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(n->val.ctype != 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:
+ 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 AMOVBU: case AMOVH: 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);
+ n1.xoffset = -(oary[0]+1);
+ }
+
+ for(i=1; i<o; i++) {
+ if(oary[i] >= 0)
+ fatal("cant happen");
+ gins(AMOVW, &n1, 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);
+ 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);
+ 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);
+ 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(l->type->width >= unmappedzero && l->op == OIND) {
+ // cannot rely on page protections to
+ // catch array ptr == 0, so dereference.
+ n2 = *reg;
+ n2.op = OINDREG;
+ n2.type = types[TUINTPTR];
+ n2.xoffset = 0;
+ regalloc(&n3, n2.type, N);
+ gins(AMOVW, &n2, &n3);
+ regfree(&n3);
+ }
+ nodconst(&n2, types[TUINT32], l->type->bound);
+ if(o & OPtrto)
+ nodconst(&n2, types[TUINT32], l->type->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);
+ 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);
+ else
+ agen(l, reg);
+
+ v = mpgetfix(r->val.u.xval);
+ if(o & ODynam) {
+
+ if(!debug['B'] && !n->etype) {
+ 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);
+ 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;
+}