summaryrefslogtreecommitdiff
path: root/src/cmd/5g
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
committerOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
commit5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch)
treec0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/5g
parent80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff)
downloadgolang-upstream/60.tar.gz
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/5g')
-rw-r--r--src/cmd/5g/Makefile36
-rw-r--r--src/cmd/5g/cgen.c1328
-rw-r--r--src/cmd/5g/cgen64.c716
-rw-r--r--src/cmd/5g/doc.go15
-rw-r--r--src/cmd/5g/galign.c39
-rw-r--r--src/cmd/5g/gg.h171
-rw-r--r--src/cmd/5g/ggen.c1030
-rw-r--r--src/cmd/5g/gobj.c623
-rw-r--r--src/cmd/5g/gsubr.c2010
-rw-r--r--src/cmd/5g/list.c331
-rw-r--r--src/cmd/5g/opt.h165
-rw-r--r--src/cmd/5g/peep.c1521
-rw-r--r--src/cmd/5g/reg.c1607
13 files changed, 9592 insertions, 0 deletions
diff --git a/src/cmd/5g/Makefile b/src/cmd/5g/Makefile
new file mode 100644
index 000000000..b47014a4e
--- /dev/null
+++ b/src/cmd/5g/Makefile
@@ -0,0 +1,36 @@
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.inc
+O:=$(HOST_O)
+
+TARG=5g
+
+HFILES=\
+ ../gc/go.h\
+ ../5l/5.out.h\
+ gg.h\
+ opt.h\
+
+OFILES=\
+ ../5l/enam.$O\
+ cgen.$O\
+ cgen64.$O\
+ cplx.$O\
+ galign.$O\
+ ggen.$O\
+ gobj.$O\
+ gsubr.$O\
+ list.$O\
+ peep.$O\
+ pgen.$O\
+ reg.$O\
+
+LIB=\
+ ../gc/gc.a\
+
+include ../../Make.ccmd
+
+%.$O: ../gc/%.c
+ $(HOST_CC) $(HOST_CFLAGS) -c -I. -o $@ ../gc/$*.c
diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c
new file mode 100644
index 000000000..6e2fbe20f
--- /dev/null
+++ b/src/cmd/5g/cgen.c
@@ -0,0 +1,1328 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "gg.h"
+
+/*
+ * generate:
+ * res = n;
+ * simplifies and calls gmove.
+ */
+void
+cgen(Node *n, Node *res)
+{
+ Node *nl, *nr, *r;
+ Node n1, n2, n3, f0, f1;
+ int a, w;
+ Prog *p1, *p2, *p3;
+ Addr addr;
+
+ if(debug['g']) {
+ dump("\ncgen-n", n);
+ dump("cgen-res", res);
+ }
+ if(n == N || n->type == T)
+ goto ret;
+
+ if(res == N || res->type == T)
+ fatal("cgen: res nil");
+
+ while(n->op == OCONVNOP)
+ n = n->left;
+
+ if(n->ullman >= UINF) {
+ if(n->op == OINDREG)
+ fatal("cgen: this is going to misscompile");
+ if(res->ullman >= UINF) {
+ tempname(&n1, n->type);
+ cgen(n, &n1);
+ cgen(&n1, res);
+ goto ret;
+ }
+ }
+
+ if(isfat(n->type)) {
+ if(n->type->width < 0)
+ fatal("forgot to compute width for %T", n->type);
+ sgen(n, res, n->type->width);
+ goto ret;
+ }
+
+
+ // update addressability for string, slice
+ // can't do in walk because n->left->addable
+ // changes if n->left is an escaping local variable.
+ switch(n->op) {
+ case OLEN:
+ if(isslice(n->left->type) || istype(n->left->type, TSTRING))
+ n->addable = n->left->addable;
+ break;
+ case OCAP:
+ if(isslice(n->left->type))
+ n->addable = n->left->addable;
+ break;
+ }
+
+ // if both are addressable, move
+ if(n->addable && res->addable) {
+ if(is64(n->type) || is64(res->type) ||
+ n->op == OREGISTER || res->op == OREGISTER ||
+ iscomplex[n->type->etype] || iscomplex[res->type->etype]) {
+ gmove(n, res);
+ } else {
+ regalloc(&n1, n->type, N);
+ gmove(n, &n1);
+ cgen(&n1, res);
+ regfree(&n1);
+ }
+ goto ret;
+ }
+
+ // if both are not addressable, use a temporary.
+ if(!n->addable && !res->addable) {
+ // could use regalloc here sometimes,
+ // but have to check for ullman >= UINF.
+ tempname(&n1, n->type);
+ cgen(n, &n1);
+ cgen(&n1, res);
+ return;
+ }
+
+ // if result is not addressable directly but n is,
+ // compute its address and then store via the address.
+ if(!res->addable) {
+ igen(res, &n1, N);
+ cgen(n, &n1);
+ regfree(&n1);
+ return;
+ }
+
+ if(complexop(n, res)) {
+ complexgen(n, res);
+ return;
+ }
+
+ // if n is sudoaddable generate addr and move
+ if (!is64(n->type) && !is64(res->type) && !iscomplex[n->type->etype] && !iscomplex[res->type->etype]) {
+ a = optoas(OAS, n->type);
+ if(sudoaddable(a, n, &addr, &w)) {
+ if (res->op != OREGISTER) {
+ regalloc(&n2, res->type, N);
+ p1 = gins(a, N, &n2);
+ p1->from = addr;
+ if(debug['g'])
+ print("%P [ignore previous line]\n", p1);
+ gmove(&n2, res);
+ regfree(&n2);
+ } else {
+ p1 = gins(a, N, res);
+ p1->from = addr;
+ if(debug['g'])
+ print("%P [ignore previous line]\n", p1);
+ }
+ sudoclean();
+ goto ret;
+ }
+ }
+
+ // otherwise, the result is addressable but n is not.
+ // let's do some computation.
+
+ nl = n->left;
+ nr = n->right;
+
+ if(nl != N && nl->ullman >= UINF)
+ if(nr != N && nr->ullman >= UINF) {
+ tempname(&n1, nl->type);
+ cgen(nl, &n1);
+ n2 = *n;
+ n2.left = &n1;
+ cgen(&n2, res);
+ goto ret;
+ }
+
+ // 64-bit ops are hard on 32-bit machine.
+ if(is64(n->type) || is64(res->type) || n->left != N && is64(n->left->type)) {
+ switch(n->op) {
+ // math goes to cgen64.
+ case OMINUS:
+ case OCOM:
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OOR:
+ case OXOR:
+ cgen64(n, res);
+ return;
+ }
+ }
+
+ if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype])
+ goto flt;
+ switch(n->op) {
+ default:
+ dump("cgen", n);
+ fatal("cgen: unknown op %N", n);
+ break;
+
+ case OREAL:
+ case OIMAG:
+ case OCOMPLEX:
+ fatal("unexpected complex");
+ break;
+
+ // these call bgen to get a bool value
+ case OOROR:
+ case OANDAND:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case ONOT:
+ p1 = gbranch(AB, T);
+ p2 = pc;
+ gmove(nodbool(1), res);
+ p3 = gbranch(AB, T);
+ patch(p1, pc);
+ bgen(n, 1, p2);
+ gmove(nodbool(0), res);
+ patch(p3, pc);
+ goto ret;
+
+ case OPLUS:
+ cgen(nl, res);
+ goto ret;
+
+ // unary
+ case OCOM:
+ a = optoas(OXOR, nl->type);
+ regalloc(&n1, nl->type, N);
+ cgen(nl, &n1);
+ nodconst(&n2, nl->type, -1);
+ gins(a, &n2, &n1);
+ gmove(&n1, res);
+ regfree(&n1);
+ goto ret;
+
+ case OMINUS:
+ nodconst(&n3, nl->type, 0);
+ regalloc(&n2, nl->type, res);
+ regalloc(&n1, nl->type, N);
+ gmove(&n3, &n2);
+ cgen(nl, &n1);
+ gins(optoas(OSUB, nl->type), &n1, &n2);
+ gmove(&n2, res);
+ regfree(&n1);
+ regfree(&n2);
+ goto ret;
+
+ // symmetric binary
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OADD:
+ case OMUL:
+ a = optoas(n->op, nl->type);
+ goto sbop;
+
+ // asymmetric binary
+ case OSUB:
+ a = optoas(n->op, nl->type);
+ goto abop;
+
+ case OLSH:
+ case ORSH:
+ cgen_shift(n->op, nl, nr, res);
+ break;
+
+ case OCONV:
+ if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) {
+ cgen(nl, res);
+ break;
+ }
+ if(nl->addable && !is64(nl->type)) {
+ regalloc(&n1, nl->type, res);
+ gmove(nl, &n1);
+ } else {
+ if(n->type->width > widthptr || is64(nl->type) || isfloat[nl->type->etype])
+ tempname(&n1, nl->type);
+ else
+ regalloc(&n1, nl->type, res);
+ cgen(nl, &n1);
+ }
+ if(n->type->width > widthptr || is64(n->type) || isfloat[n->type->etype])
+ tempname(&n2, n->type);
+ else
+ regalloc(&n2, n->type, N);
+ gmove(&n1, &n2);
+ gmove(&n2, res);
+ if(n1.op == OREGISTER)
+ regfree(&n1);
+ if(n2.op == OREGISTER)
+ regfree(&n2);
+ break;
+
+ case ODOT:
+ case ODOTPTR:
+ case OINDEX:
+ case OIND:
+ case ONAME: // PHEAP or PPARAMREF var
+ igen(n, &n1, res);
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+
+ case OLEN:
+ if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
+ // map has len in the first 32-bit word.
+ // a zero pointer means zero length
+ regalloc(&n1, types[tptr], res);
+ cgen(nl, &n1);
+
+ nodconst(&n2, types[tptr], 0);
+ regalloc(&n3, n2.type, N);
+ gmove(&n2, &n3);
+ gcmp(optoas(OCMP, types[tptr]), &n1, &n3);
+ regfree(&n3);
+ p1 = gbranch(optoas(OEQ, types[tptr]), T);
+
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.type = types[TINT32];
+ gmove(&n2, &n1);
+
+ patch(p1, pc);
+
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ if(istype(nl->type, TSTRING) || isslice(nl->type)) {
+ // both slice and string have len one pointer into the struct.
+ igen(nl, &n1, res);
+ n1.op = OREGISTER; // was OINDREG
+ regalloc(&n2, types[TUINT32], &n1);
+ n1.op = OINDREG;
+ n1.type = types[TUINT32];
+ n1.xoffset = Array_nel;
+ gmove(&n1, &n2);
+ gmove(&n2, res);
+ regfree(&n1);
+ regfree(&n2);
+ break;
+ }
+ fatal("cgen: OLEN: unknown type %lT", nl->type);
+ break;
+
+ case OCAP:
+ if(istype(nl->type, TCHAN)) {
+ // chan has cap in the second 32-bit word.
+ // a zero pointer means zero length
+ regalloc(&n1, types[tptr], res);
+ cgen(nl, &n1);
+
+ nodconst(&n2, types[tptr], 0);
+ regalloc(&n3, n2.type, N);
+ gmove(&n2, &n3);
+ gcmp(optoas(OCMP, types[tptr]), &n1, &n3);
+ regfree(&n3);
+ p1 = gbranch(optoas(OEQ, types[tptr]), T);
+
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.xoffset = 4;
+ n2.type = types[TINT32];
+ gmove(&n2, &n1);
+
+ patch(p1, pc);
+
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ if(isslice(nl->type)) {
+ regalloc(&n1, types[tptr], res);
+ agen(nl, &n1);
+ n1.op = OINDREG;
+ n1.type = types[TUINT32];
+ n1.xoffset = Array_cap;
+ gmove(&n1, res);
+ regfree(&n1);
+ break;
+ }
+ fatal("cgen: OCAP: unknown type %lT", nl->type);
+ break;
+
+ case OADDR:
+ agen(nl, res);
+ break;
+
+ case OCALLMETH:
+ cgen_callmeth(n, 0);
+ cgen_callret(n, res);
+ break;
+
+ case OCALLINTER:
+ cgen_callinter(n, res, 0);
+ cgen_callret(n, res);
+ break;
+
+ case OCALLFUNC:
+ cgen_call(n, 0);
+ cgen_callret(n, res);
+ break;
+
+ case OMOD:
+ case ODIV:
+ a = optoas(n->op, nl->type);
+ goto abop;
+ }
+ goto ret;
+
+sbop: // symmetric binary
+ if(nl->ullman < nr->ullman) {
+ r = nl;
+ nl = nr;
+ nr = r;
+ }
+
+abop: // asymmetric binary
+ // TODO(kaib): use fewer registers here.
+ if(nl->ullman >= nr->ullman) {
+ regalloc(&n1, nl->type, res);
+ cgen(nl, &n1);
+ regalloc(&n2, nr->type, N);
+ cgen(nr, &n2);
+ } else {
+ regalloc(&n2, nr->type, N);
+ cgen(nr, &n2);
+ regalloc(&n1, nl->type, res);
+ cgen(nl, &n1);
+ }
+ gins(a, &n2, &n1);
+ gmove(&n1, res);
+ regfree(&n1);
+ regfree(&n2);
+ goto ret;
+
+flt: // floating-point.
+ regalloc(&f0, nl->type, res);
+ if(nr != N)
+ goto flt2;
+
+ if(n->op == OMINUS) {
+ nr = nodintconst(-1);
+ convlit(&nr, n->type);
+ n->op = OMUL;
+ goto flt2;
+ }
+
+ // unary
+ cgen(nl, &f0);
+ if(n->op != OCONV && n->op != OPLUS)
+ gins(optoas(n->op, n->type), &f0, &f0);
+ gmove(&f0, res);
+ regfree(&f0);
+ goto ret;
+
+flt2: // binary
+ if(nl->ullman >= nr->ullman) {
+ cgen(nl, &f0);
+ regalloc(&f1, n->type, N);
+ gmove(&f0, &f1);
+ cgen(nr, &f0);
+ gins(optoas(n->op, n->type), &f0, &f1);
+ } else {
+ cgen(nr, &f0);
+ regalloc(&f1, n->type, N);
+ cgen(nl, &f1);
+ gins(optoas(n->op, n->type), &f0, &f1);
+ }
+ gmove(&f1, res);
+ regfree(&f0);
+ regfree(&f1);
+ goto ret;
+
+ret:
+ ;
+}
+
+/*
+ * generate array index into res.
+ * n might be any size; res is 32-bit.
+ * returns Prog* to patch to panic call.
+ */
+Prog*
+cgenindex(Node *n, Node *res)
+{
+ Node tmp, lo, hi, zero, n1, n2;
+
+ if(!is64(n->type)) {
+ cgen(n, res);
+ return nil;
+ }
+
+ tempname(&tmp, types[TINT64]);
+ cgen(n, &tmp);
+ split64(&tmp, &lo, &hi);
+ gmove(&lo, res);
+ if(debug['B']) {
+ splitclean();
+ return nil;
+ }
+ regalloc(&n1, types[TINT32], N);
+ regalloc(&n2, types[TINT32], N);
+ nodconst(&zero, types[TINT32], 0);
+ gmove(&hi, &n1);
+ gmove(&zero, &n2);
+ gcmp(ACMP, &n1, &n2);
+ regfree(&n2);
+ regfree(&n1);
+ splitclean();
+ return gbranch(ABNE, T);
+}
+
+/*
+ * generate:
+ * res = &n;
+ */
+void
+agen(Node *n, Node *res)
+{
+ Node *nl, *nr;
+ Node n1, n2, n3, n4, n5, tmp;
+ Prog *p1, *p2;
+ uint32 w;
+ uint64 v;
+ int r;
+
+ if(debug['g']) {
+ dump("\nagen-res", res);
+ dump("agen-r", n);
+ }
+ if(n == N || n->type == T || res == N || res->type == T)
+ fatal("agen");
+
+ while(n->op == OCONVNOP)
+ n = n->left;
+
+ if(n->addable) {
+ memset(&n1, 0, sizeof n1);
+ n1.op = OADDR;
+ n1.left = n;
+ regalloc(&n2, types[tptr], res);
+ gins(AMOVW, &n1, &n2);
+ gmove(&n2, res);
+ regfree(&n2);
+ goto ret;
+ }
+
+ nl = n->left;
+ nr = n->right;
+
+ switch(n->op) {
+ default:
+ fatal("agen: unknown op %N", n);
+ break;
+
+ case OCALLMETH:
+ case OCALLFUNC:
+ // Release res so that it is available for cgen_call.
+ // Pick it up again after the call.
+ r = -1;
+ if(n->ullman >= UINF) {
+ if(res->op == OREGISTER || res->op == OINDREG) {
+ r = res->val.u.reg;
+ reg[r]--;
+ }
+ }
+ if(n->op == OCALLMETH)
+ cgen_callmeth(n, 0);
+ else
+ cgen_call(n, 0);
+ if(r >= 0)
+ reg[r]++;
+ cgen_aret(n, res);
+ break;
+
+ case OCALLINTER:
+ cgen_callinter(n, res, 0);
+ cgen_aret(n, res);
+ break;
+
+ case OINDEX:
+ p2 = nil; // to be patched to panicindex.
+ w = n->type->width;
+ if(nr->addable) {
+ if(!isconst(nr, CTINT))
+ tempname(&tmp, types[TINT32]);
+ if(!isconst(nl, CTSTR))
+ agenr(nl, &n3, res);
+ if(!isconst(nr, CTINT)) {
+ p2 = cgenindex(nr, &tmp);
+ regalloc(&n1, tmp.type, N);
+ gmove(&tmp, &n1);
+ }
+ } else
+ if(nl->addable) {
+ if(!isconst(nr, CTINT)) {
+ tempname(&tmp, types[TINT32]);
+ p2 = cgenindex(nr, &tmp);
+ regalloc(&n1, tmp.type, N);
+ gmove(&tmp, &n1);
+ }
+ if(!isconst(nl, CTSTR)) {
+ regalloc(&n3, types[tptr], res);
+ agen(nl, &n3);
+ }
+ } else {
+ tempname(&tmp, types[TINT32]);
+ p2 = cgenindex(nr, &tmp);
+ nr = &tmp;
+ if(!isconst(nl, CTSTR))
+ agenr(nl, &n3, res);
+ regalloc(&n1, tmp.type, N);
+ gins(optoas(OAS, tmp.type), &tmp, &n1);
+ }
+
+ // &a is in &n3 (allocated in res)
+ // i is in &n1 (if not constant)
+ // w is width
+
+ // constant index
+ if(isconst(nr, CTINT)) {
+ if(isconst(nl, CTSTR))
+ fatal("constant string constant index");
+ v = mpgetfix(nr->val.u.xval);
+ if(isslice(nl->type) || nl->type->etype == TSTRING) {
+ if(!debug['B'] && !n->etype) {
+ n1 = n3;
+ n1.op = OINDREG;
+ n1.type = types[tptr];
+ n1.xoffset = Array_nel;
+ regalloc(&n4, n1.type, N);
+ cgen(&n1, &n4);
+ nodconst(&n2, types[TUINT32], v);
+ regalloc(&n5, n2.type, N);
+ gmove(&n2, &n5);
+ gcmp(optoas(OCMP, types[TUINT32]), &n4, &n5);
+ regfree(&n4);
+ regfree(&n5);
+ p1 = gbranch(optoas(OGT, types[TUINT32]), T);
+ ginscall(panicindex, 0);
+ patch(p1, pc);
+ }
+
+ n1 = n3;
+ n1.op = OINDREG;
+ n1.type = types[tptr];
+ n1.xoffset = Array_array;
+ gmove(&n1, &n3);
+ }
+
+ nodconst(&n2, types[tptr], v*w);
+ regalloc(&n4, n2.type, N);
+ gmove(&n2, &n4);
+ gins(optoas(OADD, types[tptr]), &n4, &n3);
+ regfree(&n4);
+
+ gmove(&n3, res);
+ regfree(&n3);
+ break;
+ }
+
+ regalloc(&n2, types[TINT32], &n1); // i
+ gmove(&n1, &n2);
+ regfree(&n1);
+
+ if(!debug['B'] && !n->etype) {
+ // check bounds
+ regalloc(&n4, types[TUINT32], N);
+ if(isconst(nl, CTSTR)) {
+ nodconst(&n1, types[TUINT32], nl->val.u.sval->len);
+ gmove(&n1, &n4);
+ } else if(isslice(nl->type) || nl->type->etype == TSTRING) {
+ n1 = n3;
+ n1.op = OINDREG;
+ n1.type = types[tptr];
+ n1.xoffset = Array_nel;
+ cgen(&n1, &n4);
+ } else {
+ nodconst(&n1, types[TUINT32], nl->type->bound);
+ gmove(&n1, &n4);
+ }
+ gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4);
+ regfree(&n4);
+ p1 = gbranch(optoas(OLT, types[TUINT32]), T);
+ if(p2)
+ patch(p2, pc);
+ ginscall(panicindex, 0);
+ patch(p1, pc);
+ }
+
+ if(isconst(nl, CTSTR)) {
+ regalloc(&n3, types[tptr], res);
+ p1 = gins(AMOVW, N, &n3);
+ datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
+ p1->from.type = D_CONST;
+ } else
+ if(isslice(nl->type) || nl->type->etype == TSTRING) {
+ n1 = n3;
+ n1.op = OINDREG;
+ n1.type = types[tptr];
+ n1.xoffset = Array_array;
+ gmove(&n1, &n3);
+ }
+
+ if(w == 0) {
+ // nothing to do
+ } else if(w == 1 || w == 2 || w == 4 || w == 8) {
+ memset(&n4, 0, sizeof n4);
+ n4.op = OADDR;
+ n4.left = &n2;
+ cgen(&n4, &n3);
+ if (w == 1)
+ gins(AADD, &n2, &n3);
+ else if(w == 2)
+ gshift(AADD, &n2, SHIFT_LL, 1, &n3);
+ else if(w == 4)
+ gshift(AADD, &n2, SHIFT_LL, 2, &n3);
+ else if(w == 8)
+ gshift(AADD, &n2, SHIFT_LL, 3, &n3);
+ } else {
+ regalloc(&n4, types[TUINT32], N);
+ nodconst(&n1, types[TUINT32], w);
+ gmove(&n1, &n4);
+ gins(optoas(OMUL, types[TUINT32]), &n4, &n2);
+ gins(optoas(OADD, types[tptr]), &n2, &n3);
+ regfree(&n4);
+ gmove(&n3, res);
+ }
+
+ gmove(&n3, res);
+ regfree(&n2);
+ regfree(&n3);
+ break;
+
+ case ONAME:
+ // should only get here with names in this func.
+ if(n->funcdepth > 0 && n->funcdepth != funcdepth) {
+ dump("bad agen", n);
+ fatal("agen: bad ONAME funcdepth %d != %d",
+ n->funcdepth, funcdepth);
+ }
+
+ // should only get here for heap vars or paramref
+ if(!(n->class & PHEAP) && n->class != PPARAMREF) {
+ dump("bad agen", n);
+ fatal("agen: bad ONAME class %#x", n->class);
+ }
+ cgen(n->heapaddr, res);
+ if(n->xoffset != 0) {
+ nodconst(&n1, types[TINT32], n->xoffset);
+ regalloc(&n2, n1.type, N);
+ regalloc(&n3, types[TINT32], N);
+ gmove(&n1, &n2);
+ gmove(res, &n3);
+ gins(optoas(OADD, types[tptr]), &n2, &n3);
+ gmove(&n3, res);
+ regfree(&n2);
+ regfree(&n3);
+ }
+ break;
+
+ case OIND:
+ cgen(nl, res);
+ break;
+
+ case ODOT:
+ agen(nl, res);
+ if(n->xoffset != 0) {
+ nodconst(&n1, types[TINT32], n->xoffset);
+ regalloc(&n2, n1.type, N);
+ regalloc(&n3, types[TINT32], N);
+ gmove(&n1, &n2);
+ gmove(res, &n3);
+ gins(optoas(OADD, types[tptr]), &n2, &n3);
+ gmove(&n3, res);
+ regfree(&n2);
+ regfree(&n3);
+ }
+ break;
+
+ case ODOTPTR:
+ cgen(nl, res);
+ if(n->xoffset != 0) {
+ // explicit check for nil if struct is large enough
+ // that we might derive too big a pointer.
+ if(nl->type->type->width >= unmappedzero) {
+ regalloc(&n1, types[tptr], N);
+ gmove(res, &n1);
+ p1 = gins(AMOVW, &n1, &n1);
+ p1->from.type = D_OREG;
+ p1->from.offset = 0;
+ regfree(&n1);
+ }
+ nodconst(&n1, types[TINT32], n->xoffset);
+ regalloc(&n2, n1.type, N);
+ regalloc(&n3, types[tptr], N);
+ gmove(&n1, &n2);
+ gmove(res, &n3);
+ gins(optoas(OADD, types[tptr]), &n2, &n3);
+ gmove(&n3, res);
+ regfree(&n2);
+ regfree(&n3);
+ }
+ break;
+ }
+
+ret:
+ ;
+}
+
+/*
+ * generate:
+ * newreg = &n;
+ * res = newreg
+ *
+ * on exit, a has been changed to be *newreg.
+ * caller must regfree(a).
+ */
+void
+igen(Node *n, Node *a, Node *res)
+{
+ regalloc(a, types[tptr], res);
+ agen(n, a);
+ a->op = OINDREG;
+ a->type = n->type;
+}
+
+/*
+ * generate:
+ * newreg = &n;
+ *
+ * caller must regfree(a).
+ */
+void
+agenr(Node *n, Node *a, Node *res)
+{
+ regalloc(a, types[tptr], res);
+ agen(n, a);
+}
+
+void
+gencmp0(Node *n, Type *t, int o, Prog *to)
+{
+ Node n1, n2, n3;
+ int a;
+
+ regalloc(&n1, t, N);
+ cgen(n, &n1);
+ a = optoas(OCMP, t);
+ if(a != ACMP) {
+ nodconst(&n2, t, 0);
+ regalloc(&n3, t, N);
+ gmove(&n2, &n3);
+ gcmp(a, &n1, &n3);
+ regfree(&n3);
+ } else
+ gins(ATST, &n1, N);
+ a = optoas(o, t);
+ patch(gbranch(a, t), to);
+ regfree(&n1);
+}
+
+/*
+ * generate:
+ * if(n == true) goto to;
+ */
+void
+bgen(Node *n, int true, Prog *to)
+{
+ int et, a;
+ Node *nl, *nr, *r;
+ Node n1, n2, n3, n4, tmp;
+ Prog *p1, *p2;
+
+ if(debug['g']) {
+ dump("\nbgen", n);
+ }
+
+ if(n == N)
+ n = nodbool(1);
+
+ if(n->ninit != nil)
+ genlist(n->ninit);
+
+ nl = n->left;
+ nr = n->right;
+
+ if(n->type == T) {
+ convlit(&n, types[TBOOL]);
+ if(n->type == T)
+ goto ret;
+ }
+
+ et = n->type->etype;
+ if(et != TBOOL) {
+ yyerror("cgen: bad type %T for %O", n->type, n->op);
+ patch(gins(AEND, N, N), to);
+ goto ret;
+ }
+ nl = N;
+ nr = N;
+
+ switch(n->op) {
+ default:
+ a = ONE;
+ if(!true)
+ a = OEQ;
+ gencmp0(n, n->type, a, to);
+ goto ret;
+
+ case OLITERAL:
+ // need to ask if it is bool?
+ if(!true == !n->val.u.bval)
+ patch(gbranch(AB, T), to);
+ goto ret;
+
+ case OANDAND:
+ if(!true)
+ goto caseor;
+
+ caseand:
+ p1 = gbranch(AB, T);
+ p2 = gbranch(AB, T);
+ patch(p1, pc);
+ bgen(n->left, !true, p2);
+ bgen(n->right, !true, p2);
+ p1 = gbranch(AB, T);
+ patch(p1, to);
+ patch(p2, pc);
+ goto ret;
+
+ case OOROR:
+ if(!true)
+ goto caseand;
+
+ caseor:
+ bgen(n->left, true, to);
+ bgen(n->right, true, to);
+ goto ret;
+
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OGT:
+ case OLE:
+ case OGE:
+ nr = n->right;
+ if(nr == N || nr->type == T)
+ goto ret;
+
+ case ONOT: // unary
+ nl = n->left;
+ if(nl == N || nl->type == T)
+ goto ret;
+ }
+
+ switch(n->op) {
+
+ case ONOT:
+ bgen(nl, !true, to);
+ goto ret;
+
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OGT:
+ case OLE:
+ case OGE:
+ a = n->op;
+ if(!true) {
+ if(isfloat[nl->type->etype]) {
+ // brcom is not valid on floats when NaN is involved.
+ p1 = gbranch(AB, T);
+ p2 = gbranch(AB, T);
+ patch(p1, pc);
+ bgen(n, 1, p2);
+ patch(gbranch(AB, T), to);
+ patch(p2, pc);
+ goto ret;
+ }
+ a = brcom(a);
+ true = !true;
+ }
+
+ // make simplest on right
+ if(nl->op == OLITERAL || (nl->ullman < UINF && nl->ullman < nr->ullman)) {
+ a = brrev(a);
+ r = nl;
+ nl = nr;
+ nr = r;
+ }
+
+ if(isslice(nl->type)) {
+ // only valid to cmp darray to literal nil
+ if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
+ yyerror("illegal array comparison");
+ break;
+ }
+
+ regalloc(&n1, types[tptr], N);
+ agen(nl, &n1);
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.xoffset = Array_array;
+ gencmp0(&n2, types[tptr], a, to);
+ regfree(&n1);
+ break;
+
+ a = optoas(a, types[tptr]);
+ regalloc(&n1, types[tptr], N);
+ regalloc(&n3, types[tptr], N);
+ regalloc(&n4, types[tptr], N);
+ agen(nl, &n1);
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.xoffset = Array_array;
+ gmove(&n2, &n4);
+ nodconst(&tmp, types[tptr], 0);
+ gmove(&tmp, &n3);
+ gcmp(optoas(OCMP, types[tptr]), &n4, &n3);
+ patch(gbranch(a, types[tptr]), to);
+ regfree(&n4);
+ regfree(&n3);
+ regfree(&n1);
+ break;
+ }
+
+ if(isinter(nl->type)) {
+ // front end shold only leave cmp to literal nil
+ if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
+ yyerror("illegal interface comparison");
+ break;
+ }
+
+ regalloc(&n1, types[tptr], N);
+ agen(nl, &n1);
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.xoffset = 0;
+ gencmp0(&n2, types[tptr], a, to);
+ regfree(&n1);
+ break;
+
+ a = optoas(a, types[tptr]);
+ regalloc(&n1, types[tptr], N);
+ regalloc(&n3, types[tptr], N);
+ regalloc(&n4, types[tptr], N);
+ agen(nl, &n1);
+ n2 = n1;
+ n2.op = OINDREG;
+ n2.xoffset = 0;
+ gmove(&n2, &n4);
+ nodconst(&tmp, types[tptr], 0);
+ gmove(&tmp, &n3);
+ gcmp(optoas(OCMP, types[tptr]), &n4, &n3);
+ patch(gbranch(a, types[tptr]), to);
+ regfree(&n1);
+ regfree(&n3);
+ regfree(&n4);
+ break;
+ }
+
+ if(iscomplex[nl->type->etype]) {
+ complexbool(a, nl, nr, true, to);
+ break;
+ }
+
+ if(is64(nr->type)) {
+ if(!nl->addable) {
+ tempname(&n1, nl->type);
+ cgen(nl, &n1);
+ nl = &n1;
+ }
+ if(!nr->addable) {
+ tempname(&n2, nr->type);
+ cgen(nr, &n2);
+ nr = &n2;
+ }
+ cmp64(nl, nr, a, to);
+ break;
+ }
+
+ if(nr->op == OLITERAL) {
+ if(nr->val.ctype == CTINT && mpgetfix(nr->val.u.xval) == 0) {
+ gencmp0(nl, nl->type, a, to);
+ break;
+ }
+ if(nr->val.ctype == CTNIL) {
+ gencmp0(nl, nl->type, a, to);
+ break;
+ }
+ }
+
+ a = optoas(a, nr->type);
+
+ if(nr->ullman >= UINF) {
+ regalloc(&n1, nl->type, N);
+ cgen(nl, &n1);
+
+ tempname(&tmp, nl->type);
+ gmove(&n1, &tmp);
+ regfree(&n1);
+
+ regalloc(&n2, nr->type, N);
+ cgen(nr, &n2);
+
+ regalloc(&n1, nl->type, N);
+ cgen(&tmp, &n1);
+
+ gcmp(optoas(OCMP, nr->type), &n1, &n2);
+ patch(gbranch(a, nr->type), to);
+
+ regfree(&n1);
+ regfree(&n2);
+ break;
+ }
+
+ tempname(&n3, nl->type);
+ cgen(nl, &n3);
+
+ tempname(&tmp, nr->type);
+ cgen(nr, &tmp);
+
+ regalloc(&n1, nl->type, N);
+ gmove(&n3, &n1);
+
+ regalloc(&n2, nr->type, N);
+ gmove(&tmp, &n2);
+
+ gcmp(optoas(OCMP, nr->type), &n1, &n2);
+ if(isfloat[nl->type->etype]) {
+ p1 = gbranch(ABVS, nr->type);
+ patch(gbranch(a, nr->type), to);
+ if(n->op == ONE)
+ patch(p1, to);
+ else
+ patch(p1, pc);
+ } else {
+ patch(gbranch(a, nr->type), to);
+ }
+ regfree(&n1);
+ regfree(&n2);
+ break;
+ }
+ goto ret;
+
+ret:
+ ;
+}
+
+/*
+ * n is on stack, either local variable
+ * or return value from function call.
+ * return n's offset from SP.
+ */
+int32
+stkof(Node *n)
+{
+ Type *t;
+ Iter flist;
+ int32 off;
+
+ switch(n->op) {
+ case OINDREG:
+ return n->xoffset;
+
+ case ODOT:
+ t = n->left->type;
+ if(isptr[t->etype])
+ break;
+ off = stkof(n->left);
+ if(off == -1000 || off == 1000)
+ return off;
+ return off + n->xoffset;
+
+ case OINDEX:
+ t = n->left->type;
+ if(!isfixedarray(t))
+ break;
+ off = stkof(n->left);
+ if(off == -1000 || off == 1000)
+ return off;
+ if(isconst(n->right, CTINT))
+ return off + t->type->width * mpgetfix(n->right->val.u.xval);
+ return 1000;
+
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALLFUNC:
+ t = n->left->type;
+ if(isptr[t->etype])
+ t = t->type;
+
+ t = structfirst(&flist, getoutarg(t));
+ if(t != T)
+ return t->width + 4; // correct for LR
+ break;
+ }
+
+ // botch - probably failing to recognize address
+ // arithmetic on the above. eg INDEX and DOT
+ return -1000;
+}
+
+/*
+ * block copy:
+ * memmove(&res, &n, w);
+ * NB: character copy assumed little endian architecture
+ */
+void
+sgen(Node *n, Node *res, int32 w)
+{
+ Node dst, src, tmp, nend;
+ int32 c, odst, osrc;
+ int dir, align, op;
+ Prog *p, *ploop;
+
+ if(debug['g']) {
+ print("\nsgen w=%d\n", w);
+ dump("r", n);
+ dump("res", res);
+ }
+ if(w == 0)
+ return;
+ if(w < 0)
+ fatal("sgen copy %d", w);
+ if(n->ullman >= UINF && res->ullman >= UINF)
+ fatal("sgen UINF");
+ if(n->type == T)
+ fatal("sgen: missing type");
+
+ // determine alignment.
+ // want to avoid unaligned access, so have to use
+ // smaller operations for less aligned types.
+ // for example moving [4]byte must use 4 MOVB not 1 MOVW.
+ align = n->type->align;
+ op = 0;
+ switch(align) {
+ default:
+ fatal("sgen: invalid alignment %d for %T", align, n->type);
+ case 1:
+ op = AMOVB;
+ break;
+ case 2:
+ op = AMOVH;
+ break;
+ case 4:
+ op = AMOVW;
+ break;
+ }
+ if(w%align)
+ fatal("sgen: unaligned size %d (align=%d) for %T", w, align, n->type);
+ c = w / align;
+
+ // offset on the stack
+ osrc = stkof(n);
+ odst = stkof(res);
+ if(osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000)) {
+ // osrc and odst both on stack, and at least one is in
+ // an unknown position. Could generate code to test
+ // for forward/backward copy, but instead just copy
+ // to a temporary location first.
+ tempname(&tmp, n->type);
+ sgen(n, &tmp, w);
+ sgen(&tmp, res, w);
+ return;
+ }
+ if(osrc%align != 0 || odst%align != 0)
+ fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align);
+ // if we are copying forward on the stack and
+ // the src and dst overlap, then reverse direction
+ dir = align;
+ if(osrc < odst && odst < osrc+w)
+ dir = -dir;
+
+ regalloc(&dst, types[tptr], res);
+ if(n->ullman >= res->ullman) {
+ agen(n, &dst); // temporarily use dst
+ regalloc(&src, types[tptr], N);
+ gins(AMOVW, &dst, &src);
+ agen(res, &dst);
+ } else {
+ agen(res, &dst);
+ regalloc(&src, types[tptr], N);
+ agen(n, &src);
+ }
+
+ regalloc(&tmp, types[TUINT32], N);
+
+ // set up end marker
+ memset(&nend, 0, sizeof nend);
+ if(c >= 4) {
+ regalloc(&nend, types[TUINT32], N);
+
+ p = gins(AMOVW, &src, &nend);
+ p->from.type = D_CONST;
+ if(dir < 0)
+ p->from.offset = dir;
+ else
+ p->from.offset = w;
+ }
+
+ // move src and dest to the end of block if necessary
+ if(dir < 0) {
+ p = gins(AMOVW, &src, &src);
+ p->from.type = D_CONST;
+ p->from.offset = w + dir;
+
+ p = gins(AMOVW, &dst, &dst);
+ p->from.type = D_CONST;
+ p->from.offset = w + dir;
+ }
+
+ // move
+ if(c >= 4) {
+ p = gins(op, &src, &tmp);
+ p->from.type = D_OREG;
+ p->from.offset = dir;
+ p->scond |= C_PBIT;
+ ploop = p;
+
+ p = gins(op, &tmp, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = dir;
+ p->scond |= C_PBIT;
+
+ p = gins(ACMP, &src, N);
+ raddr(&nend, p);
+
+ patch(gbranch(ABNE, T), ploop);
+ regfree(&nend);
+ } else {
+ while(c-- > 0) {
+ p = gins(op, &src, &tmp);
+ p->from.type = D_OREG;
+ p->from.offset = dir;
+ p->scond |= C_PBIT;
+ ploop = p;
+
+ p = gins(op, &tmp, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = dir;
+ p->scond |= C_PBIT;
+ }
+ }
+
+ regfree(&dst);
+ regfree(&src);
+ regfree(&tmp);
+}
diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c
new file mode 100644
index 000000000..b56df765b
--- /dev/null
+++ b/src/cmd/5g/cgen64.c
@@ -0,0 +1,716 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "gg.h"
+
+/*
+ * attempt to generate 64-bit
+ * res = n
+ * return 1 on success, 0 if op not handled.
+ */
+void
+cgen64(Node *n, Node *res)
+{
+ Node t1, t2, *l, *r;
+ Node lo1, lo2, hi1, hi2;
+ Node al, ah, bl, bh, cl, ch, s, n1, creg;
+ Prog *p1, *p2, *p3, *p4, *p5, *p6;
+
+ uint64 v;
+
+ if(res->op != OINDREG && res->op != ONAME) {
+ dump("n", n);
+ dump("res", res);
+ fatal("cgen64 %O of %O", n->op, res->op);
+ }
+
+ l = n->left;
+ if(!l->addable) {
+ tempname(&t1, l->type);
+ cgen(l, &t1);
+ l = &t1;
+ }
+
+ split64(l, &lo1, &hi1);
+ switch(n->op) {
+ default:
+ fatal("cgen64 %O", n->op);
+
+ case OMINUS:
+ split64(res, &lo2, &hi2);
+
+ regalloc(&t1, lo1.type, N);
+ regalloc(&al, lo1.type, N);
+ regalloc(&ah, hi1.type, N);
+
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+
+ gmove(ncon(0), &t1);
+ p1 = gins(ASUB, &al, &t1);
+ p1->scond |= C_SBIT;
+ gins(AMOVW, &t1, &lo2);
+
+ gmove(ncon(0), &t1);
+ gins(ASBC, &ah, &t1);
+ gins(AMOVW, &t1, &hi2);
+
+ regfree(&t1);
+ regfree(&al);
+ regfree(&ah);
+ splitclean();
+ splitclean();
+ return;
+
+ case OCOM:
+ regalloc(&t1, lo1.type, N);
+ gmove(ncon(-1), &t1);
+
+ split64(res, &lo2, &hi2);
+ regalloc(&n1, lo1.type, N);
+
+ gins(AMOVW, &lo1, &n1);
+ gins(AEOR, &t1, &n1);
+ gins(AMOVW, &n1, &lo2);
+
+ gins(AMOVW, &hi1, &n1);
+ gins(AEOR, &t1, &n1);
+ gins(AMOVW, &n1, &hi2);
+
+ regfree(&t1);
+ regfree(&n1);
+ splitclean();
+ splitclean();
+ return;
+
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OOR:
+ case OXOR:
+ // binary operators.
+ // common setup below.
+ break;
+ }
+
+ // setup for binary operators
+ r = n->right;
+ if(r != N && !r->addable) {
+ tempname(&t2, r->type);
+ cgen(r, &t2);
+ r = &t2;
+ }
+ if(is64(r->type))
+ split64(r, &lo2, &hi2);
+
+ regalloc(&al, lo1.type, N);
+ regalloc(&ah, hi1.type, N);
+
+ // Do op. Leave result in ah:al.
+ switch(n->op) {
+ default:
+ fatal("cgen64: not implemented: %N\n", n);
+
+ case OADD:
+ // TODO: Constants
+ regalloc(&bl, types[TPTR32], N);
+ regalloc(&bh, types[TPTR32], N);
+ gins(AMOVW, &hi1, &ah);
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi2, &bh);
+ gins(AMOVW, &lo2, &bl);
+ p1 = gins(AADD, &bl, &al);
+ p1->scond |= C_SBIT;
+ gins(AADC, &bh, &ah);
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+ case OSUB:
+ // TODO: Constants.
+ regalloc(&bl, types[TPTR32], N);
+ regalloc(&bh, types[TPTR32], N);
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+ gins(AMOVW, &lo2, &bl);
+ gins(AMOVW, &hi2, &bh);
+ p1 = gins(ASUB, &bl, &al);
+ p1->scond |= C_SBIT;
+ gins(ASBC, &bh, &ah);
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+ case OMUL:
+ // TODO(kaib): this can be done with 4 regs and does not need 6
+ regalloc(&bl, types[TPTR32], N);
+ regalloc(&bh, types[TPTR32], N);
+ regalloc(&cl, types[TPTR32], N);
+ regalloc(&ch, types[TPTR32], N);
+
+ // load args into bh:bl and bh:bl.
+ gins(AMOVW, &hi1, &bh);
+ gins(AMOVW, &lo1, &bl);
+ gins(AMOVW, &hi2, &ch);
+ gins(AMOVW, &lo2, &cl);
+
+ // bl * cl -> ah al
+ p1 = gins(AMULLU, N, N);
+ p1->from.type = D_REG;
+ p1->from.reg = bl.val.u.reg;
+ p1->reg = cl.val.u.reg;
+ p1->to.type = D_REGREG;
+ p1->to.reg = ah.val.u.reg;
+ p1->to.offset = al.val.u.reg;
+//print("%P\n", p1);
+
+ // bl * ch + ah -> ah
+ p1 = gins(AMULA, N, N);
+ p1->from.type = D_REG;
+ p1->from.reg = bl.val.u.reg;
+ p1->reg = ch.val.u.reg;
+ p1->to.type = D_REGREG;
+ p1->to.reg = ah.val.u.reg;
+ p1->to.offset = ah.val.u.reg;
+//print("%P\n", p1);
+
+ // bh * cl + ah -> ah
+ p1 = gins(AMULA, N, N);
+ p1->from.type = D_REG;
+ p1->from.reg = bh.val.u.reg;
+ p1->reg = cl.val.u.reg;
+ p1->to.type = D_REGREG;
+ p1->to.reg = ah.val.u.reg;
+ p1->to.offset = ah.val.u.reg;
+//print("%P\n", p1);
+
+ regfree(&bh);
+ regfree(&bl);
+ regfree(&ch);
+ regfree(&cl);
+
+ break;
+
+ case OLSH:
+ regalloc(&bl, lo1.type, N);
+ regalloc(&bh, hi1.type, N);
+ gins(AMOVW, &hi1, &bh);
+ gins(AMOVW, &lo1, &bl);
+
+ if(r->op == OLITERAL) {
+ v = mpgetfix(r->val.u.xval);
+ if(v >= 64) {
+ // TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al)
+ // here and below (verify it optimizes to EOR)
+ gins(AEOR, &al, &al);
+ gins(AEOR, &ah, &ah);
+ } else
+ if(v > 32) {
+ gins(AEOR, &al, &al);
+ // MOVW bl<<(v-32), ah
+ gshift(AMOVW, &bl, SHIFT_LL, (v-32), &ah);
+ } else
+ if(v == 32) {
+ gins(AEOR, &al, &al);
+ gins(AMOVW, &bl, &ah);
+ } else
+ if(v > 0) {
+ // MOVW bl<<v, al
+ gshift(AMOVW, &bl, SHIFT_LL, v, &al);
+
+ // MOVW bh<<v, ah
+ gshift(AMOVW, &bh, SHIFT_LL, v, &ah);
+
+ // OR bl>>(32-v), ah
+ gshift(AORR, &bl, SHIFT_LR, 32-v, &ah);
+ } else {
+ gins(AMOVW, &bl, &al);
+ gins(AMOVW, &bh, &ah);
+ }
+ goto olsh_break;
+ }
+
+ regalloc(&s, types[TUINT32], N);
+ regalloc(&creg, types[TUINT32], N);
+ if (is64(r->type)) {
+ // shift is >= 1<<32
+ split64(r, &cl, &ch);
+ gmove(&ch, &s);
+ p1 = gins(ATST, &s, N);
+ p6 = gbranch(ABNE, T);
+ gmove(&cl, &s);
+ splitclean();
+ } else {
+ gmove(r, &s);
+ p6 = P;
+ }
+ p1 = gins(ATST, &s, N);
+
+ // shift == 0
+ p1 = gins(AMOVW, &bl, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bh, &ah);
+ p1->scond = C_SCOND_EQ;
+ p2 = gbranch(ABEQ, T);
+
+ // shift is < 32
+ nodconst(&n1, types[TUINT32], 32);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO bl<<s, al
+ p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &al);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW.LO bh<<s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_LL, &s, &ah);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO s, creg
+ p1 = gins(ASUB, &s, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // OR.LO bl>>creg, ah
+ p1 = gregshift(AORR, &bl, SHIFT_LR, &creg, &ah);
+ p1->scond = C_SCOND_LO;
+
+ // BLO end
+ p3 = gbranch(ABLO, T);
+
+ // shift == 32
+ p1 = gins(AEOR, &al, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bl, &ah);
+ p1->scond = C_SCOND_EQ;
+ p4 = gbranch(ABEQ, T);
+
+ // shift is < 64
+ nodconst(&n1, types[TUINT32], 64);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // EOR.LO al, al
+ p1 = gins(AEOR, &al, &al);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW.LO creg>>1, creg
+ p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO creg, s
+ p1 = gins(ASUB, &creg, &s);
+ p1->scond = C_SCOND_LO;
+
+ // MOVW bl<<s, ah
+ p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &ah);
+ p1->scond = C_SCOND_LO;
+
+ p5 = gbranch(ABLO, T);
+
+ // shift >= 64
+ if (p6 != P) patch(p6, pc);
+ gins(AEOR, &al, &al);
+ gins(AEOR, &ah, &ah);
+
+ patch(p2, pc);
+ patch(p3, pc);
+ patch(p4, pc);
+ patch(p5, pc);
+ regfree(&s);
+ regfree(&creg);
+
+olsh_break:
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+
+ case ORSH:
+ regalloc(&bl, lo1.type, N);
+ regalloc(&bh, hi1.type, N);
+ gins(AMOVW, &hi1, &bh);
+ gins(AMOVW, &lo1, &bl);
+
+ if(r->op == OLITERAL) {
+ v = mpgetfix(r->val.u.xval);
+ if(v >= 64) {
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, al
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &al);
+
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ gins(AEOR, &al, &al);
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if(v > 32) {
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->(v-32), al
+ gshift(AMOVW, &bh, SHIFT_AR, v-32, &al);
+
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ // MOVW bh>>(v-32), al
+ gshift(AMOVW, &bh, SHIFT_LR, v-32, &al);
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if(v == 32) {
+ gins(AMOVW, &bh, &al);
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, ah
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ } else {
+ gins(AEOR, &ah, &ah);
+ }
+ } else
+ if( v > 0) {
+ // MOVW bl>>v, al
+ gshift(AMOVW, &bl, SHIFT_LR, v, &al);
+
+ // OR bh<<(32-v), al
+ gshift(AORR, &bh, SHIFT_LL, 32-v, &al);
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->v, ah
+ gshift(AMOVW, &bh, SHIFT_AR, v, &ah);
+ } else {
+ // MOVW bh>>v, ah
+ gshift(AMOVW, &bh, SHIFT_LR, v, &ah);
+ }
+ } else {
+ gins(AMOVW, &bl, &al);
+ gins(AMOVW, &bh, &ah);
+ }
+ goto orsh_break;
+ }
+
+ regalloc(&s, types[TUINT32], N);
+ regalloc(&creg, types[TUINT32], N);
+ if(is64(r->type)) {
+ // shift is >= 1<<32
+ split64(r, &cl, &ch);
+ gmove(&ch, &s);
+ gins(ATST, &s, N);
+ if(bh.type->etype == TINT32)
+ p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ else
+ p1 = gins(AEOR, &ah, &ah);
+ p1->scond = C_SCOND_NE;
+ p6 = gbranch(ABNE, T);
+ gmove(&cl, &s);
+ splitclean();
+ } else {
+ gmove(r, &s);
+ p6 = P;
+ }
+ p1 = gins(ATST, &s, N);
+
+ // shift == 0
+ p1 = gins(AMOVW, &bl, &al);
+ p1->scond = C_SCOND_EQ;
+ p1 = gins(AMOVW, &bh, &ah);
+ p1->scond = C_SCOND_EQ;
+ p2 = gbranch(ABEQ, T);
+
+ // check if shift is < 32
+ nodconst(&n1, types[TUINT32], 32);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO bl>>s, al
+ p1 = gregshift(AMOVW, &bl, SHIFT_LR, &s, &al);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO s,creg
+ p1 = gins(ASUB, &s, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // OR.LO bh<<(32-s), al
+ p1 = gregshift(AORR, &bh, SHIFT_LL, &creg, &al);
+ p1->scond = C_SCOND_LO;
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &ah);
+ } else {
+ // MOVW bh>>s, ah
+ p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &ah);
+ }
+ p1->scond = C_SCOND_LO;
+
+ // BLO end
+ p3 = gbranch(ABLO, T);
+
+ // shift == 32
+ p1 = gins(AMOVW, &bh, &al);
+ p1->scond = C_SCOND_EQ;
+ if(bh.type->etype == TINT32)
+ p1 = gshift(AMOVW, &bh, SHIFT_AR, 31, &ah);
+ else
+ p1 = gins(AEOR, &ah, &ah);
+ p4 = gbranch(ABEQ, T);
+
+ // check if shift is < 64
+ nodconst(&n1, types[TUINT32], 64);
+ gmove(&n1, &creg);
+ gcmp(ACMP, &s, &creg);
+
+ // MOVW.LO creg>>1, creg
+ p1 = gshift(AMOVW, &creg, SHIFT_LR, 1, &creg);
+ p1->scond = C_SCOND_LO;
+
+ // SUB.LO creg, s
+ p1 = gins(ASUB, &creg, &s);
+ p1->scond = C_SCOND_LO;
+
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->(s-32), al
+ p1 = gregshift(AMOVW, &bh, SHIFT_AR, &s, &al);
+ p1->scond = C_SCOND_LO;
+ } else {
+ // MOVW bh>>(v-32), al
+ p1 = gregshift(AMOVW, &bh, SHIFT_LR, &s, &al);
+ p1->scond = C_SCOND_LO;
+ }
+
+ // BLO end
+ p5 = gbranch(ABLO, T);
+
+ // s >= 64
+ if(p6 != P)
+ patch(p6, pc);
+ if(bh.type->etype == TINT32) {
+ // MOVW bh->31, al
+ gshift(AMOVW, &bh, SHIFT_AR, 31, &al);
+ } else {
+ gins(AEOR, &al, &al);
+ }
+
+ patch(p2, pc);
+ patch(p3, pc);
+ patch(p4, pc);
+ patch(p5, pc);
+ regfree(&s);
+ regfree(&creg);
+
+
+orsh_break:
+ regfree(&bl);
+ regfree(&bh);
+ break;
+
+ case OXOR:
+ case OAND:
+ case OOR:
+ // TODO(kaib): literal optimizations
+ // make constant the right side (it usually is anyway).
+// if(lo1.op == OLITERAL) {
+// nswap(&lo1, &lo2);
+// nswap(&hi1, &hi2);
+// }
+// if(lo2.op == OLITERAL) {
+// // special cases for constants.
+// lv = mpgetfix(lo2.val.u.xval);
+// hv = mpgetfix(hi2.val.u.xval);
+// splitclean(); // right side
+// split64(res, &lo2, &hi2);
+// switch(n->op) {
+// case OXOR:
+// gmove(&lo1, &lo2);
+// gmove(&hi1, &hi2);
+// switch(lv) {
+// case 0:
+// break;
+// case 0xffffffffu:
+// gins(ANOTL, N, &lo2);
+// break;
+// default:
+// gins(AXORL, ncon(lv), &lo2);
+// break;
+// }
+// switch(hv) {
+// case 0:
+// break;
+// case 0xffffffffu:
+// gins(ANOTL, N, &hi2);
+// break;
+// default:
+// gins(AXORL, ncon(hv), &hi2);
+// break;
+// }
+// break;
+
+// case OAND:
+// switch(lv) {
+// case 0:
+// gins(AMOVL, ncon(0), &lo2);
+// break;
+// default:
+// gmove(&lo1, &lo2);
+// if(lv != 0xffffffffu)
+// gins(AANDL, ncon(lv), &lo2);
+// break;
+// }
+// switch(hv) {
+// case 0:
+// gins(AMOVL, ncon(0), &hi2);
+// break;
+// default:
+// gmove(&hi1, &hi2);
+// if(hv != 0xffffffffu)
+// gins(AANDL, ncon(hv), &hi2);
+// break;
+// }
+// break;
+
+// case OOR:
+// switch(lv) {
+// case 0:
+// gmove(&lo1, &lo2);
+// break;
+// case 0xffffffffu:
+// gins(AMOVL, ncon(0xffffffffu), &lo2);
+// break;
+// default:
+// gmove(&lo1, &lo2);
+// gins(AORL, ncon(lv), &lo2);
+// break;
+// }
+// switch(hv) {
+// case 0:
+// gmove(&hi1, &hi2);
+// break;
+// case 0xffffffffu:
+// gins(AMOVL, ncon(0xffffffffu), &hi2);
+// break;
+// default:
+// gmove(&hi1, &hi2);
+// gins(AORL, ncon(hv), &hi2);
+// break;
+// }
+// break;
+// }
+// splitclean();
+// splitclean();
+// goto out;
+// }
+ regalloc(&n1, lo1.type, N);
+ gins(AMOVW, &lo1, &al);
+ gins(AMOVW, &hi1, &ah);
+ gins(AMOVW, &lo2, &n1);
+ gins(optoas(n->op, lo1.type), &n1, &al);
+ gins(AMOVW, &hi2, &n1);
+ gins(optoas(n->op, lo1.type), &n1, &ah);
+ regfree(&n1);
+ break;
+ }
+ if(is64(r->type))
+ splitclean();
+ splitclean();
+
+ split64(res, &lo1, &hi1);
+ gins(AMOVW, &al, &lo1);
+ gins(AMOVW, &ah, &hi1);
+ splitclean();
+
+//out:
+ regfree(&al);
+ regfree(&ah);
+}
+
+/*
+ * generate comparison of nl, nr, both 64-bit.
+ * nl is memory; nr is constant or memory.
+ */
+void
+cmp64(Node *nl, Node *nr, int op, Prog *to)
+{
+ Node lo1, hi1, lo2, hi2, r1, r2;
+ Prog *br;
+ Type *t;
+
+ split64(nl, &lo1, &hi1);
+ split64(nr, &lo2, &hi2);
+
+ // compare most significant word;
+ // if they differ, we're done.
+ t = hi1.type;
+ regalloc(&r1, types[TINT32], N);
+ regalloc(&r2, types[TINT32], N);
+ gins(AMOVW, &hi1, &r1);
+ gins(AMOVW, &hi2, &r2);
+ gcmp(ACMP, &r1, &r2);
+ regfree(&r1);
+ regfree(&r2);
+
+ br = P;
+ switch(op) {
+ default:
+ fatal("cmp64 %O %T", op, t);
+ case OEQ:
+ // cmp hi
+ // bne L
+ // cmp lo
+ // beq to
+ // L:
+ br = gbranch(ABNE, T);
+ break;
+ case ONE:
+ // cmp hi
+ // bne to
+ // cmp lo
+ // bne to
+ patch(gbranch(ABNE, T), to);
+ break;
+ case OGE:
+ case OGT:
+ // cmp hi
+ // bgt to
+ // blt L
+ // cmp lo
+ // bge to (or bgt to)
+ // L:
+ patch(gbranch(optoas(OGT, t), T), to);
+ br = gbranch(optoas(OLT, t), T);
+ break;
+ case OLE:
+ case OLT:
+ // cmp hi
+ // blt to
+ // bgt L
+ // cmp lo
+ // ble to (or jlt to)
+ // L:
+ patch(gbranch(optoas(OLT, t), T), to);
+ br = gbranch(optoas(OGT, t), T);
+ break;
+ }
+
+ // compare least significant word
+ t = lo1.type;
+ regalloc(&r1, types[TINT32], N);
+ regalloc(&r2, types[TINT32], N);
+ gins(AMOVW, &lo1, &r1);
+ gins(AMOVW, &lo2, &r2);
+ gcmp(ACMP, &r1, &r2);
+ regfree(&r1);
+ regfree(&r2);
+
+ // jump again
+ patch(gbranch(optoas(op, t), T), to);
+
+ // point first branch down here if appropriate
+ if(br != P)
+ patch(br, pc);
+
+ splitclean();
+ splitclean();
+}
diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go
new file mode 100644
index 000000000..e86013bdd
--- /dev/null
+++ b/src/cmd/5g/doc.go
@@ -0,0 +1,15 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+
+5g is the version of the gc compiler for the ARM.
+The $GOARCH for these tools is arm.
+
+It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go.
+
+There is no instruction optimizer, so the -N flag is a no-op.
+
+*/
+package documentation
diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c
new file mode 100644
index 000000000..12766102f
--- /dev/null
+++ b/src/cmd/5g/galign.c
@@ -0,0 +1,39 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "gg.h"
+
+int thechar = '5';
+char* thestring = "arm";
+
+vlong MAXWIDTH = (1LL<<32) - 1;
+
+/*
+ * go declares several platform-specific type aliases:
+ * int, uint, float, and uintptr
+ */
+Typedef typedefs[] =
+{
+ "int", TINT, TINT32,
+ "uint", TUINT, TUINT32,
+ "uintptr", TUINTPTR, TUINT32,
+ 0
+};
+
+void
+betypeinit(void)
+{
+ widthptr = 4;
+
+ zprog.link = P;
+ zprog.as = AGOK;
+ zprog.scond = C_SCOND_NONE;
+ zprog.reg = NREG;
+ zprog.from.type = D_NONE;
+ zprog.from.name = D_NONE;
+ zprog.from.reg = NREG;
+ zprog.to = zprog.from;
+
+ listinit();
+}
diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h
new file mode 100644
index 000000000..ce4558e21
--- /dev/null
+++ b/src/cmd/5g/gg.h
@@ -0,0 +1,171 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+
+#include "../gc/go.h"
+#include "../5l/5.out.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+typedef struct Addr Addr;
+
+struct Addr
+{
+ int32 offset;
+ int32 offset2;
+ double dval;
+ Prog* branch;
+ char sval[NSNAME];
+
+ Sym* sym;
+ Node* node;
+ int width;
+ uchar type;
+ char name;
+ uchar reg;
+ char pun;
+ uchar etype;
+};
+#define A ((Addr*)0)
+
+struct Prog
+{
+ short as; // opcode
+ uint32 loc; // pc offset in this func
+ uint32 lineno; // source line that generated this
+ Addr from; // src address
+ Addr to; // dst address
+ Prog* link; // next instruction in this func
+ void* regp; // points to enclosing Reg struct
+ uchar reg; // doubles as width in DATA op
+ uchar scond;
+};
+
+#define REGALLOC_R0 0
+#define REGALLOC_RMAX REGEXT
+#define REGALLOC_F0 (REGALLOC_RMAX+1)
+#define REGALLOC_FMAX (REGALLOC_F0 + FREGEXT)
+
+EXTERN Biobuf* bout;
+EXTERN int32 dynloc;
+EXTERN uchar reg[REGALLOC_FMAX+1];
+EXTERN int32 pcloc; // instruction counter
+EXTERN Strlit emptystring;
+extern char* anames[];
+EXTERN Hist* hist;
+EXTERN Prog zprog;
+EXTERN Node* curfn;
+EXTERN Node* newproc;
+EXTERN Node* deferproc;
+EXTERN Node* deferreturn;
+EXTERN Node* panicindex;
+EXTERN Node* panicslice;
+EXTERN Node* throwreturn;
+EXTERN long unmappedzero;
+EXTERN int maxstksize;
+
+/*
+ * gen.c
+ */
+void compile(Node*);
+void proglist(void);
+void gen(Node*);
+Node* lookdot(Node*, Node*, int);
+void cgen_as(Node*, Node*);
+void cgen_callmeth(Node*, int);
+void cgen_callinter(Node*, Node*, int);
+void cgen_proc(Node*, int);
+void cgen_callret(Node*, Node*);
+void cgen_dcl(Node*);
+int cgen_inline(Node*, Node*);
+int needconvert(Type*, Type*);
+void genconv(Type*, Type*);
+void allocparams(void);
+void checklabels();
+void ginscall(Node*, int);
+
+/*
+ * cgen
+ */
+void agen(Node*, Node*);
+Prog* cgenindex(Node *, Node *);
+void igen(Node*, Node*, Node*);
+void agenr(Node *n, Node *a, Node *res);
+vlong fieldoffset(Type*, Node*);
+void bgen(Node*, int, Prog*);
+void sgen(Node*, Node*, int32);
+void gmove(Node*, Node*);
+Prog* gins(int, Node*, Node*);
+int samaddr(Node*, Node*);
+void raddr(Node *n, Prog *p);
+Prog* gcmp(int, Node*, Node*);
+Prog* gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs);
+Prog * gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs);
+void naddr(Node*, Addr*, int);
+void cgen_aret(Node*, Node*);
+void cgen_shift(int, Node*, Node*, Node*);
+
+/*
+ * cgen64.c
+ */
+void cmp64(Node*, Node*, int, Prog*);
+void cgen64(Node*, Node*);
+
+/*
+ * gsubr.c
+ */
+void clearp(Prog*);
+void proglist(void);
+Prog* gbranch(int, Type*);
+Prog* prog(int);
+void gaddoffset(Node*);
+void gconv(int, int);
+int conv2pt(Type*);
+vlong convvtox(vlong, int);
+void fnparam(Type*, int, int);
+Prog* gop(int, Node*, Node*, Node*);
+void setconst(Addr*, vlong);
+void setaddr(Addr*, Node*);
+int optoas(int, Type*);
+void ginit(void);
+void gclean(void);
+void regalloc(Node*, Type*, Node*);
+void regfree(Node*);
+Node* nodarg(Type*, int);
+void nodreg(Node*, Type*, int);
+void nodindreg(Node*, Type*, int);
+void buildtxt(void);
+Plist* newplist(void);
+int isfat(Type*);
+int dotaddable(Node*, Node*);
+void sudoclean(void);
+int sudoaddable(int, Node*, Addr*, int*);
+void afunclit(Addr*);
+void datagostring(Strlit*, Addr*);
+void split64(Node*, Node*, Node*);
+void splitclean(void);
+Node* ncon(uint32 i);
+
+/*
+ * obj.c
+ */
+void datastring(char*, int, Addr*);
+
+/*
+ * list.c
+ */
+int Aconv(Fmt*);
+int Cconv(Fmt*);
+int Dconv(Fmt*);
+int Mconv(Fmt*);
+int Pconv(Fmt*);
+int Rconv(Fmt*);
+int Yconv(Fmt*);
+void listinit(void);
+
+void zaddr(Biobuf*, Addr*, int);
diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c
new file mode 100644
index 000000000..3f5f47e7b
--- /dev/null
+++ b/src/cmd/5g/ggen.c
@@ -0,0 +1,1030 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#undef EXTERN
+#define EXTERN
+#include "gg.h"
+#include "opt.h"
+
+void
+defframe(Prog *ptxt)
+{
+ // fill in argument size
+ ptxt->to.type = D_CONST2;
+ ptxt->reg = 0; // flags
+ ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr);
+
+ // fill in final stack size
+ if(stksize > maxstksize)
+ maxstksize = stksize;
+ ptxt->to.offset = rnd(maxstksize+maxarg, widthptr);
+ maxstksize = 0;
+}
+
+// Sweep the prog list to mark any used nodes.
+void
+markautoused(Prog* p)
+{
+ for (; p; p = p->link) {
+ if (p->from.name == D_AUTO && p->from.node)
+ p->from.node->used++;
+
+ if (p->to.name == D_AUTO && p->to.node)
+ p->to.node->used++;
+ }
+}
+
+// Fixup instructions after compactframe has moved all autos around.
+void
+fixautoused(Prog* p)
+{
+ for (; p; p = p->link) {
+ if (p->from.name == D_AUTO && p->from.node)
+ p->from.offset += p->from.node->stkdelta;
+
+ if (p->to.name == D_AUTO && p->to.node)
+ p->to.offset += p->to.node->stkdelta;
+ }
+}
+
+/*
+ * generate:
+ * call f
+ * proc=0 normal call
+ * proc=1 goroutine run in new proc
+ * proc=2 defer call save away stack
+ */
+void
+ginscall(Node *f, int proc)
+{
+ Prog *p;
+ Node n1, r, con;
+
+ switch(proc) {
+ default:
+ fatal("ginscall: bad proc %d", proc);
+ break;
+
+ case 0: // normal call
+ p = gins(ABL, N, f);
+ afunclit(&p->to);
+ break;
+
+ case 1: // call in new proc (go)
+ case 2: // deferred call (defer)
+ regalloc(&r, types[tptr], N);
+ p = gins(AMOVW, N, &r);
+ p->from.type = D_OREG;
+ p->from.reg = REGSP;
+
+ p = gins(AMOVW, &r, N);
+ p->to.type = D_OREG;
+ p->to.reg = REGSP;
+ p->to.offset = -12;
+ p->scond |= C_WBIT;
+
+ memset(&n1, 0, sizeof n1);
+ n1.op = OADDR;
+ n1.left = f;
+ gins(AMOVW, &n1, &r);
+
+ p = gins(AMOVW, &r, N);
+ p->to.type = D_OREG;
+ p->to.reg = REGSP;
+ p->to.offset = 8;
+
+ nodconst(&con, types[TINT32], argsize(f->type));
+ gins(AMOVW, &con, &r);
+ p = gins(AMOVW, &r, N);
+ p->to.type = D_OREG;
+ p->to.reg = REGSP;
+ p->to.offset = 4;
+ regfree(&r);
+
+ if(proc == 1)
+ ginscall(newproc, 0);
+ else
+ ginscall(deferproc, 0);
+
+ nodreg(&r, types[tptr], 1);
+ p = gins(AMOVW, N, N);
+ p->from.type = D_CONST;
+ p->from.reg = REGSP;
+ p->from.offset = 12;
+ p->to.reg = REGSP;
+ p->to.type = D_REG;
+
+ if(proc == 2) {
+ nodconst(&con, types[TINT32], 0);
+ p = gins(ACMP, &con, N);
+ p->reg = 0;
+ patch(gbranch(ABNE, T), retpc);
+ }
+ break;
+ }
+}
+
+/*
+ * n is call to interface method.
+ * generate res = n.
+ */
+void
+cgen_callinter(Node *n, Node *res, int proc)
+{
+ int r;
+ Node *i, *f;
+ Node tmpi, nodo, nodr, nodsp;
+
+ i = n->left;
+ if(i->op != ODOTINTER)
+ fatal("cgen_callinter: not ODOTINTER %O", i->op);
+
+ f = i->right; // field
+ if(f->op != ONAME)
+ fatal("cgen_callinter: not ONAME %O", f->op);
+
+ i = i->left; // interface
+
+ // Release res register during genlist and cgen,
+ // which might have their own function calls.
+ r = -1;
+ if(res != N && (res->op == OREGISTER || res->op == OINDREG)) {
+ r = res->val.u.reg;
+ reg[r]--;
+ }
+
+ if(!i->addable) {
+ tempname(&tmpi, i->type);
+ cgen(i, &tmpi);
+ i = &tmpi;
+ }
+
+ genlist(n->list); // args
+ if(r >= 0)
+ reg[r]++;
+
+ regalloc(&nodr, types[tptr], res);
+ regalloc(&nodo, types[tptr], &nodr);
+ nodo.op = OINDREG;
+
+ agen(i, &nodr); // REG = &inter
+
+ nodindreg(&nodsp, types[tptr], REGSP);
+ nodsp.xoffset = 4;
+ nodo.xoffset += widthptr;
+ cgen(&nodo, &nodsp); // 4(SP) = 4(REG) -- i.data
+
+ nodo.xoffset -= widthptr;
+ cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
+
+ nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
+ cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f]
+
+ // BOTCH nodr.type = fntype;
+ nodr.type = n->left->type;
+ ginscall(&nodr, proc);
+
+ regfree(&nodr);
+ regfree(&nodo);
+
+ setmaxarg(n->left->type);
+}
+
+/*
+ * generate function call;
+ * proc=0 normal call
+ * proc=1 goroutine run in new proc
+ * proc=2 defer call save away stack
+ */
+void
+cgen_call(Node *n, int proc)
+{
+ Type *t;
+ Node nod, afun;
+
+ if(n == N)
+ return;
+
+ if(n->left->ullman >= UINF) {
+ // if name involves a fn call
+ // precompute the address of the fn
+ tempname(&afun, types[tptr]);
+ cgen(n->left, &afun);
+ }
+
+ genlist(n->list); // assign the args
+ t = n->left->type;
+
+ setmaxarg(t);
+
+ // call tempname pointer
+ if(n->left->ullman >= UINF) {
+ regalloc(&nod, types[tptr], N);
+ cgen_as(&nod, &afun);
+ nod.type = t;
+ ginscall(&nod, proc);
+ regfree(&nod);
+ goto ret;
+ }
+
+ // call pointer
+ if(n->left->op != ONAME || n->left->class != PFUNC) {
+ regalloc(&nod, types[tptr], N);
+ cgen_as(&nod, n->left);
+ nod.type = t;
+ ginscall(&nod, proc);
+ regfree(&nod);
+ goto ret;
+ }
+
+ // call direct
+ n->left->method = 1;
+ ginscall(n->left, proc);
+
+
+ret:
+ ;
+}
+
+/*
+ * call to n has already been generated.
+ * generate:
+ * res = return value from call.
+ */
+void
+cgen_callret(Node *n, Node *res)
+{
+ Node nod;
+ Type *fp, *t;
+ Iter flist;
+
+ t = n->left->type;
+ if(t->etype == TPTR32 || t->etype == TPTR64)
+ t = t->type;
+
+ fp = structfirst(&flist, getoutarg(t));
+ if(fp == T)
+ fatal("cgen_callret: nil");
+
+ memset(&nod, 0, sizeof(nod));
+ nod.op = OINDREG;
+ nod.val.u.reg = REGSP;
+ nod.addable = 1;
+
+ nod.xoffset = fp->width + 4; // +4: saved lr at 0(SP)
+ nod.type = fp->type;
+ cgen_as(res, &nod);
+}
+
+/*
+ * call to n has already been generated.
+ * generate:
+ * res = &return value from call.
+ */
+void
+cgen_aret(Node *n, Node *res)
+{
+ Node nod1, nod2;
+ Type *fp, *t;
+ Iter flist;
+
+ t = n->left->type;
+ if(isptr[t->etype])
+ t = t->type;
+
+ fp = structfirst(&flist, getoutarg(t));
+ if(fp == T)
+ fatal("cgen_aret: nil");
+
+ memset(&nod1, 0, sizeof(nod1));
+ nod1.op = OINDREG;
+ nod1.val.u.reg = REGSP;
+ nod1.addable = 1;
+
+ nod1.xoffset = fp->width + 4; // +4: saved lr at 0(SP)
+ nod1.type = fp->type;
+
+ if(res->op != OREGISTER) {
+ regalloc(&nod2, types[tptr], res);
+ agen(&nod1, &nod2);
+ gins(AMOVW, &nod2, res);
+ regfree(&nod2);
+ } else
+ agen(&nod1, res);
+}
+
+/*
+ * generate return.
+ * n->left is assignments to return values.
+ */
+void
+cgen_ret(Node *n)
+{
+ genlist(n->list); // copy out args
+ if(hasdefer || curfn->exit)
+ gjmp(retpc);
+ else
+ gins(ARET, N, N);
+}
+
+/*
+ * generate += *= etc.
+ */
+void
+cgen_asop(Node *n)
+{
+ Node n1, n2, n3, n4;
+ Node *nl, *nr;
+ Prog *p1;
+ Addr addr;
+ int a, w;
+
+ nl = n->left;
+ nr = n->right;
+
+ if(nr->ullman >= UINF && nl->ullman >= UINF) {
+ tempname(&n1, nr->type);
+ cgen(nr, &n1);
+ n2 = *n;
+ n2.right = &n1;
+ cgen_asop(&n2);
+ goto ret;
+ }
+
+ if(!isint[nl->type->etype])
+ goto hard;
+ if(!isint[nr->type->etype])
+ goto hard;
+ if(is64(nl->type) || is64(nr->type))
+ goto hard64;
+
+ switch(n->etype) {
+ case OADD:
+ case OSUB:
+ case OXOR:
+ case OAND:
+ case OOR:
+ a = optoas(n->etype, nl->type);
+ if(nl->addable) {
+ regalloc(&n3, nr->type, N);
+ cgen(nr, &n3);
+ regalloc(&n2, nl->type, N);
+ cgen(nl, &n2);
+ gins(a, &n3, &n2);
+ cgen(&n2, nl);
+ regfree(&n2);
+ regfree(&n3);
+ goto ret;
+ }
+ if(nr->ullman < UINF)
+ if(sudoaddable(a, nl, &addr, &w)) {
+ w = optoas(OAS, nl->type);
+ regalloc(&n2, nl->type, N);
+ p1 = gins(w, N, &n2);
+ p1->from = addr;
+ regalloc(&n3, nr->type, N);
+ cgen(nr, &n3);
+ gins(a, &n3, &n2);
+ p1 = gins(w, &n2, N);
+ p1->to = addr;
+ regfree(&n2);
+ regfree(&n3);
+ sudoclean();
+ goto ret;
+ }
+ }
+
+hard:
+ n2.op = 0;
+ n1.op = 0;
+ if(nr->ullman >= nl->ullman || nl->addable) {
+ regalloc(&n2, nr->type, N);
+ cgen(nr, &n2);
+ nr = &n2;
+ } else {
+ tempname(&n2, nr->type);
+ cgen(nr, &n2);
+ nr = &n2;
+ }
+ if(!nl->addable) {
+ igen(nl, &n1, N);
+ nl = &n1;
+ }
+
+ n3 = *n;
+ n3.left = nl;
+ n3.right = nr;
+ n3.op = n->etype;
+
+ regalloc(&n4, nl->type, N);
+ cgen(&n3, &n4);
+ gmove(&n4, nl);
+
+ if(n1.op)
+ regfree(&n1);
+ if(n2.op == OREGISTER)
+ regfree(&n2);
+ regfree(&n4);
+ goto ret;
+
+hard64:
+ if(nr->ullman > nl->ullman) {
+ tempname(&n2, nr->type);
+ cgen(nr, &n2);
+ igen(nl, &n1, N);
+ } else {
+ igen(nl, &n1, N);
+ tempname(&n2, nr->type);
+ cgen(nr, &n2);
+ }
+
+ n3 = *n;
+ n3.left = &n1;
+ n3.right = &n2;
+ n3.op = n->etype;
+
+ cgen(&n3, &n1);
+
+ret:
+ ;
+}
+
+int
+samereg(Node *a, Node *b)
+{
+ if(a->op != OREGISTER)
+ return 0;
+ if(b->op != OREGISTER)
+ return 0;
+ if(a->val.u.reg != b->val.u.reg)
+ return 0;
+ return 1;
+}
+
+/*
+ * generate shift according to op, one of:
+ * res = nl << nr
+ * res = nl >> nr
+ */
+void
+cgen_shift(int op, Node *nl, Node *nr, Node *res)
+{
+ Node n1, n2, n3, nt, t, lo, hi;
+ int w;
+ Prog *p1, *p2, *p3;
+ Type *tr;
+ uvlong sc;
+
+ if(nl->type->width > 4)
+ fatal("cgen_shift %T", nl->type);
+
+ w = nl->type->width * 8;
+
+ if(nr->op == OLITERAL) {
+ regalloc(&n1, nl->type, res);
+ cgen(nl, &n1);
+ sc = mpgetfix(nr->val.u.xval);
+ if(sc == 0) {
+ // nothing to do
+ } else if(sc >= nl->type->width*8) {
+ if(op == ORSH && issigned[nl->type->etype])
+ gshift(AMOVW, &n1, SHIFT_AR, w, &n1);
+ else
+ gins(AEOR, &n1, &n1);
+ } else {
+ if(op == ORSH && issigned[nl->type->etype])
+ gshift(AMOVW, &n1, SHIFT_AR, sc, &n1);
+ else if(op == ORSH)
+ gshift(AMOVW, &n1, SHIFT_LR, sc, &n1);
+ else // OLSH
+ gshift(AMOVW, &n1, SHIFT_LL, sc, &n1);
+ }
+ gmove(&n1, res);
+ regfree(&n1);
+ return;
+ }
+
+ tr = nr->type;
+ if(tr->width > 4) {
+ tempname(&nt, nr->type);
+ if(nl->ullman >= nr->ullman) {
+ regalloc(&n2, nl->type, res);
+ cgen(nl, &n2);
+ cgen(nr, &nt);
+ n1 = nt;
+ } else {
+ cgen(nr, &nt);
+ regalloc(&n2, nl->type, res);
+ cgen(nl, &n2);
+ }
+ split64(&nt, &lo, &hi);
+ regalloc(&n1, types[TUINT32], N);
+ regalloc(&n3, types[TUINT32], N);
+ gmove(&lo, &n1);
+ gmove(&hi, &n3);
+ gins(ATST, &n3, N);
+ nodconst(&t, types[TUINT32], w);
+ p1 = gins(AMOVW, &t, &n1);
+ p1->scond = C_SCOND_NE;
+ tr = types[TUINT32];
+ regfree(&n3);
+ } else {
+ if(nl->ullman >= nr->ullman) {
+ regalloc(&n2, nl->type, res);
+ cgen(nl, &n2);
+ regalloc(&n1, nr->type, N);
+ cgen(nr, &n1);
+ } else {
+ regalloc(&n1, nr->type, N);
+ cgen(nr, &n1);
+ regalloc(&n2, nl->type, res);
+ cgen(nl, &n2);
+ }
+ }
+
+ // test for shift being 0
+ p1 = gins(ATST, &n1, N);
+ p3 = gbranch(ABEQ, T);
+
+ // test and fix up large shifts
+ regalloc(&n3, tr, N);
+ nodconst(&t, types[TUINT32], w);
+ gmove(&t, &n3);
+ gcmp(ACMP, &n1, &n3);
+ if(op == ORSH) {
+ if(issigned[nl->type->etype]) {
+ p1 = gshift(AMOVW, &n2, SHIFT_AR, w-1, &n2);
+ p2 = gregshift(AMOVW, &n2, SHIFT_AR, &n1, &n2);
+ } else {
+ p1 = gins(AEOR, &n2, &n2);
+ p2 = gregshift(AMOVW, &n2, SHIFT_LR, &n1, &n2);
+ }
+ p1->scond = C_SCOND_HS;
+ p2->scond = C_SCOND_LO;
+ } else {
+ p1 = gins(AEOR, &n2, &n2);
+ p2 = gregshift(AMOVW, &n2, SHIFT_LL, &n1, &n2);
+ p1->scond = C_SCOND_HS;
+ p2->scond = C_SCOND_LO;
+ }
+ regfree(&n3);
+
+ patch(p3, pc);
+ gmove(&n2, res);
+
+ regfree(&n1);
+ regfree(&n2);
+}
+
+void
+clearfat(Node *nl)
+{
+ uint32 w, c, q;
+ Node dst, nc, nz, end;
+ Prog *p, *pl;
+
+ /* clear a fat object */
+ if(debug['g'])
+ dump("\nclearfat", nl);
+
+ w = nl->type->width;
+ c = w % 4; // bytes
+ q = w / 4; // quads
+
+ regalloc(&dst, types[tptr], N);
+ agen(nl, &dst);
+ nodconst(&nc, types[TUINT32], 0);
+ regalloc(&nz, types[TUINT32], 0);
+ cgen(&nc, &nz);
+
+ if(q >= 4) {
+ regalloc(&end, types[tptr], N);
+ p = gins(AMOVW, &dst, &end);
+ p->from.type = D_CONST;
+ p->from.offset = q*4;
+
+ p = gins(AMOVW, &nz, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = 4;
+ p->scond |= C_PBIT;
+ pl = p;
+
+ p = gins(ACMP, &dst, N);
+ raddr(&end, p);
+ patch(gbranch(ABNE, T), pl);
+
+ regfree(&end);
+ } else
+ while(q > 0) {
+ p = gins(AMOVW, &nz, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = 4;
+ p->scond |= C_PBIT;
+//print("1. %P\n", p);
+ q--;
+ }
+
+ while(c > 0) {
+ p = gins(AMOVBU, &nz, &dst);
+ p->to.type = D_OREG;
+ p->to.offset = 1;
+ p->scond |= C_PBIT;
+//print("2. %P\n", p);
+ c--;
+ }
+ regfree(&dst);
+ regfree(&nz);
+}
+
+static int
+regcmp(const void *va, const void *vb)
+{
+ Node *ra, *rb;
+
+ ra = (Node*)va;
+ rb = (Node*)vb;
+ return ra->local - rb->local;
+}
+
+static Prog* throwpc;
+
+// We're only going to bother inlining if we can
+// convert all the arguments to 32 bits safely. Can we?
+static int
+fix64(NodeList *nn, int n)
+{
+ NodeList *l;
+ Node *r;
+ int i;
+
+ l = nn;
+ for(i=0; i<n; i++) {
+ r = l->n->right;
+ if(is64(r->type) && !smallintconst(r)) {
+ if(r->op == OCONV)
+ r = r->left;
+ if(is64(r->type))
+ return 0;
+ }
+ l = l->next;
+ }
+ return 1;
+}
+
+void
+getargs(NodeList *nn, Node *reg, int n)
+{
+ NodeList *l;
+ int i;
+
+ throwpc = nil;
+
+ l = nn;
+ for(i=0; i<n; i++) {
+ if(!smallintconst(l->n->right) && !isslice(l->n->right->type)) {
+ regalloc(reg+i, l->n->right->type, N);
+ cgen(l->n->right, reg+i);
+ } else
+ reg[i] = *l->n->right;
+ if(reg[i].local != 0)
+ yyerror("local used");
+ reg[i].local = l->n->left->xoffset;
+ l = l->next;
+ }
+ qsort((void*)reg, n, sizeof(*reg), regcmp);
+ for(i=0; i<n; i++)
+ reg[i].local = 0;
+}
+
+void
+cmpandthrow(Node *nl, Node *nr)
+{
+ vlong cl;
+ Prog *p1;
+ int op;
+ Node *c, n1, n2;
+
+ op = OLE;
+ if(smallintconst(nl)) {
+ cl = mpgetfix(nl->val.u.xval);
+ if(cl == 0)
+ return;
+ if(smallintconst(nr))
+ return;
+
+ // put the constant on the right
+ op = brrev(op);
+ c = nl;
+ nl = nr;
+ nr = c;
+ }
+
+ n1.op = OXXX;
+ if(nr->op != OREGISTER) {
+ regalloc(&n1, types[TUINT32], N);
+ gmove(nr, &n1);
+ nr = &n1;
+ }
+ n2.op = OXXX;
+ if(nl->op != OREGISTER) {
+ regalloc(&n2, types[TUINT32], N);
+ gmove(nl, &n2);
+ nl = &n2;
+ }
+ gcmp(optoas(OCMP, types[TUINT32]), nl, nr);
+ if(nr == &n1)
+ regfree(&n1);
+ if(nl == &n2)
+ regfree(&n2);
+ if(throwpc == nil) {
+ p1 = gbranch(optoas(op, types[TUINT32]), T);
+ throwpc = pc;
+ ginscall(panicslice, 0);
+ patch(p1, pc);
+ } else {
+ op = brcom(op);
+ p1 = gbranch(optoas(op, types[TUINT32]), T);
+ patch(p1, throwpc);
+ }
+}
+
+int
+sleasy(Node *n)
+{
+ if(n->op != ONAME)
+ return 0;
+ if(!n->addable)
+ return 0;
+ return 1;
+}
+
+// generate inline code for
+// slicearray
+// sliceslice
+// arraytoslice
+int
+cgen_inline(Node *n, Node *res)
+{
+ Node nodes[5];
+ Node n1, n2, n3, nres, ntemp;
+ vlong v;
+ int i, narg;
+
+ if(n->op != OCALLFUNC)
+ goto no;
+ if(!n->left->addable)
+ goto no;
+ if(n->left->sym == S)
+ goto no;
+ if(n->left->sym->pkg != runtimepkg)
+ goto no;
+ if(strcmp(n->left->sym->name, "slicearray") == 0)
+ goto slicearray;
+ if(strcmp(n->left->sym->name, "sliceslice") == 0) {
+ narg = 4;
+ goto sliceslice;
+ }
+ if(strcmp(n->left->sym->name, "sliceslice1") == 0) {
+ narg = 3;
+ goto sliceslice;
+ }
+ goto no;
+
+slicearray:
+ if(!sleasy(res))
+ goto no;
+ if(!fix64(n->list, 5))
+ goto no;
+ getargs(n->list, nodes, 5);
+
+ // if(hb[3] > nel[1]) goto throw
+ cmpandthrow(&nodes[3], &nodes[1]);
+
+ // if(lb[2] > hb[3]) goto throw
+ cmpandthrow(&nodes[2], &nodes[3]);
+
+ // len = hb[3] - lb[2] (destroys hb)
+ n2 = *res;
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_nel;
+
+ if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) {
+ v = mpgetfix(nodes[3].val.u.xval) -
+ mpgetfix(nodes[2].val.u.xval);
+ nodconst(&n1, types[TUINT32], v);
+ gmove(&n1, &n2);
+ } else {
+ regalloc(&n1, types[TUINT32], &nodes[3]);
+ gmove(&nodes[3], &n1);
+ if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0)
+ gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1);
+ gmove(&n1, &n2);
+ regfree(&n1);
+ }
+
+ // cap = nel[1] - lb[2] (destroys nel)
+ n2 = *res;
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_cap;
+
+ if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) {
+ v = mpgetfix(nodes[1].val.u.xval) -
+ mpgetfix(nodes[2].val.u.xval);
+ nodconst(&n1, types[TUINT32], v);
+ gmove(&n1, &n2);
+ } else {
+ regalloc(&n1, types[TUINT32], &nodes[1]);
+ gmove(&nodes[1], &n1);
+ if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0)
+ gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1);
+ gmove(&n1, &n2);
+ regfree(&n1);
+ }
+
+ // if slice could be too big, dereference to
+ // catch nil array pointer.
+ if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) {
+ n2 = nodes[0];
+ n2.xoffset = 0;
+ n2.op = OINDREG;
+ n2.type = types[TUINT8];
+ regalloc(&n1, types[TUINT32], N);
+ gins(AMOVB, &n2, &n1);
+ regfree(&n1);
+ }
+
+ // ary = old[0] + (lb[2] * width[4]) (destroys old)
+ n2 = *res;
+ n2.type = types[tptr];
+ n2.xoffset += Array_array;
+
+ if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) {
+ v = mpgetfix(nodes[2].val.u.xval) *
+ mpgetfix(nodes[4].val.u.xval);
+ if(v != 0) {
+ nodconst(&n1, types[tptr], v);
+ gins(optoas(OADD, types[tptr]), &n1, &nodes[0]);
+ }
+ } else {
+ regalloc(&n1, types[tptr], &nodes[2]);
+ gmove(&nodes[2], &n1);
+ if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) {
+ regalloc(&n3, types[tptr], N);
+ gmove(&nodes[4], &n3);
+ gins(optoas(OMUL, types[tptr]), &n3, &n1);
+ regfree(&n3);
+ }
+ gins(optoas(OADD, types[tptr]), &n1, &nodes[0]);
+ regfree(&n1);
+ }
+ gmove(&nodes[0], &n2);
+
+ for(i=0; i<5; i++) {
+ if(nodes[i].op == OREGISTER)
+ regfree(&nodes[i]);
+ }
+ return 1;
+
+sliceslice:
+ if(!fix64(n->list, narg))
+ goto no;
+ ntemp.op = OXXX;
+ if(!sleasy(n->list->n->right)) {
+ Node *n0;
+
+ n0 = n->list->n->right;
+ tempname(&ntemp, res->type);
+ cgen(n0, &ntemp);
+ n->list->n->right = &ntemp;
+ getargs(n->list, nodes, narg);
+ n->list->n->right = n0;
+ } else
+ getargs(n->list, nodes, narg);
+
+ nres = *res; // result
+ if(!sleasy(res)) {
+ if(ntemp.op == OXXX)
+ tempname(&ntemp, res->type);
+ nres = ntemp;
+ }
+
+ if(narg == 3) { // old[lb:]
+ // move width to where it would be for old[lb:hb]
+ nodes[3] = nodes[2];
+ nodes[2].op = OXXX;
+
+ // if(lb[1] > old.nel[0]) goto throw;
+ n2 = nodes[0];
+ n2.xoffset += Array_nel;
+ n2.type = types[TUINT32];
+ cmpandthrow(&nodes[1], &n2);
+
+ // ret.nel = old.nel[0]-lb[1];
+ n2 = nodes[0];
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_nel;
+
+ regalloc(&n1, types[TUINT32], N);
+ gmove(&n2, &n1);
+ if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0)
+ gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1);
+
+ n2 = nres;
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_nel;
+ gmove(&n1, &n2);
+ regfree(&n1);
+ } else { // old[lb:hb]
+ // if(hb[2] > old.cap[0]) goto throw;
+ n2 = nodes[0];
+ n2.xoffset += Array_cap;
+ n2.type = types[TUINT32];
+ cmpandthrow(&nodes[2], &n2);
+
+ // if(lb[1] > hb[2]) goto throw;
+ cmpandthrow(&nodes[1], &nodes[2]);
+
+ // ret.len = hb[2]-lb[1]; (destroys hb[2])
+ n2 = nres;
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_nel;
+
+ if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) {
+ v = mpgetfix(nodes[2].val.u.xval) -
+ mpgetfix(nodes[1].val.u.xval);
+ nodconst(&n1, types[TUINT32], v);
+ gmove(&n1, &n2);
+ } else {
+ regalloc(&n1, types[TUINT32], &nodes[2]);
+ gmove(&nodes[2], &n1);
+ if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0)
+ gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1);
+ gmove(&n1, &n2);
+ regfree(&n1);
+ }
+ }
+
+ // ret.cap = old.cap[0]-lb[1]; (uses hb[2])
+ n2 = nodes[0];
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_cap;
+
+ regalloc(&n1, types[TUINT32], &nodes[2]);
+ gmove(&n2, &n1);
+ if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0)
+ gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1);
+
+ n2 = nres;
+ n2.type = types[TUINT32];
+ n2.xoffset += Array_cap;
+ gmove(&n1, &n2);
+ regfree(&n1);
+
+ // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1])
+ n2 = nodes[0];
+ n2.type = types[tptr];
+ n2.xoffset += Array_array;
+ regalloc(&n3, types[tptr], N);
+ gmove(&n2, &n3);
+
+ regalloc(&n1, types[tptr], &nodes[1]);
+ if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) {
+ gmove(&n2, &n1);
+ v = mpgetfix(nodes[1].val.u.xval) *
+ mpgetfix(nodes[3].val.u.xval);
+ if(v != 0) {
+ nodconst(&n2, types[tptr], v);
+ gins(optoas(OADD, types[tptr]), &n3, &n1);
+ }
+ } else {
+ gmove(&nodes[1], &n1);
+ if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) {
+ regalloc(&n2, types[tptr], N);
+ gmove(&nodes[3], &n2);
+ gins(optoas(OMUL, types[tptr]), &n2, &n1);
+ regfree(&n2);
+ }
+ gins(optoas(OADD, types[tptr]), &n3, &n1);
+ }
+ regfree(&n3);
+
+ n2 = nres;
+ n2.type = types[tptr];
+ n2.xoffset += Array_array;
+ gmove(&n1, &n2);
+ regfree(&n1);
+
+ for(i=0; i<4; i++) {
+ if(nodes[i].op == OREGISTER)
+ regfree(&nodes[i]);
+ }
+
+ if(!sleasy(res)) {
+ cgen(&nres, res);
+ }
+ return 1;
+
+no:
+ return 0;
+}
diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c
new file mode 100644
index 000000000..27c8be67d
--- /dev/null
+++ b/src/cmd/5g/gobj.c
@@ -0,0 +1,623 @@
+// Derived from Inferno utils/5c/swt.c
+// http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.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"
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+ Bputc(b, ANAME); /* as */
+ Bputc(b, t); /* type */
+ Bputc(b, s->sym); /* sym */
+
+ Bputname(b, s);
+}
+
+void
+zfile(Biobuf *b, char *p, int n)
+{
+ Bputc(b, ANAME);
+ Bputc(b, D_FILE);
+ Bputc(b, 1);
+ Bputc(b, '<');
+ Bwrite(b, p, n);
+ Bputc(b, 0);
+}
+
+void
+zhist(Biobuf *b, int line, vlong offset)
+{
+ Addr a;
+
+ Bputc(b, AHISTORY);
+ Bputc(b, C_SCOND_NONE);
+ Bputc(b, NREG);
+ Bputc(b, line);
+ Bputc(b, line>>8);
+ Bputc(b, line>>16);
+ Bputc(b, line>>24);
+ zaddr(b, &zprog.from, 0);
+ a = zprog.to;
+ if(offset != 0) {
+ a.offset = offset;
+ a.type = D_CONST;
+ }
+ zaddr(b, &a, 0);
+}
+
+void
+zaddr(Biobuf *b, Addr *a, int s)
+{
+ int32 l;
+ uint64 e;
+ int i;
+ char *n;
+
+ switch(a->type) {
+ case D_STATIC:
+ case D_AUTO:
+ case D_EXTERN:
+ case D_PARAM:
+ // TODO(kaib): remove once everything seems to work
+ fatal("We should no longer generate these as types");
+
+ default:
+ Bputc(b, a->type);
+ Bputc(b, a->reg);
+ Bputc(b, s);
+ Bputc(b, a->name);
+ }
+
+ switch(a->type) {
+ default:
+ print("unknown type %d in zaddr\n", a->type);
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_PSR:
+ break;
+
+ case D_CONST2:
+ l = a->offset2;
+ Bputc(b, l);
+ Bputc(b, l>>8);
+ Bputc(b, l>>16);
+ Bputc(b, l>>24); // fall through
+ case D_OREG:
+ case D_CONST:
+ case D_SHIFT:
+ case D_STATIC:
+ case D_AUTO:
+ case D_EXTERN:
+ case D_PARAM:
+ l = a->offset;
+ Bputc(b, l);
+ Bputc(b, l>>8);
+ Bputc(b, l>>16);
+ Bputc(b, l>>24);
+ break;
+
+ case D_BRANCH:
+ if(a->branch == nil)
+ fatal("unpatched branch");
+ a->offset = a->branch->loc;
+ l = a->offset;
+ Bputc(b, l);
+ Bputc(b, l>>8);
+ Bputc(b, l>>16);
+ Bputc(b, l>>24);
+ break;
+
+ case D_SCONST:
+ n = a->sval;
+ for(i=0; i<NSNAME; i++) {
+ Bputc(b, *n);
+ n++;
+ }
+ break;
+
+ case D_REGREG:
+ Bputc(b, a->offset);
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ l = e;
+ Bputc(b, l);
+ Bputc(b, l>>8);
+ Bputc(b, l>>16);
+ Bputc(b, l>>24);
+ l = e >> 32;
+ Bputc(b, l);
+ Bputc(b, l>>8);
+ Bputc(b, l>>16);
+ Bputc(b, l>>24);
+ break;
+ }
+}
+
+void
+dumpfuncs(void)
+{
+ Plist *pl;
+ int sf, st, t, sym;
+ struct { Sym *sym; short type; } h[NSYM];
+ Sym *s;
+ Prog *p;
+
+ for(sym=0; sym<NSYM; sym++) {
+ h[sym].sym = S;
+ h[sym].type = 0;
+ }
+ sym = 1;
+
+ // fix up pc
+ pcloc = 0;
+ for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
+ for(p=pl->firstpc; p!=P; p=p->link) {
+ p->loc = pcloc;
+ if(p->as != ADATA && p->as != AGLOBL)
+ pcloc++;
+ }
+ }
+
+ // put out functions
+ for(pl=plist; pl!=nil; pl=pl->link) {
+ if(isblank(pl->name))
+ continue;
+
+ if(debug['S']) {
+ s = S;
+ if(pl->name != N)
+ s = pl->name->sym;
+ print("\n--- prog list \"%S\" ---\n", s);
+ for(p=pl->firstpc; p!=P; p=p->link)
+ print("%P\n", p);
+ }
+
+ for(p=pl->firstpc; p!=P; p=p->link) {
+ jackpot:
+ sf = 0;
+ s = p->from.sym;
+ while(s != S) {
+ sf = s->sym;
+ if(sf < 0 || sf >= NSYM)
+ sf = 0;
+ t = p->from.name;
+ if(t == D_ADDR)
+ t = p->from.name;
+ if(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ s->sym = sym;
+ zname(bout, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ sf = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ break;
+ }
+ st = 0;
+ s = p->to.sym;
+ while(s != S) {
+ st = s->sym;
+ if(st < 0 || st >= NSYM)
+ st = 0;
+ t = p->to.name;
+ if(t == D_ADDR)
+ t = p->to.name;
+ if(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ s->sym = sym;
+ zname(bout, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ Bputc(bout, p->as);
+ Bputc(bout, p->scond);
+ Bputc(bout, p->reg);
+ Bputc(bout, p->lineno);
+ Bputc(bout, p->lineno>>8);
+ Bputc(bout, p->lineno>>16);
+ Bputc(bout, p->lineno>>24);
+ zaddr(bout, &p->from, sf);
+ zaddr(bout, &p->to, st);
+ }
+ }
+}
+
+/* deferred DATA output */
+static Prog *strdat;
+static Prog *estrdat;
+static int gflag;
+static Prog *savepc;
+
+void
+data(void)
+{
+ gflag = debug['g'];
+ debug['g'] = 0;
+
+ if(estrdat == nil) {
+ strdat = mal(sizeof(*pc));
+ clearp(strdat);
+ estrdat = strdat;
+ }
+ if(savepc)
+ fatal("data phase error");
+ savepc = pc;
+ pc = estrdat;
+}
+
+void
+text(void)
+{
+ if(!savepc)
+ fatal("text phase error");
+ debug['g'] = gflag;
+ estrdat = pc;
+ pc = savepc;
+ savepc = nil;
+}
+
+void
+dumpdata(void)
+{
+ Prog *p;
+
+ if(estrdat == nil)
+ return;
+ *pc = *strdat;
+ if(gflag)
+ for(p=pc; p!=estrdat; p=p->link)
+ print("%P\n", p);
+ pc = estrdat;
+}
+
+int
+dsname(Sym *sym, int off, char *t, int n)
+{
+ Prog *p;
+
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.etype = TINT32;
+ p->from.offset = off;
+ p->from.reg = NREG;
+ p->from.sym = sym;
+
+ p->reg = n;
+
+ p->to.type = D_SCONST;
+ p->to.name = D_NONE;
+ p->to.reg = NREG;
+ p->to.offset = 0;
+ memmove(p->to.sval, t, n);
+ return off + n;
+}
+
+/*
+ * make a refer to the data s, s+len
+ * emitting DATA if needed.
+ */
+void
+datastring(char *s, int len, Addr *a)
+{
+ Sym *sym;
+
+ sym = stringsym(s, len);
+ a->type = D_OREG;
+ a->name = D_EXTERN;
+ a->etype = TINT32;
+ a->offset = widthptr+4; // skip header
+ a->reg = NREG;
+ a->sym = sym;
+}
+
+/*
+ * make a refer to the string sval,
+ * emitting DATA if needed.
+ */
+void
+datagostring(Strlit *sval, Addr *a)
+{
+ Sym *sym;
+
+ sym = stringsym(sval->s, sval->len);
+ a->type = D_OREG;
+ a->name = D_EXTERN;
+ a->etype = TINT32;
+ a->offset = 0; // header
+ a->reg = NREG;
+ a->sym = sym;
+}
+
+void
+gdata(Node *nam, Node *nr, int wid)
+{
+ Prog *p;
+ vlong v;
+
+ if(wid == 8 && is64(nr->type)) {
+ v = mpgetfix(nr->val.u.xval);
+ p = gins(ADATA, nam, nodintconst(v));
+ p->reg = 4;
+ p = gins(ADATA, nam, nodintconst(v>>32));
+ p->reg = 4;
+ p->from.offset += 4;
+ return;
+ }
+ p = gins(ADATA, nam, nr);
+ p->reg = wid;
+}
+
+void
+gdatacomplex(Node *nam, Mpcplx *cval)
+{
+ Prog *p;
+ int w;
+
+ w = cplxsubtype(nam->type->etype);
+ w = types[w]->width;
+
+ p = gins(ADATA, nam, N);
+ p->reg = w;
+ p->to.type = D_FCONST;
+ p->to.dval = mpgetflt(&cval->real);
+
+ p = gins(ADATA, nam, N);
+ p->reg = w;
+ p->from.offset += w;
+ p->to.type = D_FCONST;
+ p->to.dval = mpgetflt(&cval->imag);
+}
+
+void
+gdatastring(Node *nam, Strlit *sval)
+{
+ Prog *p;
+ Node nod1;
+
+ p = gins(ADATA, nam, N);
+ datastring(sval->s, sval->len, &p->to);
+ p->reg = types[tptr]->width;
+ p->to.type = D_CONST;
+ p->to.etype = TINT32;
+//print("%P\n", p);
+
+ nodconst(&nod1, types[TINT32], sval->len);
+ p = gins(ADATA, nam, &nod1);
+ p->reg = types[TINT32]->width;
+ p->from.offset += types[tptr]->width;
+}
+
+int
+dstringptr(Sym *s, int off, char *str)
+{
+ Prog *p;
+
+ off = rnd(off, widthptr);
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = off;
+ p->reg = widthptr;
+
+ datastring(str, strlen(str)+1, &p->to);
+ p->to.type = D_CONST;
+ p->to.etype = TINT32;
+ off += widthptr;
+
+ return off;
+}
+
+int
+dgostrlitptr(Sym *s, int off, Strlit *lit)
+{
+ Prog *p;
+
+ if(lit == nil)
+ return duintptr(s, off, 0);
+
+ off = rnd(off, widthptr);
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = off;
+ p->reg = widthptr;
+ datagostring(lit, &p->to);
+ p->to.type = D_CONST;
+ p->to.etype = TINT32;
+ off += widthptr;
+
+ return off;
+}
+
+int
+dgostringptr(Sym *s, int off, char *str)
+{
+ int n;
+ Strlit *lit;
+
+ if(str == nil)
+ return duintptr(s, off, 0);
+
+ n = strlen(str);
+ lit = mal(sizeof *lit + n);
+ strcpy(lit->s, str);
+ lit->len = n;
+ return dgostrlitptr(s, off, lit);
+}
+
+int
+duintxx(Sym *s, int off, uint64 v, int wid)
+{
+ Prog *p;
+
+ off = rnd(off, wid);
+
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = off;
+ p->reg = wid;
+ p->to.type = D_CONST;
+ p->to.name = D_NONE;
+ p->to.offset = v;
+ off += wid;
+
+ return off;
+}
+
+int
+dsymptr(Sym *s, int off, Sym *x, int xoff)
+{
+ Prog *p;
+
+ off = rnd(off, widthptr);
+
+ p = gins(ADATA, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = s;
+ p->from.offset = off;
+ p->reg = widthptr;
+ p->to.type = D_CONST;
+ p->to.name = D_EXTERN;
+ p->to.sym = x;
+ p->to.offset = xoff;
+ off += widthptr;
+
+ return off;
+}
+
+
+void
+genembedtramp(Type *rcvr, Type *method, Sym *newnam, int iface)
+{
+ // TODO(kaib): re-implement genembedtramp
+ genwrapper(rcvr, method, newnam, iface);
+/*
+ Sym *e;
+ int c, d, o;
+ Prog *p;
+ Type *f;
+
+ e = method->sym;
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(e, rcvr, d, nil, 0);
+ if(c == 1)
+ goto out;
+ }
+ fatal("genembedtramp %T.%S", rcvr, method->sym);
+
+out:
+ newplist()->name = newname(newnam);
+
+ //TEXT main·S_test2(SB),7,$0
+ p = pc;
+ gins(ATEXT, N, N);
+ p->from.type = D_OREG;
+ p->from.name = D_EXTERN;
+ p->from.sym = newnam;
+ p->to.type = D_CONST2;
+ p->reg = 7;
+ p->to.offset2 = 0;
+ p->to.reg = NREG;
+//print("1. %P\n", p);
+
+ o = 0;
+ for(c=d-1; c>=0; c--) {
+ f = dotlist[c].field;
+ o += f->width;
+ if(!isptr[f->type->etype])
+ continue;
+
+ //MOVW o(R0), R0
+ p = pc;
+ gins(AMOVW, N, N);
+ p->from.type = D_OREG;
+ p->from.reg = REGARG;
+ p->from.offset = o;
+ p->to.type = D_REG;
+ p->to.reg = REGARG;
+//print("2. %P\n", p);
+ o = 0;
+ }
+ if(o != 0) {
+ //MOVW $XX(R0), R0
+ p = pc;
+ gins(AMOVW, N, N);
+ p->from.type = D_CONST;
+ p->from.reg = REGARG;
+ p->from.offset = o;
+ p->to.type = D_REG;
+ p->to.reg = REGARG;
+//print("3. %P\n", p);
+ }
+
+ f = dotlist[0].field;
+ //B main·*Sub_test2(SB)
+ if(isptr[f->type->etype])
+ f = f->type;
+ p = pc;
+ gins(AB, N, N);
+ p->to.type = D_OREG;
+ p->to.reg = NREG;
+ p->to.name = D_EXTERN;
+ p->to.sym = methodsym(method->sym, ptrto(f->type), 0);
+//print("4. %P\n", p);
+
+ pc->as = ARET; // overwrite AEND
+*/
+}
+
+void
+nopout(Prog *p)
+{
+ p->as = ANOP;
+}
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;
+}
diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c
new file mode 100644
index 000000000..0c6dbbf71
--- /dev/null
+++ b/src/cmd/5g/list.c
@@ -0,0 +1,331 @@
+// Derived from Inferno utils/5c/list.c
+// http://code.google.com/p/inferno-os/source/browse/utils/5c/list.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): make 5g/list.c congruent with 5l/list.c
+
+static int sconsize;
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv); // as
+ fmtinstall('C', Cconv); // conditional execution bit
+ fmtinstall('P', Pconv); // Prog*
+ fmtinstall('D', Dconv); // Addr*
+ fmtinstall('Y', Yconv); // sconst
+ fmtinstall('R', Rconv); // register
+ fmtinstall('M', Mconv); // names
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ], str1[STRINGSZ];
+ Prog *p;
+
+ p = va_arg(fp->args, Prog*);
+ sconsize = 8;
+ switch(p->as) {
+ default:
+ snprint(str1, sizeof(str1), "%A%C", p->as, p->scond);
+ if(p->reg == NREG)
+ snprint(str, sizeof(str), "%.4d (%L) %-7s %D,%D",
+ p->loc, p->lineno, str1, &p->from, &p->to);
+ else
+ if (p->from.type != D_FREG) {
+ snprint(str, sizeof(str), "%.4d (%L) %-7s %D,R%d,%D",
+ p->loc, p->lineno, str1, &p->from, p->reg, &p->to);
+ } else
+ snprint(str, sizeof(str), "%.4d (%L) %-7A%C %D,F%d,%D",
+ p->loc, p->lineno, p->as, p->scond, &p->from, p->reg, &p->to);
+ break;
+
+ case ADATA:
+ snprint(str, sizeof(str), "%.4d (%L) %-7A %D/%d,%D",
+ p->loc, p->lineno, p->as, &p->from, p->reg, &p->to);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ char *op;
+ Addr *a;
+ int i;
+ int32 v;
+
+ a = va_arg(fp->args, Addr*);
+ if(a == A) {
+ sprint(str, "<nil>");
+ goto conv;
+ }
+ i = a->type;
+ switch(i) {
+
+ default:
+ sprint(str, "GOK-type(%d)", a->type);
+ break;
+
+ case D_NONE:
+ str[0] = 0;
+ if(a->name != D_NONE || a->reg != NREG || a->sym != S)
+ sprint(str, "%M(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ if(a->reg != NREG)
+ sprint(str, "$%M(R%d)", a, a->reg);
+ else
+ sprint(str, "$%M", a);
+ break;
+
+ case D_CONST2:
+ sprint(str, "$%d-%d", a->offset, a->offset2);
+ break;
+
+ case D_SHIFT:
+ v = a->offset;
+ op = "<<>>->@>" + (((v>>5) & 3) << 1);
+ if(v & (1<<4))
+ sprint(str, "R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15);
+ else
+ sprint(str, "R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31);
+ if(a->reg != NREG)
+ sprint(str+strlen(str), "(R%d)", a->reg);
+ break;
+
+ case D_OCONST:
+ sprint(str, "$*$%M", a);
+ if(a->reg != NREG)
+ sprint(str, "%M(R%d)(CONST)", a, a->reg);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%M(R%d)", a, a->reg);
+ else
+ sprint(str, "%M", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%M(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_REGREG:
+ sprint(str, "(R%d,R%d)", a->reg, (int)a->offset);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%M(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_FREG:
+ sprint(str, "F%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%M(R%d)(REG)", a, a->reg);
+ break;
+
+ case D_BRANCH:
+ if(a->branch == P || a->branch->loc == 0) {
+ if(a->sym != S)
+ sprint(str, "%s+%d(APC)", a->sym->name, a->offset);
+ else
+ sprint(str, "%d(APC)", a->offset);
+ } else
+ if(a->sym != S)
+ sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc);
+ else
+ sprint(str, "%d(APC)", a->branch->loc);
+ break;
+
+ case D_FCONST:
+ snprint(str, sizeof(str), "$(%.17e)", a->dval);
+ break;
+
+ case D_SCONST:
+ snprint(str, sizeof(str), "$\"%Y\"", a->sval);
+ break;
+
+ // TODO(kaib): Add back
+// case D_ADDR:
+// a->type = a->index;
+// a->index = D_NONE;
+// snprint(str, sizeof(str), "$%D", a);
+// a->index = a->type;
+// a->type = D_ADDR;
+// goto conv;
+ }
+conv:
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ int i;
+
+ i = va_arg(fp->args, int);
+ return fmtstrcpy(fp, anames[i]);
+}
+
+char* strcond[16] =
+{
+ ".EQ",
+ ".NE",
+ ".HS",
+ ".LO",
+ ".MI",
+ ".PL",
+ ".VS",
+ ".VC",
+ ".HI",
+ ".LS",
+ ".GE",
+ ".LT",
+ ".GT",
+ ".LE",
+ "",
+ ".NV"
+};
+
+int
+Cconv(Fmt *fp)
+{
+ char s[STRINGSZ];
+ int c;
+
+ c = va_arg(fp->args, int);
+ strcpy(s, strcond[c & C_SCOND]);
+ if(c & C_SBIT)
+ strcat(s, ".S");
+ if(c & C_PBIT)
+ strcat(s, ".P");
+ if(c & C_WBIT)
+ strcat(s, ".W");
+ if(c & C_UBIT) /* ambiguous with FBIT */
+ strcat(s, ".U");
+ return fmtstrcpy(fp, s);
+}
+
+int
+Yconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<sconsize; i++) {
+ c = a[i] & 0xff;
+ if((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9')) {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ default:
+ if(c < 040 || c >= 0177)
+ break; /* not portable */
+ p[-1] = c;
+ continue;
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+int
+Rconv(Fmt *fp)
+{
+ int r;
+ char str[STRINGSZ];
+
+ r = va_arg(fp->args, int);
+ snprint(str, sizeof(str), "R%d", r);
+ return fmtstrcpy(fp, str);
+}
+
+int
+Mconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Addr *a;
+
+ a = va_arg(fp->args, Addr*);
+ switch(a->name) {
+ default:
+ snprint(str, sizeof(str), "GOK-name(%d)", a->name);
+ break;
+
+ case D_NONE:
+ snprint(str, sizeof(str), "%d", a->offset);
+ break;
+
+ case D_EXTERN:
+ snprint(str, sizeof(str), "%S+%d(SB)", a->sym, a->offset);
+ break;
+
+ case D_STATIC:
+ snprint(str, sizeof(str), "%S<>+%d(SB)", a->sym, a->offset);
+ break;
+
+ case D_AUTO:
+ snprint(str, sizeof(str), "%S+%d(SP)", a->sym, a->offset);
+ break;
+
+ case D_PARAM:
+ snprint(str, sizeof(str), "%S+%d(FP)", a->sym, a->offset);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h
new file mode 100644
index 000000000..7a0070fc9
--- /dev/null
+++ b/src/cmd/5g/opt.h
@@ -0,0 +1,165 @@
+// Inferno utils/5c/gc.h
+// http://code.google.com/p/inferno-os/source/browse/utils/5c/gc.h
+//
+// 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.
+
+#define Z N
+#define Adr Addr
+
+#define D_HI D_NONE
+#define D_LO D_NONE
+
+#define isregtype(t) ((t)>= D_AX && (t)<=D_R15)
+
+#define BLOAD(r) band(bnot(r->refbehind), r->refahead)
+#define BSTORE(r) band(bnot(r->calbehind), r->calahead)
+#define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z])
+#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z])
+
+#define CLOAD 5
+#define CREF 5
+#define CINF 1000
+#define LOOP 3
+
+typedef struct Reg Reg;
+typedef struct Rgn Rgn;
+
+struct Reg
+{
+
+ Bits set;
+ Bits use1;
+ Bits use2;
+
+ Bits refbehind;
+ Bits refahead;
+ Bits calbehind;
+ Bits calahead;
+ Bits regdiff;
+ Bits act;
+
+ int32 regu; // register used bitmap
+ int32 rpo; // reverse post ordering
+ int32 active;
+
+ uint16 loop; // x5 for every loop
+ uchar refset; // diagnostic generated
+
+ Reg* p1;
+ Reg* p2;
+ Reg* p2link;
+ Reg* s1;
+ Reg* s2;
+ Reg* link;
+ Prog* prog;
+};
+#define R ((Reg*)0)
+
+#define NRGN 600
+struct Rgn
+{
+ Reg* enter;
+ short cost;
+ short varno;
+ short regno;
+};
+
+EXTERN int32 exregoffset; // not set
+EXTERN int32 exfregoffset; // not set
+EXTERN Reg* firstr;
+EXTERN Reg* lastr;
+EXTERN Reg zreg;
+EXTERN Reg* freer;
+EXTERN Reg** rpo2r;
+EXTERN Rgn region[NRGN];
+EXTERN Rgn* rgp;
+EXTERN int nregion;
+EXTERN int nvar;
+EXTERN int32 regbits;
+EXTERN int32 exregbits;
+EXTERN Bits externs;
+EXTERN Bits params;
+EXTERN Bits consts;
+EXTERN Bits addrs;
+EXTERN Bits ovar;
+EXTERN int change;
+EXTERN int32 maxnr;
+EXTERN int32* idom;
+
+EXTERN struct
+{
+ int32 ncvtreg;
+ int32 nspill;
+ int32 nreload;
+ int32 ndelmov;
+ int32 nvar;
+ int32 naddr;
+} ostats;
+
+/*
+ * reg.c
+ */
+Reg* rega(void);
+int rcmp(const void*, const void*);
+void regopt(Prog*);
+void addmove(Reg*, int, int, int);
+Bits mkvar(Reg *r, Adr *a);
+void prop(Reg*, Bits, Bits);
+void loopit(Reg*, int32);
+void synch(Reg*, Bits);
+uint32 allreg(uint32, Rgn*);
+void paint1(Reg*, int);
+uint32 paint2(Reg*, int);
+void paint3(Reg*, int, int32, int);
+void addreg(Adr*, int);
+void dumpit(char *str, Reg *r0);
+int noreturn(Prog *p);
+
+/*
+ * peep.c
+ */
+void peep(void);
+void excise(Reg*);
+Reg* uniqp(Reg*);
+Reg* uniqs(Reg*);
+int regtyp(Adr*);
+int anyvar(Adr*);
+int subprop(Reg*);
+int copyprop(Reg*);
+int copy1(Adr*, Adr*, Reg*, int);
+int copyu(Prog*, Adr*, Adr*);
+
+int copyas(Adr*, Adr*);
+int copyau(Adr*, Adr*);
+int copysub(Adr*, Adr*, Adr*, int);
+int copysub1(Prog*, Adr*, Adr*, int);
+
+int32 RtoB(int);
+int32 FtoB(int);
+int BtoR(int32);
+int BtoF(int32);
diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c
new file mode 100644
index 000000000..6cc93db12
--- /dev/null
+++ b/src/cmd/5g/peep.c
@@ -0,0 +1,1521 @@
+// Inferno utils/5c/peep.c
+// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.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"
+#include "opt.h"
+
+int xtramodes(Reg*, Adr*);
+int shiftprop(Reg *r);
+void constprop(Adr *c1, Adr *v1, Reg *r);
+void predicate(void);
+int copyau1(Prog *p, Adr *v);
+int isdconst(Addr *a);
+
+void
+peep(void)
+{
+ Reg *r, *r1, *r2;
+ Prog *p, *p1;
+ int t;
+/*
+ * complete R structure
+ */
+ for(r=firstr; r!=R; r=r1) {
+ r1 = r->link;
+ if(r1 == R)
+ break;
+ p = r->prog->link;
+ while(p != r1->prog)
+ switch(p->as) {
+ default:
+ r2 = rega();
+ r->link = r2;
+ r2->link = r1;
+
+ r2->prog = p;
+ r2->p1 = r;
+ r->s1 = r2;
+ r2->s1 = r1;
+ r1->p1 = r2;
+
+ r = r2;
+
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ p = p->link;
+ }
+ }
+//dumpit("begin", firstr);
+
+loop1:
+
+ t = 0;
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ /*
+ * elide shift into D_SHIFT operand of subsequent instruction
+ */
+// if(shiftprop(r)) {
+// excise(r);
+// t++;
+// break;
+// }
+ break;
+
+ case AMOVW:
+ case AMOVF:
+ case AMOVD:
+ if(regtyp(&p->from))
+ if(p->from.type == p->to.type)
+ if(p->scond == C_SCOND_NONE) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ break;
+ }
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ break;
+ }
+ }
+ break;
+
+ if(p->scond == C_SCOND_NONE)
+ if(regtyp(&p->to))
+ if(isdconst(&p->from)) {
+ constprop(&p->from, &p->to, r->s1);
+ }
+ break;
+ }
+ }
+ if(t)
+ goto loop1;
+
+return;
+
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+// case AEOR:
+// /*
+// * EOR -1,x,y => MVN x,y
+// */
+// if(isdconst(&p->from) && p->from.offset == -1) {
+// p->as = AMVN;
+// p->from.type = D_REG;
+// if(p->reg != NREG)
+// p->from.reg = p->reg;
+// else
+// p->from.reg = p->to.reg;
+// p->reg = NREG;
+// }
+// break;
+
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ /*
+ * look for MOVB x,R; MOVB R,R
+ */
+ if(p->to.type != D_REG)
+ break;
+ if(r1 == R)
+ break;
+ p1 = r1->prog;
+ if(p1->as != p->as)
+ break;
+ if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+ break;
+ if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+ break;
+ excise(r1);
+ break;
+ }
+ r1 = r->link;
+ }
+
+// for(r=firstr; r!=R; r=r->link) {
+// p = r->prog;
+// switch(p->as) {
+// case AMOVW:
+// case AMOVB:
+// case AMOVBU:
+// if(p->from.type == D_OREG && p->from.offset == 0)
+// xtramodes(r, &p->from);
+// else
+// if(p->to.type == D_OREG && p->to.offset == 0)
+// xtramodes(r, &p->to);
+// else
+// continue;
+// break;
+// case ACMP:
+// /*
+// * elide CMP $0,x if calculation of x can set condition codes
+// */
+// if(isdconst(&p->from) || p->from.offset != 0)
+// continue;
+// r2 = r->s1;
+// if(r2 == R)
+// continue;
+// t = r2->prog->as;
+// switch(t) {
+// default:
+// continue;
+// case ABEQ:
+// case ABNE:
+// case ABMI:
+// case ABPL:
+// break;
+// case ABGE:
+// t = ABPL;
+// break;
+// case ABLT:
+// t = ABMI;
+// break;
+// case ABHI:
+// t = ABNE;
+// break;
+// case ABLS:
+// t = ABEQ;
+// break;
+// }
+// r1 = r;
+// do
+// r1 = uniqp(r1);
+// while (r1 != R && r1->prog->as == ANOP);
+// if(r1 == R)
+// continue;
+// p1 = r1->prog;
+// if(p1->to.type != D_REG)
+// continue;
+// if(p1->to.reg != p->reg)
+// if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg))
+// continue;
+//
+// switch(p1->as) {
+// default:
+// continue;
+// case AMOVW:
+// if(p1->from.type != D_REG)
+// continue;
+// case AAND:
+// case AEOR:
+// case AORR:
+// case ABIC:
+// case AMVN:
+// case ASUB:
+// case ARSB:
+// case AADD:
+// case AADC:
+// case ASBC:
+// case ARSC:
+// break;
+// }
+// p1->scond |= C_SBIT;
+// r2->prog->as = t;
+// excise(r);
+// continue;
+// }
+// }
+
+ predicate();
+}
+
+Reg*
+uniqp(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->p1;
+ if(r1 == R) {
+ r1 = r->p2;
+ if(r1 == R || r1->p2link != R)
+ return R;
+ } else
+ if(r->p2 != R)
+ return R;
+ return r1;
+}
+
+Reg*
+uniqs(Reg *r)
+{
+ Reg *r1;
+
+ r1 = r->s1;
+ if(r1 == R) {
+ r1 = r->s2;
+ if(r1 == R)
+ return R;
+ } else
+ if(r->s2 != R)
+ return R;
+ return r1;
+}
+
+int
+regtyp(Adr *a)
+{
+
+ if(a->type == D_REG)
+ return 1;
+ if(a->type == D_FREG)
+ return 1;
+ return 0;
+}
+
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R0
+ * ADD b, R0 / no use of R1
+ * MOV R0, R1
+ * would be converted to
+ * MOV a, R1
+ * ADD b, R1
+ * MOV R1, R0
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ */
+int
+subprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+ int t;
+
+ p = r0->prog;
+ v1 = &p->from;
+ if(!regtyp(v1))
+ return 0;
+ v2 = &p->to;
+ if(!regtyp(v2))
+ return 0;
+ for(r=uniqp(r0); r!=R; r=uniqp(r)) {
+ if(uniqs(r) == R)
+ break;
+ p = r->prog;
+ switch(p->as) {
+ case ABL:
+ return 0;
+
+ case AMULLU:
+ case AMULA:
+ case AMVN:
+ return 0;
+
+ case ACMN:
+ case AADD:
+ case ASUB:
+ case ASBC:
+ case ARSB:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AORR:
+ case AAND:
+ case AEOR:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case ADIVU:
+ case AMOD:
+ case AMODU:
+
+ case AADDD:
+ case AADDF:
+ case ASUBD:
+ case ASUBF:
+ case AMULD:
+ case AMULF:
+ case ADIVD:
+ case ADIVF:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ if(p->scond == C_SCOND_NONE) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ goto gotit;
+ }
+ break;
+
+ case AMOVF:
+ case AMOVD:
+ case AMOVW:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ if(p->scond == C_SCOND_NONE)
+ goto gotit;
+ break;
+
+ case AMOVM:
+ t = 1<<v2->reg;
+ if((p->from.type == D_CONST && (p->from.offset&t)) ||
+ (p->to.type == D_CONST && (p->to.offset&t)))
+ return 0;
+ break;
+ }
+ if(copyau(&p->from, v2) ||
+ copyau1(p, v2) ||
+ copyau(&p->to, v2))
+ break;
+ if(copysub(&p->from, v1, v2, 0) ||
+ copysub1(p, v1, v2, 0) ||
+ copysub(&p->to, v1, v2, 0))
+ break;
+ }
+ return 0;
+
+gotit:
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P']) {
+ print("gotit: %D->%D\n%P", v1, v2, r->prog);
+ if(p->from.type == v2->type)
+ print(" excise");
+ print("\n");
+ }
+ for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+ p = r->prog;
+ copysub(&p->from, v1, v2, 1);
+ copysub1(p, v1, v2, 1);
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P'])
+ print("%P\n", r->prog);
+ }
+ t = v1->reg;
+ v1->reg = v2->reg;
+ v2->reg = t;
+ if(debug['P'])
+ print("%P last\n", r->prog);
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success
+ */
+int
+copyprop(Reg *r0)
+{
+ Prog *p;
+ Adr *v1, *v2;
+ Reg *r;
+
+ p = r0->prog;
+ v1 = &p->from;
+ v2 = &p->to;
+ if(copyas(v1, v2))
+ return 1;
+ for(r=firstr; r!=R; r=r->link)
+ r->active = 0;
+ return copy1(v1, v2, r0->s1, 0);
+}
+
+int
+copy1(Adr *v1, Adr *v2, Reg *r, int f)
+{
+ int t;
+ Prog *p;
+
+ if(r->active) {
+ if(debug['P'])
+ print("act set; return 1\n");
+ return 1;
+ }
+ r->active = 1;
+ if(debug['P'])
+ print("copy %D->%D f=%d\n", v1, v2, f);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(!f && uniqp(r) == R) {
+ f = 1;
+ if(debug['P'])
+ print("; merge; f=%d", f);
+ }
+ t = copyu(p, v2, A);
+ switch(t) {
+ case 2: /* rar, cant split */
+ if(debug['P'])
+ print("; %Drar; return 0\n", v2);
+ return 0;
+
+ case 3: /* set */
+ if(debug['P'])
+ print("; %Dset; return 1\n", v2);
+ return 1;
+
+ case 1: /* used, substitute */
+ case 4: /* use and set */
+ if(f) {
+ if(!debug['P'])
+ return 0;
+ if(t == 4)
+ print("; %Dused+set and f=%d; return 0\n", v2, f);
+ else
+ print("; %Dused and f=%d; return 0\n", v2, f);
+ return 0;
+ }
+ if(copyu(p, v2, v1)) {
+ if(debug['P'])
+ print("; sub fail; return 0\n");
+ return 0;
+ }
+ if(debug['P'])
+ print("; sub%D/%D", v2, v1);
+ if(t == 4) {
+ if(debug['P'])
+ print("; %Dused+set; return 1\n", v2);
+ return 1;
+ }
+ break;
+ }
+ if(!f) {
+ t = copyu(p, v1, A);
+ if(!f && (t == 2 || t == 3 || t == 4)) {
+ f = 1;
+ if(debug['P'])
+ print("; %Dset and !f; f=%d", v1, f);
+ }
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ if(!copy1(v1, v2, r->s2, f))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant constants.
+ * $c1->v1
+ * ($c1->v2 s/$c1/v1)*
+ * set v1 return
+ * The v1->v2 should be eliminated by copy propagation.
+ */
+void
+constprop(Adr *c1, Adr *v1, Reg *r)
+{
+ Prog *p;
+
+ if(debug['P'])
+ print("constprop %D->%D\n", c1, v1);
+ for(; r != R; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(uniqp(r) == R) {
+ if(debug['P'])
+ print("; merge; return\n");
+ return;
+ }
+ if(p->as == AMOVW && copyas(&p->from, c1)) {
+ if(debug['P'])
+ print("; sub%D/%D", &p->from, v1);
+ p->from = *v1;
+ } else if(copyu(p, v1, A) > 1) {
+ if(debug['P'])
+ print("; %Dset; return\n", v1);
+ return;
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ constprop(c1, v1, r->s2);
+ }
+}
+
+/*
+ * ASLL x,y,w
+ * .. (not use w, not set x y w)
+ * AXXX w,a,b (a != w)
+ * .. (not use w)
+ * (set w)
+ * ----------- changed to
+ * ..
+ * AXXX (x<<y),a,b
+ * ..
+ */
+#define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; }
+int
+shiftprop(Reg *r)
+{
+ Reg *r1;
+ Prog *p, *p1, *p2;
+ int n, o;
+ Adr a;
+
+ p = r->prog;
+ if(p->to.type != D_REG)
+ FAIL("BOTCH: result not reg");
+ n = p->to.reg;
+ a = zprog.from;
+ if(p->reg != NREG && p->reg != p->to.reg) {
+ a.type = D_REG;
+ a.reg = p->reg;
+ }
+ if(debug['P'])
+ print("shiftprop\n%P", p);
+ r1 = r;
+ for(;;) {
+ /* find first use of shift result; abort if shift operands or result are changed */
+ r1 = uniqs(r1);
+ if(r1 == R)
+ FAIL("branch");
+ if(uniqp(r1) == R)
+ FAIL("merge");
+ p1 = r1->prog;
+ if(debug['P'])
+ print("\n%P", p1);
+ switch(copyu(p1, &p->to, A)) {
+ case 0: /* not used or set */
+ if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) ||
+ (a.type == D_REG && copyu(p1, &a, A) > 1))
+ FAIL("args modified");
+ continue;
+ case 3: /* set, not used */
+ FAIL("BOTCH: noref");
+ }
+ break;
+ }
+ /* check whether substitution can be done */
+ switch(p1->as) {
+ default:
+ FAIL("non-dpi");
+ case AAND:
+ case AEOR:
+ case AADD:
+ case AADC:
+ case AORR:
+ case ASUB:
+ case ASBC:
+ case ARSB:
+ case ARSC:
+ if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) {
+ if(p1->from.type != D_REG)
+ FAIL("can't swap");
+ p1->reg = p1->from.reg;
+ p1->from.reg = n;
+ switch(p1->as) {
+ case ASUB:
+ p1->as = ARSB;
+ break;
+ case ARSB:
+ p1->as = ASUB;
+ break;
+ case ASBC:
+ p1->as = ARSC;
+ break;
+ case ARSC:
+ p1->as = ASBC;
+ break;
+ }
+ if(debug['P'])
+ print("\t=>%P", p1);
+ }
+ case ABIC:
+ case ATST:
+ case ACMP:
+ case ACMN:
+ if(p1->reg == n)
+ FAIL("can't swap");
+ if(p1->reg == NREG && p1->to.reg == n)
+ FAIL("shift result used twice");
+// case AMVN:
+ if(p1->from.type == D_SHIFT)
+ FAIL("shift result used in shift");
+ if(p1->from.type != D_REG || p1->from.reg != n)
+ FAIL("BOTCH: where is it used?");
+ break;
+ }
+ /* check whether shift result is used subsequently */
+ p2 = p1;
+ if(p1->to.reg != n)
+ for (;;) {
+ r1 = uniqs(r1);
+ if(r1 == R)
+ FAIL("inconclusive");
+ p1 = r1->prog;
+ if(debug['P'])
+ print("\n%P", p1);
+ switch(copyu(p1, &p->to, A)) {
+ case 0: /* not used or set */
+ continue;
+ case 3: /* set, not used */
+ break;
+ default:/* used */
+ FAIL("reused");
+ }
+ break;
+ }
+
+ /* make the substitution */
+ p2->from.type = D_SHIFT;
+ p2->from.reg = NREG;
+ o = p->reg;
+ if(o == NREG)
+ o = p->to.reg;
+
+ switch(p->from.type){
+ case D_CONST:
+ o |= (p->from.offset&0x1f)<<7;
+ break;
+ case D_REG:
+ o |= (1<<4) | (p->from.reg<<8);
+ break;
+ }
+ switch(p->as){
+ case ASLL:
+ o |= 0<<5;
+ break;
+ case ASRL:
+ o |= 1<<5;
+ break;
+ case ASRA:
+ o |= 2<<5;
+ break;
+ }
+ p2->from.offset = o;
+ if(debug['P'])
+ print("\t=>%P\tSUCCEED\n", p2);
+ return 1;
+}
+
+Reg*
+findpre(Reg *r, Adr *v)
+{
+ Reg *r1;
+
+ for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) {
+ if(uniqs(r1) != r)
+ return R;
+ switch(copyu(r1->prog, v, A)) {
+ case 1: /* used */
+ case 2: /* read-alter-rewrite */
+ return R;
+ case 3: /* set */
+ case 4: /* set and used */
+ return r1;
+ }
+ }
+ return R;
+}
+
+Reg*
+findinc(Reg *r, Reg *r2, Adr *v)
+{
+ Reg *r1;
+ Prog *p;
+
+
+ for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) {
+ if(uniqp(r1) != r)
+ return R;
+ switch(copyu(r1->prog, v, A)) {
+ case 0: /* not touched */
+ continue;
+ case 4: /* set and used */
+ p = r1->prog;
+ if(p->as == AADD)
+ if(isdconst(&p->from))
+ if(p->from.offset > -4096 && p->from.offset < 4096)
+ return r1;
+ default:
+ return R;
+ }
+ }
+ return R;
+}
+
+int
+nochange(Reg *r, Reg *r2, Prog *p)
+{
+ Adr a[3];
+ int i, n;
+
+ if(r == r2)
+ return 1;
+ n = 0;
+ if(p->reg != NREG && p->reg != p->to.reg) {
+ a[n].type = D_REG;
+ a[n++].reg = p->reg;
+ }
+ switch(p->from.type) {
+ case D_SHIFT:
+ a[n].type = D_REG;
+ a[n++].reg = p->from.offset&0xf;
+ case D_REG:
+ a[n].type = D_REG;
+ a[n++].reg = p->from.reg;
+ }
+ if(n == 0)
+ return 1;
+ for(; r!=R && r!=r2; r=uniqs(r)) {
+ p = r->prog;
+ for(i=0; i<n; i++)
+ if(copyu(p, &a[i], A) > 1)
+ return 0;
+ }
+ return 1;
+}
+
+int
+findu1(Reg *r, Adr *v)
+{
+ for(; r != R; r = r->s1) {
+ if(r->active)
+ return 0;
+ r->active = 1;
+ switch(copyu(r->prog, v, A)) {
+ case 1: /* used */
+ case 2: /* read-alter-rewrite */
+ case 4: /* set and used */
+ return 1;
+ case 3: /* set */
+ return 0;
+ }
+ if(r->s2)
+ if (findu1(r->s2, v))
+ return 1;
+ }
+ return 0;
+}
+
+int
+finduse(Reg *r, Adr *v)
+{
+ Reg *r1;
+
+ for(r1=firstr; r1!=R; r1=r1->link)
+ r1->active = 0;
+ return findu1(r, v);
+}
+
+int
+xtramodes(Reg *r, Adr *a)
+{
+ Reg *r1, *r2, *r3;
+ Prog *p, *p1;
+ Adr v;
+
+ p = r->prog;
+ if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */
+ return 0;
+ v = *a;
+ v.type = D_REG;
+ r1 = findpre(r, &v);
+ if(r1 != R) {
+ p1 = r1->prog;
+ if(p1->to.type == D_REG && p1->to.reg == v.reg)
+ switch(p1->as) {
+ case AADD:
+ if(p1->from.type == D_REG ||
+ (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 &&
+ (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) ||
+ (p1->from.type == D_CONST &&
+ p1->from.offset > -4096 && p1->from.offset < 4096))
+ if(nochange(uniqs(r1), r, p1)) {
+ if(a != &p->from || v.reg != p->to.reg)
+ if (finduse(r->s1, &v)) {
+ if(p1->reg == NREG || p1->reg == v.reg)
+ /* pre-indexing */
+ p->scond |= C_WBIT;
+ else return 0;
+ }
+ switch (p1->from.type) {
+ case D_REG:
+ /* register offset */
+ a->type = D_SHIFT;
+ a->offset = p1->from.reg;
+ break;
+ case D_SHIFT:
+ /* scaled register offset */
+ a->type = D_SHIFT;
+ case D_CONST:
+ /* immediate offset */
+ a->offset = p1->from.offset;
+ break;
+ }
+ if(p1->reg != NREG)
+ a->reg = p1->reg;
+ excise(r1);
+ return 1;
+ }
+ break;
+ case AMOVW:
+ if(p1->from.type == D_REG)
+ if((r2 = findinc(r1, r, &p1->from)) != R) {
+ for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3))
+ ;
+ if(r3 == r) {
+ /* post-indexing */
+ p1 = r2->prog;
+ a->reg = p1->to.reg;
+ a->offset = p1->from.offset;
+ p->scond |= C_PBIT;
+ if(!finduse(r, &r1->prog->to))
+ excise(r1);
+ excise(r2);
+ return 1;
+ }
+ }
+ break;
+ }
+ }
+ if(a != &p->from || a->reg != p->to.reg)
+ if((r1 = findinc(r, R, &v)) != R) {
+ /* post-indexing */
+ p1 = r1->prog;
+ a->offset = p1->from.offset;
+ p->scond |= C_PBIT;
+ excise(r1);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * return
+ * 1 if v only used (and substitute),
+ * 2 if read-alter-rewrite
+ * 3 if set
+ * 4 if set and used
+ * 0 otherwise (not touched)
+ */
+int
+copyu(Prog *p, Adr *v, Adr *s)
+{
+ switch(p->as) {
+
+ default:
+ print("copyu: cant find %A\n", p->as);
+ return 2;
+
+ case AMOVM:
+ if(v->type != D_REG)
+ return 0;
+ if(p->from.type == D_CONST) { /* read reglist, read/rar */
+ if(s != A) {
+ if(p->from.offset&(1<<v->reg))
+ return 1;
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v)) {
+ if(p->scond&C_WBIT)
+ return 2;
+ return 1;
+ }
+ if(p->from.offset&(1<<v->reg))
+ return 1;
+ } else { /* read/rar, write reglist */
+ if(s != A) {
+ if(p->to.offset&(1<<v->reg))
+ return 1;
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->from, v)) {
+ if(p->scond&C_WBIT)
+ return 2;
+ if(p->to.offset&(1<<v->reg))
+ return 4;
+ return 1;
+ }
+ if(p->to.offset&(1<<v->reg))
+ return 3;
+ }
+ return 0;
+
+ case ANOP: /* read,, write */
+ case AMOVW:
+ case AMOVF:
+ case AMOVD:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVFW:
+ case AMOVWF:
+ case AMOVDW:
+ case AMOVWD:
+ case AMOVFD:
+ case AMOVDF:
+ if(p->scond&(C_WBIT|C_PBIT))
+ if(v->type == D_REG) {
+ if(p->from.type == D_OREG || p->from.type == D_SHIFT) {
+ if(p->from.reg == v->reg)
+ return 2;
+ } else {
+ if(p->to.reg == v->reg)
+ return 2;
+ }
+ }
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(p->scond != C_SCOND_NONE)
+ return 2;
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case AMULLU: /* read, read, write, write */
+ case AMULA:
+ case AMVN:
+ return 2;
+
+ case AADD: /* read, read, write */
+ case AADC:
+ case ASUB:
+ case ASBC:
+ case ARSB:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AORR:
+ case AAND:
+ case AEOR:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case ADIVU:
+ case AMOD:
+ case AMODU:
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+
+ case ACMPF: /* read, read, */
+ case ACMPD:
+ case ACMP:
+ case ACMN:
+ case ACASE:
+ case ATST: /* read,, */
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(copysub1(p, v, s, 1))
+ return 1;
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(p->scond != C_SCOND_NONE)
+ return 2;
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ if(copyau(&p->from, v))
+ return 4;
+ if(copyau1(p, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ABEQ: /* read, read */
+ case ABNE:
+ case ABCS:
+ case ABHS:
+ case ABCC:
+ case ABLO:
+ case ABMI:
+ case ABPL:
+ case ABVS:
+ case ABVC:
+ case ABHI:
+ case ABLS:
+ case ABGE:
+ case ABLT:
+ case ABGT:
+ case ABLE:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub1(p, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ return 0;
+
+ case AB: /* funny */
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ARET: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == REGRET)
+ return 2;
+ if(v->type == D_FREG)
+ if(v->reg == FREGRET)
+ return 2;
+
+ case ABL: /* funny */
+ if(v->type == D_REG) {
+ if(v->reg <= REGEXT && v->reg > exregoffset)
+ return 2;
+ if(v->reg == (uchar)REGARG)
+ return 2;
+ }
+ if(v->type == D_FREG)
+ if(v->reg <= FREGEXT && v->reg > exfregoffset)
+ return 2;
+
+ if(s != A) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 4;
+ return 3;
+
+ case ATEXT: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == (uchar)REGARG)
+ return 3;
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * direct reference,
+ * could be set/use depending on
+ * semantics
+ */
+int
+copyas(Adr *a, Adr *v)
+{
+
+ if(regtyp(v)) {
+ if(a->type == v->type)
+ if(a->reg == v->reg)
+ return 1;
+ } else
+ if(v->type == D_CONST) { /* for constprop */
+ if(a->type == v->type)
+ if(a->name == v->name)
+ if(a->sym == v->sym)
+ if(a->reg == v->reg)
+ if(a->offset == v->offset)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * either direct or indirect
+ */
+int
+copyau(Adr *a, Adr *v)
+{
+
+ if(copyas(a, v))
+ return 1;
+ if(v->type == D_REG) {
+ if(a->type == D_CONST && a->reg != NREG) {
+ if(a->reg == v->reg)
+ return 1;
+ } else
+ if(a->type == D_OREG) {
+ if(a->reg == v->reg)
+ return 1;
+ } else
+ if(a->type == D_REGREG) {
+ if(a->reg == v->reg)
+ return 1;
+ if(a->offset == v->reg)
+ return 1;
+ } else
+ if(a->type == D_SHIFT) {
+ if((a->offset&0xf) == v->reg)
+ return 1;
+ if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * compare v to the center
+ * register in p (p->reg)
+ * the trick is that this
+ * register might be D_REG
+ * D_FREG. there are basically
+ * two cases,
+ * ADD r,r,r
+ * CMP r,r,
+ */
+int
+copyau1(Prog *p, Adr *v)
+{
+
+ if(regtyp(v))
+ if(p->reg == v->reg) {
+ if(p->to.type != D_NONE) {
+ if(v->type == p->to.type)
+ return 1;
+ return 0;
+ }
+ if(p->from.type != D_NONE) {
+ if(v->type == p->from.type)
+ return 1;
+ return 0;
+ }
+ print("copyau1: cant tell %P\n", p);
+ }
+ return 0;
+}
+
+/*
+ * substitute s for v in a
+ * return failure to substitute
+ */
+int
+copysub(Adr *a, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau(a, v)) {
+ if(a->type == D_SHIFT) {
+ if((a->offset&0xf) == v->reg)
+ a->offset = (a->offset&~0xf)|s->reg;
+ if((a->offset&(1<<4)) && (a->offset>>8) == v->reg)
+ a->offset = (a->offset&~(0xf<<8))|(s->reg<<8);
+ } else
+ if(a->type == D_REGREG) {
+ if(a->offset == v->reg)
+ a->offset = s->reg;
+ if(a->reg == v->reg)
+ a->reg = s->reg;
+ } else
+ a->reg = s->reg;
+ }
+ return 0;
+}
+
+int
+copysub1(Prog *p1, Adr *v, Adr *s, int f)
+{
+
+ if(f)
+ if(copyau1(p1, v))
+ p1->reg = s->reg;
+ return 0;
+}
+
+struct {
+ int opcode;
+ int notopcode;
+ int scond;
+ int notscond;
+} predinfo[] = {
+ { ABEQ, ABNE, 0x0, 0x1, },
+ { ABNE, ABEQ, 0x1, 0x0, },
+ { ABCS, ABCC, 0x2, 0x3, },
+ { ABHS, ABLO, 0x2, 0x3, },
+ { ABCC, ABCS, 0x3, 0x2, },
+ { ABLO, ABHS, 0x3, 0x2, },
+ { ABMI, ABPL, 0x4, 0x5, },
+ { ABPL, ABMI, 0x5, 0x4, },
+ { ABVS, ABVC, 0x6, 0x7, },
+ { ABVC, ABVS, 0x7, 0x6, },
+ { ABHI, ABLS, 0x8, 0x9, },
+ { ABLS, ABHI, 0x9, 0x8, },
+ { ABGE, ABLT, 0xA, 0xB, },
+ { ABLT, ABGE, 0xB, 0xA, },
+ { ABGT, ABLE, 0xC, 0xD, },
+ { ABLE, ABGT, 0xD, 0xC, },
+};
+
+typedef struct {
+ Reg *start;
+ Reg *last;
+ Reg *end;
+ int len;
+} Joininfo;
+
+enum {
+ Join,
+ Split,
+ End,
+ Branch,
+ Setcond,
+ Toolong
+};
+
+enum {
+ Falsecond,
+ Truecond,
+ Delbranch,
+ Keepbranch
+};
+
+int
+isbranch(Prog *p)
+{
+ return (ABEQ <= p->as) && (p->as <= ABLE);
+}
+
+int
+predicable(Prog *p)
+{
+ switch(p->as) {
+ case ANOP:
+ case AXXX:
+ case ADATA:
+ case AGLOBL:
+ case AGOK:
+ case AHISTORY:
+ case ANAME:
+ case ASIGNAME:
+ case ATEXT:
+ case AWORD:
+ case ABCASE:
+ case ACASE:
+ return 0;
+ }
+ if(isbranch(p))
+ return 0;
+ return 1;
+}
+
+/*
+ * Depends on an analysis of the encodings performed by 5l.
+ * These seem to be all of the opcodes that lead to the "S" bit
+ * being set in the instruction encodings.
+ *
+ * C_SBIT may also have been set explicitly in p->scond.
+ */
+int
+modifiescpsr(Prog *p)
+{
+ switch(p->as) {
+ case AMULLU:
+ case AMULA:
+ case AMULU:
+ case ADIVU:
+
+ case ATEQ:
+ case ACMN:
+ case ATST:
+ case ACMP:
+ case AMUL:
+ case ADIV:
+ case AMOD:
+ case AMODU:
+ case ABL:
+ return 1;
+ }
+ if(p->scond & C_SBIT)
+ return 1;
+ return 0;
+}
+
+/*
+ * Find the maximal chain of instructions starting with r which could
+ * be executed conditionally
+ */
+int
+joinsplit(Reg *r, Joininfo *j)
+{
+ j->start = r;
+ j->last = r;
+ j->len = 0;
+ do {
+ if (r->p2 && (r->p1 || r->p2->p2link)) {
+ j->end = r;
+ return Join;
+ }
+ if (r->s1 && r->s2) {
+ j->end = r;
+ return Split;
+ }
+ j->last = r;
+ if (r->prog->as != ANOP)
+ j->len++;
+ if (!r->s1 && !r->s2) {
+ j->end = r->link;
+ return End;
+ }
+ if (r->s2) {
+ j->end = r->s2;
+ return Branch;
+ }
+ if (modifiescpsr(r->prog)) {
+ j->end = r->s1;
+ return Setcond;
+ }
+ r = r->s1;
+ } while (j->len < 4);
+ j->end = r;
+ return Toolong;
+}
+
+Reg*
+successor(Reg *r)
+{
+ if(r->s1)
+ return r->s1;
+ else
+ return r->s2;
+}
+
+void
+applypred(Reg *rstart, Joininfo *j, int cond, int branch)
+{
+ int pred;
+ Reg *r;
+
+ if(j->len == 0)
+ return;
+ if(cond == Truecond)
+ pred = predinfo[rstart->prog->as - ABEQ].scond;
+ else
+ pred = predinfo[rstart->prog->as - ABEQ].notscond;
+
+ for(r = j->start;; r = successor(r)) {
+ if(r->prog->as == AB) {
+ if(r != j->last || branch == Delbranch)
+ excise(r);
+ else {
+ if(cond == Truecond)
+ r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode;
+ else
+ r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode;
+ }
+ }
+ else
+ if(predicable(r->prog))
+ r->prog->scond = (r->prog->scond&~C_SCOND)|pred;
+ if(r->s1 != r->link) {
+ r->s1 = r->link;
+ r->link->p1 = r;
+ }
+ if(r == j->last)
+ break;
+ }
+}
+
+void
+predicate(void)
+{
+ Reg *r;
+ int t1, t2;
+ Joininfo j1, j2;
+
+ for(r=firstr; r!=R; r=r->link) {
+ if (isbranch(r->prog)) {
+ t1 = joinsplit(r->s1, &j1);
+ t2 = joinsplit(r->s2, &j2);
+ if(j1.last->link != j2.start)
+ continue;
+ if(j1.end == j2.end)
+ if((t1 == Branch && (t2 == Join || t2 == Setcond)) ||
+ (t2 == Join && (t1 == Join || t1 == Setcond))) {
+ applypred(r, &j1, Falsecond, Delbranch);
+ applypred(r, &j2, Truecond, Delbranch);
+ excise(r);
+ continue;
+ }
+ if(t1 == End || t1 == Branch) {
+ applypred(r, &j1, Falsecond, Keepbranch);
+ excise(r);
+ continue;
+ }
+ }
+ }
+}
+
+int
+isdconst(Addr *a)
+{
+ if(a->type == D_CONST && a->reg == NREG)
+ return 1;
+ return 0;
+}
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
new file mode 100644
index 000000000..2d2a6d01a
--- /dev/null
+++ b/src/cmd/5g/reg.c
@@ -0,0 +1,1607 @@
+// Inferno utils/5c/reg.c
+// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.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"
+#include "opt.h"
+
+#define NREGVAR 24
+#define REGBITS ((uint32)0xffffff)
+#define P2R(p) (Reg*)(p->reg)
+
+ void addsplits(void);
+ int noreturn(Prog *p);
+static int first = 0;
+
+Reg*
+rega(void)
+{
+ Reg *r;
+
+ r = freer;
+ if(r == R) {
+ r = mal(sizeof(*r));
+ } else
+ freer = r->link;
+
+ *r = zreg;
+ return r;
+}
+
+int
+rcmp(const void *a1, const void *a2)
+{
+ Rgn *p1, *p2;
+ int c1, c2;
+
+ p1 = (Rgn*)a1;
+ p2 = (Rgn*)a2;
+ c1 = p2->cost;
+ c2 = p1->cost;
+ if(c1 -= c2)
+ return c1;
+ return p2->varno - p1->varno;
+}
+
+static void
+setoutvar(void)
+{
+ Type *t;
+ Node *n;
+ Addr a;
+ Iter save;
+ Bits bit;
+ int z;
+
+ t = structfirst(&save, getoutarg(curfn->type));
+ while(t != T) {
+ n = nodarg(t, 1);
+ a = zprog.from;
+ naddr(n, &a, 0);
+ bit = mkvar(R, &a);
+ for(z=0; z<BITS; z++)
+ ovar.b[z] |= bit.b[z];
+ t = structnext(&save);
+ }
+//if(bany(b))
+//print("ovars = %Q\n", &ovar);
+}
+
+void
+excise(Reg *r)
+{
+ Prog *p;
+
+ p = r->prog;
+ p->as = ANOP;
+ p->scond = zprog.scond;
+ p->from = zprog.from;
+ p->to = zprog.to;
+ p->reg = zprog.reg;
+}
+
+static void
+setaddrs(Bits bit)
+{
+ int i, n;
+ Var *v;
+ Sym *s;
+
+ while(bany(&bit)) {
+ // convert each bit to a variable
+ i = bnum(bit);
+ s = var[i].sym;
+ n = var[i].name;
+ bit.b[i/32] &= ~(1L<<(i%32));
+
+ // disable all pieces of that variable
+ for(i=0; i<nvar; i++) {
+ v = var+i;
+ if(v->sym == s && v->name == n)
+ v->addr = 2;
+ }
+ }
+}
+
+static char* regname[] = {
+ ".R0",
+ ".R1",
+ ".R2",
+ ".R3",
+ ".R4",
+ ".R5",
+ ".R6",
+ ".R7",
+ ".R8",
+ ".R9",
+ ".R10",
+ ".R11",
+ ".R12",
+ ".R13",
+ ".R14",
+ ".R15",
+ ".F0",
+ ".F1",
+ ".F2",
+ ".F3",
+ ".F4",
+ ".F5",
+ ".F6",
+ ".F7",
+};
+
+void
+regopt(Prog *firstp)
+{
+ Reg *r, *r1;
+ Prog *p;
+ int i, z, nr;
+ uint32 vreg;
+ Bits bit;
+
+ if(first == 0) {
+ fmtinstall('Q', Qconv);
+ }
+
+ first++;
+ if(debug['K']) {
+ if(first != 13)
+ return;
+// debug['R'] = 2;
+// debug['P'] = 2;
+ print("optimizing %S\n", curfn->nname->sym);
+ }
+
+ // count instructions
+ nr = 0;
+ for(p=firstp; p!=P; p=p->link)
+ nr++;
+
+ // if too big dont bother
+ if(nr >= 10000) {
+// print("********** %S is too big (%d)\n", curfn->nname->sym, nr);
+ return;
+ }
+
+ r1 = R;
+ firstr = R;
+ lastr = R;
+
+ /*
+ * control flow is more complicated in generated go code
+ * than in generated c code. define pseudo-variables for
+ * registers, so we have complete register usage information.
+ */
+ nvar = NREGVAR;
+ memset(var, 0, NREGVAR*sizeof var[0]);
+ for(i=0; i<NREGVAR; i++)
+ var[i].sym = lookup(regname[i]);
+
+ regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC);
+ for(z=0; z<BITS; z++) {
+ externs.b[z] = 0;
+ params.b[z] = 0;
+ consts.b[z] = 0;
+ addrs.b[z] = 0;
+ ovar.b[z] = 0;
+ }
+
+ // build list of return variables
+ setoutvar();
+
+ /*
+ * pass 1
+ * build aux data structure
+ * allocate pcs
+ * find use and set of variables
+ */
+ nr = 0;
+ for(p=firstp; p != P; p = p->link) {
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ continue;
+ }
+ r = rega();
+ nr++;
+ if(firstr == R) {
+ firstr = r;
+ lastr = r;
+ } else {
+ lastr->link = r;
+ r->p1 = lastr;
+ lastr->s1 = r;
+ lastr = r;
+ }
+ r->prog = p;
+ p->regp = r;
+
+ r1 = r->p1;
+ if(r1 != R) {
+ switch(r1->prog->as) {
+ case ARET:
+ case AB:
+ case ARFE:
+ r->p1 = R;
+ r1->s1 = R;
+ }
+ }
+
+ /*
+ * left side always read
+ */
+ bit = mkvar(r, &p->from);
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+
+ /*
+ * middle always read when present
+ */
+ if(p->reg != NREG) {
+ if(p->from.type != D_FREG)
+ r->use1.b[0] |= RtoB(p->reg);
+ else
+ r->use1.b[0] |= FtoB(p->reg);
+ }
+
+ /*
+ * right side depends on opcode
+ */
+ bit = mkvar(r, &p->to);
+ if(bany(&bit))
+ switch(p->as) {
+ default:
+ yyerror("reg: unknown op: %A", p->as);
+ break;
+
+ /*
+ * right side read
+ */
+ case ATST:
+ case ATEQ:
+ case ACMP:
+ case ACMN:
+ case ACMPD:
+ case ACMPF:
+ rightread:
+ for(z=0; z<BITS; z++)
+ r->use2.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * right side read or read+write, depending on middle
+ * ADD x, z => z += x
+ * ADD x, y, z => z = x + y
+ */
+ case AADD:
+ case AAND:
+ case AEOR:
+ case ASUB:
+ case ARSB:
+ case AADC:
+ case ASBC:
+ case ARSC:
+ case AORR:
+ case ABIC:
+ case ASLL:
+ case ASRL:
+ case ASRA:
+ case AMUL:
+ case AMULU:
+ case ADIV:
+ case AMOD:
+ case AMODU:
+ case ADIVU:
+ if(p->reg != NREG)
+ goto rightread;
+ // fall through
+
+ /*
+ * right side read+write
+ */
+ case AADDF:
+ case AADDD:
+ case ASUBF:
+ case ASUBD:
+ case AMULF:
+ case AMULD:
+ case ADIVF:
+ case ADIVD:
+ case AMULA:
+ case AMULAL:
+ case AMULALU:
+ for(z=0; z<BITS; z++) {
+ r->use2.b[z] |= bit.b[z];
+ r->set.b[z] |= bit.b[z];
+ }
+ break;
+
+ /*
+ * right side write
+ */
+ case ANOP:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVD:
+ case AMOVDF:
+ case AMOVDW:
+ case AMOVF:
+ case AMOVFW:
+ case AMOVH:
+ case AMOVHU:
+ case AMOVW:
+ case AMOVWD:
+ case AMOVWF:
+ case AMVN:
+ case AMULL:
+ case AMULLU:
+ if((p->scond & C_SCOND) != C_SCOND_NONE)
+ for(z=0; z<BITS; z++)
+ r->use2.b[z] |= bit.b[z];
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * funny
+ */
+ case ABL:
+ setaddrs(bit);
+ break;
+ }
+
+ if(p->as == AMOVM) {
+ z = p->to.offset;
+ if(p->from.type == D_CONST)
+ z = p->from.offset;
+ for(i=0; z; i++) {
+ if(z&1)
+ regbits |= RtoB(i);
+ z >>= 1;
+ }
+ }
+ }
+ if(firstr == R)
+ return;
+
+ for(i=0; i<nvar; i++) {
+ Var *v = var+i;
+ if(v->addr) {
+ bit = blsh(i);
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ }
+
+// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n",
+// i, v->addr, v->etype, v->width, v->sym, v->offset);
+ }
+
+ /*
+ * pass 2
+ * turn branch references to pointers
+ * build back pointers
+ */
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH) {
+ if(p->to.branch == P)
+ fatal("pnil %P", p);
+ r1 = p->to.branch->regp;
+ if(r1 == R)
+ fatal("rnil %P", p);
+ if(r1 == r) {
+ //fatal("ref to self %P", p);
+ continue;
+ }
+ r->s2 = r1;
+ r->p2link = r1->p2;
+ r1->p2 = r;
+ }
+ }
+ if(debug['R']) {
+ p = firstr->prog;
+ print("\n%L %D\n", p->lineno, &p->from);
+ print(" addr = %Q\n", addrs);
+ }
+
+ /*
+ * pass 2.5
+ * find looping structure
+ */
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ change = 0;
+ loopit(firstr, nr);
+
+ /*
+ * pass 3
+ * iterate propagating usage
+ * back until flow graph is complete
+ */
+loop1:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ for(r = firstr; r != R; r = r->link)
+ if(r->prog->as == ARET)
+ prop(r, zbits, zbits);
+loop11:
+ /* pick up unreachable code */
+ i = 0;
+ for(r = firstr; r != R; r = r1) {
+ r1 = r->link;
+ if(r1 && r1->active && !r->active) {
+ prop(r, zbits, zbits);
+ i = 1;
+ }
+ }
+ if(i)
+ goto loop11;
+ if(change)
+ goto loop1;
+
+
+ /*
+ * pass 4
+ * iterate propagating register/variable synchrony
+ * forward until graph is complete
+ */
+loop2:
+ change = 0;
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ synch(firstr, zbits);
+ if(change)
+ goto loop2;
+
+ addsplits();
+
+ if(debug['R'] > 1) {
+ print("\nprop structure:\n");
+ for(r = firstr; r != R; r = r->link) {
+ print("%d:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++) {
+ bit.b[z] = r->set.b[z] |
+ r->refahead.b[z] | r->calahead.b[z] |
+ r->refbehind.b[z] | r->calbehind.b[z] |
+ r->use1.b[z] | r->use2.b[z];
+ bit.b[z] &= ~addrs.b[z];
+ }
+
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->use1))
+ print(" u1=%Q", r->use1);
+ if(bany(&r->use2))
+ print(" u2=%Q", r->use2);
+ if(bany(&r->set))
+ print(" st=%Q", r->set);
+ if(bany(&r->refahead))
+ print(" ra=%Q", r->refahead);
+ if(bany(&r->calahead))
+ print(" ca=%Q", r->calahead);
+ if(bany(&r->refbehind))
+ print(" rb=%Q", r->refbehind);
+ if(bany(&r->calbehind))
+ print(" cb=%Q", r->calbehind);
+ }
+ print("\n");
+ }
+ }
+
+ /*
+ * pass 4.5
+ * move register pseudo-variables into regu.
+ */
+ for(r = firstr; r != R; r = r->link) {
+ r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS;
+
+ r->set.b[0] &= ~REGBITS;
+ r->use1.b[0] &= ~REGBITS;
+ r->use2.b[0] &= ~REGBITS;
+ r->refbehind.b[0] &= ~REGBITS;
+ r->refahead.b[0] &= ~REGBITS;
+ r->calbehind.b[0] &= ~REGBITS;
+ r->calahead.b[0] &= ~REGBITS;
+ r->regdiff.b[0] &= ~REGBITS;
+ r->act.b[0] &= ~REGBITS;
+ }
+
+ /*
+ * pass 5
+ * isolate regions
+ * calculate costs (paint1)
+ */
+ r = firstr;
+ if(r) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) &
+ ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]);
+ if(bany(&bit) & !r->refset) {
+ // should never happen - all variables are preset
+ if(debug['w'])
+ print("%L: used and not set: %Q\n", r->prog->lineno, bit);
+ r->refset = 1;
+ }
+ }
+
+ for(r = firstr; r != R; r = r->link)
+ r->act = zbits;
+ rgp = region;
+ nregion = 0;
+ for(r = firstr; r != R; r = r->link) {
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->set.b[z] &
+ ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]);
+ if(bany(&bit) && !r->refset) {
+ if(debug['w'])
+ print("%L: set and not used: %Q\n", r->prog->lineno, bit);
+ r->refset = 1;
+ excise(r);
+ }
+ for(z=0; z<BITS; z++)
+ bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ rgp->enter = r;
+ rgp->varno = i;
+ change = 0;
+ if(debug['R'] > 1)
+ print("\n");
+ paint1(r, i);
+ bit.b[i/32] &= ~(1L<<(i%32));
+ if(change <= 0) {
+ if(debug['R'])
+ print("%L $%d: %Q\n",
+ r->prog->lineno, change, blsh(i));
+ continue;
+ }
+ rgp->cost = change;
+ nregion++;
+ if(nregion >= NRGN) {
+ if(debug['R'] > 1)
+ print("too many regions\n");
+ goto brk;
+ }
+ rgp++;
+ }
+ }
+brk:
+ qsort(region, nregion, sizeof(region[0]), rcmp);
+
+ /*
+ * pass 6
+ * determine used registers (paint2)
+ * replace code (paint3)
+ */
+ rgp = region;
+ for(i=0; i<nregion; i++) {
+ bit = blsh(rgp->varno);
+ vreg = paint2(rgp->enter, rgp->varno);
+ vreg = allreg(vreg, rgp);
+ if(debug['R']) {
+ if(rgp->regno >= NREG)
+ print("%L $%d F%d: %Q\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno-NREG,
+ bit);
+ else
+ print("%L $%d R%d: %Q\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno,
+ bit);
+ }
+ if(rgp->regno != 0)
+ paint3(rgp->enter, rgp->varno, vreg, rgp->regno);
+ rgp++;
+ }
+ /*
+ * pass 7
+ * peep-hole on basic block
+ */
+ if(!debug['R'] || debug['P']) {
+ peep();
+ }
+
+ /*
+ * last pass
+ * eliminate nops
+ * free aux structures
+ * adjust the stack pointer
+ * MOVW.W R1,-12(R13) <<- start
+ * MOVW R0,R1
+ * MOVW R1,8(R13)
+ * MOVW $0,R1
+ * MOVW R1,4(R13)
+ * BL ,runtime.newproc+0(SB)
+ * MOVW &ft+-32(SP),R7 <<- adjust
+ * MOVW &j+-40(SP),R6 <<- adjust
+ * MOVW autotmp_0003+-24(SP),R5 <<- adjust
+ * MOVW $12(R13),R13 <<- finish
+ */
+ vreg = 0;
+ for(p = firstp; p != P; p = p->link) {
+ while(p->link != P && p->link->as == ANOP)
+ p->link = p->link->link;
+ if(p->to.type == D_BRANCH)
+ while(p->to.branch != P && p->to.branch->as == ANOP)
+ p->to.branch = p->to.branch->link;
+ if(p->as == AMOVW && p->to.reg == 13) {
+ if(p->scond & C_WBIT) {
+ vreg = -p->to.offset; // in adjust region
+// print("%P adjusting %d\n", p, vreg);
+ continue;
+ }
+ if(p->from.type == D_CONST && p->to.type == D_REG) {
+ if(p->from.offset != vreg)
+ print("in and out different\n");
+// print("%P finish %d\n", p, vreg);
+ vreg = 0; // done adjust region
+ continue;
+ }
+
+// print("%P %d %d from type\n", p, p->from.type, D_CONST);
+// print("%P %d %d to type\n\n", p, p->to.type, D_REG);
+ }
+
+ if(p->as == AMOVW && vreg != 0) {
+ if(p->from.sym != S)
+ if(p->from.name == D_AUTO || p->from.name == D_PARAM) {
+ p->from.offset += vreg;
+// print("%P adjusting from %d %d\n", p, vreg, p->from.type);
+ }
+ if(p->to.sym != S)
+ if(p->to.name == D_AUTO || p->to.name == D_PARAM) {
+ p->to.offset += vreg;
+// print("%P adjusting to %d %d\n", p, vreg, p->from.type);
+ }
+ }
+ }
+ if(r1 != R) {
+ r1->link = freer;
+ freer = firstr;
+ }
+
+}
+
+void
+addsplits(void)
+{
+ Reg *r, *r1;
+ int z, i;
+ Bits bit;
+
+ for(r = firstr; r != R; r = r->link) {
+ if(r->loop > 1)
+ continue;
+ if(r->prog->as == ABL)
+ continue;
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link) {
+ if(r1->loop <= 1)
+ continue;
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r1->calbehind.b[z] &
+ (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) &
+ ~(r->calahead.b[z] & addrs.b[z]);
+ while(bany(&bit)) {
+ i = bnum(bit);
+ bit.b[i/32] &= ~(1L << (i%32));
+ }
+ }
+ }
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+ Prog *p, *p1, *p2;
+ Adr *a;
+ Var *v;
+
+ p1 = mal(sizeof(*p1));
+ *p1 = zprog;
+ p = r->prog;
+
+ // If there's a stack fixup coming (after BL newproc or BL deferproc),
+ // delay the load until after the fixup.
+ p2 = p->link;
+ if(p2 && p2->as == AMOVW && p2->from.type == D_CONST && p2->from.reg == REGSP && p2->to.reg == REGSP && p2->to.type == D_REG)
+ p = p2;
+
+ p1->link = p->link;
+ p->link = p1;
+ p1->lineno = p->lineno;
+
+ v = var + bn;
+
+ a = &p1->to;
+ a->sym = v->sym;
+ a->name = v->name;
+ a->node = v->node;
+ a->offset = v->offset;
+ a->etype = v->etype;
+ a->type = D_OREG;
+ if(a->etype == TARRAY || a->sym == S)
+ a->type = D_CONST;
+
+ if(v->addr)
+ fatal("addmove: shouldnt be doing this %A\n", a);
+
+ switch(v->etype) {
+ default:
+ print("What is this %E\n", v->etype);
+
+ case TINT8:
+ p1->as = AMOVB;
+ break;
+ case TBOOL:
+ case TUINT8:
+//print("movbu %E %d %S\n", v->etype, bn, v->sym);
+ p1->as = AMOVBU;
+ break;
+ case TINT16:
+ p1->as = AMOVH;
+ break;
+ case TUINT16:
+ p1->as = AMOVHU;
+ break;
+ case TINT32:
+ case TUINT32:
+ case TPTR32:
+ p1->as = AMOVW;
+ break;
+ case TFLOAT32:
+ p1->as = AMOVF;
+ break;
+ case TFLOAT64:
+ p1->as = AMOVD;
+ break;
+ }
+
+ p1->from.type = D_REG;
+ p1->from.reg = rn;
+ if(rn >= NREG) {
+ p1->from.type = D_FREG;
+ p1->from.reg = rn-NREG;
+ }
+ if(!f) {
+ p1->from = *a;
+ *a = zprog.from;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+ if(v->etype == TUINT8 || v->etype == TBOOL)
+ p1->as = AMOVBU;
+ if(v->etype == TUINT16)
+ p1->as = AMOVHU;
+ }
+ if(debug['R'])
+ print("%P\t.a%P\n", p, p1);
+}
+
+static int
+overlap(int32 o1, int w1, int32 o2, int w2)
+{
+ int32 t1, t2;
+
+ t1 = o1+w1;
+ t2 = o2+w2;
+
+ if(!(t1 > o2 && t2 > o1))
+ return 0;
+
+ return 1;
+}
+
+Bits
+mkvar(Reg *r, Adr *a)
+{
+ Var *v;
+ int i, t, n, et, z, w, flag;
+ int32 o;
+ Bits bit;
+ Sym *s;
+
+ // mark registers used
+ t = a->type;
+ n = D_NONE;
+
+ flag = 0;
+ switch(t) {
+ default:
+ print("type %d %d %D\n", t, a->name, a);
+ goto none;
+
+ case D_NONE:
+ case D_FCONST:
+ case D_BRANCH:
+ break;
+
+ case D_CONST:
+ flag = 1;
+ goto onereg;
+
+ case D_REGREG:
+ bit = zbits;
+ if(a->offset != NREG)
+ bit.b[0] |= RtoB(a->offset);
+ if(a->reg != NREG)
+ bit.b[0] |= RtoB(a->reg);
+ return bit;
+
+ case D_REG:
+ case D_SHIFT:
+ onereg:
+ if(a->reg != NREG) {
+ bit = zbits;
+ bit.b[0] = RtoB(a->reg);
+ return bit;
+ }
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG) {
+ if(a == &r->prog->from)
+ r->use1.b[0] |= RtoB(a->reg);
+ else
+ r->use2.b[0] |= RtoB(a->reg);
+ if(r->prog->scond & (C_PBIT|C_WBIT))
+ r->set.b[0] |= RtoB(a->reg);
+ }
+ break;
+
+ case D_FREG:
+ if(a->reg != NREG) {
+ bit = zbits;
+ bit.b[0] = FtoB(a->reg);
+ return bit;
+ }
+ break;
+ }
+
+ switch(a->name) {
+ default:
+ goto none;
+
+ case D_EXTERN:
+ case D_STATIC:
+ case D_AUTO:
+ case D_PARAM:
+ n = a->name;
+ break;
+ }
+
+ s = a->sym;
+ if(s == S)
+ goto none;
+ if(s->name[0] == '.')
+ goto none;
+ et = a->etype;
+ o = a->offset;
+ w = a->width;
+
+ for(i=0; i<nvar; i++) {
+ v = var+i;
+ if(v->sym == s && v->name == n) {
+ if(v->offset == o)
+ if(v->etype == et)
+ if(v->width == w)
+ if(!flag)
+ return blsh(i);
+
+ // if they overlap, disable both
+ if(overlap(v->offset, v->width, o, w)) {
+ v->addr = 1;
+ flag = 1;
+ }
+ }
+ }
+
+ switch(et) {
+ case 0:
+ case TFUNC:
+ case TARRAY:
+ case TSTRING:
+ goto none;
+ }
+
+ if(nvar >= NVAR) {
+ if(debug['w'] > 1 && s)
+ fatal("variable not optimized: %D", a);
+ goto none;
+ }
+
+ i = nvar;
+ nvar++;
+//print("var %d %E %D %S\n", i, et, a, s);
+ v = var+i;
+ v->sym = s;
+ v->offset = o;
+ v->name = n;
+// v->gotype = a->gotype;
+ v->etype = et;
+ v->width = w;
+ v->addr = flag; // funny punning
+ v->node = a->node;
+
+ if(debug['R'])
+ print("bit=%2d et=%2d w=%d+%d %S %D flag=%d\n", i, et, o, w, s, a, v->addr);
+
+ bit = blsh(i);
+ if(n == D_EXTERN || n == D_STATIC)
+ for(z=0; z<BITS; z++)
+ externs.b[z] |= bit.b[z];
+ if(n == D_PARAM)
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+
+ return bit;
+
+none:
+ return zbits;
+}
+
+void
+prop(Reg *r, Bits ref, Bits cal)
+{
+ Reg *r1, *r2;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->p1) {
+ for(z=0; z<BITS; z++) {
+ ref.b[z] |= r1->refahead.b[z];
+ if(ref.b[z] != r1->refahead.b[z]) {
+ r1->refahead.b[z] = ref.b[z];
+ change++;
+ }
+ cal.b[z] |= r1->calahead.b[z];
+ if(cal.b[z] != r1->calahead.b[z]) {
+ r1->calahead.b[z] = cal.b[z];
+ change++;
+ }
+ }
+ switch(r1->prog->as) {
+ case ABL:
+ if(noreturn(r1->prog))
+ break;
+ for(z=0; z<BITS; z++) {
+ cal.b[z] |= ref.b[z] | externs.b[z];
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ATEXT:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = 0;
+ ref.b[z] = 0;
+ }
+ break;
+
+ case ARET:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = externs.b[z] | ovar.b[z];
+ ref.b[z] = 0;
+ }
+ break;
+ }
+ for(z=0; z<BITS; z++) {
+ ref.b[z] = (ref.b[z] & ~r1->set.b[z]) |
+ r1->use1.b[z] | r1->use2.b[z];
+ cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]);
+ r1->refbehind.b[z] = ref.b[z];
+ r1->calbehind.b[z] = cal.b[z];
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ }
+ for(; r != r1; r = r->p1)
+ for(r2 = r->p2; r2 != R; r2 = r2->p2link)
+ prop(r2, r->refbehind, r->calbehind);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+int32
+postorder(Reg *r, Reg **rpo2r, int32 n)
+{
+ Reg *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+int32
+rpolca(int32 *idom, int32 rpo1, int32 rpo2)
+{
+ int32 t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal("bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+int
+doms(int32 *idom, int32 r, int32 s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+int
+loophead(int32 *idom, Reg *r)
+{
+ int32 src;
+
+ src = r->rpo;
+ if(r->p1 != R && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != R; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+void
+loopmark(Reg **rpo2r, int32 head, Reg *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != R)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != R; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+loopit(Reg *r, int32 nr)
+{
+ Reg *r1;
+ int32 i, d, me;
+
+ if(nr > maxnr) {
+ rpo2r = mal(nr * sizeof(Reg*));
+ idom = mal(nr * sizeof(int32));
+ maxnr = nr;
+ }
+ d = postorder(r, rpo2r, 0);
+ if(d > nr)
+ fatal("too many reg nodes");
+ nr = d;
+ for(i = 0; i < nr / 2; i++){
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ if(r1->p1 != R && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++){
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != R && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+}
+
+void
+synch(Reg *r, Bits dif)
+{
+ Reg *r1;
+ int z;
+
+ for(r1 = r; r1 != R; r1 = r1->s1) {
+ for(z=0; z<BITS; z++) {
+ dif.b[z] = (dif.b[z] &
+ ~(~r1->refbehind.b[z] & r1->refahead.b[z])) |
+ r1->set.b[z] | r1->regdiff.b[z];
+ if(dif.b[z] != r1->regdiff.b[z]) {
+ r1->regdiff.b[z] = dif.b[z];
+ change++;
+ }
+ }
+ if(r1->active)
+ break;
+ r1->active = 1;
+ for(z=0; z<BITS; z++)
+ dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]);
+ if(r1->s2 != R)
+ synch(r1->s2, dif);
+ }
+}
+
+uint32
+allreg(uint32 b, Rgn *r)
+{
+ Var *v;
+ int i;
+
+ v = var + r->varno;
+ r->regno = 0;
+ switch(v->etype) {
+
+ default:
+ fatal("unknown etype %d/%E", bitno(b), v->etype);
+ break;
+
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TPTR32:
+ i = BtoR(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i;
+ return RtoB(i);
+ }
+ break;
+
+ case TFLOAT32:
+ case TFLOAT64:
+ i = BtoF(~b);
+ if(i && r->cost >= 0) {
+ r->regno = i+NREG;
+ return FtoB(i);
+ }
+ break;
+
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ case TINTER:
+ case TSTRUCT:
+ case TARRAY:
+ break;
+ }
+ return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ uint32 bb;
+
+ z = bn/32;
+ bb = 1L<<(bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] > 1)
+ print("%d%P\td %Q $%d\n", r->loop,
+ r->prog, blsh(bn), change);
+ }
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] > 1)
+ print("%d%P\tu1 %Q $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ change += CREF * r->loop;
+ if(debug['R'] > 1)
+ print("%d%P\tu2 %Q $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] > 1)
+ print("%d%P\tst %Q $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint1(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint1(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+uint32
+paint2(Reg *r, int bn)
+{
+ Reg *r1;
+ int z;
+ uint32 bb, vreg;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ vreg = regbits;
+ if(!(r->act.b[z] & bb))
+ return vreg;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(!(r1->act.b[z] & bb))
+ break;
+ r = r1;
+ }
+ for(;;) {
+ r->act.b[z] &= ~bb;
+
+ vreg |= r->regu;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ vreg |= paint2(r1, bn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ vreg |= paint2(r1, bn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(!(r->act.b[z] & bb))
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+ return vreg;
+}
+
+void
+paint3(Reg *r, int bn, int32 rb, int rn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ uint32 bb;
+
+ z = bn/32;
+ bb = 1L << (bn%32);
+ if(r->act.b[z] & bb)
+ return;
+ for(;;) {
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ r1 = r->p1;
+ if(r1 == R)
+ break;
+ if(!(r1->refahead.b[z] & bb))
+ break;
+ if(r1->act.b[z] & bb)
+ break;
+ r = r1;
+ }
+
+ if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb)
+ addmove(r, bn, rn, 0);
+
+ for(;;) {
+ r->act.b[z] |= bb;
+ p = r->prog;
+
+ if(r->use1.b[z] & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->from, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ if(debug['R'])
+ print("%P", p);
+ addreg(&p->to, rn);
+ if(debug['R'])
+ print("\t.c%P\n", p);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb)
+ addmove(r, bn, rn, 1);
+ r->regu |= rb;
+
+ if(r->refbehind.b[z] & bb)
+ for(r1 = r->p2; r1 != R; r1 = r1->p2link)
+ if(r1->refahead.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+
+ if(!(r->refahead.b[z] & bb))
+ break;
+ r1 = r->s2;
+ if(r1 != R)
+ if(r1->refbehind.b[z] & bb)
+ paint3(r1, bn, rb, rn);
+ r = r->s1;
+ if(r == R)
+ break;
+ if(r->act.b[z] & bb)
+ break;
+ if(!(r->refbehind.b[z] & bb))
+ break;
+ }
+}
+
+void
+addreg(Adr *a, int rn)
+{
+ a->sym = 0;
+ a->name = D_NONE;
+ a->type = D_REG;
+ a->reg = rn;
+ if(rn >= NREG) {
+ a->type = D_FREG;
+ a->reg = rn-NREG;
+ }
+}
+
+/*
+ * bit reg
+ * 0 R0
+ * 1 R1
+ * ... ...
+ * 10 R10
+ */
+int32
+RtoB(int r)
+{
+ if(r >= REGTMP-2) // excluded R9 and R10 for m and g
+ return 0;
+ return 1L << r;
+}
+
+int
+BtoR(int32 b)
+{
+ b &= 0x01fcL; // excluded R9 and R10 for m and g
+ if(b == 0)
+ return 0;
+ return bitno(b);
+}
+
+/*
+ * bit reg
+ * 18 F2
+ * 19 F3
+ * ... ...
+ * 23 F7
+ */
+int32
+FtoB(int f)
+{
+
+ if(f < 2 || f > NFREG-1)
+ return 0;
+ return 1L << (f + 16);
+}
+
+int
+BtoF(int32 b)
+{
+
+ b &= 0xfc0000L;
+ if(b == 0)
+ return 0;
+ return bitno(b) - 16;
+}
+
+static Sym* symlist[10];
+
+int
+noreturn(Prog *p)
+{
+ Sym *s;
+ int i;
+
+ if(symlist[0] == S) {
+ symlist[0] = pkglookup("panicindex", runtimepkg);
+ symlist[1] = pkglookup("panicslice", runtimepkg);
+ symlist[2] = pkglookup("throwinit", runtimepkg);
+ symlist[3] = pkglookup("panic", runtimepkg);
+ symlist[4] = pkglookup("panicwrap", runtimepkg);
+ }
+
+ s = p->to.sym;
+ if(s == S)
+ return 0;
+ for(i=0; symlist[i]!=S; i++)
+ if(s == symlist[i])
+ return 1;
+ return 0;
+}
+
+void
+dumpone(Reg *r)
+{
+ int z;
+ Bits bit;
+
+ print("%d:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++)
+ bit.b[z] =
+ r->set.b[z] |
+ r->use1.b[z] |
+ r->use2.b[z] |
+ r->refbehind.b[z] |
+ r->refahead.b[z] |
+ r->calbehind.b[z] |
+ r->calahead.b[z] |
+ r->regdiff.b[z] |
+ r->act.b[z] |
+ 0;
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->set))
+ print(" s:%Q", r->set);
+ if(bany(&r->use1))
+ print(" u1:%Q", r->use1);
+ if(bany(&r->use2))
+ print(" u2:%Q", r->use2);
+ if(bany(&r->refbehind))
+ print(" rb:%Q ", r->refbehind);
+ if(bany(&r->refahead))
+ print(" ra:%Q ", r->refahead);
+ if(bany(&r->calbehind))
+ print("cb:%Q ", r->calbehind);
+ if(bany(&r->calahead))
+ print(" ca:%Q ", r->calahead);
+ if(bany(&r->regdiff))
+ print(" d:%Q ", r->regdiff);
+ if(bany(&r->act))
+ print(" a:%Q ", r->act);
+ }
+ print("\n");
+}
+
+void
+dumpit(char *str, Reg *r0)
+{
+ Reg *r, *r1;
+
+ print("\n%s\n", str);
+ for(r = r0; r != R; r = r->link) {
+ dumpone(r);
+ r1 = r->p2;
+ if(r1 != R) {
+ print(" pred:");
+ for(; r1 != R; r1 = r1->p2link)
+ print(" %.4ud", r1->prog->loc);
+ print("\n");
+ }
+// r1 = r->s1;
+// if(r1 != R) {
+// print(" succ:");
+// for(; r1 != R; r1 = r1->s1)
+// print(" %.4ud", r1->prog->loc);
+// print("\n");
+// }
+ }
+}