summaryrefslogtreecommitdiff
path: root/sys/src/cmd/qc
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/qc
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/qc')
-rwxr-xr-xsys/src/cmd/qc/cgen.c1459
-rwxr-xr-xsys/src/cmd/qc/enam.c383
-rwxr-xr-xsys/src/cmd/qc/gc.h357
-rwxr-xr-xsys/src/cmd/qc/list.c243
-rwxr-xr-xsys/src/cmd/qc/machcap.c98
-rwxr-xr-xsys/src/cmd/qc/mkenam18
-rwxr-xr-xsys/src/cmd/qc/mkfile42
-rwxr-xr-xsys/src/cmd/qc/mul.c609
-rwxr-xr-xsys/src/cmd/qc/peep.c966
-rwxr-xr-xsys/src/cmd/qc/q.out.h488
-rwxr-xr-xsys/src/cmd/qc/reg.c1121
-rwxr-xr-xsys/src/cmd/qc/sgen.c257
-rwxr-xr-xsys/src/cmd/qc/swt.c664
-rwxr-xr-xsys/src/cmd/qc/txt.c1747
14 files changed, 8452 insertions, 0 deletions
diff --git a/sys/src/cmd/qc/cgen.c b/sys/src/cmd/qc/cgen.c
new file mode 100755
index 000000000..d26704deb
--- /dev/null
+++ b/sys/src/cmd/qc/cgen.c
@@ -0,0 +1,1459 @@
+#include "gc.h"
+
+static void cmpv(Node*, int, Node*);
+static void testv(Node*, int);
+static void cgen64(Node*, Node*);
+static int isvconstable(int, vlong);
+
+void
+cgen(Node *n, Node *nn)
+{
+ Node *l, *r;
+ Prog *p1;
+ Node nod, nod1, nod2, nod3, nod4;
+ int o;
+ long v, curs;
+
+ if(debug['g']) {
+ prtree(nn, "cgen lhs");
+ prtree(n, "cgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(typesu[n->type->etype]) {
+ sugen(n, nn, n->type->width);
+ return;
+ }
+ if(typev[n->type->etype]) {
+ switch(n->op) {
+ case OCONST:
+ case OFUNC:
+ cgen64(n, nn);
+ return;
+ }
+ }
+ l = n->left;
+ r = n->right;
+ o = n->op;
+ if(n->addable >= INDEXED) {
+ if(nn == Z) {
+ switch(o) {
+ default:
+ nullwarn(Z, Z);
+ break;
+ case OINDEX:
+ nullwarn(l, r);
+ break;
+ }
+ return;
+ }
+ gmove(n, nn);
+ return;
+ }
+ curs = cursafe;
+
+ if(n->complex >= FNX)
+ if(l->complex >= FNX)
+ if(r != Z && r->complex >= FNX)
+ switch(o) {
+ default:
+ if(!typev[r->type->etype]) {
+ regret(&nod, r);
+ cgen(r, &nod);
+ regsalloc(&nod1, r);
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ } else {
+ regsalloc(&nod1, r);
+ cgen(r, &nod1);
+ }
+
+ nod = *n;
+ nod.right = &nod1;
+ cgen(&nod, nn);
+ return;
+
+ case OFUNC:
+ case OCOMMA:
+ case OANDAND:
+ case OOROR:
+ case OCOND:
+ case ODOT:
+ break;
+ }
+
+ switch(o) {
+ default:
+ diag(n, "unknown op in cgen: %O", o);
+ break;
+
+ case ONEG:
+ case OCOM:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, &nod, Z, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ case OAS:
+ if(l->op == OBIT)
+ goto bitas;
+ if(l->addable >= INDEXED) {
+ if(nn != Z || r->addable < INDEXED) {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ gmove(&nod, l);
+ regfree(&nod);
+ } else
+ gmove(r, l);
+ break;
+ }
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, l, Z);
+ if(r->addable >= INDEXED) {
+ gmove(r, &nod1);
+ if(nn != Z)
+ gmove(r, nn);
+ regfree(&nod1);
+ break;
+ }
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ reglcgen(&nod1, l, Z);
+ }
+ gmove(&nod, &nod1);
+ regfree(&nod);
+ regfree(&nod1);
+ break;
+
+ bitas:
+ n = l->left;
+ regalloc(&nod, r, nn);
+ if(l->complex >= r->complex) {
+ reglcgen(&nod1, n, Z);
+ cgen(r, &nod);
+ } else {
+ cgen(r, &nod);
+ reglcgen(&nod1, n, Z);
+ }
+ regalloc(&nod2, n, Z);
+ gopcode(OAS, &nod1, Z, &nod2);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OBIT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ bitload(n, &nod, Z, Z, nn);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OXOR:
+ if(nn != Z)
+ if(r->op == OCONST && r->vconst == -1){
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(OCOM, &nod, Z, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+
+ case OADD:
+ case OSUB:
+ case OAND:
+ case OOR:
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ /*
+ * immediate operands
+ */
+ if(nn != Z && r->op == OCONST && !typefd[n->type->etype] &&
+ (!typev[n->type->etype] || isvconstable(o, r->vconst))) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(o == OAND || r->vconst != 0)
+ gopcode(o, r, Z, &nod);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+ }
+
+ case OMUL:
+ case OLMUL:
+ case OLDIV:
+ case OLMOD:
+ case ODIV:
+ case OMOD:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ if((o == OMUL || o == OLMUL) && !typev[n->type->etype]) {
+ if(mulcon(n, nn))
+ break;
+ if(debug['M'])
+ print("%L multiply\n", n->lineno);
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ if(o != OMUL || typev[n->type->etype] || !sconst(r)) {
+ regalloc(&nod1, r, Z);
+ cgen(r, &nod1);
+ gopcode(o, &nod1, Z, &nod);
+ regfree(&nod1);
+ } else
+ gopcode(o, r, Z, &nod);
+ } else {
+ regalloc(&nod1, r, nn);
+ cgen(r, &nod1);
+ regalloc(&nod, l, Z);
+ cgen(l, &nod);
+ gopcode(o, &nod1, Z, &nod);
+ regfree(&nod1);
+ }
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ break;
+
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ case OASAND:
+ case OASADD:
+ case OASSUB:
+ case OASXOR:
+ case OASOR:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(r->op == OCONST && !typefd[r->type->etype] && !typefd[n->type->etype] &&
+ (!typev[n->type->etype] || isvconstable(o, r->vconst))) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ gopcode(o, r, Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+ }
+
+ case OASLMUL:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMUL:
+ case OASDIV:
+ case OASMOD:
+ if(l->op == OBIT)
+ goto asbitop;
+ if(l->complex >= r->complex) {
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ regalloc(&nod, r, Z);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, Z);
+ cgen(r, &nod);
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+ }
+ regalloc(&nod1, n, nn);
+ gopcode(OAS, &nod2, Z, &nod1);
+ gopcode(o, &nod, Z, &nod1);
+ gopcode(OAS, &nod1, Z, &nod2);
+ if(nn != Z)
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ asbitop:
+ regalloc(&nod4, n, nn);
+ regalloc(&nod3, r, Z);
+ if(l->complex >= r->complex) {
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ cgen(r, &nod3);
+ } else {
+ cgen(r, &nod3);
+ bitload(l, &nod, &nod1, &nod2, &nod4);
+ }
+ gmove(&nod, &nod4);
+ gopcode(n->op, &nod3, Z, &nod4);
+ regfree(&nod3);
+ gmove(&nod4, &nod);
+ regfree(&nod4);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+
+ case OADDR:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ lcgen(l, nn);
+ break;
+
+ case OFUNC:
+ if(l->complex >= FNX) {
+ if(l->op != OIND)
+ diag(n, "bad function call");
+
+ regret(&nod, l->left);
+ cgen(l->left, &nod);
+ regsalloc(&nod1, l->left);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+
+ nod = *n;
+ nod.left = &nod2;
+ nod2 = *l;
+ nod2.left = &nod1;
+ nod2.complex = 1;
+ cgen(&nod, nn);
+
+ return;
+ }
+ o = reg[REGARG];
+ gargs(r, &nod, &nod1);
+ if(l->addable < INDEXED) {
+ reglcgen(&nod, l, Z);
+ gopcode(OFUNC, Z, Z, &nod);
+ regfree(&nod);
+ } else
+ gopcode(OFUNC, Z, Z, l);
+ if(REGARG)
+ if(o != reg[REGARG])
+ reg[REGARG]--;
+ if(nn != Z) {
+ regret(&nod, n);
+ gopcode(OAS, &nod, Z, nn);
+ regfree(&nod);
+ }
+ break;
+
+ case OIND:
+ if(nn == Z) {
+ cgen(l, nn);
+ break;
+ }
+ regialloc(&nod, n, nn);
+ r = l;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ cgen(l, &nod);
+ nod.xoffset += v;
+ r->vconst = v;
+ } else
+ cgen(l, &nod);
+ regind(&nod, n);
+ gmove(&nod, nn);
+ regfree(&nod);
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OLO:
+ case OLS:
+ case OHI:
+ case OHS:
+ if(nn == Z) {
+ nullwarn(l, r);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ boolgen(n, 1, nn);
+ if(nn == Z)
+ patch(p, pc);
+ break;
+
+ case ONOT:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ boolgen(n, 1, nn);
+ break;
+
+ case OCOMMA:
+ cgen(l, Z);
+ cgen(r, nn);
+ break;
+
+ case OCAST:
+ if(nn == Z) {
+ nullwarn(l, Z);
+ break;
+ }
+ /*
+ * convert from types l->n->nn
+ */
+ if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
+ /* both null, gen l->nn */
+ cgen(l, nn);
+ break;
+ }
+ if(typev[l->type->etype] || typev[n->type->etype]) {
+ cgen64(n, nn);
+ break;
+ }
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ regalloc(&nod1, n, &nod);
+ gmove(&nod, &nod1);
+ gmove(&nod1, nn);
+ regfree(&nod1);
+ regfree(&nod);
+ break;
+
+ case ODOT:
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod = *nodrat;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod.xoffset += (long)r->vconst;
+ nod.type = n->type;
+ cgen(&nod, nn);
+ }
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ cgen(r->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ cgen(r->right, nn);
+ patch(p1, pc);
+ break;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPOSTDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+ if(nn == Z)
+ goto pre;
+
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ regalloc(&nod1, l, Z);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, &nod, &nod1);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, &nod, &nod1);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), &nod, &nod1);
+ gopcode(OAS, &nod1, Z, &nod2);
+
+ regfree(&nod);
+ regfree(&nod1);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ case OPREINC:
+ case OPREDEC:
+ v = 1;
+ if(l->type->etype == TIND)
+ v = l->type->link->width;
+ if(o == OPREDEC)
+ v = -v;
+ if(l->op == OBIT)
+ goto bitinc;
+
+ pre:
+ if(l->addable < INDEXED)
+ reglcgen(&nod2, l, Z);
+ else
+ nod2 = *l;
+
+ regalloc(&nod, l, nn);
+ gopcode(OAS, &nod2, Z, &nod);
+ if(typefd[l->type->etype]) {
+ regalloc(&nod3, l, Z);
+ if(v < 0) {
+ gopcode(OAS, nodfconst(-v), Z, &nod3);
+ gopcode(OSUB, &nod3, Z, &nod);
+ } else {
+ gopcode(OAS, nodfconst(v), Z, &nod3);
+ gopcode(OADD, &nod3, Z, &nod);
+ }
+ regfree(&nod3);
+ } else
+ gopcode(OADD, nodconst(v), Z, &nod);
+ gopcode(OAS, &nod, Z, &nod2);
+ if(nn && l->op == ONAME) /* in x=++i, emit USED(i) */
+ gins(ANOP, l, Z);
+
+ regfree(&nod);
+ if(l->addable < INDEXED)
+ regfree(&nod2);
+ break;
+
+ bitinc:
+ if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
+ bitload(l, &nod, &nod1, &nod2, Z);
+ gopcode(OAS, &nod, Z, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, Z);
+ break;
+ }
+ bitload(l, &nod, &nod1, &nod2, nn);
+ gopcode(OADD, nodconst(v), Z, &nod);
+ bitstore(l, &nod, &nod1, &nod2, nn);
+ break;
+ }
+ cursafe = curs;
+}
+
+void
+reglcgen(Node *t, Node *n, Node *nn)
+{
+ Node *r;
+ long v;
+
+ regialloc(t, n, nn);
+ if(n->op == OIND) {
+ r = n->left;
+ while(r->op == OADD)
+ r = r->right;
+ if(sconst(r)) {
+ v = r->vconst;
+ r->vconst = 0;
+ lcgen(n, t);
+ t->xoffset += v;
+ r->vconst = v;
+ regind(t, n);
+ return;
+ }
+ }
+ lcgen(n, t);
+ regind(t, n);
+}
+
+void
+lcgen(Node *n, Node *nn)
+{
+ Prog *p1;
+ Node nod;
+
+ if(debug['g']) {
+ prtree(nn, "lcgen lhs");
+ prtree(n, "lcgen");
+ }
+ if(n == Z || n->type == T)
+ return;
+ if(nn == Z) {
+ nn = &nod;
+ regalloc(&nod, n, Z);
+ }
+ switch(n->op) {
+ default:
+ if(n->addable < INDEXED) {
+ diag(n, "unknown op in lcgen: %O", n->op);
+ break;
+ }
+ nod = *n;
+ nod.op = OADDR;
+ nod.left = n;
+ nod.right = Z;
+ nod.type = types[TIND];
+ gopcode(OAS, &nod, Z, nn);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, n->left);
+ lcgen(n->right, nn);
+ break;
+
+ case OIND:
+ cgen(n->left, nn);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ lcgen(n->right->left, nn);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ lcgen(n->right->right, nn);
+ patch(p1, pc);
+ break;
+ }
+}
+
+void
+bcgen(Node *n, int true)
+{
+
+ if(n->type == T)
+ gbranch(OGOTO);
+ else
+ boolgen(n, true, Z);
+}
+
+void
+boolgen(Node *n, int true, Node *nn)
+{
+ int o, uns;
+ Prog *p1, *p2;
+ Node *l, *r, nod, nod1;
+ long curs;
+
+ if(debug['g']) {
+ prtree(nn, "boolgen lhs");
+ prtree(n, "boolgen");
+ }
+ uns = 0;
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ switch(n->op) {
+
+ default:
+ if(n->op == OCONST) {
+ o = vconst(n);
+ if(!true)
+ o = !o;
+ gbranch(OGOTO);
+ if(o) {
+ p1 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ }
+ goto com;
+ }
+ if(typev[n->type->etype]) {
+ testv(n, true);
+ goto com;
+ }
+ regalloc(&nod, n, nn);
+ cgen(n, &nod);
+ o = ONE;
+ if(true)
+ o = comrel[relindex(o)];
+ if(typefd[n->type->etype]) {
+ nodreg(&nod1, n, NREG+FREGZERO);
+ gopcode(o, &nod, Z, &nod1);
+ } else
+ gopcode(o, &nod, Z, nodconst(0));
+ regfree(&nod);
+ goto com;
+
+ case OCOMMA:
+ cgen(l, Z);
+ boolgen(r, true, nn);
+ break;
+
+ case ONOT:
+ boolgen(l, !true, nn);
+ break;
+
+ case OCOND:
+ bcgen(l, 1);
+ p1 = p;
+ bcgen(r->left, true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ bcgen(r->right, !true);
+ patch(p2, pc);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OANDAND:
+ if(!true)
+ goto caseor;
+
+ caseand:
+ bcgen(l, true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ patch(p1, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ goto com;
+
+ case OOROR:
+ if(!true)
+ goto caseand;
+
+ caseor:
+ bcgen(l, !true);
+ p1 = p;
+ bcgen(r, !true);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ goto com;
+
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ uns = 1;
+ /* fall through */
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ if(typev[l->type->etype]){
+ cmpv(n, true, Z);
+ goto com;
+ }
+ o = n->op;
+ if(true)
+ o = comrel[relindex(o)];
+ if(l->complex >= FNX && r->complex >= FNX) {
+ regret(&nod, r);
+ cgen(r, &nod);
+ regsalloc(&nod1, r);
+ gopcode(OAS, &nod, Z, &nod1);
+ regfree(&nod);
+ nod = *n;
+ nod.right = &nod1;
+ boolgen(&nod, true, nn);
+ break;
+ }
+ if(!uns && sconst(r) || (uns || o == OEQ || o == ONE) && uconst(r)) {
+ regalloc(&nod, l, nn);
+ cgen(l, &nod);
+ gopcode(o, &nod, Z, r);
+ regfree(&nod);
+ goto com;
+ }
+ if(l->complex >= r->complex) {
+ regalloc(&nod1, l, nn);
+ cgen(l, &nod1);
+ regalloc(&nod, r, Z);
+ cgen(r, &nod);
+ } else {
+ regalloc(&nod, r, nn);
+ cgen(r, &nod);
+ regalloc(&nod1, l, Z);
+ cgen(l, &nod1);
+ }
+ gopcode(o, &nod1, Z, &nod);
+ regfree(&nod);
+ regfree(&nod1);
+
+ com:
+ if(nn != Z) {
+ p1 = p;
+ gopcode(OAS, nodconst(1L), Z, nn);
+ gbranch(OGOTO);
+ p2 = p;
+ patch(p1, pc);
+ gopcode(OAS, nodconst(0L), Z, nn);
+ patch(p2, pc);
+ }
+ break;
+ }
+ cursafe = curs;
+}
+
+void
+sugen(Node *n, Node *nn, long w)
+{
+ Prog *p1;
+ Node nod0, nod1, nod2, nod3, nod4, *l, *r;
+ Type *t;
+ long pc1;
+ int i, m, c;
+
+ if(n == Z || n->type == T)
+ return;
+ if(nn == nodrat)
+ if(w > nrathole)
+ nrathole = w;
+ if(debug['g']) {
+ prtree(nn, "sugen lhs");
+ prtree(n, "sugen");
+ }
+ if(typev[n->type->etype]) {
+ diag(n, "old vlong sugen: %O", n->op);
+ return;
+ }
+ switch(n->op) {
+ case OIND:
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+
+ default:
+ goto copy;
+
+ case ODOT:
+ l = n->left;
+ sugen(l, nodrat, l->type->width);
+ if(nn != Z) {
+ warn(n, "non-interruptable temporary");
+ nod1 = *nodrat;
+ r = n->right;
+ if(!r || r->op != OCONST) {
+ diag(n, "DOT and no offset");
+ break;
+ }
+ nod1.xoffset += (long)r->vconst;
+ nod1.type = n->type;
+ sugen(&nod1, nn, w);
+ }
+ break;
+
+ case OSTRUCT:
+ /*
+ * rewrite so lhs has no side effects
+ */
+ if(nn != Z && side(nn)) {
+ nod1 = *n;
+ nod1.type = typ(TIND, n->type);
+ regalloc(&nod2, &nod1, Z);
+ lcgen(nn, &nod2);
+ regsalloc(&nod0, &nod1);
+ gopcode(OAS, &nod2, Z, &nod0);
+ regfree(&nod2);
+
+ nod1 = *n;
+ nod1.op = OIND;
+ nod1.left = &nod0;
+ nod1.right = Z;
+ nod1.complex = 1;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ r = n->left;
+ for(t = n->type->link; t != T; t = t->down) {
+ l = r;
+ if(r->op == OLIST) {
+ l = r->left;
+ r = r->right;
+ }
+ if(nn == Z) {
+ cgen(l, nn);
+ continue;
+ }
+ /*
+ * hand craft *(&nn + o) = l
+ */
+ nod0 = znode;
+ nod0.op = OAS;
+ nod0.type = t;
+ nod0.left = &nod1;
+ nod0.right = l;
+
+ nod1 = znode;
+ nod1.op = OIND;
+ nod1.type = t;
+ nod1.left = &nod2;
+
+ nod2 = znode;
+ nod2.op = OADD;
+ nod2.type = typ(TIND, t);
+ nod2.left = &nod3;
+ nod2.right = &nod4;
+
+ nod3 = znode;
+ nod3.op = OADDR;
+ nod3.type = nod2.type;
+ nod3.left = nn;
+
+ nod4 = znode;
+ nod4.op = OCONST;
+ nod4.type = nod2.type;
+ nod4.vconst = t->offset;
+
+ ccom(&nod0);
+ acom(&nod0);
+ xcom(&nod0);
+ nod0.addable = 0;
+
+ /* prtree(&nod0, "hand craft"); /* */
+ cgen(&nod0, Z);
+ }
+ break;
+
+ case OAS:
+ if(nn == Z) {
+ if(n->addable < INDEXED)
+ sugen(n->right, n->left, w);
+ break;
+ }
+ /* BOTCH -- functions can clobber rathole */
+ sugen(n->right, nodrat, w);
+ warn(n, "non-interruptable temporary");
+ sugen(nodrat, n->left, w);
+ sugen(nodrat, nn, w);
+ break;
+
+ case OFUNC:
+ /* this transformation should probably be done earlier */
+ if(nn == Z) {
+ sugen(n, nodrat, w);
+ break;
+ }
+ if(nn->op != OIND) {
+ nn = new1(OADDR, nn, Z);
+ nn->type = types[TIND];
+ nn->addable = 0;
+ } else
+ nn = nn->left;
+ n = new(OFUNC, n->left, new(OLIST, nn, n->right));
+ n->complex = FNX;
+ n->type = types[TVOID];
+ n->left->type = types[TVOID];
+ cgen(n, Z);
+ break;
+
+ case OCOND:
+ bcgen(n->left, 1);
+ p1 = p;
+ sugen(n->right->left, nn, w);
+ gbranch(OGOTO);
+ patch(p1, pc);
+ p1 = p;
+ sugen(n->right->right, nn, w);
+ patch(p1, pc);
+ break;
+
+ case OCOMMA:
+ cgen(n->left, Z);
+ sugen(n->right, nn, w);
+ break;
+ }
+ return;
+
+copy:
+ if(nn == Z)
+ return;
+ if(n->complex >= FNX && nn->complex >= FNX) {
+ t = nn->type;
+ nn->type = types[TLONG];
+ regialloc(&nod1, nn, Z);
+ lcgen(nn, &nod1);
+ regsalloc(&nod2, nn);
+ nn->type = t;
+
+ gmove(&nod1, &nod2);
+ regfree(&nod1);
+
+ nod2.type = typ(TIND, t);
+
+ nod1 = nod2;
+ nod1.op = OIND;
+ nod1.left = &nod2;
+ nod1.right = Z;
+ nod1.complex = 1;
+ nod1.type = t;
+
+ sugen(n, &nod1, w);
+ return;
+ }
+
+ if(n->complex > nn->complex) {
+ t = n->type;
+ n->type = types[TLONG];
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+
+ t = nn->type;
+ nn->type = types[TLONG];
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+ } else {
+ t = nn->type;
+ nn->type = types[TLONG];
+ reglcgen(&nod2, nn, Z);
+ nn->type = t;
+
+ t = n->type;
+ n->type = types[TLONG];
+ reglcgen(&nod1, n, Z);
+ n->type = t;
+ }
+
+ w /= SZ_LONG;
+ if(w <= 5) {
+ layout(&nod1, &nod2, w, 0, Z);
+ goto out;
+ }
+
+ /*
+ * minimize space for unrolling loop
+ * 3,4,5 times. (6 or more is never minimum)
+ * if small structure, try 2 also.
+ */
+ c = 0; /* set */
+ m = 100;
+ i = 3;
+ if(w <= 15)
+ i = 2;
+ for(; i<=5; i++)
+ if(i + w%i <= m) {
+ c = i;
+ m = c + w%c;
+ }
+
+ regalloc(&nod3, &regnode, Z);
+ layout(&nod1, &nod2, w%c, w/c, &nod3);
+
+ pc1 = pc;
+ layout(&nod1, &nod2, c, 0, Z);
+
+ gopcode(OSUB, nodconst(1L), Z, &nod3);
+ nod1.op = OREGISTER;
+ gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1);
+ nod2.op = OREGISTER;
+ gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2);
+
+ gopcode(OGT, &nod3, Z, nodconst(0));
+ patch(p, pc1);
+
+ regfree(&nod3);
+out:
+ regfree(&nod1);
+ regfree(&nod2);
+}
+
+void
+layout(Node *f, Node *t, int c, int cv, Node *cn)
+{
+ Node t1, t2;
+
+ while(c > 3) {
+ layout(f, t, 2, 0, Z);
+ c -= 2;
+ }
+
+ regalloc(&t1, &regnode, Z);
+ regalloc(&t2, &regnode, Z);
+ if(c > 0) {
+ gopcode(OAS, f, Z, &t1);
+ f->xoffset += SZ_LONG;
+ }
+ if(cn != Z)
+ gopcode(OAS, nodconst(cv), Z, cn);
+ if(c > 1) {
+ gopcode(OAS, f, Z, &t2);
+ f->xoffset += SZ_LONG;
+ }
+ if(c > 0) {
+ gopcode(OAS, &t1, Z, t);
+ t->xoffset += SZ_LONG;
+ }
+ if(c > 2) {
+ gopcode(OAS, f, Z, &t1);
+ f->xoffset += SZ_LONG;
+ }
+ if(c > 1) {
+ gopcode(OAS, &t2, Z, t);
+ t->xoffset += SZ_LONG;
+ }
+ if(c > 2) {
+ gopcode(OAS, &t1, Z, t);
+ t->xoffset += SZ_LONG;
+ }
+ regfree(&t1);
+ regfree(&t2);
+}
+
+/*
+ * is the vlong's value directly addressible?
+ */
+int
+isvdirect(Node *n)
+{
+ return n->op == ONAME || n->op == OCONST || n->op == OINDREG;
+}
+
+/*
+ * can the constant be used with given vlong op?
+ */
+static int
+isvconstable(int o, vlong v)
+{
+ switch(o) {
+ case OADD:
+ case OASADD:
+ /* there isn't an immediate form for ADDE/SUBE, but there are special ADDME/ADDZE etc */
+ return v == 0 || v == -1;
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ case OASLSHR:
+ case OASASHL:
+ case OASASHR:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * most 64-bit operations: cgen into a register pair, then operate.
+ * 64-bit comparisons are handled a little differently because the two underlying
+ * comparisons can be compiled separately, since the calculations don't interact.
+ */
+
+static void
+vcgen(Node *n, Node *o, int *f)
+{
+ *f = 0;
+ if(!isvdirect(n)) {
+ if(n->complex >= FNX) {
+ regsalloc(o, n);
+ cgen(n, o);
+ return;
+ }
+ *f = 1;
+ if(n->addable < INDEXED && n->op != OIND && n->op != OINDEX) {
+ regalloc(o, n, Z);
+ cgen(n, o);
+ } else
+ reglcgen(o, n, Z);
+ } else
+ *o = *n;
+}
+
+static int
+isuns(int op)
+{
+ switch(op){
+ case OLO:
+ case OLS:
+ case OHI:
+ case OHS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+gcmpv(Node *l, Node *r, void (*mov)(Node*, Node*, int), int op)
+{
+ Node vl, vr;
+
+ regalloc(&vl, &regnode, Z);
+ mov(l, &vl, 0);
+ regalloc(&vr, &regnode, Z);
+ mov(r, &vr, 1+isuns(op));
+ gopcode(op, &vl, Z, &vr);
+ if(vl.op == OREGISTER)
+ regfree(&vl);
+ if(vr.op == OREGISTER)
+ regfree(&vr);
+}
+
+static void
+brcondv(Node *l, Node *r, int chi, int clo)
+{
+ Prog *p1, *p2, *p3, *p4;
+
+ gcmpv(l, r, gloadhi, chi);
+ p1 = p;
+ gins(ABNE, Z, Z);
+ p2 = p;
+ gcmpv(l, r, gloadlo, clo);
+ p3 = p;
+ gbranch(OGOTO);
+ p4 = p;
+ patch(p1, pc);
+ patch(p3, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ patch(p4, pc);
+}
+
+static void
+testv(Node *n, int true)
+{
+ Node nod;
+
+ nod = znode;
+ nod.op = ONE;
+ nod.left = n;
+ nod.right = new1(0, Z, Z);
+ *nod.right = *nodconst(0);
+ nod.right->type = n->type;
+ nod.type = types[TLONG];
+ cmpv(&nod, true, Z);
+}
+
+/*
+ * comparison for vlong does high and low order parts separately,
+ * which saves loading the latter if the high order comparison suffices
+ */
+static void
+cmpv(Node *n, int true, Node *nn)
+{
+ Node *l, *r, nod, nod1;
+ int o, f1, f2;
+ Prog *p1, *p2;
+ long curs;
+
+ if(debug['g']) {
+ if(nn != nil)
+ prtree(nn, "cmpv lhs");
+ prtree(n, "cmpv");
+ }
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ if(l->complex >= FNX && r->complex >= FNX) {
+ regsalloc(&nod1, r);
+ cgen(r, &nod1);
+ nod = *n;
+ nod.right = &nod1;
+ cmpv(&nod, true, nn);
+ cursafe = curs;
+ return;
+ }
+ if(l->complex >= r->complex) {
+ vcgen(l, &nod1, &f1);
+ vcgen(r, &nod, &f2);
+ } else {
+ vcgen(r, &nod, &f2);
+ vcgen(l, &nod1, &f1);
+ }
+ nod.type = types[TLONG];
+ nod1.type = types[TLONG];
+ o = n->op;
+ if(true)
+ o = comrel[relindex(o)];
+ switch(o){
+ case OEQ:
+ gcmpv(&nod1, &nod, gloadhi, ONE);
+ p1 = p;
+ gcmpv(&nod1, &nod, gloadlo, ONE);
+ p2 = p;
+ gbranch(OGOTO);
+ patch(p1, pc);
+ patch(p2, pc);
+ break;
+ case ONE:
+ gcmpv(&nod1, &nod, gloadhi, ONE);
+ p1 = p;
+ gcmpv(&nod1, &nod, gloadlo, OEQ);
+ p2 = p;
+ patch(p1, pc);
+ gbranch(OGOTO);
+ patch(p2, pc);
+ break;
+ case OLE:
+ brcondv(&nod1, &nod, OLT, OLS);
+ break;
+ case OGT:
+ brcondv(&nod1, &nod, OGT, OHI);
+ break;
+ case OLS:
+ brcondv(&nod1, &nod, OLO, OLS);
+ break;
+ case OHI:
+ brcondv(&nod1, &nod, OHI, OHI);
+ break;
+ case OLT:
+ brcondv(&nod1, &nod, OLT, OLO);
+ break;
+ case OGE:
+ brcondv(&nod1, &nod, OGT, OHS);
+ break;
+ case OLO:
+ brcondv(&nod1, &nod, OLO, OLO);
+ break;
+ case OHS:
+ brcondv(&nod1, &nod, OHI, OHS);
+ break;
+ default:
+ diag(n, "bad cmpv");
+ return;
+ }
+ if(f1)
+ regfree(&nod1);
+ if(f2)
+ regfree(&nod);
+ cursafe = curs;
+}
+
+static void
+cgen64(Node *n, Node *nn)
+{
+ Node *l, *r, *d;
+ Node nod, nod1;
+ long curs;
+ Type *t;
+ int o, m;
+
+ curs = cursafe;
+ l = n->left;
+ r = n->right;
+ o = n->op;
+ switch(o) {
+
+ case OCONST:
+ if(nn == Z) {
+ nullwarn(n->left, Z);
+ break;
+ }
+ if(nn->op != OREGPAIR) {
+//prtree(n, "cgen64 const");
+ t = nn->type;
+ nn->type = types[TLONG];
+ reglcgen(&nod1, nn, Z);
+ nn->type = t;
+
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gmove(nod32const(n->vconst>>32), &nod1);
+ else
+ gmove(nod32const(n->vconst), &nod1);
+ nod1.xoffset += SZ_LONG;
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gmove(nod32const(n->vconst), &nod1);
+ else
+ gmove(nod32const(n->vconst>>32), &nod1);
+
+ regfree(&nod1);
+ } else
+ gmove(n, nn);
+ break;
+
+ case OCAST:
+ /*
+ * convert from types l->n->nn
+ */
+ if(typev[l->type->etype]){
+ /* vlong to non-vlong */
+ if(!isvdirect(l)) {
+ if(l->addable < INDEXED && l->op != OIND && l->op != OINDEX) {
+ regalloc(&nod, l, l);
+ cgen(l, &nod);
+ regalloc(&nod1, n, nn);
+ gmove(nod.right, &nod1);
+ } else {
+ reglcgen(&nod, l, Z);
+ regalloc(&nod1, n, nn);
+ gloadlo(&nod, &nod1, 0); /* TO DO: not correct for typefd */
+ }
+ regfree(&nod);
+ } else {
+ regalloc(&nod1, n, nn);
+ gloadlo(l, &nod1, 0); /* TO DO: not correct for typefd */
+ }
+ }else{
+ /* non-vlong to vlong */
+ regalloc(&nod, l, Z);
+ cgen(l, &nod);
+ regalloc(&nod1, n, nn);
+ gmove(&nod, nod1.right);
+ if(typeu[l->type->etype])
+ gmove(nodconst(0), nod1.left);
+ else
+ gopcode(OASHR, nodconst(31), nod1.right, nod1.left);
+ regfree(&nod);
+ }
+ gmove(&nod1, nn);
+ regfree(&nod1);
+ break;
+
+ case OFUNC:
+ /* this transformation should probably be done earlier */
+ if(nn == Z) {
+ regsalloc(&nod1, n);
+ nn = &nod1;
+ }
+ m = 0;
+ if(nn->op != OIND) {
+ if(nn->op == OREGPAIR) {
+ m = 1;
+ regsalloc(&nod1, nn);
+ d = &nod1;
+ }else
+ d = nn;
+ d = new1(OADDR, d, Z);
+ d->type = types[TIND];
+ d->addable = 0;
+ } else
+ d = nn->left;
+ n = new(OFUNC, l, new(OLIST, d, r));
+ n->complex = FNX;
+ n->type = types[TVOID];
+ n->left->type = types[TVOID];
+ cgen(n, Z);
+ if(m)
+ gmove(&nod1, nn);
+ break;
+
+ default:
+ diag(n, "bad cgen64 %O", o);
+ break;
+ }
+ cursafe = curs;
+}
diff --git a/sys/src/cmd/qc/enam.c b/sys/src/cmd/qc/enam.c
new file mode 100755
index 000000000..e09c3f948
--- /dev/null
+++ b/sys/src/cmd/qc/enam.c
@@ -0,0 +1,383 @@
+char *anames[] =
+{
+ "XXX",
+ "ADD",
+ "ADDCC",
+ "ADDV",
+ "ADDVCC",
+ "ADDC",
+ "ADDCCC",
+ "ADDCV",
+ "ADDCVCC",
+ "ADDME",
+ "ADDMECC",
+ "ADDMEVCC",
+ "ADDMEV",
+ "ADDE",
+ "ADDECC",
+ "ADDEVCC",
+ "ADDEV",
+ "ADDZE",
+ "ADDZECC",
+ "ADDZEVCC",
+ "ADDZEV",
+ "AND",
+ "ANDCC",
+ "ANDN",
+ "ANDNCC",
+ "BC",
+ "BCL",
+ "BEQ",
+ "BGE",
+ "BGT",
+ "BL",
+ "BLE",
+ "BLT",
+ "BNE",
+ "BR",
+ "BVC",
+ "BVS",
+ "CMP",
+ "CMPU",
+ "CNTLZW",
+ "CNTLZWCC",
+ "CRAND",
+ "CRANDN",
+ "CREQV",
+ "CRNAND",
+ "CRNOR",
+ "CROR",
+ "CRORN",
+ "CRXOR",
+ "DIVW",
+ "DIVWCC",
+ "DIVWVCC",
+ "DIVWV",
+ "DIVWU",
+ "DIVWUCC",
+ "DIVWUVCC",
+ "DIVWUV",
+ "EQV",
+ "EQVCC",
+ "EXTSB",
+ "EXTSBCC",
+ "EXTSH",
+ "EXTSHCC",
+ "FABS",
+ "FABSCC",
+ "FADD",
+ "FADDCC",
+ "FADDS",
+ "FADDSCC",
+ "FCMPO",
+ "FCMPU",
+ "FCTIW",
+ "FCTIWCC",
+ "FCTIWZ",
+ "FCTIWZCC",
+ "FDIV",
+ "FDIVCC",
+ "FDIVS",
+ "FDIVSCC",
+ "FMADD",
+ "FMADDCC",
+ "FMADDS",
+ "FMADDSCC",
+ "FMOVD",
+ "FMOVDCC",
+ "FMOVDU",
+ "FMOVS",
+ "FMOVSU",
+ "FMSUB",
+ "FMSUBCC",
+ "FMSUBS",
+ "FMSUBSCC",
+ "FMUL",
+ "FMULCC",
+ "FMULS",
+ "FMULSCC",
+ "FNABS",
+ "FNABSCC",
+ "FNEG",
+ "FNEGCC",
+ "FNMADD",
+ "FNMADDCC",
+ "FNMADDS",
+ "FNMADDSCC",
+ "FNMSUB",
+ "FNMSUBCC",
+ "FNMSUBS",
+ "FNMSUBSCC",
+ "FRSP",
+ "FRSPCC",
+ "FSUB",
+ "FSUBCC",
+ "FSUBS",
+ "FSUBSCC",
+ "MOVMW",
+ "LSW",
+ "LWAR",
+ "MOVWBR",
+ "MOVB",
+ "MOVBU",
+ "MOVBZ",
+ "MOVBZU",
+ "MOVH",
+ "MOVHBR",
+ "MOVHU",
+ "MOVHZ",
+ "MOVHZU",
+ "MOVW",
+ "MOVWU",
+ "MOVFL",
+ "MOVCRFS",
+ "MTFSB0",
+ "MTFSB0CC",
+ "MTFSB1",
+ "MTFSB1CC",
+ "MULHW",
+ "MULHWCC",
+ "MULHWU",
+ "MULHWUCC",
+ "MULLW",
+ "MULLWCC",
+ "MULLWVCC",
+ "MULLWV",
+ "NAND",
+ "NANDCC",
+ "NEG",
+ "NEGCC",
+ "NEGVCC",
+ "NEGV",
+ "NOR",
+ "NORCC",
+ "OR",
+ "ORCC",
+ "ORN",
+ "ORNCC",
+ "REM",
+ "REMCC",
+ "REMV",
+ "REMVCC",
+ "REMU",
+ "REMUCC",
+ "REMUV",
+ "REMUVCC",
+ "RFI",
+ "RLWMI",
+ "RLWMICC",
+ "RLWNM",
+ "RLWNMCC",
+ "SLW",
+ "SLWCC",
+ "SRW",
+ "SRAW",
+ "SRAWCC",
+ "SRWCC",
+ "STSW",
+ "STWCCC",
+ "SUB",
+ "SUBCC",
+ "SUBVCC",
+ "SUBC",
+ "SUBCCC",
+ "SUBCV",
+ "SUBCVCC",
+ "SUBME",
+ "SUBMECC",
+ "SUBMEVCC",
+ "SUBMEV",
+ "SUBV",
+ "SUBE",
+ "SUBECC",
+ "SUBEV",
+ "SUBEVCC",
+ "SUBZE",
+ "SUBZECC",
+ "SUBZEVCC",
+ "SUBZEV",
+ "SYNC",
+ "XOR",
+ "XORCC",
+ "DCBF",
+ "DCBI",
+ "DCBST",
+ "DCBT",
+ "DCBTST",
+ "DCBZ",
+ "ECIWX",
+ "ECOWX",
+ "EIEIO",
+ "ICBI",
+ "ISYNC",
+ "TLBIE",
+ "TW",
+ "SYSCALL",
+ "DATA",
+ "GLOBL",
+ "GOK",
+ "HISTORY",
+ "NAME",
+ "NOP",
+ "RETURN",
+ "TEXT",
+ "WORD",
+ "END",
+ "DYNT",
+ "INIT",
+ "SIGNAME",
+ "MACCHW",
+ "MACCHWCC",
+ "MACCHWS",
+ "MACCHWSCC",
+ "MACCHWSU",
+ "MACCHWSUCC",
+ "MACCHWSUV",
+ "MACCHWSUVCC",
+ "MACCHWSV",
+ "MACCHWSVCC",
+ "MACCHWU",
+ "MACCHWUCC",
+ "MACCHWUV",
+ "MACCHWUVCC",
+ "MACCHWV",
+ "MACCHWVCC",
+ "MACHHW",
+ "MACHHWCC",
+ "MACHHWV",
+ "MACHHWVCC",
+ "MACHHWS",
+ "MACHHWSCC",
+ "MACHHWSV",
+ "MACHHWSVCC",
+ "MACHHWSU",
+ "MACHHWSUCC",
+ "MACHHWSUV",
+ "MACHHWSUVCC",
+ "MACHHWU",
+ "MACHHWUCC",
+ "MACHHWUV",
+ "MACHHWUVCC",
+ "MACLHW",
+ "MACLHWCC",
+ "MACLHWS",
+ "MACLHWSCC",
+ "MACLHWSU",
+ "MACLHWSUCC",
+ "MACLHWSUV",
+ "MACLHWSUVCC",
+ "MACLHWSV",
+ "MACLHWSVCC",
+ "MACLHWU",
+ "MACLHWUCC",
+ "MACLHWUV",
+ "MACLHWUVCC",
+ "MACLHWV",
+ "MACLHWVCC",
+ "MULCHW",
+ "MULCHWCC",
+ "MULCHWU",
+ "MULCHWUCC",
+ "MULHHW",
+ "MULHHWCC",
+ "MULHHWU",
+ "MULHHWUCC",
+ "MULLHW",
+ "MULLHWCC",
+ "MULLHWU",
+ "MULLHWUCC",
+ "NMACCHW",
+ "NMACCHWCC",
+ "NMACCHWS",
+ "NMACCHWSCC",
+ "NMACCHWSV",
+ "NMACCHWSVCC",
+ "NMACCHWV",
+ "NMACCHWVCC",
+ "NMACHHW",
+ "NMACHHWCC",
+ "NMACHHWS",
+ "NMACHHWSCC",
+ "NMACHHWSV",
+ "NMACHHWSVCC",
+ "NMACHHWV",
+ "NMACHHWVCC",
+ "NMACLHW",
+ "NMACLHWCC",
+ "NMACLHWS",
+ "NMACLHWSCC",
+ "NMACLHWSV",
+ "NMACLHWSVCC",
+ "NMACLHWV",
+ "NMACLHWVCC",
+ "RFCI",
+ "FRES",
+ "FRESCC",
+ "FRSQRTE",
+ "FRSQRTECC",
+ "FSEL",
+ "FSELCC",
+ "FSQRT",
+ "FSQRTCC",
+ "FSQRTS",
+ "FSQRTSCC",
+ "FPSEL",
+ "FPMUL",
+ "FXMUL",
+ "FXPMUL",
+ "FXSMUL",
+ "FPADD",
+ "FPSUB",
+ "FPRE",
+ "FPRSQRTE",
+ "FPMADD",
+ "FXMADD",
+ "FXCPMADD",
+ "FXCSMADD",
+ "FPNMADD",
+ "FXNMADD",
+ "FXCPNMADD",
+ "FXCSNMADD",
+ "FPMSUB",
+ "FXMSUB",
+ "FXCPMSUB",
+ "FXCSMSUB",
+ "FPNMSUB",
+ "FXNMSUB",
+ "FXCPNMSUB",
+ "FXCSNMSUB",
+ "FPABS",
+ "FPNEG",
+ "FPRSP",
+ "FPNABS",
+ "FSCMP",
+ "FSABS",
+ "FSNEG",
+ "FSNABS",
+ "FPCTIW",
+ "FPCTIWZ",
+ "FMOVSPD",
+ "FMOVPSD",
+ "FXCPNPMA",
+ "FXCSNPMA",
+ "FXCPNSMA",
+ "FXCSNSMA",
+ "FXCXNPMA",
+ "FXCXNSMA",
+ "FXCXMA",
+ "FXCXNMS",
+ "FSMOVS",
+ "FSMOVSU",
+ "FSMOVD",
+ "FSMOVDU",
+ "FXMOVS",
+ "FXMOVSU",
+ "FXMOVD",
+ "FXMOVDU",
+ "FPMOVS",
+ "FPMOVSU",
+ "FPMOVD",
+ "FPMOVDU",
+ "FPMOVIW",
+ "LAST",
+};
diff --git a/sys/src/cmd/qc/gc.h b/sys/src/cmd/qc/gc.h
new file mode 100755
index 000000000..c130a6139
--- /dev/null
+++ b/sys/src/cmd/qc/gc.h
@@ -0,0 +1,357 @@
+#include "../cc/cc.h"
+#include "../qc/q.out.h"
+
+/*
+ * qc/power
+ * powerpc
+ */
+#define SZ_CHAR 1
+#define SZ_SHORT 2
+#define SZ_INT 4
+#define SZ_LONG 4
+#define SZ_IND 4
+#define SZ_FLOAT 4
+#define SZ_VLONG 8
+#define SZ_DOUBLE 8
+#define FNX 100
+
+typedef struct Adr Adr;
+typedef struct Prog Prog;
+typedef struct Case Case;
+typedef struct C1 C1;
+typedef struct Multab Multab;
+typedef struct Hintab Hintab;
+typedef struct Var Var;
+typedef struct Reg Reg;
+typedef struct Rgn Rgn;
+
+struct Adr
+{
+ union
+ {
+ long offset;
+ double dval;
+ char sval[NSNAME];
+ };
+ Sym* sym;
+ char type;
+ char reg;
+ char name;
+ char etype;
+};
+#define A ((Adr*)0)
+
+#define INDEXED 9
+struct Prog
+{
+ Adr from;
+ Adr from3; /* third argument for fmadd, fmsub, ... */
+ Adr to;
+ Prog* link;
+ long lineno;
+ short as;
+ char reg;
+};
+#define P ((Prog*)0)
+
+struct Case
+{
+ Case* link;
+ long val;
+ long label;
+ char def;
+ char isv;
+};
+#define C ((Case*)0)
+
+struct C1
+{
+ long val;
+ long label;
+};
+
+struct Multab
+{
+ long val;
+ char code[20];
+};
+
+struct Hintab
+{
+ ushort val;
+ char hint[10];
+};
+
+struct Var
+{
+ long offset;
+ Sym* sym;
+ char name;
+ char etype;
+};
+
+struct Reg
+{
+ long pc;
+ long rpo; /* reverse post ordering */
+
+ Bits set;
+ Bits use1;
+ Bits use2;
+
+ Bits refbehind;
+ Bits refahead;
+ Bits calbehind;
+ Bits calahead;
+ Bits regdiff;
+ Bits act;
+
+ long regu;
+ long loop; /* could be shorter */
+
+ union
+ {
+ Reg* log5;
+ long active;
+ };
+ 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 long breakpc;
+EXTERN long nbreak;
+EXTERN Case* cases;
+EXTERN Node constnode;
+EXTERN Node fconstnode;
+EXTERN long continpc;
+EXTERN long curarg;
+EXTERN long cursafe;
+EXTERN Prog* firstp;
+EXTERN Prog* lastp;
+EXTERN int hintabsize;
+EXTERN long maxargsafe;
+EXTERN Multab multab[20];
+EXTERN int mnstring;
+EXTERN Node* nodrat;
+EXTERN Node* nodret;
+EXTERN Node* nodsafe;
+EXTERN Node* nodretv;
+EXTERN long nrathole;
+EXTERN long nstring;
+EXTERN Prog* p;
+EXTERN long pc;
+EXTERN Node regnode;
+EXTERN char string[NSNAME];
+EXTERN Sym* symrathole;
+EXTERN Node znode;
+EXTERN Prog zprog;
+EXTERN int reg[NREG+NREG];
+EXTERN long exregoffset;
+EXTERN long exfregoffset;
+
+#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 bset(a,n) ((a).b[(n)/32]&(1L<<(n)%32))
+
+#define CLOAD 5
+#define CREF 5
+#define CINF 1000
+#define LOOP 3
+
+EXTERN Rgn region[NRGN];
+EXTERN Rgn* rgp;
+EXTERN int nregion;
+EXTERN int nvar;
+
+EXTERN Bits externs;
+EXTERN Bits params;
+EXTERN Bits consts;
+EXTERN Bits addrs;
+
+EXTERN long regbits;
+EXTERN long exregbits;
+
+EXTERN int change;
+EXTERN int suppress;
+
+EXTERN Reg* firstr;
+EXTERN Reg* lastr;
+EXTERN Reg zreg;
+EXTERN Reg* freer;
+EXTERN Var var[NVAR];
+EXTERN long* idom;
+EXTERN Reg** rpo2r;
+EXTERN long maxnr;
+
+#define R0ISZERO (debug['0']==0)
+
+extern char* anames[];
+extern Hintab hintab[];
+
+/*
+ * sgen.c
+ */
+void codgen(Node*, Node*);
+void gen(Node*);
+void usedset(Node*, int);
+void noretval(int);
+void xcom(Node*);
+int bcomplex(Node*, Node*);
+
+/*
+ * cgen.c
+ */
+void cgen(Node*, Node*);
+void reglcgen(Node*, Node*, Node*);
+void lcgen(Node*, Node*);
+void bcgen(Node*, int);
+void boolgen(Node*, int, Node*);
+void sugen(Node*, Node*, long);
+void layout(Node*, Node*, int, int, Node*);
+
+/*
+ * txt.c
+ */
+void ginit(void);
+void gclean(void);
+void nextpc(void);
+void gargs(Node*, Node*, Node*);
+void garg1(Node*, Node*, Node*, int, Node**);
+Node* nodconst(long);
+Node* nod32const(vlong);
+Node* nodfconst(double);
+void nodreg(Node*, Node*, int);
+void regret(Node*, Node*);
+void regalloc(Node*, Node*, Node*);
+void regfree(Node*);
+void regialloc(Node*, Node*, Node*);
+void regsalloc(Node*, Node*);
+void regaalloc1(Node*, Node*);
+void regaalloc(Node*, Node*);
+void regind(Node*, Node*);
+void gprep(Node*, Node*);
+void raddr(Node*, Prog*);
+void naddr(Node*, Adr*);
+void gloadhi(Node*, Node*, int);
+void gloadlo(Node*, Node*, int);
+void gmove(Node*, Node*);
+void gins(int a, Node*, Node*);
+void gins3(int a, Node*, Node*, Node*);
+void gopcode(int, Node*, Node*, Node*);
+int samaddr(Node*, Node*);
+void gbranch(int);
+void patch(Prog*, long);
+int sconst(Node*);
+int sval(long);
+int uconst(Node*);
+long hi64v(Node*);
+long lo64v(Node*);
+Node* hi64(Node*);
+Node* lo64(Node*);
+void gpseudo(int, Sym*, Node*);
+
+/*
+ * swt.c
+ */
+int swcmp(void*, void*);
+void doswit(Node*);
+void swit1(C1*, int, long, Node*);
+void swit2(C1*, int, long, Node*, Node*);
+void casf(void);
+void bitload(Node*, Node*, Node*, Node*, Node*);
+void bitstore(Node*, Node*, Node*, Node*, Node*);
+long outstring(char*, long);
+int mulcon(Node*, Node*);
+Multab* mulcon0(Node*, long);
+int mulcon1(Node*, long, Node*);
+void nullwarn(Node*, Node*);
+void sextern(Sym*, Node*, long, long);
+void gextern(Sym*, Node*, long, long);
+void outcode(void);
+void ieeedtod(Ieee*, double);
+
+/*
+ * list
+ */
+void listinit(void);
+int Pconv(Fmt*);
+int Aconv(Fmt*);
+int Dconv(Fmt*);
+int Sconv(Fmt*);
+int Nconv(Fmt*);
+int Bconv(Fmt*);
+
+/*
+ * reg.c
+ */
+Reg* rega(void);
+int rcmp(void*, void*);
+void regopt(Prog*);
+void addmove(Reg*, int, int, int);
+Bits mkvar(Adr*, int);
+void prop(Reg*, Bits, Bits);
+void loopit(Reg*, long);
+void synch(Reg*, Bits);
+ulong allreg(ulong, Rgn*);
+void paint1(Reg*, int);
+ulong paint2(Reg*, int);
+void paint3(Reg*, int, long, int);
+void addreg(Adr*, int);
+
+/*
+ * peep.c
+ */
+void peep(void);
+void excise(Reg*);
+Reg* uniqp(Reg*);
+Reg* uniqs(Reg*);
+int regtyp(Adr*);
+int regzer(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 copyau1(Prog*, Adr*);
+int copysub(Adr*, Adr*, Adr*, int);
+int copysub1(Prog*, Adr*, Adr*, int);
+
+long RtoB(int);
+long FtoB(int);
+int BtoR(long);
+int BtoF(long);
+
+/*
+ * com64.c
+ */
+int com64(Node*);
+void com64init(void);
+void bool64(Node*);
+
+#pragma varargck type "A" int
+#pragma varargck type "B" Bits
+#pragma varargck type "D" Adr*
+#pragma varargck type "N" Adr*
+#pragma varargck type "P" Prog*
+#pragma varargck type "S" char*
diff --git a/sys/src/cmd/qc/list.c b/sys/src/cmd/qc/list.c
new file mode 100755
index 000000000..f0f98a132
--- /dev/null
+++ b/sys/src/cmd/qc/list.c
@@ -0,0 +1,243 @@
+#define EXTERN
+#include "gc.h"
+
+void
+listinit(void)
+{
+
+ fmtinstall('A', Aconv);
+ fmtinstall('P', Pconv);
+ fmtinstall('S', Sconv);
+ fmtinstall('N', Nconv);
+ fmtinstall('D', Dconv);
+ fmtinstall('B', Bconv);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char str[STRINGSZ], ss[STRINGSZ], *s;
+ Bits bits;
+ int i;
+
+ str[0] = 0;
+ bits = va_arg(fp->args, Bits);
+ while(bany(&bits)) {
+ i = bnum(bits);
+ if(str[0])
+ strcat(str, " ");
+ if(var[i].sym == S) {
+ sprint(ss, "$%ld", var[i].offset);
+ s = ss;
+ } else
+ s = var[i].sym->name;
+ if(strlen(str) + strlen(s) + 1 >= STRINGSZ)
+ break;
+ strcat(str, s);
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Pconv(Fmt *fp)
+{
+ char str[STRINGSZ], *s;
+ Prog *p;
+ int a;
+
+ p = va_arg(fp->args, Prog*);
+ a = p->as;
+ if(a == ADATA)
+ sprint(str, " %A %D/%d,%D", a, &p->from, p->reg, &p->to);
+ else if(p->as == ATEXT)
+ sprint(str, " %A %D,%d,%D", a, &p->from, p->reg, &p->to);
+ else {
+ s = seprint(str, str+sizeof(str), " %A %D", a, &p->from);
+ if(p->reg != NREG)
+ s = seprint(s, str+sizeof(str), ",%c%d", p->from.type==D_FREG? 'F': 'R', p->reg);
+ if(p->from3.type != D_NONE)
+ s = seprint(s, str+sizeof(str), ",%D", &p->from3);
+ seprint(s, s+sizeof(str), ",%D", &p->to);
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Aconv(Fmt *fp)
+{
+ char *s;
+ int a;
+
+ a = va_arg(fp->args, int);
+ s = "???";
+ if(a >= AXXX && a <= ALAST)
+ s = anames[a];
+ return fmtstrcpy(fp, s);
+}
+
+int
+Dconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+
+ a = va_arg(fp->args, Adr*);
+ switch(a->type) {
+
+ 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, "%N(R%d)(NONE)", a, a->reg);
+ break;
+
+ case D_CONST:
+ if(a->reg != NREG)
+ sprint(str, "$%N(R%d)", a, a->reg);
+ else
+ sprint(str, "$%N", a);
+ break;
+
+ case D_OREG:
+ if(a->reg != NREG)
+ sprint(str, "%N(R%d)", a, a->reg);
+ else
+ sprint(str, "%N", a);
+ break;
+
+ case D_REG:
+ sprint(str, "R%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(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, "%N(F%d)(REG)", a, a->reg);
+ break;
+
+ case D_CREG:
+ sprint(str, "C%d", a->reg);
+ if(a->name != D_NONE || a->sym != S)
+ sprint(str, "%N(C%d)(REG)", a, a->reg);
+ break;
+
+ case D_BRANCH:
+ sprint(str, "%ld(PC)", a->offset-pc);
+ break;
+
+ case D_FCONST:
+ sprint(str, "$%.17e", a->dval);
+ break;
+
+ case D_SCONST:
+ sprint(str, "$\"%S\"", a->sval);
+ break;
+ }
+ return fmtstrcpy(fp, str);
+}
+
+int
+Sconv(Fmt *fp)
+{
+ int i, c;
+ char str[STRINGSZ], *p, *a;
+
+ a = va_arg(fp->args, char*);
+ p = str;
+ for(i=0; i<NSNAME; i++) {
+ c = a[i] & 0xff;
+ if(c >= 'a' && c <= 'z' ||
+ c >= 'A' && c <= 'Z' ||
+ c >= '0' && c <= '9' ||
+ c == ' ' || c == '%') {
+ *p++ = c;
+ continue;
+ }
+ *p++ = '\\';
+ switch(c) {
+ case 0:
+ *p++ = 'z';
+ continue;
+ case '\\':
+ case '"':
+ *p++ = c;
+ continue;
+ case '\n':
+ *p++ = 'n';
+ continue;
+ case '\t':
+ *p++ = 't';
+ continue;
+ case '\r':
+ *p++ = 'r';
+ continue;
+ case '\f':
+ *p++ = 'f';
+ continue;
+ }
+ *p++ = (c>>6) + '0';
+ *p++ = ((c>>3) & 7) + '0';
+ *p++ = (c & 7) + '0';
+ }
+ *p = 0;
+ return fmtstrcpy(fp, str);
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char str[STRINGSZ];
+ Adr *a;
+ Sym *s;
+ int i, l, b, n;
+
+ a = va_arg(fp->args, Adr*);
+ s = a->sym;
+ if(s == S) {
+ if(a->offset > 64 || -a->offset > 64) {
+ n = 0;
+ l = a->offset & 1;
+ for(i=0; i<32; i++){
+ b = (a->offset >> i) & 1;
+ if(b != l)
+ n++;
+ l = b;
+ }
+ if(n < 2) {
+ sprint(str, "%#lux", a->offset);
+ goto out;
+ }
+ }
+ sprint(str, "%ld", a->offset);
+ goto out;
+ }
+ switch(a->name) {
+ default:
+ sprint(str, "GOK-name(%d)", a->name);
+ break;
+
+ case D_EXTERN:
+ sprint(str, "%s+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_STATIC:
+ sprint(str, "%s<>+%ld(SB)", s->name, a->offset);
+ break;
+
+ case D_AUTO:
+ sprint(str, "%s-%ld(SP)", s->name, -a->offset);
+ break;
+
+ case D_PARAM:
+ sprint(str, "%s+%ld(FP)", s->name, a->offset);
+ break;
+ }
+out:
+ return fmtstrcpy(fp, str);
+}
diff --git a/sys/src/cmd/qc/machcap.c b/sys/src/cmd/qc/machcap.c
new file mode 100755
index 000000000..e9f9a9881
--- /dev/null
+++ b/sys/src/cmd/qc/machcap.c
@@ -0,0 +1,98 @@
+#include "gc.h"
+
+int
+machcap(Node *n)
+{
+ if(n == Z)
+ return 1; /* test */
+ switch(n->op){
+
+ case OADD:
+ case OAND:
+ case OOR:
+ case OSUB:
+ case OXOR:
+ if(typev[n->left->type->etype])
+ return 1;
+ break;
+
+ case OMUL:
+ case OLMUL:
+ case OASMUL:
+ case OASLMUL:
+ return 1;
+
+ case OLSHR:
+ case OASHR:
+ case OASHL:
+ case OASASHL:
+ case OASASHR:
+ case OASLSHR:
+ return 1;
+
+ case OCAST:
+ if(typev[n->type->etype]) {
+ if(!typefd[n->left->type->etype])
+ return 1;
+ } else if(!typefd[n->type->etype]) {
+ if(typev[n->left->type->etype])
+ return 1;
+ }
+ break;
+
+ case OCOMMA:
+ case OCOND:
+ case OLIST:
+ case OANDAND:
+ case OOROR:
+ case ONOT:
+ return 1;
+
+ case OCOM:
+ case ONEG:
+ if(typechl[n->left->type->etype])
+ return 1;
+ if(typev[n->left->type->etype])
+ return 1;
+ return 0;
+
+ case OASADD:
+ case OASSUB:
+ case OASAND:
+ case OASOR:
+ case OASXOR:
+ return 1;
+
+ case OPOSTINC:
+ case OPOSTDEC:
+ case OPREINC:
+ case OPREDEC:
+ return 1;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OGT:
+ case OLT:
+ case OGE:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ return 1;
+
+ case ODIV:
+ case OLDIV:
+ case OLMOD:
+ case OMOD:
+ return 0;
+
+ case OASDIV:
+ case OASLDIV:
+ case OASLMOD:
+ case OASMOD:
+ return 0;
+
+ }
+ return 0;
+}
diff --git a/sys/src/cmd/qc/mkenam b/sys/src/cmd/qc/mkenam
new file mode 100755
index 000000000..6250da92a
--- /dev/null
+++ b/sys/src/cmd/qc/mkenam
@@ -0,0 +1,18 @@
+ed - ../qc/q.out.h <<'!'
+v/^ A/d
+g/^ AEND/s//&,/
+g/^ ALAST/s//&,/
+g/[ ]*=.*,/s//,/
+v/,/p
+,s/^ A/ "/
+,s/,.*$/",/
+1i
+char *anames[] =
+{
+.
+,a
+};
+.
+w enam.c
+Q
+!
diff --git a/sys/src/cmd/qc/mkfile b/sys/src/cmd/qc/mkfile
new file mode 100755
index 000000000..fe4f00ad5
--- /dev/null
+++ b/sys/src/cmd/qc/mkfile
@@ -0,0 +1,42 @@
+</$objtype/mkfile
+
+TARG=qc
+OFILES=\
+ cgen.$O\
+ enam.$O\
+ list.$O\
+ machcap.$O\
+ mul.$O\
+ peep.$O\
+ pgen.$O\
+ pswt.$O\
+ reg.$O\
+ sgen.$O\
+ swt.$O\
+ txt.$O\
+
+HFILES=\
+ gc.h\
+ q.out.h\
+ ../cc/cc.h\
+
+LIB=../cc/cc.a$O
+
+BIN=/$objtype/bin
+</sys/src/cmd/mkone
+
+$LIB:
+ cd ../cc
+ mk install
+ mk clean
+
+%.$O: ../cc/%.c
+ $CC $CFLAGS ../cc/$stem.c
+
+t:V: $O.out
+ $O.out -S t
+ $LD -o t.out t.$O
+ t.out
+
+enam.c: q.out.h
+ rc mkenam
diff --git a/sys/src/cmd/qc/mul.c b/sys/src/cmd/qc/mul.c
new file mode 100755
index 000000000..6918c1fed
--- /dev/null
+++ b/sys/src/cmd/qc/mul.c
@@ -0,0 +1,609 @@
+#include "gc.h"
+
+/*
+ * code sequences for multiply by constant.
+ * [a-l][0-3]
+ * lsl $(A-'a'),r0,r1
+ * [+][0-7]
+ * add r0,r1,r2
+ * [-][0-7]
+ * sub r0,r1,r2
+ */
+
+static int multabp;
+static long mulval;
+static char* mulcp;
+static long valmax;
+static int shmax;
+
+static int docode(char *hp, char *cp, int r0, int r1);
+static int gen1(int len);
+static int gen2(int len, long r1);
+static int gen3(int len, long r0, long r1, int flag);
+enum
+{
+ SR1 = 1<<0, /* r1 has been shifted */
+ SR0 = 1<<1, /* r0 has been shifted */
+ UR1 = 1<<2, /* r1 has not been used */
+ UR0 = 1<<3, /* r0 has not been used */
+};
+
+Multab*
+mulcon0(Node *n, long v)
+{
+ int a1, a2, g;
+ Multab *m, *m1;
+ char hint[10];
+
+ if(v < 0)
+ v = -v;
+
+ /*
+ * look in cache
+ */
+ m = multab;
+ for(g=0; g<nelem(multab); g++) {
+ if(m->val == v) {
+ if(m->code[0] == 0)
+ return 0;
+ return m;
+ }
+ m++;
+ }
+
+ /*
+ * select a spot in cache to overwrite
+ */
+ multabp++;
+ if(multabp < 0 || multabp >= nelem(multab))
+ multabp = 0;
+ m = multab+multabp;
+ m->val = v;
+ mulval = v;
+
+ /*
+ * look in execption hint table
+ */
+ a1 = 0;
+ a2 = hintabsize;
+ for(;;) {
+ if(a1 >= a2)
+ goto no;
+ g = (a2 + a1)/2;
+ if(v < hintab[g].val) {
+ a2 = g;
+ continue;
+ }
+ if(v > hintab[g].val) {
+ a1 = g+1;
+ continue;
+ }
+ break;
+ }
+
+ if(docode(hintab[g].hint, m->code, 1, 0))
+ return m;
+ print("%L: multiply table failure %ld\n", n->lineno, v);
+ m->code[0] = 0;
+ return 0;
+
+no:
+ /*
+ * try to search
+ */
+ hint[0] = 0;
+ for(g=1; g<=6; g++) {
+ if(g >= 6 && v >= 65535)
+ break;
+ mulcp = hint+g;
+ *mulcp = 0;
+ if(gen1(g)) {
+ if(docode(hint, m->code, 1, 0))
+ return m;
+ print("%L: multiply table failure (g=%d h=%s) %ld\n",
+ n->lineno, g, hint, v);
+ break;
+ }
+ }
+
+ /*
+ * try a recur followed by a shift
+ */
+ g = 0;
+ while(!(v & 1)) {
+ g++;
+ v >>= 1;
+ }
+ if(g) {
+ m1 = mulcon0(n, v);
+ if(m1) {
+ strcpy(m->code, m1->code);
+ sprint(strchr(m->code, 0), "%c0", g+'a');
+ return m;
+ }
+ }
+ m->code[0] = 0;
+ return 0;
+}
+
+static int
+docode(char *hp, char *cp, int r0, int r1)
+{
+ int c, i;
+
+ c = *hp++;
+ *cp = c;
+ cp += 2;
+ switch(c) {
+ default:
+ c -= 'a';
+ if(c < 1 || c >= 30)
+ break;
+ for(i=0; i<4; i++) {
+ switch(i) {
+ case 0:
+ if(docode(hp, cp, r0<<c, r1))
+ goto out;
+ break;
+ case 1:
+ if(docode(hp, cp, r1<<c, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r0, r0<<c))
+ goto out;
+ break;
+ case 3:
+ if(docode(hp, cp, r0, r1<<c))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '+':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0+r1, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0+r1))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case '-':
+ for(i=0; i<8; i++) {
+ cp[-1] = i+'0';
+ switch(i) {
+ case 1:
+ if(docode(hp, cp, r0-r1, r1))
+ goto out;
+ break;
+ case 2:
+ if(docode(hp, cp, r1-r0, r1))
+ goto out;
+ break;
+ case 5:
+ if(docode(hp, cp, r0, r0-r1))
+ goto out;
+ break;
+ case 6:
+ if(docode(hp, cp, r0, r1-r0))
+ goto out;
+ break;
+ }
+ }
+ break;
+
+ case 0:
+ if(r0 == mulval)
+ return 1;
+ }
+ return 0;
+
+out:
+ cp[-1] = i+'0';
+ return 1;
+}
+
+static int
+gen1(int len)
+{
+ int i;
+
+ for(shmax=1; shmax<30; shmax++) {
+ valmax = 1<<shmax;
+ if(valmax >= mulval)
+ break;
+ }
+ if(mulval == 1)
+ return 1;
+
+ len--;
+ for(i=1; i<=shmax; i++)
+ if(gen2(len, 1<<i)) {
+ *--mulcp = 'a'+i;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gen2(int len, long r1)
+{
+ int i;
+
+ if(len <= 0) {
+ if(r1 == mulval)
+ return 1;
+ return 0;
+ }
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(gen3(len, r1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, r1-1, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+ if(gen3(len, 1, r1+1, UR1)) {
+ i = '+';
+ goto out;
+ }
+ if(gen3(len, 1, r1-1, UR1)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ if(mulval == r1+1) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-1) {
+ i = '-';
+ goto out;
+ }
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+static int
+gen3(int len, long r0, long r1, int flag)
+{
+ int i, f1, f2;
+ long x;
+
+ if(r0 <= 0 ||
+ r0 >= r1 ||
+ r1 > valmax)
+ return 0;
+
+ len--;
+ if(len == 0)
+ goto calcr0;
+
+ if(!(flag & UR1)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & UR0)) {
+ f1 = UR1|SR1;
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r1, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR1)) {
+ f1 = UR1|SR1|(flag&UR0);
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x > valmax)
+ break;
+ if(gen3(len, r0, x, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ if(!(flag & SR0)) {
+ f1 = UR0|SR0|(flag&(SR1|UR1));
+
+ f2 = UR1|SR1;
+ if(flag & UR1)
+ f2 |= UR0;
+ if(flag & SR1)
+ f2 |= SR0;
+
+ for(i=1; i<=shmax; i++) {
+ x = r0<<i;
+ if(x > valmax)
+ break;
+ if(x > r1) {
+ if(gen3(len, r1, x, f2)) {
+ i += 'a';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r1, f1)) {
+ i += 'a';
+ goto out;
+ }
+ }
+ }
+
+ x = r1+r0;
+ if(gen3(len, r0, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ if(gen3(len, r1, x, UR1)) {
+ i = '+';
+ goto out;
+ }
+
+ x = r1-r0;
+ if(gen3(len, x, r1, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ if(x > r0) {
+ if(gen3(len, r0, x, UR1)) {
+ i = '-';
+ goto out;
+ }
+ } else
+ if(gen3(len, x, r0, UR0)) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+calcr0:
+ f1 = flag & (UR0|UR1);
+ if(f1 == UR1) {
+ for(i=1; i<=shmax; i++) {
+ x = r1<<i;
+ if(x >= mulval) {
+ if(x == mulval) {
+ i += 'a';
+ goto out;
+ }
+ break;
+ }
+ }
+ }
+
+ if(mulval == r1+r0) {
+ i = '+';
+ goto out;
+ }
+ if(mulval == r1-r0) {
+ i = '-';
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *--mulcp = i;
+ return 1;
+}
+
+/*
+ * hint table has numbers that
+ * the search algorithm fails on.
+ * <1000:
+ * all numbers
+ * <5000:
+ * ÷ by 5
+ * <10000:
+ * ÷ by 50
+ * <65536:
+ * ÷ by 250
+ */
+Hintab hintab[] =
+{
+ 683, "b++d+e+",
+ 687, "b+e++e-",
+ 691, "b++d+e+",
+ 731, "b++d+e+",
+ 811, "b++d+i+",
+ 821, "b++e+e+",
+ 843, "b+d++e+",
+ 851, "b+f-+e-",
+ 853, "b++e+e+",
+ 877, "c++++g-",
+ 933, "b+c++g-",
+ 981, "c-+e-d+",
+ 1375, "b+c+b+h-",
+ 1675, "d+b++h+",
+ 2425, "c++f-e+",
+ 2675, "c+d++f-",
+ 2750, "b+d-b+h-",
+ 2775, "c-+g-e-",
+ 3125, "b++e+g+",
+ 3275, "b+c+g+e+",
+ 3350, "c++++i+",
+ 3475, "c-+e-f-",
+ 3525, "c-+d+g-",
+ 3625, "c-+e-j+",
+ 3675, "b+d+d+e+",
+ 3725, "b+d-+h+",
+ 3925, "b+d+f-d-",
+ 4275, "b+g++e+",
+ 4325, "b+h-+d+",
+ 4425, "b+b+g-j-",
+ 4525, "b+d-d+f+",
+ 4675, "c++d-g+",
+ 4775, "b+d+b+g-",
+ 4825, "c+c-+i-",
+ 4850, "c++++i-",
+ 4925, "b++e-g-",
+ 4975, "c+f++e-",
+ 5500, "b+g-c+d+",
+ 6700, "d+b++i+",
+ 9700, "d++++j-",
+ 11000, "b+f-c-h-",
+ 11750, "b+d+g+j-",
+ 12500, "b+c+e-k+",
+ 13250, "b+d+e-f+",
+ 13750, "b+h-c-d+",
+ 14250, "b+g-c+e-",
+ 14500, "c+f+j-d-",
+ 14750, "d-g--f+",
+ 16750, "b+e-d-n+",
+ 17750, "c+h-b+e+",
+ 18250, "d+b+h-d+",
+ 18750, "b+g-++f+",
+ 19250, "b+e+b+h+",
+ 19750, "b++h--f-",
+ 20250, "b+e-l-c+",
+ 20750, "c++bi+e-",
+ 21250, "b+i+l+c+",
+ 22000, "b+e+d-g-",
+ 22250, "b+d-h+k-",
+ 22750, "b+d-e-g+",
+ 23250, "b+c+h+e-",
+ 23500, "b+g-c-g-",
+ 23750, "b+g-b+h-",
+ 24250, "c++g+m-",
+ 24750, "b+e+e+j-",
+ 25000, "b++dh+g+",
+ 25250, "b+e+d-g-",
+ 25750, "b+e+b+j+",
+ 26250, "b+h+c+e+",
+ 26500, "b+h+c+g+",
+ 26750, "b+d+e+g-",
+ 27250, "b+e+e+f+",
+ 27500, "c-i-c-d+",
+ 27750, "b+bd++j+",
+ 28250, "d-d-++i-",
+ 28500, "c+c-h-e-",
+ 29000, "b+g-d-f+",
+ 29500, "c+h+++e-",
+ 29750, "b+g+f-c+",
+ 30250, "b+f-g-c+",
+ 33500, "c-f-d-n+",
+ 33750, "b+d-b+j-",
+ 34250, "c+e+++i+",
+ 35250, "e+b+d+k+",
+ 35500, "c+e+d-g-",
+ 35750, "c+i-++e+",
+ 36250, "b+bh-d+e+",
+ 36500, "c+c-h-e-",
+ 36750, "d+e--i+",
+ 37250, "b+g+g+b+",
+ 37500, "b+h-b+f+",
+ 37750, "c+be++j-",
+ 38500, "b+e+b+i+",
+ 38750, "d+i-b+d+",
+ 39250, "b+g-l-+d+",
+ 39500, "b+g-c+g-",
+ 39750, "b+bh-c+f-",
+ 40250, "b+bf+d+g-",
+ 40500, "b+g-c+g+",
+ 40750, "c+b+i-e+",
+ 41250, "d++bf+h+",
+ 41500, "b+j+c+d-",
+ 41750, "c+f+b+h-",
+ 42500, "c+h++g+",
+ 42750, "b+g+d-f-",
+ 43250, "b+l-e+d-",
+ 43750, "c+bd+h+f-",
+ 44000, "b+f+g-d-",
+ 44250, "b+d-g--f+",
+ 44500, "c+e+c+h+",
+ 44750, "b+e+d-h-",
+ 45250, "b++g+j-g+",
+ 45500, "c+d+e-g+",
+ 45750, "b+d-h-e-",
+ 46250, "c+bd++j+",
+ 46500, "b+d-c-j-",
+ 46750, "e-e-b+g-",
+ 47000, "b+c+d-j-",
+ 47250, "b+e+e-g-",
+ 47500, "b+g-c-h-",
+ 47750, "b+f-c+h-",
+ 48250, "d--h+n-",
+ 48500, "b+c-g+m-",
+ 48750, "b+e+e-g+",
+ 49500, "c-f+e+j-",
+ 49750, "c+c+g++f-",
+ 50000, "b+e+e+k+",
+ 50250, "b++i++g+",
+ 50500, "c+g+f-i+",
+ 50750, "b+e+d+k-",
+ 51500, "b+i+c-f+",
+ 51750, "b+bd+g-e-",
+ 52250, "b+d+g-j+",
+ 52500, "c+c+f+g+",
+ 52750, "b+c+e+i+",
+ 53000, "b+i+c+g+",
+ 53500, "c+g+g-n+",
+ 53750, "b+j+d-c+",
+ 54250, "b+d-g-j-",
+ 54500, "c-f+e+f+",
+ 54750, "b+f-+c+g+",
+ 55000, "b+g-d-g-",
+ 55250, "b+e+e+g+",
+ 55500, "b+cd++j+",
+ 55750, "b+bh-d-f-",
+ 56250, "c+d-b+j-",
+ 56500, "c+d+c+i+",
+ 56750, "b+e+d++h-",
+ 57000, "b+d+g-f+",
+ 57250, "b+f-m+d-",
+ 57750, "b+i+c+e-",
+ 58000, "b+e+d+h+",
+ 58250, "c+b+g+g+",
+ 58750, "d-e-j--e+",
+ 59000, "d-i-+e+",
+ 59250, "e--h-m+",
+ 59500, "c+c-h+f-",
+ 59750, "b+bh-e+i-",
+ 60250, "b+bh-e-e-",
+ 60500, "c+c-g-g-",
+ 60750, "b+e-l-e-",
+ 61250, "b+g-g-c+",
+ 61750, "b+g-c+g+",
+ 62250, "f--+c-i-",
+ 62750, "e+f--+g+",
+ 64750, "b+f+d+p-",
+};
+int hintabsize = nelem(hintab);
diff --git a/sys/src/cmd/qc/peep.c b/sys/src/cmd/qc/peep.c
new file mode 100755
index 000000000..6cd72dfcb
--- /dev/null
+++ b/sys/src/cmd/qc/peep.c
@@ -0,0 +1,966 @@
+#include "gc.h"
+
+void
+peep(void)
+{
+ Reg *r, *r1, *r2;
+ Prog *p, *p1;
+ int t;
+/*
+ * complete R structure
+ */
+ t = 0;
+ 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;
+ t++;
+
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ p = p->link;
+ }
+ }
+
+loop1:
+ t = 0;
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(p->as == AMOVW || p->as == AFMOVS || p->as == AFMOVD)
+ if(regtyp(&p->to)) {
+ if(regtyp(&p->from))
+ if(p->from.type == p->to.type) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ if(regzer(&p->from))
+ if(p->to.type == D_REG) {
+ p->from.type = D_REG;
+ p->from.reg = REGZERO;
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ }
+ }
+ if(t)
+ goto loop1;
+ /*
+ * look for MOVB x,R; MOVB R,R
+ */
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ default:
+ continue;
+ case AMOVH:
+ case AMOVHZ:
+ case AMOVB:
+ case AMOVBZ:
+ if(p->to.type != D_REG)
+ continue;
+ break;
+ }
+ r1 = r->link;
+ if(r1 == R)
+ continue;
+ p1 = r1->prog;
+ if(p1->as != p->as)
+ continue;
+ if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+ continue;
+ if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+ continue;
+ excise(r1);
+ }
+
+ if(debug['Q'] > 1)
+ return; /* allow following code improvement to be suppressed */
+
+ /*
+ * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R
+ * when OP can set condition codes correctly
+ */
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ case ACMP:
+ if(!regzer(&p->to))
+ continue;
+ r1 = r->s1;
+ if(r1 == R)
+ continue;
+ switch(r1->prog->as) {
+ default:
+ continue;
+ case ABCL:
+ case ABC:
+ /* the conditions can be complex and these are currently little used */
+ continue;
+ case ABEQ:
+ case ABGE:
+ case ABGT:
+ case ABLE:
+ case ABLT:
+ case ABNE:
+ case ABVC:
+ case ABVS:
+ 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 || p1->to.reg != p->from.reg)
+ continue;
+ switch(p1->as) {
+ case ASUB:
+ case AADD:
+ case AXOR:
+ case AOR:
+ /* irregular instructions */
+ if(p1->from.type == D_CONST)
+ continue;
+ break;
+ }
+ switch(p1->as) {
+ default:
+ continue;
+ case AMOVW:
+ if(p1->from.type != D_REG)
+ continue;
+ continue;
+ case AANDCC:
+ case AANDNCC:
+ case AORCC:
+ case AORNCC:
+ case AXORCC:
+ case ASUBCC:
+ case ASUBECC:
+ case ASUBMECC:
+ case ASUBZECC:
+ case AADDCC:
+ case AADDCCC:
+ case AADDECC:
+ case AADDMECC:
+ case AADDZECC:
+ case ARLWMICC:
+ case ARLWNMCC:
+ t = p1->as;
+ break;
+ /* don't deal with floating point instructions for now */
+/*
+ case AFABS: t = AFABSCC; break;
+ case AFADD: t = AFADDCC; break;
+ case AFADDS: t = AFADDSCC; break;
+ case AFCTIW: t = AFCTIWCC; break;
+ case AFCTIWZ: t = AFCTIWZCC; break;
+ case AFDIV: t = AFDIVCC; break;
+ case AFDIVS: t = AFDIVSCC; break;
+ case AFMADD: t = AFMADDCC; break;
+ case AFMADDS: t = AFMADDSCC; break;
+ case AFMOVD: t = AFMOVDCC; break;
+ case AFMSUB: t = AFMSUBCC; break;
+ case AFMSUBS: t = AFMSUBSCC; break;
+ case AFMUL: t = AFMULCC; break;
+ case AFMULS: t = AFMULSCC; break;
+ case AFNABS: t = AFNABSCC; break;
+ case AFNEG: t = AFNEGCC; break;
+ case AFNMADD: t = AFNMADDCC; break;
+ case AFNMADDS: t = AFNMADDSCC; break;
+ case AFNMSUB: t = AFNMSUBCC; break;
+ case AFNMSUBS: t = AFNMSUBSCC; break;
+ case AFRSP: t = AFRSPCC; break;
+ case AFSUB: t = AFSUBCC; break;
+ case AFSUBS: t = AFSUBSCC; break;
+ case ACNTLZW: t = ACNTLZWCC; break;
+ case AMTFSB0: t = AMTFSB0CC; break;
+ case AMTFSB1: t = AMTFSB1CC; break;
+*/
+ case AADD: t = AADDCC; break;
+ case AADDV: t = AADDVCC; break;
+ case AADDC: t = AADDCCC; break;
+ case AADDCV: t = AADDCVCC; break;
+ case AADDME: t = AADDMECC; break;
+ case AADDMEV: t = AADDMEVCC; break;
+ case AADDE: t = AADDECC; break;
+ case AADDEV: t = AADDEVCC; break;
+ case AADDZE: t = AADDZECC; break;
+ case AADDZEV: t = AADDZEVCC; break;
+ case AAND: t = AANDCC; break;
+ case AANDN: t = AANDNCC; break;
+ case ADIVW: t = ADIVWCC; break;
+ case ADIVWV: t = ADIVWVCC; break;
+ case ADIVWU: t = ADIVWUCC; break;
+ case ADIVWUV: t = ADIVWUVCC; break;
+ case AEQV: t = AEQVCC; break;
+ case AEXTSB: t = AEXTSBCC; break;
+ case AEXTSH: t = AEXTSHCC; break;
+ case AMULHW: t = AMULHWCC; break;
+ case AMULHWU: t = AMULHWUCC; break;
+ case AMULLW: t = AMULLWCC; break;
+ case AMULLWV: t = AMULLWVCC; break;
+ case ANAND: t = ANANDCC; break;
+ case ANEG: t = ANEGCC; break;
+ case ANEGV: t = ANEGVCC; break;
+ case ANOR: t = ANORCC; break;
+ case AOR: t = AORCC; break;
+ case AORN: t = AORNCC; break;
+ case AREM: t = AREMCC; break;
+ case AREMV: t = AREMVCC; break;
+ case AREMU: t = AREMUCC; break;
+ case AREMUV: t = AREMUVCC; break;
+ case ARLWMI: t = ARLWMICC; break;
+ case ARLWNM: t = ARLWNMCC; break;
+ case ASLW: t = ASLWCC; break;
+ case ASRAW: t = ASRAWCC; break;
+ case ASRW: t = ASRWCC; break;
+ case ASUB: t = ASUBCC; break;
+ case ASUBV: t = ASUBVCC; break;
+ case ASUBC: t = ASUBCCC; break;
+ case ASUBCV: t = ASUBCVCC; break;
+ case ASUBME: t = ASUBMECC; break;
+ case ASUBMEV: t = ASUBMEVCC; break;
+ case ASUBE: t = ASUBECC; break;
+ case ASUBEV: t = ASUBEVCC; break;
+ case ASUBZE: t = ASUBZECC; break;
+ case ASUBZEV: t = ASUBZEVCC; break;
+ case AXOR: t = AXORCC; break;
+ break;
+ }
+ if(debug['Q'])
+ print("cmp %P; %P -> ", p1, p);
+ p1->as = t;
+ if(debug['Q'])
+ print("%P\n", p1);
+ excise(r);
+ continue;
+ }
+ }
+}
+
+void
+excise(Reg *r)
+{
+ Prog *p;
+
+ p = r->prog;
+ p->as = ANOP;
+ p->from = zprog.from;
+ p->from3 = zprog.from3;
+ p->to = zprog.to;
+ p->reg = zprog.reg; /**/
+}
+
+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;
+}
+
+/*
+ * if the system forces R0 to be zero,
+ * convert references to $0 to references to R0.
+ */
+regzer(Adr *a)
+{
+ if(R0ISZERO) {
+ if(a->type == D_CONST)
+ if(a->sym == S)
+ if(a->offset == 0)
+ return 1;
+ if(a->type == D_REG)
+ if(a->reg == REGZERO)
+ return 1;
+ }
+ return 0;
+}
+
+regtyp(Adr *a)
+{
+
+ if(a->type == D_REG) {
+ if(!R0ISZERO || a->reg != REGZERO)
+ return 1;
+ return 0;
+ }
+ 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 AADD:
+ case AADDC:
+ case AADDCC:
+ case AADDE:
+ case AADDECC:
+ case ASUB:
+ case ASUBCC:
+ case ASUBC:
+ case ASUBCCC:
+ case ASUBE:
+ case ASUBECC:
+ case ASLW:
+ case ASLWCC:
+ case ASRW:
+ case ASRWCC:
+ case ASRAW:
+ case ASRAWCC:
+ case AOR:
+ case AORCC:
+ case AORN:
+ case AORNCC:
+ case AAND:
+ case AANDCC:
+ case AANDN:
+ case AANDNCC:
+ case ANAND:
+ case ANANDCC:
+ case ANOR:
+ case ANORCC:
+ case AXOR:
+ case AXORCC:
+ case AMULHW:
+ case AMULHWU:
+ case AMULLW:
+ case ADIVW:
+ case ADIVWU:
+ case AREM:
+ case AREMU:
+ case ARLWNM:
+ case ARLWNMCC:
+
+ case AFADD:
+ case AFADDS:
+ case AFSUB:
+ case AFSUBS:
+ case AFMUL:
+ case AFMULS:
+ case AFDIV:
+ case AFDIVS:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg) {
+ if(p->reg == NREG)
+ p->reg = p->to.reg;
+ goto gotit;
+ }
+ break;
+
+ case AADDME:
+ case AADDMECC:
+ case AADDZE:
+ case AADDZECC:
+ case ASUBME:
+ case ASUBMECC:
+ case ASUBZE:
+ case ASUBZECC:
+ case ANEG:
+ case ANEGCC:
+ case AFNEG:
+ case AFNEGCC:
+ case AFMOVS:
+ case AFMOVD:
+ case AMOVW:
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ goto gotit;
+ 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);
+}
+
+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;
+}
+
+/*
+ * 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:
+ if(debug['P'])
+ print(" (???)");
+ return 2;
+
+ case ANOP: /* read, write */
+ case AMOVW:
+ case AMOVH:
+ case AMOVHZ:
+ case AMOVB:
+ case AMOVBZ:
+
+ case ANEG:
+ case ANEGCC:
+ case AADDME:
+ case AADDMECC:
+ case AADDZE:
+ case AADDZECC:
+ case ASUBME:
+ case ASUBMECC:
+ case ASUBZE:
+ case ASUBZECC:
+
+ case AFCTIW:
+ case AFCTIWZ:
+ case AFMOVS:
+ case AFMOVD:
+ case AFRSP:
+ case AFNEG:
+ case AFNEGCC:
+ 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(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 ARLWMI: /* read read rar */
+ case ARLWMICC:
+ if(copyas(&p->to, v))
+ return 2;
+ /* fall through */
+
+ case AADD: /* read read write */
+ case AADDC:
+ case AADDE:
+ case ASUB:
+ case ASLW:
+ case ASRW:
+ case ASRAW:
+ case AOR:
+ case AORCC:
+ case AORN:
+ case AORNCC:
+ case AAND:
+ case AANDCC:
+ case AANDN:
+ case AANDNCC:
+ case ANAND:
+ case ANANDCC:
+ case ANOR:
+ case ANORCC:
+ case AXOR:
+ case AMULHW:
+ case AMULHWU:
+ case AMULLW:
+ case ADIVW:
+ case ADIVWU:
+ case AREM:
+ case AREMU:
+ case ARLWNM:
+ case ARLWNMCC:
+
+ case AFADDS:
+ case AFADD:
+ case AFSUBS:
+ case AFSUB:
+ case AFMULS:
+ case AFMUL:
+ case AFDIVS:
+ case AFDIV:
+ 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->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:
+ case ABGT:
+ case ABGE:
+ case ABLT:
+ case ABLE:
+ case ABNE:
+ case ABVC:
+ case ABVS:
+ break;
+
+ case ACMP: /* read read */
+ case ACMPU:
+ case AFCMPO:
+ case AFCMPU:
+ if(s != A) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub(&p->to, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ break;
+
+ case ABR: /* 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 ARETURN: /* 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 == 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 == REGARG)
+ return 3;
+ return 0;
+ }
+ return 0;
+}
+
+int
+a2type(Prog *p)
+{
+
+ switch(p->as) {
+ case AADD:
+ case AADDC:
+ case AADDCC:
+ case AADDCCC:
+ case AADDE:
+ case AADDECC:
+ case AADDME:
+ case AADDMECC:
+ case AADDZE:
+ case AADDZECC:
+ case ASUB:
+ case ASUBC:
+ case ASUBCC:
+ case ASUBCCC:
+ case ASUBE:
+ case ASUBECC:
+ case ASUBME:
+ case ASUBMECC:
+ case ASUBZE:
+ case ASUBZECC:
+ case ASLW:
+ case ASLWCC:
+ case ASRW:
+ case ASRWCC:
+ case ASRAW:
+ case ASRAWCC:
+ case AOR:
+ case AORCC:
+ case AORN:
+ case AORNCC:
+ case AAND:
+ case AANDCC:
+ case AANDN:
+ case AANDNCC:
+ case AXOR:
+ case AXORCC:
+ case ANEG:
+ case ANEGCC:
+ case AMULHW:
+ case AMULHWU:
+ case AMULLW:
+ case AMULLWCC:
+ case ADIVW:
+ case ADIVWCC:
+ case ADIVWU:
+ case ADIVWUCC:
+ case AREM:
+ case AREMCC:
+ case AREMU:
+ case AREMUCC:
+ case ANAND:
+ case ANANDCC:
+ case ANOR:
+ case ANORCC:
+ case ARLWMI:
+ case ARLWMICC:
+ case ARLWNM:
+ case ARLWNMCC:
+ return D_REG;
+
+ case AFADDS:
+ case AFADDSCC:
+ case AFADD:
+ case AFADDCC:
+ case AFSUBS:
+ case AFSUBSCC:
+ case AFSUB:
+ case AFSUBCC:
+ case AFMULS:
+ case AFMULSCC:
+ case AFMUL:
+ case AFMULCC:
+ case AFDIVS:
+ case AFDIVSCC:
+ case AFDIV:
+ case AFDIVCC:
+ case AFNEG:
+ case AFNEGCC:
+ return D_FREG;
+ }
+ return D_NONE;
+}
+
+/*
+ * 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;
+ 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_OREG)
+ if(v->reg == a->reg)
+ return 1;
+ return 0;
+}
+
+int
+copyau1(Prog *p, Adr *v)
+{
+
+ if(regtyp(v))
+ if(p->from.type == v->type || p->to.type == v->type)
+ if(p->reg == v->reg) {
+ if(a2type(p) != v->type)
+ print("botch a2type %P\n", p);
+ return 1;
+ }
+ 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))
+ 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;
+}
diff --git a/sys/src/cmd/qc/q.out.h b/sys/src/cmd/qc/q.out.h
new file mode 100755
index 000000000..fb67b34b9
--- /dev/null
+++ b/sys/src/cmd/qc/q.out.h
@@ -0,0 +1,488 @@
+#define NSNAME 8
+#define NSYM 50
+#define NREG 32
+
+#define NOPROF (1<<0)
+#define DUPOK (1<<1)
+
+enum
+{
+ REGZERO = 0, /* set to zero */
+ REGSP = 1,
+ REGSB = 2,
+ REGRET = 3,
+ REGARG = 3,
+ REGMIN = 7, /* register variables allocated from here to REGMAX */
+ REGMAX = 27,
+ REGEXT = 30, /* external registers allocated from here down */
+ REGTMP = 31, /* used by the linker */
+
+ FREGRET = 0,
+ FREGMIN = 17, /* first register variable */
+ FREGEXT = 26, /* first external register */
+ FREGCVI = 27, /* floating conversion constant */
+ FREGZERO = 28, /* both float and double */
+ FREGHALF = 29, /* double */
+ FREGONE = 30, /* double */
+ FREGTWO = 31 /* double */
+/*
+ * GENERAL:
+ *
+ * compiler allocates R3 up as temps
+ * compiler allocates register variables R7-R27
+ * compiler allocates external registers R30 down
+ *
+ * compiler allocates register variables F17-F26
+ * compiler allocates external registers F26 down
+ */
+};
+
+enum as
+{
+ AXXX = 0,
+ AADD,
+ AADDCC,
+ AADDV,
+ AADDVCC,
+ AADDC,
+ AADDCCC,
+ AADDCV,
+ AADDCVCC,
+ AADDME,
+ AADDMECC,
+ AADDMEVCC,
+ AADDMEV,
+ AADDE,
+ AADDECC,
+ AADDEVCC,
+ AADDEV,
+ AADDZE,
+ AADDZECC,
+ AADDZEVCC,
+ AADDZEV,
+ AAND,
+ AANDCC,
+ AANDN,
+ AANDNCC,
+ ABC,
+ ABCL,
+ ABEQ,
+ ABGE,
+ ABGT,
+ ABL,
+ ABLE,
+ ABLT,
+ ABNE,
+ ABR,
+ ABVC,
+ ABVS,
+ ACMP,
+ ACMPU,
+ ACNTLZW,
+ ACNTLZWCC,
+ ACRAND,
+ ACRANDN,
+ ACREQV,
+ ACRNAND,
+ ACRNOR,
+ ACROR,
+ ACRORN,
+ ACRXOR,
+ ADIVW,
+ ADIVWCC,
+ ADIVWVCC,
+ ADIVWV,
+ ADIVWU,
+ ADIVWUCC,
+ ADIVWUVCC,
+ ADIVWUV,
+ AEQV,
+ AEQVCC,
+ AEXTSB,
+ AEXTSBCC,
+ AEXTSH,
+ AEXTSHCC,
+ AFABS,
+ AFABSCC,
+ AFADD,
+ AFADDCC,
+ AFADDS,
+ AFADDSCC,
+ AFCMPO,
+ AFCMPU,
+ AFCTIW,
+ AFCTIWCC,
+ AFCTIWZ,
+ AFCTIWZCC,
+ AFDIV,
+ AFDIVCC,
+ AFDIVS,
+ AFDIVSCC,
+ AFMADD,
+ AFMADDCC,
+ AFMADDS,
+ AFMADDSCC,
+ AFMOVD,
+ AFMOVDCC,
+ AFMOVDU,
+ AFMOVS,
+ AFMOVSU,
+ AFMSUB,
+ AFMSUBCC,
+ AFMSUBS,
+ AFMSUBSCC,
+ AFMUL,
+ AFMULCC,
+ AFMULS,
+ AFMULSCC,
+ AFNABS,
+ AFNABSCC,
+ AFNEG,
+ AFNEGCC,
+ AFNMADD,
+ AFNMADDCC,
+ AFNMADDS,
+ AFNMADDSCC,
+ AFNMSUB,
+ AFNMSUBCC,
+ AFNMSUBS,
+ AFNMSUBSCC,
+ AFRSP,
+ AFRSPCC,
+ AFSUB,
+ AFSUBCC,
+ AFSUBS,
+ AFSUBSCC,
+ AMOVMW,
+ ALSW,
+ ALWAR,
+ AMOVWBR,
+ AMOVB,
+ AMOVBU,
+ AMOVBZ,
+ AMOVBZU,
+ AMOVH,
+ AMOVHBR,
+ AMOVHU,
+ AMOVHZ,
+ AMOVHZU,
+ AMOVW,
+ AMOVWU,
+ AMOVFL,
+ AMOVCRFS,
+ AMTFSB0,
+ AMTFSB0CC,
+ AMTFSB1,
+ AMTFSB1CC,
+ AMULHW,
+ AMULHWCC,
+ AMULHWU,
+ AMULHWUCC,
+ AMULLW,
+ AMULLWCC,
+ AMULLWVCC,
+ AMULLWV,
+ ANAND,
+ ANANDCC,
+ ANEG,
+ ANEGCC,
+ ANEGVCC,
+ ANEGV,
+ ANOR,
+ ANORCC,
+ AOR,
+ AORCC,
+ AORN,
+ AORNCC,
+ AREM,
+ AREMCC,
+ AREMV,
+ AREMVCC,
+ AREMU,
+ AREMUCC,
+ AREMUV,
+ AREMUVCC,
+ ARFI,
+ ARLWMI,
+ ARLWMICC,
+ ARLWNM,
+ ARLWNMCC,
+ ASLW,
+ ASLWCC,
+ ASRW,
+ ASRAW,
+ ASRAWCC,
+ ASRWCC,
+ ASTSW,
+ ASTWCCC,
+ ASUB,
+ ASUBCC,
+ ASUBVCC,
+ ASUBC,
+ ASUBCCC,
+ ASUBCV,
+ ASUBCVCC,
+ ASUBME,
+ ASUBMECC,
+ ASUBMEVCC,
+ ASUBMEV,
+ ASUBV,
+ ASUBE,
+ ASUBECC,
+ ASUBEV,
+ ASUBEVCC,
+ ASUBZE,
+ ASUBZECC,
+ ASUBZEVCC,
+ ASUBZEV,
+ ASYNC,
+ AXOR,
+ AXORCC,
+
+ ADCBF,
+ ADCBI,
+ ADCBST,
+ ADCBT,
+ ADCBTST,
+ ADCBZ,
+ AECIWX,
+ AECOWX,
+ AEIEIO,
+ AICBI,
+ AISYNC,
+ ATLBIE,
+ ATW,
+
+ ASYSCALL,
+ ADATA,
+ AGLOBL,
+ AGOK,
+ AHISTORY,
+ ANAME,
+ ANOP,
+ ARETURN,
+ ATEXT,
+ AWORD,
+ AEND,
+ ADYNT,
+ AINIT,
+ ASIGNAME,
+
+ /* IBM powerpc embedded; not portable */
+ AMACCHW,
+ AMACCHWCC,
+ AMACCHWS,
+ AMACCHWSCC,
+ AMACCHWSU,
+ AMACCHWSUCC,
+ AMACCHWSUV,
+ AMACCHWSUVCC,
+ AMACCHWSV,
+ AMACCHWSVCC,
+ AMACCHWU,
+ AMACCHWUCC,
+ AMACCHWUV,
+ AMACCHWUVCC,
+ AMACCHWV,
+ AMACCHWVCC,
+ AMACHHW,
+ AMACHHWCC,
+ AMACHHWV,
+ AMACHHWVCC,
+ AMACHHWS,
+ AMACHHWSCC,
+ AMACHHWSV,
+ AMACHHWSVCC,
+ AMACHHWSU,
+ AMACHHWSUCC,
+ AMACHHWSUV,
+ AMACHHWSUVCC,
+ AMACHHWU,
+ AMACHHWUCC,
+ AMACHHWUV,
+ AMACHHWUVCC,
+ AMACLHW,
+ AMACLHWCC,
+ AMACLHWS,
+ AMACLHWSCC,
+ AMACLHWSU,
+ AMACLHWSUCC,
+ AMACLHWSUV,
+ AMACLHWSUVCC,
+ AMACLHWSV,
+ AMACLHWSVCC,
+ AMACLHWU,
+ AMACLHWUCC,
+ AMACLHWUV,
+ AMACLHWUVCC,
+ AMACLHWV,
+ AMACLHWVCC,
+ AMULCHW,
+ AMULCHWCC,
+ AMULCHWU,
+ AMULCHWUCC,
+ AMULHHW,
+ AMULHHWCC,
+ AMULHHWU,
+ AMULHHWUCC,
+ AMULLHW,
+ AMULLHWCC,
+ AMULLHWU,
+ AMULLHWUCC,
+ ANMACCHW,
+ ANMACCHWCC,
+ ANMACCHWS,
+ ANMACCHWSCC,
+ ANMACCHWSV,
+ ANMACCHWSVCC,
+ ANMACCHWV,
+ ANMACCHWVCC,
+ ANMACHHW,
+ ANMACHHWCC,
+ ANMACHHWS,
+ ANMACHHWSCC,
+ ANMACHHWSV,
+ ANMACHHWSVCC,
+ ANMACHHWV,
+ ANMACHHWVCC,
+ ANMACLHW,
+ ANMACLHWCC,
+ ANMACLHWS,
+ ANMACLHWSCC,
+ ANMACLHWSV,
+ ANMACLHWSVCC,
+ ANMACLHWV,
+ ANMACLHWVCC,
+
+ ARFCI,
+
+ /* optional on 32-bit */
+ AFRES,
+ AFRESCC,
+ AFRSQRTE,
+ AFRSQRTECC,
+ AFSEL,
+ AFSELCC,
+ AFSQRT,
+ AFSQRTCC,
+ AFSQRTS,
+ AFSQRTSCC,
+
+ /* parallel, cross, and secondary */
+ AFPSEL,
+ AFPMUL,
+ AFXMUL,
+ AFXPMUL,
+ AFXSMUL,
+ AFPADD,
+ AFPSUB,
+ AFPRE,
+ AFPRSQRTE,
+ AFPMADD,
+ AFXMADD,
+ AFXCPMADD,
+ AFXCSMADD,
+ AFPNMADD,
+ AFXNMADD,
+ AFXCPNMADD,
+ AFXCSNMADD,
+ AFPMSUB,
+ AFXMSUB,
+ AFXCPMSUB,
+ AFXCSMSUB,
+ AFPNMSUB,
+ AFXNMSUB,
+ AFXCPNMSUB,
+ AFXCSNMSUB,
+ AFPABS,
+ AFPNEG,
+ AFPRSP,
+ AFPNABS,
+ AFSCMP,
+ AFSABS,
+ AFSNEG,
+ AFSNABS,
+ AFPCTIW,
+ AFPCTIWZ,
+ AFMOVSPD,
+ AFMOVPSD,
+ AFXCPNPMA,
+ AFXCSNPMA,
+ AFXCPNSMA,
+ AFXCSNSMA,
+ AFXCXNPMA,
+ AFXCXNSMA,
+ AFXCXMA,
+ AFXCXNMS,
+
+ /* parallel, cross, and secondary load and store */
+ AFSMOVS,
+ AFSMOVSU,
+ AFSMOVD,
+ AFSMOVDU,
+ AFXMOVS,
+ AFXMOVSU,
+ AFXMOVD,
+ AFXMOVDU,
+ AFPMOVS,
+ AFPMOVSU,
+ AFPMOVD,
+ AFPMOVDU,
+ AFPMOVIW,
+
+ ALAST
+};
+
+/* type/name */
+enum
+{
+ D_GOK = 0,
+ D_NONE,
+
+/* name */
+ D_EXTERN,
+ D_STATIC,
+ D_AUTO,
+ D_PARAM,
+
+/* type */
+ D_BRANCH,
+ D_OREG,
+ D_CONST,
+ D_FCONST,
+ D_SCONST,
+ D_REG,
+ D_FPSCR,
+ D_MSR,
+ D_FREG,
+ D_CREG,
+ D_SPR,
+ D_SREG, /* segment register */
+ D_OPT, /* branch/trap option */
+ D_FILE,
+ D_FILE1,
+ D_DCR, /* device control register */
+
+/* reg names iff type is D_SPR */
+ D_XER = 1,
+ D_LR = 8,
+ D_CTR = 9
+ /* and many supervisor level registers */
+};
+
+/*
+ * this is the ranlib header
+ */
+#define SYMDEF "__.SYMDEF"
+
+/*
+ * this is the simulated IEEE floating point
+ */
+typedef struct ieee Ieee;
+struct ieee
+{
+ long l; /* contains ls-man 0xffffffff */
+ long h; /* contains sign 0x80000000
+ exp 0x7ff00000
+ ms-man 0x000fffff */
+};
diff --git a/sys/src/cmd/qc/reg.c b/sys/src/cmd/qc/reg.c
new file mode 100755
index 000000000..33152f27e
--- /dev/null
+++ b/sys/src/cmd/qc/reg.c
@@ -0,0 +1,1121 @@
+#include "gc.h"
+
+Reg*
+rega(void)
+{
+ Reg *r;
+
+ r = freer;
+ if(r == R) {
+ r = alloc(sizeof(*r));
+ } else
+ freer = r->link;
+
+ *r = zreg;
+ return r;
+}
+
+int
+rcmp(void *a1, void *a2)
+{
+ Rgn *p1, *p2;
+ int c1, c2;
+
+ p1 = a1;
+ p2 = a2;
+ c1 = p2->cost;
+ c2 = p1->cost;
+ if(c1 -= c2)
+ return c1;
+ return p2->varno - p1->varno;
+}
+
+void
+regopt(Prog *p)
+{
+ Reg *r, *r1, *r2;
+ Prog *p1;
+ int i, z;
+ long initpc, val, npc;
+ ulong vreg;
+ Bits bit;
+ struct
+ {
+ long m;
+ long c;
+ Reg* p;
+ } log5[6], *lp;
+
+ firstr = R;
+ lastr = R;
+ nvar = 0;
+ regbits = 0;
+ for(z=0; z<BITS; z++) {
+ externs.b[z] = 0;
+ params.b[z] = 0;
+ consts.b[z] = 0;
+ addrs.b[z] = 0;
+ }
+
+ /*
+ * pass 1
+ * build aux data structure
+ * allocate pcs
+ * find use and set of variables
+ */
+ val = 5L * 5L * 5L * 5L * 5L;
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->m = val;
+ lp->c = 0;
+ lp->p = R;
+ val /= 5L;
+ lp++;
+ }
+ val = 0;
+ for(; p != P; p = p->link) {
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ continue;
+ }
+ r = rega();
+ if(firstr == R) {
+ firstr = r;
+ lastr = r;
+ } else {
+ lastr->link = r;
+ r->p1 = lastr;
+ lastr->s1 = r;
+ lastr = r;
+ }
+ r->prog = p;
+ r->pc = val;
+ val++;
+
+ lp = log5;
+ for(i=0; i<5; i++) {
+ lp->c--;
+ if(lp->c <= 0) {
+ lp->c = lp->m;
+ if(lp->p != R)
+ lp->p->log5 = r;
+ lp->p = r;
+ (lp+1)->c = 0;
+ break;
+ }
+ lp++;
+ }
+
+ r1 = r->p1;
+ if(r1 != R)
+ switch(r1->prog->as) {
+ case ARETURN:
+ case ABR:
+ case ARFI:
+ r->p1 = R;
+ r1->s1 = R;
+ }
+
+ /*
+ * left side always read
+ */
+ bit = mkvar(&p->from, p->as==AMOVW);
+ for(z=0; z<BITS; z++)
+ r->use1.b[z] |= bit.b[z];
+
+ /*
+ * right side depends on opcode
+ */
+ bit = mkvar(&p->to, 0);
+ if(bany(&bit))
+ switch(p->as) {
+ default:
+ diag(Z, "reg: unknown asop: %A", p->as);
+ break;
+
+ /*
+ * right side write
+ */
+ case ANOP:
+ case AMOVB:
+ case AMOVBU:
+ case AMOVBZ:
+ case AMOVBZU:
+ case AMOVH:
+ case AMOVHBR:
+ case AMOVHU:
+ case AMOVHZ:
+ case AMOVHZU:
+ case AMOVW:
+ case AMOVWU:
+ case AFMOVD:
+ case AFMOVDCC:
+ case AFMOVDU:
+ case AFMOVS:
+ case AFMOVSU:
+ case AFRSP:
+ for(z=0; z<BITS; z++)
+ r->set.b[z] |= bit.b[z];
+ break;
+
+ /*
+ * funny
+ */
+ case ABL:
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ break;
+ }
+ }
+ if(firstr == R)
+ return;
+ initpc = pc - val;
+ npc = val;
+
+ /*
+ * 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) {
+ val = p->to.offset - initpc;
+ r1 = firstr;
+ while(r1 != R) {
+ r2 = r1->log5;
+ if(r2 != R && val >= r2->pc) {
+ r1 = r2;
+ continue;
+ }
+ if(r1->pc == val)
+ break;
+ r1 = r1->link;
+ }
+ if(r1 == R) {
+ nearln = p->lineno;
+ diag(Z, "ref not found\n%P", p);
+ continue;
+ }
+ if(r1 == r) {
+ nearln = p->lineno;
+ diag(Z, "ref to self\n%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);
+ }
+
+ /*
+ * pass 2.5
+ * find looping structure
+ */
+ for(r = firstr; r != R; r = r->link)
+ r->active = 0;
+ change = 0;
+ loopit(firstr, npc);
+ if(debug['R'] && debug['v']) {
+ print("\nlooping structure:\n");
+ for(r = firstr; r != R; r = r->link) {
+ print("%ld:%P", r->loop, r->prog);
+ for(z=0; z<BITS; z++)
+ bit.b[z] = r->use1.b[z] |
+ r->use2.b[z] | r->set.b[z];
+ if(bany(&bit)) {
+ print("\t");
+ if(bany(&r->use1))
+ print(" u1=%B", r->use1);
+ if(bany(&r->use2))
+ print(" u2=%B", r->use2);
+ if(bany(&r->set))
+ print(" st=%B", r->set);
+ }
+ print("\n");
+ }
+ }
+
+ /*
+ * 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 == ARETURN)
+ 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;
+
+
+ /*
+ * 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)) {
+ nearln = r->prog->lineno;
+ warn(Z, "used and not set: %B", bit);
+ if(debug['R'] && !debug['w'])
+ print("used and not set: %B\n", bit);
+ }
+ }
+ if(debug['R'] && debug['v'])
+ print("\nprop structure:\n");
+ for(r = firstr; r != R; r = r->link)
+ r->act = zbits;
+ rgp = region;
+ nregion = 0;
+ for(r = firstr; r != R; r = r->link) {
+ if(debug['R'] && debug['v'])
+ print("%P\n set = %B; rah = %B; cal = %B\n",
+ r->prog, r->set, r->refahead, r->calahead);
+ 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)) {
+ nearln = r->prog->lineno;
+ warn(Z, "set and not used: %B", bit);
+ if(debug['R'])
+ print("set an not used: %B\n", bit);
+ 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'] && debug['v'])
+ print("\n");
+ paint1(r, i);
+ bit.b[i/32] &= ~(1L<<(i%32));
+ if(change <= 0) {
+ if(debug['R'])
+ print("%L$%d: %B\n",
+ r->prog->lineno, change, blsh(i));
+ continue;
+ }
+ rgp->cost = change;
+ nregion++;
+ if(nregion >= NRGN) {
+ warn(Z, "too many regions");
+ 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: %B\n",
+ rgp->enter->prog->lineno,
+ rgp->cost,
+ rgp->regno-NREG,
+ bit);
+ else
+ print("%L$%d R%d: %B\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();
+
+ /*
+ * pass 8
+ * recalculate pc
+ */
+ val = initpc;
+ for(r = firstr; r != R; r = r1) {
+ r->pc = val;
+ p = r->prog;
+ p1 = P;
+ r1 = r->link;
+ if(r1 != R)
+ p1 = r1->prog;
+ for(; p != p1; p = p->link) {
+ switch(p->as) {
+ default:
+ val++;
+ break;
+
+ case ANOP:
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ break;
+ }
+ }
+ }
+ pc = val;
+
+ /*
+ * fix up branches
+ */
+ if(debug['R'])
+ if(bany(&addrs))
+ print("addrs: %B\n", addrs);
+
+ r1 = 0; /* set */
+ for(r = firstr; r != R; r = r->link) {
+ p = r->prog;
+ if(p->to.type == D_BRANCH)
+ p->to.offset = r->s2->pc;
+ r1 = r;
+ }
+
+ /*
+ * last pass
+ * eliminate nops
+ * free aux structures
+ */
+ for(p = firstr->prog; p != P; p = p->link){
+ while(p->link && p->link->as == ANOP)
+ p->link = p->link->link;
+ }
+ if(r1 != R) {
+ r1->link = freer;
+ freer = firstr;
+ }
+}
+
+/*
+ * add mov b,rn
+ * just after r
+ */
+void
+addmove(Reg *r, int bn, int rn, int f)
+{
+ Prog *p, *p1;
+ Adr *a;
+ Var *v;
+
+ p1 = alloc(sizeof(*p1));
+ *p1 = zprog;
+ p = r->prog;
+
+ 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->offset = v->offset;
+ a->etype = v->etype;
+ a->type = D_OREG;
+ if(a->etype == TARRAY || a->sym == S)
+ a->type = D_CONST;
+
+ p1->as = AMOVW;
+ if(v->etype == TCHAR || v->etype == TUCHAR)
+ p1->as = AMOVB;
+ if(v->etype == TSHORT || v->etype == TUSHORT)
+ p1->as = AMOVH;
+ if(v->etype == TFLOAT)
+ p1->as = AFMOVS;
+ if(v->etype == TDOUBLE)
+ p1->as = AFMOVD;
+
+ 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 == TUCHAR)
+ p1->as = AMOVBZ;
+ if(v->etype == TUSHORT)
+ p1->as = AMOVHZ;
+ }
+ if(debug['R'])
+ print("%P\t.a%P\n", p, p1);
+}
+
+Bits
+mkvar(Adr *a, int docon)
+{
+ Var *v;
+ int i, t, n, et, z;
+ long o;
+ Bits bit;
+ Sym *s;
+
+ t = a->type;
+ if(t == D_REG && a->reg != NREG)
+ regbits |= RtoB(a->reg);
+ if(t == D_FREG && a->reg != NREG)
+ regbits |= FtoB(a->reg);
+ s = a->sym;
+ o = a->offset;
+ et = a->etype;
+ if(s == S) {
+ if(t != D_CONST || !docon || a->reg != NREG)
+ goto none;
+ et = TLONG;
+ }
+ if(t == D_CONST) {
+ if(s == S && sval(o))
+ goto none;
+ }
+ n = a->name;
+ v = var;
+ for(i=0; i<nvar; i++) {
+ if(s == v->sym)
+ if(n == v->name)
+ if(o == v->offset)
+ goto out;
+ v++;
+ }
+ if(s)
+ if(s->name[0] == '.')
+ goto none;
+ if(nvar >= NVAR) {
+ if(debug['w'] > 1 && s)
+ warn(Z, "variable not optimized: %s", s->name);
+ goto none;
+ }
+ i = nvar;
+ nvar++;
+ v = &var[i];
+ v->sym = s;
+ v->offset = o;
+ v->etype = et;
+ v->name = n;
+ if(debug['R'])
+ print("bit=%2d et=%2d %D\n", i, et, a);
+out:
+ 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];
+ if(v->etype != et || !typechlpfd[et]) /* funny punning */
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ if(t == D_CONST) {
+ if(s == S) {
+ for(z=0; z<BITS; z++)
+ consts.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(et != TARRAY)
+ for(z=0; z<BITS; z++)
+ addrs.b[z] |= bit.b[z];
+ for(z=0; z<BITS; z++)
+ params.b[z] |= bit.b[z];
+ return bit;
+ }
+ if(t == D_OREG)
+ 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:
+ 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 ARETURN:
+ for(z=0; z<BITS; z++) {
+ cal.b[z] = externs.b[z];
+ ref.b[z] = 0;
+ }
+ }
+ 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
+ */
+long
+postorder(Reg *r, Reg **rpo2r, long 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;
+}
+
+long
+rpolca(long *idom, long rpo1, long rpo2)
+{
+ long 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(Z, "bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+int
+doms(long *idom, long r, long s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+int
+loophead(long *idom, Reg *r)
+{
+ long 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, long 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, long nr)
+{
+ Reg *r1;
+ long i, d, me;
+
+ if(nr > maxnr) {
+ rpo2r = alloc(nr * sizeof(Reg*));
+ idom = alloc(nr * sizeof(long));
+ maxnr = nr;
+ }
+
+ d = postorder(r, rpo2r, 0);
+ if(d > nr)
+ fatal(Z, "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);
+ }
+}
+
+ulong
+allreg(ulong b, Rgn *r)
+{
+ Var *v;
+ int i;
+
+ v = var + r->varno;
+ r->regno = 0;
+ switch(v->etype) {
+
+ default:
+ diag(Z, "unknown etype %d/%d", bitno(b), v->etype);
+ break;
+
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TARRAY:
+ i = BtoR(~b);
+ if(i && r->cost > 0) {
+ r->regno = i;
+ return RtoB(i);
+ }
+ break;
+
+ case TDOUBLE:
+ case TFLOAT:
+ i = BtoF(~b);
+ if(i && r->cost > 0) {
+ r->regno = i+NREG;
+ return FtoB(i);
+ }
+ break;
+ }
+ return 0;
+}
+
+void
+paint1(Reg *r, int bn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong 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'] && debug['v'])
+ print("%ld%P\tld %B $%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(p->to.type == D_FREG && p->as == AMOVW)
+ change = -CINF; /* cant go Rreg to Freg */
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu1 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if((r->use2.b[z]|r->set.b[z]) & bb) {
+ change += CREF * r->loop;
+ if(p->from.type == D_FREG && p->as == AMOVW)
+ change = -CINF; /* cant go Rreg to Freg */
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tu2 %B $%d\n", r->loop,
+ p, blsh(bn), change);
+ }
+
+ if(STORE(r) & r->regdiff.b[z] & bb) {
+ change -= CLOAD * r->loop;
+ if(debug['R'] && debug['v'])
+ print("%ld%P\tst %B $%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;
+ }
+}
+
+ulong
+paint2(Reg *r, int bn)
+{
+ Reg *r1;
+ int z;
+ ulong 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, long rb, int rn)
+{
+ Reg *r1;
+ Prog *p;
+ int z;
+ ulong 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;
+ }
+}
+
+/*
+ * track register variables including external registers:
+ * bit reg
+ * 0 R7
+ * 1 R8
+ * ... ...
+ * 21 R28
+ */
+long
+RtoB(int r)
+{
+
+ if(r >= REGMIN && r <= REGMAX)
+ return 1L << (r-REGMIN);
+ return 0;
+}
+
+int
+BtoR(long b)
+{
+ b &= 0x001fffffL;
+ if(b == 0)
+ return 0;
+ return bitno(b) + REGMIN;
+}
+
+/*
+ * bit reg
+ * 22 F17
+ * 23 F18
+ * ... ...
+ * 31 F26
+ */
+long
+FtoB(int f)
+{
+ if(f < FREGMIN || f > FREGEXT)
+ return 0;
+ return 1L << (f - FREGMIN + 22);
+}
+
+int
+BtoF(long b)
+{
+
+ b &= 0xffc00000L;
+ if(b == 0)
+ return 0;
+ return bitno(b) - 22 + FREGMIN;
+}
diff --git a/sys/src/cmd/qc/sgen.c b/sys/src/cmd/qc/sgen.c
new file mode 100755
index 000000000..37542ebbd
--- /dev/null
+++ b/sys/src/cmd/qc/sgen.c
@@ -0,0 +1,257 @@
+#include "gc.h"
+
+void
+noretval(int n)
+{
+
+ if(n & 1) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_REG;
+ p->to.reg = REGRET;
+ }
+ if(n & 2) {
+ gins(ANOP, Z, Z);
+ p->to.type = D_FREG;
+ p->to.reg = FREGRET;
+ }
+}
+
+/*
+ * calculate addressability as follows
+ * CONST ==> 20 $value
+ * NAME ==> 10 name
+ * REGISTER ==> 11 register
+ * INDREG ==> 12 *[(reg)+offset]
+ * &10 ==> 2 $name
+ * ADD(2, 20) ==> 2 $name+offset
+ * ADD(3, 20) ==> 3 $(reg)+offset
+ * &12 ==> 3 $(reg)+offset
+ * *11 ==> 11 ??
+ * *2 ==> 10 name
+ * *3 ==> 12 *(reg)+offset
+ * calculate complexity (number of registers)
+ */
+void
+xcom(Node *n)
+{
+ Node *l, *r;
+ int v, nr;
+
+ if(n == Z)
+ return;
+ l = n->left;
+ r = n->right;
+ n->addable = 0;
+ n->complex = 0;
+ switch(n->op) {
+ case OCONST:
+ n->addable = 20;
+ return;
+
+ case OREGISTER:
+ n->addable = 11;
+ return;
+
+ case OINDREG:
+ n->addable = 12;
+ return;
+
+ case ONAME:
+ n->addable = 10;
+ return;
+
+ case OADDR:
+ xcom(l);
+ if(l->addable == 10)
+ n->addable = 2;
+ if(l->addable == 12)
+ n->addable = 3;
+ break;
+
+ case OIND:
+ xcom(l);
+ if(l->addable == 11)
+ n->addable = 12;
+ if(l->addable == 3)
+ n->addable = 12;
+ if(l->addable == 2)
+ n->addable = 10;
+ break;
+
+ case OADD:
+ xcom(l);
+ xcom(r);
+ if(l->addable == 20) {
+ if(r->addable == 2)
+ n->addable = 2;
+ if(r->addable == 3)
+ n->addable = 3;
+ }
+ if(r->addable == 20) {
+ if(l->addable == 2)
+ n->addable = 2;
+ if(l->addable == 3)
+ n->addable = 3;
+ }
+ break;
+
+ case OASMUL:
+ case OASLMUL:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OASASHL;
+ r->vconst = v;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OMUL:
+ case OLMUL:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OASHL;
+ r->vconst = v;
+ r->type = types[TINT];
+ }
+ v = vlog(l);
+ if(v >= 0) {
+ n->op = OASHL;
+ n->left = r;
+ n->right = l;
+ r = l;
+ l = n->left;
+ r->vconst = v;
+ r->type = types[TINT];
+ simplifyshift(n);
+ }
+ break;
+
+ case OASLDIV:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OASLSHR;
+ r->vconst = v;
+ r->type = types[TINT];
+ }
+ break;
+
+ case OLDIV:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OLSHR;
+ r->vconst = v;
+ r->type = types[TINT];
+ simplifyshift(n);
+ }
+ break;
+
+ case OASLMOD:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OASAND;
+ r->vconst--;
+ }
+ break;
+
+ case OLMOD:
+ xcom(l);
+ xcom(r);
+ v = vlog(r);
+ if(v >= 0) {
+ n->op = OAND;
+ r->vconst--;
+ }
+ break;
+
+ case OLSHR:
+ case OASHL:
+ case OASHR:
+ xcom(l);
+ xcom(r);
+ simplifyshift(n);
+ break;
+
+ default:
+ if(l != Z)
+ xcom(l);
+ if(r != Z)
+ xcom(r);
+ break;
+ }
+ if(n->addable >= 10)
+ return;
+ if(l != Z)
+ n->complex = l->complex;
+ if(r != Z) {
+ nr = 1;
+ if(r->type != T && typev[r->type->etype] || n->type != T && typev[n->type->etype]) {
+ nr = 2;
+ if(n->op == OMUL || n->op == OLMUL)
+ nr += 3;
+ }
+ if(r->complex == n->complex)
+ n->complex = r->complex+nr;
+ else
+ if(r->complex > n->complex)
+ n->complex = r->complex;
+ }
+ if(n->complex == 0){
+ n->complex++;
+ if(n->type != T && typev[n->type->etype])
+ n->complex++;
+ }
+
+ if(com64(n))
+ return;
+
+ switch(n->op) {
+
+ case OFUNC:
+ n->complex = FNX;
+ break;
+
+ case OEQ:
+ case ONE:
+ case OLE:
+ case OLT:
+ case OGE:
+ case OGT:
+ case OHI:
+ case OHS:
+ case OLO:
+ case OLS:
+ /*
+ * immediate operators, make const on right
+ */
+ if(l->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ n->op = invrel[relindex(n->op)];
+ }
+ break;
+
+ case OADD:
+ case OXOR:
+ case OAND:
+ case OOR:
+ /*
+ * immediate operators, make const on right
+ */
+ if(l->op == OCONST) {
+ n->left = r;
+ n->right = l;
+ }
+ break;
+ }
+}
+
diff --git a/sys/src/cmd/qc/swt.c b/sys/src/cmd/qc/swt.c
new file mode 100755
index 000000000..f0edacc87
--- /dev/null
+++ b/sys/src/cmd/qc/swt.c
@@ -0,0 +1,664 @@
+#include "gc.h"
+
+static int doubleflag;
+
+void
+swit1(C1 *q, int nc, long def, Node *n)
+{
+ Node tn;
+
+ regalloc(&tn, &regnode, Z);
+ swit2(q, nc, def, n, &tn);
+ regfree(&tn);
+}
+
+void
+swit2(C1 *q, int nc, long def, Node *n, Node *tn)
+{
+ C1 *r;
+ int i;
+ Prog *sp;
+
+ if(nc < 5) {
+ for(i=0; i<nc; i++) {
+ if(sval(q->val)) {
+ gopcode(OEQ, n, Z, nodconst(q->val));
+ } else {
+ gopcode(OSUB, nodconst(q->val), n, tn);
+ gopcode(OEQ, tn, Z, nodconst(0));
+ }
+ patch(p, q->label);
+ q++;
+ }
+ gbranch(OGOTO);
+ patch(p, def);
+ return;
+ }
+ i = nc / 2;
+ r = q+i;
+ if(sval(r->val)) {
+ gopcode(OGT, n, Z, nodconst(r->val));
+ sp = p;
+ } else {
+ gopcode(OSUB, nodconst(r->val), n, tn);
+ gopcode(OGT, tn, Z, nodconst(0));
+ sp = p;
+ }
+ gbranch(OGOTO);
+ p->as = ABEQ;
+ patch(p, r->label);
+ swit2(q, i, def, n, tn);
+
+ patch(sp, pc);
+ swit2(r+1, nc-i-1, def, n, tn);
+}
+
+void
+bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ int sh;
+ long v;
+ Node *l;
+
+ /*
+ * n1 gets adjusted/masked value
+ * n2 gets address of cell
+ * n3 gets contents of cell
+ */
+ l = b->left;
+ if(n2 != Z) {
+ regalloc(n1, l, nn);
+ reglcgen(n2, l, Z);
+ regalloc(n3, l, Z);
+ gopcode(OAS, n2, Z, n3);
+ gopcode(OAS, n3, Z, n1);
+ } else {
+ regalloc(n1, l, nn);
+ cgen(l, n1);
+ }
+ if(b->type->shift == 0 && typeu[b->type->etype]) {
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ } else {
+ sh = 32 - b->type->shift - b->type->nbits;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, n1);
+ sh += b->type->shift;
+ if(sh > 0)
+ if(typeu[b->type->etype])
+ gopcode(OLSHR, nodconst(sh), Z, n1);
+ else
+ gopcode(OASHR, nodconst(sh), Z, n1);
+ }
+}
+
+void
+bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn)
+{
+ long v;
+ Node nod, *l;
+ int sh;
+
+ /*
+ * n1 has adjusted/masked value
+ * n2 has address of cell
+ * n3 has contents of cell
+ */
+ l = b->left;
+ regalloc(&nod, l, Z);
+ v = ~0 + (1L << b->type->nbits);
+ gopcode(OAND, nodconst(v), Z, n1);
+ gopcode(OAS, n1, Z, &nod);
+ if(nn != Z)
+ gopcode(OAS, n1, Z, nn);
+ sh = b->type->shift;
+ if(sh > 0)
+ gopcode(OASHL, nodconst(sh), Z, &nod);
+ v <<= sh;
+ gopcode(OAND, nodconst(~v), Z, n3);
+ gopcode(OOR, n3, Z, &nod);
+ gopcode(OAS, &nod, Z, n2);
+
+ regfree(&nod);
+ regfree(n1);
+ regfree(n2);
+ regfree(n3);
+}
+
+long
+outstring(char *s, long n)
+{
+ long r;
+
+ if(suppress)
+ return nstring;
+ r = nstring;
+ while(n) {
+ string[mnstring] = *s++;
+ mnstring++;
+ nstring++;
+ if(mnstring >= NSNAME) {
+ gpseudo(ADATA, symstring, nodconst(0L));
+ p->from.offset += nstring - NSNAME;
+ p->reg = NSNAME;
+ p->to.type = D_SCONST;
+ memmove(p->to.sval, string, NSNAME);
+ mnstring = 0;
+ }
+ n--;
+ }
+ return r;
+}
+
+int
+mulcon(Node *n, Node *nn)
+{
+ Node *l, *r, nod1, nod2;
+ Multab *m;
+ long v;
+ int o;
+ char code[sizeof(m->code)+2], *p;
+
+ if(typefd[n->type->etype])
+ return 0;
+ l = n->left;
+ r = n->right;
+ if(l->op == OCONST) {
+ l = r;
+ r = n->left;
+ }
+ if(r->op != OCONST)
+ return 0;
+ v = convvtox(r->vconst, n->type->etype);
+ if(v != r->vconst) {
+ if(debug['M'])
+ print("%L multiply conv: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+ m = mulcon0(n, v);
+ if(!m) {
+ if(debug['M'])
+ print("%L multiply table: %lld\n", n->lineno, r->vconst);
+ return 0;
+ }
+
+ memmove(code, m->code, sizeof(m->code));
+ code[sizeof(m->code)] = 0;
+
+ p = code;
+ if(p[1] == 'i')
+ p += 2;
+ regalloc(&nod1, n, nn);
+ cgen(l, &nod1);
+ if(v < 0)
+ gopcode(ONEG, &nod1, Z, &nod1);
+ regalloc(&nod2, n, Z);
+
+loop:
+ switch(*p) {
+ case 0:
+ regfree(&nod2);
+ gopcode(OAS, &nod1, Z, nn);
+ regfree(&nod1);
+ return 1;
+ case '+':
+ o = OADD;
+ goto addsub;
+ case '-':
+ o = OSUB;
+ addsub: /* number is r,n,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&4)
+ r = &nod2;
+ n = &nod1;
+ if(v&2)
+ n = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ gopcode(o, l, n, r);
+ break;
+ default: /* op is shiftcount, number is r,l */
+ v = p[1] - '0';
+ r = &nod1;
+ if(v&2)
+ r = &nod2;
+ l = &nod1;
+ if(v&1)
+ l = &nod2;
+ v = *p - 'a';
+ if(v < 0 || v >= 32) {
+ diag(n, "mulcon unknown op: %c%c", p[0], p[1]);
+ break;
+ }
+ gopcode(OASHL, nodconst(v), l, r);
+ break;
+ }
+ p += 2;
+ goto loop;
+}
+
+void
+sextern(Sym *s, Node *a, long o, long w)
+{
+ long e, lw;
+
+ for(e=0; e<w; e+=NSNAME) {
+ lw = NSNAME;
+ if(w-e < lw)
+ lw = w-e;
+ gpseudo(ADATA, s, nodconst(0));
+ p->from.offset += o+e;
+ p->reg = lw;
+ p->to.type = D_SCONST;
+ memmove(p->to.sval, a->cstring+e, lw);
+ }
+}
+
+void
+gextern(Sym *s, Node *a, long o, long w)
+{
+ if(a->op == OCONST && typev[a->type->etype]) {
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gpseudo(ADATA, s, nod32const(a->vconst>>32));
+ else
+ gpseudo(ADATA, s, nod32const(a->vconst));
+ p->from.offset += o;
+ p->reg = 4;
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gpseudo(ADATA, s, nod32const(a->vconst));
+ else
+ gpseudo(ADATA, s, nod32const(a->vconst>>32));
+ p->from.offset += o + 4;
+ p->reg = 4;
+ return;
+ }
+ gpseudo(ADATA, s, a);
+ p->from.offset += o;
+ p->reg = w;
+ if(p->to.type == D_OREG)
+ p->to.type = D_CONST;
+}
+
+void zname(Biobuf*, Sym*, int);
+char* zaddr(char*, Adr*, int);
+void zwrite(Biobuf*, Prog*, int, int);
+void outhist(Biobuf*);
+
+void
+outcode(void)
+{
+ struct { Sym *sym; short type; } h[NSYM];
+ Prog *p;
+ Sym *s;
+ int sf, st, t, sym;
+
+ if(debug['S']) {
+ for(p = firstp; p != P; p = p->link)
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc--;
+ for(p = firstp; p != P; p = p->link) {
+ print("%P\n", p);
+ if(p->as != ADATA && p->as != AGLOBL)
+ pc++;
+ }
+ }
+ outhist(&outbuf);
+ for(sym=0; sym<NSYM; sym++) {
+ h[sym].sym = S;
+ h[sym].type = 0;
+ }
+ sym = 1;
+ for(p = firstp; 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(h[sf].type == t)
+ if(h[sf].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, 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(h[st].type == t)
+ if(h[st].sym == s)
+ break;
+ s->sym = sym;
+ zname(&outbuf, s, t);
+ h[sym].sym = s;
+ h[sym].type = t;
+ st = sym;
+ sym++;
+ if(sym >= NSYM)
+ sym = 1;
+ if(st == sf)
+ goto jackpot;
+ break;
+ }
+ zwrite(&outbuf, p, sf, st);
+ }
+ firstp = P;
+ lastp = P;
+}
+
+void
+zwrite(Biobuf *b, Prog *p, int sf, int st)
+{
+ char bf[100], *bp;
+ long l;
+
+ bf[0] = p->as;
+ bf[1] = p->as>>8;
+ bf[2] = p->reg;
+ if(p->from3.type != D_NONE)
+ bf[2] |= 0x40;
+ l = p->lineno;
+ bf[3] = l;
+ bf[4] = l>>8;
+ bf[5] = l>>16;
+ bf[6] = l>>24;
+ bp = zaddr(bf+7, &p->from, sf);
+ if(bf[2] & 0x40)
+ bp = zaddr(bp, &p->from3, 0);
+ bp = zaddr(bp, &p->to, st);
+ Bwrite(b, bf, bp-bf);
+}
+
+void
+outhist(Biobuf *b)
+{
+ Hist *h;
+ char *p, *q, *op, c;
+ Prog pg;
+ int n;
+
+ pg = zprog;
+ pg.as = AHISTORY;
+ c = pathchar();
+ for(h = hist; h != H; h = h->link) {
+ p = h->name;
+ op = 0;
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && p && p[1] == ':'){
+ p += 2;
+ c = *p;
+ }
+ if(p && p[0] != c && h->offset == 0 && pathname){
+ /* on windows skip drive specifier in pathname */
+ if(systemtype(Windows) && pathname[1] == ':') {
+ op = p;
+ p = pathname+2;
+ c = *p;
+ } else if(pathname[0] == c){
+ op = p;
+ p = pathname;
+ }
+ }
+ while(p) {
+ q = utfrune(p, c);
+ if(q) {
+ n = q-p;
+ if(n == 0){
+ n = 1; /* leading "/" */
+ *p = '/'; /* don't emit "\" on windows */
+ }
+ q++;
+ } else {
+ n = strlen(p);
+ q = 0;
+ }
+ if(n) {
+ Bputc(b, ANAME);
+ Bputc(b, ANAME>>8);
+ Bputc(b, D_FILE);
+ Bputc(b, 1);
+ Bputc(b, '<');
+ Bwrite(b, p, n);
+ Bputc(b, 0);
+ }
+ p = q;
+ if(p == 0 && op) {
+ p = op;
+ op = 0;
+ }
+ }
+ pg.lineno = h->line;
+ pg.to.type = zprog.to.type;
+ pg.to.offset = h->offset;
+ if(h->offset)
+ pg.to.type = D_CONST;
+
+ zwrite(b, &pg, 0, 0);
+ }
+}
+
+void
+zname(Biobuf *b, Sym *s, int t)
+{
+ char *n, bf[8];
+ ulong sig;
+
+ n = s->name;
+ if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
+ sig = sign(s);
+ bf[0] = ASIGNAME;
+ bf[1] = ASIGNAME>>8;
+ bf[2] = sig;
+ bf[3] = sig>>8;
+ bf[4] = sig>>16;
+ bf[5] = sig>>24;
+ bf[6] = t;
+ bf[7] = s->sym;
+ Bwrite(b, bf, 8);
+ s->sig = SIGDONE;
+ }
+ else{
+ bf[0] = ANAME;
+ bf[1] = ANAME>>8;
+ bf[2] = t; /* type */
+ bf[3] = s->sym; /* sym */
+ Bwrite(b, bf, 4);
+ }
+ Bwrite(b, n, strlen(n)+1);
+}
+
+char*
+zaddr(char *bp, Adr *a, int s)
+{
+ long l;
+ Ieee e;
+
+ bp[0] = a->type;
+ bp[1] = a->reg;
+ bp[2] = s;
+ bp[3] = a->name;
+ bp += 4;
+ switch(a->type) {
+ default:
+ diag(Z, "unknown type %d in zaddr", a->type);
+
+ case D_NONE:
+ case D_REG:
+ case D_FREG:
+ case D_CREG:
+ break;
+
+ case D_OREG:
+ case D_CONST:
+ case D_BRANCH:
+ l = a->offset;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+
+ case D_SCONST:
+ memmove(bp, a->sval, NSNAME);
+ bp += NSNAME;
+ break;
+
+ case D_FCONST:
+ ieeedtod(&e, a->dval);
+ l = e.l;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ l = e.h;
+ bp[0] = l;
+ bp[1] = l>>8;
+ bp[2] = l>>16;
+ bp[3] = l>>24;
+ bp += 4;
+ break;
+ }
+ return bp;
+}
+
+static int
+doubled(Type *t)
+{
+ Type *v;
+
+ if(debug['4'])
+ return 0;
+ if(t->nbits != 0)
+ return 0;
+ switch(t->etype){
+ case TDOUBLE:
+ return 1;
+
+ case TARRAY:
+ for(v=t; v->etype==TARRAY; v=v->link)
+ ;
+ return v->etype == TDOUBLE;
+
+ case TSTRUCT:
+ case TUNION:
+ for(v = t->link; v != T; v = v->down)
+ if(doubled(v))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+long
+align(long i, Type *t, int op)
+{
+ long o;
+ Type *v;
+ int w, pc;
+
+ o = i;
+ w = 1;
+ pc = 0;
+ switch(op) {
+ default:
+ diag(Z, "unknown align opcode %d", op);
+ break;
+
+ case Asu2: /* padding at end of a struct */
+ w = doubled(t)? SZ_DOUBLE: SZ_LONG;
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael1: /* initial align of struct element (also automatic) */
+ for(v=t; v->etype==TARRAY; v=v->link)
+ ;
+ w = ewidth[v->etype];
+ if(w <= 0 || w >= SZ_LONG){
+ if(doubled(v)){
+ w = SZ_DOUBLE;
+ doubleflag = 1;
+ }else
+ w = SZ_LONG;
+ }
+ if(packflg)
+ w = packflg;
+ break;
+
+ case Ael2: /* width of a struct element */
+ o += t->width;
+ break;
+
+ case Aarg0: /* initial passbyptr argument in arg list */
+ if(typesuv[t->etype]) {
+ o = align(o, types[TIND], Aarg1);
+ o = align(o, types[TIND], Aarg2);
+ }
+ break;
+
+ case Aarg1: /* initial align of parameter */
+ w = ewidth[t->etype];
+ if(w <= 0 || w >= SZ_LONG) {
+ if(doubled(t)){
+ w = SZ_DOUBLE;
+ pc = SZ_LONG; /* alignment must account for pc */
+ hasdoubled = 1;
+ }else
+ w = SZ_LONG;
+ break;
+ }
+ o += SZ_LONG - w; /* big endian adjustment */
+ w = 1;
+ break;
+
+ case Aarg2: /* width of a parameter */
+ o += t->width;
+ w = SZ_LONG;
+ if(doubled(t)){
+ pc = SZ_LONG;
+ hasdoubled = 1;
+ }
+ break;
+
+ case Aaut3: /* total align of automatic */
+ doubleflag = 0;
+ o = align(o, t, Ael1);
+ o = align(o, t, Ael2);
+ hasdoubled |= doubleflag;
+ break;
+ }
+ o = round(o+pc, w)-pc;
+ if(debug['A'])
+ print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
+ return o;
+}
+
+long
+maxround(long max, long v)
+{
+ int w;
+
+ w = SZ_LONG;
+ if((debug['8'] || hasdoubled) && !debug['4'])
+ w = SZ_DOUBLE;
+ v += w-1;
+ if(v > max)
+ max = round(v, w);
+ return max;
+}
diff --git a/sys/src/cmd/qc/txt.c b/sys/src/cmd/qc/txt.c
new file mode 100755
index 000000000..1c6f4d7f8
--- /dev/null
+++ b/sys/src/cmd/qc/txt.c
@@ -0,0 +1,1747 @@
+#include "gc.h"
+
+static int resvreg[nelem(reg)];
+
+static void gopcode64(int, Node*, Node*, Node*);
+static void gori64(int, Node*, Node*, Node*);
+static void gandi64(int, Node*, Node*, Node*);
+
+void
+ginit(void)
+{
+ Type *t;
+
+ thechar = 'q';
+ thestring = "power";
+ exregoffset = REGEXT;
+ exfregoffset = FREGEXT;
+ listinit();
+ nstring = 0;
+ mnstring = 0;
+ nrathole = 0;
+ pc = 0;
+ breakpc = -1;
+ continpc = -1;
+ cases = C;
+ firstp = P;
+ lastp = P;
+ tfield = types[TLONG];
+
+ zprog.link = P;
+ zprog.as = AGOK;
+ zprog.reg = NREG;
+ zprog.from.type = D_NONE;
+ zprog.from.name = D_NONE;
+ zprog.from.reg = NREG;
+ zprog.from3 = zprog.from;
+ zprog.to = zprog.from;
+
+ regnode.op = OREGISTER;
+ regnode.class = CEXREG;
+ regnode.reg = 0;
+ regnode.complex = 0;
+ regnode.addable = 11;
+ regnode.type = types[TLONG];
+
+ constnode.op = OCONST;
+ constnode.class = CXXX;
+ constnode.complex = 0;
+ constnode.addable = 20;
+ constnode.type = types[TLONG];
+
+ fconstnode.op = OCONST;
+ fconstnode.class = CXXX;
+ fconstnode.complex = 0;
+ fconstnode.addable = 20;
+ fconstnode.type = types[TDOUBLE];
+
+ nodsafe = new(ONAME, Z, Z);
+ nodsafe->sym = slookup(".safe");
+ nodsafe->type = types[TINT];
+ nodsafe->etype = types[TINT]->etype;
+ nodsafe->class = CAUTO;
+ complex(nodsafe);
+
+ t = typ(TARRAY, types[TCHAR]);
+ symrathole = slookup(".rathole");
+ symrathole->class = CGLOBL;
+ symrathole->type = t;
+
+ nodrat = new(ONAME, Z, Z);
+ nodrat->sym = symrathole;
+ nodrat->type = types[TIND];
+ nodrat->etype = TVOID;
+ nodrat->class = CGLOBL;
+ complex(nodrat);
+ nodrat->type = t;
+
+ com64init();
+
+ memset(reg, 0, sizeof(reg));
+ reg[REGZERO] = 1; /* don't use */
+ reg[REGTMP] = 1;
+ reg[FREGCVI+NREG] = 1;
+ reg[FREGZERO+NREG] = 1;
+ reg[FREGHALF+NREG] = 1;
+ reg[FREGONE+NREG] = 1;
+ reg[FREGTWO+NREG] = 1;
+ memmove(resvreg, reg, sizeof(reg));
+}
+
+void
+gclean(void)
+{
+ int i;
+ Sym *s;
+
+ for(i=0; i<NREG; i++)
+ if(reg[i] && !resvreg[i])
+ diag(Z, "reg %d left allocated", i);
+ for(i=NREG; i<NREG+NREG; i++)
+ if(reg[i] && !resvreg[i])
+ diag(Z, "freg %d left allocated", i-NREG);
+ while(mnstring)
+ outstring("", 1L);
+ symstring->type->width = nstring;
+ symrathole->type->width = nrathole;
+ for(i=0; i<NHASH; i++)
+ for(s = hash[i]; s != S; s = s->link) {
+ if(s->type == T)
+ continue;
+ if(s->type->width == 0)
+ continue;
+ if(s->class != CGLOBL && s->class != CSTATIC)
+ continue;
+ if(s->type == types[TENUM])
+ continue;
+ gpseudo(AGLOBL, s, nodconst(s->type->width));
+ }
+ nextpc();
+ p->as = AEND;
+ outcode();
+}
+
+void
+nextpc(void)
+{
+
+ p = alloc(sizeof(*p));
+ *p = zprog;
+ p->lineno = nearln;
+ pc++;
+ if(firstp == P) {
+ firstp = p;
+ lastp = p;
+ return;
+ }
+ lastp->link = p;
+ lastp = p;
+}
+
+void
+gargs(Node *n, Node *tn1, Node *tn2)
+{
+ long regs;
+ Node fnxargs[20], *fnxp;
+
+ regs = cursafe;
+
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 0, &fnxp); /* compile fns to temps */
+
+ curarg = 0;
+ fnxp = fnxargs;
+ garg1(n, tn1, tn2, 1, &fnxp); /* compile normal args and temps */
+
+ cursafe = regs;
+}
+
+void
+garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
+{
+ Node nod;
+
+ if(n == Z)
+ return;
+ if(n->op == OLIST) {
+ garg1(n->left, tn1, tn2, f, fnxp);
+ garg1(n->right, tn1, tn2, f, fnxp);
+ return;
+ }
+ if(f == 0) {
+ if(n->complex >= FNX) {
+ regsalloc(*fnxp, n);
+ nod = znode;
+ nod.op = OAS;
+ nod.left = *fnxp;
+ nod.right = n;
+ nod.type = n->type;
+ cgen(&nod, Z);
+ (*fnxp)++;
+ }
+ return;
+ }
+ if(typesuv[n->type->etype]) {
+ regaalloc(tn2, n);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn2);
+ (*fnxp)++;
+ } else
+ cgen(n, tn2);
+ return;
+ }
+ if(REGARG && curarg == 0 && typechlp[n->type->etype]) {
+ regaalloc1(tn1, n);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ return;
+ }
+ if(vconst(n) == 0) {
+ regaalloc(tn2, n);
+ gopcode(OAS, n, Z, tn2);
+ return;
+ }
+ regalloc(tn1, n, Z);
+ if(n->complex >= FNX) {
+ cgen(*fnxp, tn1);
+ (*fnxp)++;
+ } else
+ cgen(n, tn1);
+ regaalloc(tn2, n);
+ gopcode(OAS, tn1, Z, tn2);
+ regfree(tn1);
+}
+
+Node*
+nod32const(vlong v)
+{
+ constnode.vconst = v & MASK(32);
+ return &constnode;
+}
+
+Node*
+nodconst(long v)
+{
+ constnode.vconst = v;
+ return &constnode;
+}
+
+Node*
+nodfconst(double d)
+{
+ fconstnode.fconst = d;
+ return &fconstnode;
+}
+
+void
+nodreg(Node *n, Node *nn, int reg)
+{
+ *n = regnode;
+ n->reg = reg;
+ n->type = nn->type;
+ n->lineno = nn->lineno;
+}
+
+void
+regret(Node *n, Node *nn)
+{
+ int r;
+
+ r = REGRET;
+ if(typefd[nn->type->etype])
+ r = FREGRET+NREG;
+ nodreg(n, nn, r);
+ reg[r]++;
+}
+
+void
+regalloc(Node *n, Node *tn, Node *o)
+{
+ int i, j;
+ static int lasti;
+
+ switch(tn->type->etype) {
+ case TCHAR:
+ case TUCHAR:
+ case TSHORT:
+ case TUSHORT:
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i > 0 && i < NREG)
+ goto out;
+ }
+ j = lasti + REGRET+1;
+ for(i=REGRET+1; i<NREG; i++) {
+ if(j >= NREG)
+ j = REGRET+1;
+ if(reg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j++;
+ }
+ diag(tn, "out of fixed registers");
+ goto err;
+
+ case TFLOAT:
+ case TDOUBLE:
+ if(o != Z && o->op == OREGISTER) {
+ i = o->reg;
+ if(i >= NREG && i < NREG+NREG)
+ goto out;
+ }
+ j = lasti + NREG;
+ for(i=NREG; i<NREG+NREG; i++) {
+ if(j >= NREG+NREG)
+ j = NREG;
+ if(reg[j] == 0) {
+ i = j;
+ goto out;
+ }
+ j++;
+ }
+ diag(tn, "out of float registers");
+ goto err;
+
+ case TVLONG:
+ case TUVLONG:
+ n->op = OREGPAIR;
+ n->complex = 0; /* already in registers */
+ n->addable = 11;
+ n->type = tn->type;
+ n->lineno = nearln;
+ n->left = alloc(sizeof(Node));
+ n->right = alloc(sizeof(Node));
+ if(o != Z && o->op == OREGPAIR) {
+ regalloc(n->left, &regnode, o->left);
+ regalloc(n->right, &regnode, o->right);
+ } else {
+ regalloc(n->left, &regnode, Z);
+ regalloc(n->right, &regnode, Z);
+ }
+ n->right->type = types[TULONG];
+ if(tn->type->etype == TUVLONG)
+ n->left->type = types[TULONG]; /* TO DO: is this a bad idea? */
+ return;
+ }
+ diag(tn, "unknown type in regalloc: %T", tn->type);
+err:
+ i = 0;
+out:
+ if(i)
+ reg[i]++;
+ lasti++;
+ if(lasti >= 5)
+ lasti = 0;
+ nodreg(n, tn, i);
+}
+
+void
+regialloc(Node *n, Node *tn, Node *o)
+{
+ Node nod;
+
+ nod = *tn;
+ nod.type = types[TIND];
+ regalloc(n, &nod, o);
+}
+
+void
+regfree(Node *n)
+{
+ int i;
+
+ if(n->op == OREGPAIR) {
+ regfree(n->left);
+ regfree(n->right);
+ return;
+ }
+ i = 0;
+ if(n->op != OREGISTER && n->op != OINDREG)
+ goto err;
+ i = n->reg;
+ if(i < 0 || i >= sizeof(reg))
+ goto err;
+ if(reg[i] <= 0)
+ goto err;
+ reg[i]--;
+ return;
+err:
+ diag(n, "error in regfree: %d [%d]", i, reg[i]);
+ prtree(n, "regfree");
+}
+
+void
+regsalloc(Node *n, Node *nn)
+{
+ cursafe = align(cursafe+stkoff, nn->type, Aaut3)-stkoff;
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+// if(nn->type->etype == TDOUBLE || nn->type->etype == TVLONG){
+// extern int hasdoubled;
+// fprint(2, "stkoff=%ld cursafe=%ld curarg=%ld %d\n", stkoff, cursafe, curarg, hasdoubled);
+// }
+ *n = *nodsafe;
+ n->xoffset = -(stkoff + cursafe);
+ n->type = nn->type;
+ n->etype = nn->type->etype;
+ n->lineno = nn->lineno;
+}
+
+void
+regaalloc1(Node *n, Node *nn)
+{
+ nodreg(n, nn, REGARG);
+ reg[REGARG]++;
+ curarg = align(curarg, nn->type, Aarg1);
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regaalloc(Node *n, Node *nn)
+{
+ curarg = align(curarg, nn->type, Aarg1);
+ *n = *nn;
+ n->op = OINDREG;
+ n->reg = REGSP;
+ n->xoffset = curarg + SZ_LONG;
+ n->complex = 0;
+ n->addable = 20;
+ curarg = align(curarg, nn->type, Aarg2);
+ maxargsafe = maxround(maxargsafe, cursafe+curarg);
+}
+
+void
+regind(Node *n, Node *nn)
+{
+
+ if(n->op != OREGISTER) {
+ diag(n, "regind not OREGISTER");
+ return;
+ }
+ n->op = OINDREG;
+ n->type = nn->type;
+}
+
+void
+raddr(Node *n, Prog *p)
+{
+ Adr a;
+
+ naddr(n, &a);
+ if(R0ISZERO && a.type == D_CONST && a.offset == 0) {
+ a.type = D_REG;
+ a.reg = REGZERO;
+ }
+ if(a.type != D_REG && a.type != D_FREG) {
+ if(n)
+ diag(n, "bad in raddr: %O", n->op);
+ else
+ diag(n, "bad in raddr: <null>");
+ p->reg = NREG;
+ } else
+ p->reg = a.reg;
+}
+
+void
+naddr(Node *n, Adr *a)
+{
+ long v;
+
+ a->type = D_NONE;
+ if(n == Z)
+ return;
+ switch(n->op) {
+ default:
+ bad:
+ diag(n, "bad in naddr: %O", n->op);
+ break;
+
+ case OREGISTER:
+ a->type = D_REG;
+ a->sym = S;
+ a->reg = n->reg;
+ if(a->reg >= NREG) {
+ a->type = D_FREG;
+ a->reg -= NREG;
+ }
+ break;
+
+ case OIND:
+ naddr(n->left, a);
+ a->offset += n->xoffset; /* little hack for reglcgenv */
+ if(a->type == D_REG) {
+ a->type = D_OREG;
+ break;
+ }
+ if(a->type == D_CONST) {
+ a->type = D_OREG;
+ break;
+ }
+ goto bad;
+
+ case OINDREG:
+ a->type = D_OREG;
+ a->sym = S;
+ a->offset = n->xoffset;
+ a->reg = n->reg;
+ break;
+
+ case ONAME:
+ a->etype = n->etype;
+ a->type = D_OREG;
+ a->name = D_STATIC;
+ a->sym = n->sym;
+ a->offset = n->xoffset;
+ if(n->class == CSTATIC)
+ break;
+ if(n->class == CEXTERN || n->class == CGLOBL) {
+ a->name = D_EXTERN;
+ break;
+ }
+ if(n->class == CAUTO) {
+ a->name = D_AUTO;
+ break;
+ }
+ if(n->class == CPARAM) {
+ a->name = D_PARAM;
+ break;
+ }
+ goto bad;
+
+ case OCONST:
+ a->sym = S;
+ a->reg = NREG;
+ if(typefd[n->type->etype]) {
+ a->type = D_FCONST;
+ a->dval = n->fconst;
+ } else {
+ a->type = D_CONST;
+ a->offset = n->vconst;
+ }
+ break;
+
+ case OADDR:
+ naddr(n->left, a);
+ if(a->type == D_OREG) {
+ a->type = D_CONST;
+ break;
+ }
+ goto bad;
+
+ case OADD:
+ if(n->left->op == OCONST) {
+ naddr(n->left, a);
+ v = a->offset;
+ naddr(n->right, a);
+ } else {
+ naddr(n->right, a);
+ v = a->offset;
+ naddr(n->left, a);
+ }
+ a->offset += v;
+ break;
+
+ }
+}
+
+void
+gloadhi(Node *f, Node *t, int c)
+{
+ Type *ot;
+
+ if(f->op == OCONST){
+ f = nodconst((long)(f->vconst>>32));
+ if(c==1 && sconst(f) || c==2 && uconst(f)){
+ if(t->op == OREGISTER)
+ regfree(t);
+ *t = *f;
+ return;
+ }
+ }
+ if(f->op == OREGPAIR) {
+ gmove(f->left, t);
+ return;
+ }
+ ot = f->type;
+ f->type = types[TLONG];
+ gmove(f, t);
+ f->type = ot;
+}
+
+void
+gloadlo(Node *f, Node *t, int c)
+{
+ Type *ot;
+
+ if(f->op == OCONST){
+ f = nodconst((long)f->vconst);
+ if(c && uconst(f)){
+ if(t->op == OREGISTER)
+ regfree(t);
+ *t = *f;
+ return;
+ }
+ }
+ if(f->op == OREGPAIR) {
+ gmove(f->right, t);
+ return;
+ }
+ ot = f->type;
+ f->type = types[TLONG];
+ f->xoffset += SZ_LONG;
+ if(0){
+ prtree(f, "gloadlo f"); prtree(t, "gloadlo t");
+ }
+ gmove(f, t);
+ f->xoffset -= SZ_LONG;
+ f->type = ot;
+}
+
+void
+fop(int as, int f1, int f2, Node *t)
+{
+ Node nod1, nod2, nod3;
+
+ nodreg(&nod1, t, NREG+f1);
+ nodreg(&nod2, t, NREG+f2);
+ regalloc(&nod3, t, t);
+ gopcode(as, &nod1, &nod2, &nod3);
+ gmove(&nod3, t);
+ regfree(&nod3);
+}
+
+void
+gmove(Node *f, Node *t)
+{
+ int ft, tt, a;
+ Node nod, fxc0, fxc1, fxc2, fxrat;
+ Prog *p1;
+ double d;
+
+ ft = f->type->etype;
+ tt = t->type->etype;
+
+ if(ft == TDOUBLE && f->op == OCONST) {
+ d = f->fconst;
+ if(d == 0.0) {
+ a = FREGZERO;
+ goto ffreg;
+ }
+ if(d == 0.5) {
+ a = FREGHALF;
+ goto ffreg;
+ }
+ if(d == 1.0) {
+ a = FREGONE;
+ goto ffreg;
+ }
+ if(d == 2.0) {
+ a = FREGTWO;
+ goto ffreg;
+ }
+ if(d == -.5) {
+ fop(OSUB, FREGHALF, FREGZERO, t);
+ return;
+ }
+ if(d == -1.0) {
+ fop(OSUB, FREGONE, FREGZERO, t);
+ return;
+ }
+ if(d == -2.0) {
+ fop(OSUB, FREGTWO, FREGZERO, t);
+ return;
+ }
+ if(d == 1.5) {
+ fop(OADD, FREGONE, FREGHALF, t);
+ return;
+ }
+ if(d == 2.5) {
+ fop(OADD, FREGTWO, FREGHALF, t);
+ return;
+ }
+ if(d == 3.0) {
+ fop(OADD, FREGTWO, FREGONE, t);
+ return;
+ }
+ }
+ if(ft == TFLOAT && f->op == OCONST) {
+ d = f->fconst;
+ if(d == 0) {
+ a = FREGZERO;
+ ffreg:
+ nodreg(&nod, f, NREG+a);
+ gmove(&nod, t);
+ return;
+ }
+ }
+ if((ft == TVLONG || ft == TUVLONG) && f->op == OCONST && t->op == OREGPAIR) {
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gmove(nod32const(f->vconst>>32), t->left);
+ else
+ gmove(nod32const(f->vconst), t->left);
+ if(align(0, types[TCHAR], Aarg1)) /* isbigendian */
+ gmove(nod32const(f->vconst), t->right);
+ else
+ gmove(nod32const(f->vconst>>32), t->right);
+ return;
+ }
+ /*
+ * a load --
+ * put it into a register then
+ * worry what to do with it.
+ */
+ if(f->op == ONAME || f->op == OINDREG || f->op == OIND) {
+ switch(ft) {
+ default:
+ a = AMOVW;
+ break;
+ case TFLOAT:
+ a = AFMOVS;
+ break;
+ case TDOUBLE:
+ a = AFMOVD;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUCHAR:
+ a = AMOVBZ;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TUSHORT:
+ a = AMOVHZ;
+ break;
+ }
+ if(typev[ft]) {
+ if(typev[tt]) {
+ regalloc(&nod, f, t);
+ /* low order first, because its value will be used first */
+ f->xoffset += SZ_LONG;
+ gins(AMOVW, f, nod.right);
+ f->xoffset -= SZ_LONG;
+ gins(AMOVW, f, nod.left);
+ } else {
+ /* assumed not float or double */
+ regalloc(&nod, &regnode, t);
+ f->xoffset += SZ_LONG;
+ gins(AMOVW, f, &nod);
+ f->xoffset -= SZ_LONG;
+ }
+ } else {
+ regalloc(&nod, f, t);
+ gins(a, f, &nod);
+ }
+ gmove(&nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * a store --
+ * put it into a register then
+ * store it.
+ */
+ if(t->op == ONAME || t->op == OINDREG || t->op == OIND) {
+ switch(tt) {
+ default:
+ a = AMOVW;
+ break;
+ case TUCHAR:
+ a = AMOVBZ;
+ break;
+ case TCHAR:
+ a = AMOVB;
+ break;
+ case TUSHORT:
+ a = AMOVHZ;
+ break;
+ case TSHORT:
+ a = AMOVH;
+ break;
+ case TFLOAT:
+ a = AFMOVS;
+ break;
+ case TDOUBLE:
+ a = AFMOVD;
+ break;
+ }
+ if(R0ISZERO && !typefd[ft] && vconst(f) == 0) {
+ gins(a, f, t);
+ if(typev[tt]) {
+ t->xoffset += SZ_LONG;
+ gins(a, f, t);
+ t->xoffset -= SZ_LONG;
+ }
+ return;
+ }
+ if(ft == tt)
+ regalloc(&nod, t, f);
+ else
+ regalloc(&nod, t, Z);
+ gmove(f, &nod);
+ if(typev[tt]) {
+ t->xoffset += SZ_LONG;
+ gins(a, nod.right, t);
+ t->xoffset -= SZ_LONG;
+ gins(a, nod.left, t);
+ } else
+ gins(a, &nod, t);
+ regfree(&nod);
+ return;
+ }
+
+ /*
+ * type x type cross table
+ */
+ a = AGOK;
+ switch(ft) {
+ case TDOUBLE:
+ case TFLOAT:
+ switch(tt) {
+ case TDOUBLE:
+ a = AFMOVD;
+ if(ft == TFLOAT)
+ a = AFMOVS; /* AFMOVSD */
+ break;
+ case TFLOAT:
+ a = AFRSP;
+ if(ft == TFLOAT)
+ a = AFMOVS;
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ /* BUG: not right for unsigned long */
+ regalloc(&nod, f, Z); /* should be type float */
+ regsalloc(&fxrat, &fconstnode);
+ gins(AFCTIWZ, f, &nod);
+ gins(AFMOVD, &nod, &fxrat);
+ regfree(&nod);
+ fxrat.type = nodrat->type;
+ fxrat.etype = nodrat->etype;
+ fxrat.xoffset += 4;
+ gins(AMOVW, &fxrat, t);
+ gmove(t, t);
+ return;
+ }
+ break;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ switch(tt) {
+ case TDOUBLE:
+ case TFLOAT:
+ goto fxtofl;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ case TFLOAT:
+ goto fxtofl;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ a = AMOVH;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUSHORT:
+ switch(tt) {
+ case TDOUBLE:
+ case TFLOAT:
+ goto fxtofl;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ a = AMOVHZ;
+ break;
+ case TSHORT:
+ case TUSHORT:
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ case TFLOAT:
+ goto fxtofl;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVB;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TUCHAR:
+ switch(tt) {
+ case TDOUBLE:
+ case TFLOAT:
+ fxtofl:
+ /*
+ * rat[0] = 0x43300000; rat[1] = f^0x80000000;
+ * t = *(double*)rat - FREGCVI;
+ * is-unsigned(t) => if(t<0) t += 2^32;
+ * could be streamlined for int-to-float
+ */
+ regalloc(&fxc0, f, Z);
+ regalloc(&fxc2, f, Z);
+ regsalloc(&fxrat, &fconstnode); /* should be type float */
+ gins(AMOVW, nodconst(0x43300000L), &fxc0);
+ gins(AMOVW, f, &fxc2);
+ gins(AMOVW, &fxc0, &fxrat);
+ gins(AXOR, nodconst(0x80000000L), &fxc2);
+ fxc1 = fxrat;
+ fxc1.type = nodrat->type;
+ fxc1.etype = nodrat->etype;
+ fxc1.xoffset += SZ_LONG;
+ gins(AMOVW, &fxc2, &fxc1);
+ regfree(&fxc2);
+ regfree(&fxc0);
+ regalloc(&nod, t, t); /* should be type float */
+ gins(AFMOVD, &fxrat, &nod);
+ nodreg(&fxc1, t, NREG+FREGCVI);
+ gins(AFSUB, &fxc1, &nod);
+ a = AFMOVD;
+ if(tt == TFLOAT)
+ a = AFRSP;
+ gins(a, &nod, t);
+ regfree(&nod);
+ if(ft == TULONG) {
+ regalloc(&nod, t, Z);
+ if(tt == TFLOAT) {
+ gins(AFCMPU, t, Z);
+ p->to.type = D_FREG;
+ p->to.reg = FREGZERO;
+ gins(ABGE, Z, Z);
+ p1 = p;
+ gins(AFMOVS, nodfconst(4294967296.), &nod);
+ gins(AFADDS, &nod, t);
+ } else {
+ gins(AFCMPU, t, Z);
+ p->to.type = D_FREG;
+ p->to.reg = FREGZERO;
+ gins(ABGE, Z, Z);
+ p1 = p;
+ gins(AFMOVD, nodfconst(4294967296.), &nod);
+ gins(AFADD, &nod, t);
+ }
+ patch(p1, pc);
+ regfree(&nod);
+ }
+ return;
+ case TINT:
+ case TUINT:
+ case TLONG:
+ case TULONG:
+ case TIND:
+ case TSHORT:
+ case TUSHORT:
+ a = AMOVBZ;
+ break;
+ case TCHAR:
+ case TUCHAR:
+ a = AMOVW;
+ break;
+ }
+ break;
+ case TVLONG:
+ case TUVLONG:
+ switch(tt) {
+ case TVLONG:
+ case TUVLONG:
+ a = AMOVW;
+ break;
+ }
+ break;
+ }
+ if(a == AGOK)
+ diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type);
+ if(a == AMOVW || a == AFMOVS || a == AFMOVD)
+ if(samaddr(f, t))
+ return;
+ if(typev[ft]) {
+ if(f->op != OREGPAIR || t->op != OREGPAIR)
+ diag(Z, "bad vlong in gmove (%O->%O)", f->op, t->op);
+ gins(a, f->left, t->left);
+ gins(a, f->right, t->right);
+ } else
+ gins(a, f, t);
+}
+
+void
+gins(int a, Node *f, Node *t)
+{
+
+ nextpc();
+ p->as = a;
+ if(f != Z)
+ naddr(f, &p->from);
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+void
+gins3(int a, Node *f1, Node *f2, Node *t)
+{
+ Adr ta;
+
+ nextpc();
+ p->as = a;
+ if(f1 != Z)
+ naddr(f1, &p->from);
+ if(f2 != Z && (f2->op != OREGISTER || !samaddr(f2, t))) {
+ ta = zprog.from; /* TO DO */
+ naddr(f2, &ta);
+ p->reg = ta.reg;
+ if(ta.type == D_CONST && ta.offset == 0) {
+ if(R0ISZERO)
+ p->reg = REGZERO;
+ else
+ diag(Z, "REGZERO in gins3 %A", a);
+ }else if(ta.type == D_CONST)
+ p->from3 = ta;
+ }
+ if(t != Z)
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+void
+gins4(int a, Node *f1, Node *f2, Node *f3, Node *t)
+{
+ Adr ta;
+
+ nextpc();
+ p->as = a;
+ naddr(f1, &p->from);
+ if(f2->op != OREGISTER && (f2->op != OCONST || vconst(f2) != 0))
+ diag(f2, "invalid gins4");
+ naddr(f2, &ta);
+ p->reg = ta.reg;
+ if(ta.type == D_CONST && ta.offset == 0)
+ p->reg = REGZERO;
+ naddr(f3, &p->from3);
+ naddr(t, &p->to);
+ if(debug['g'])
+ print("%P\n", p);
+}
+
+void
+gopcode(int o, Node *f1, Node *f2, Node *t)
+{
+ int a, et, uns;
+
+ if(o == OAS) {
+ gmove(f1, t);
+ return;
+ }
+ et = TLONG;
+ if(f1 != Z && f1->type != T) {
+ if(f1->op == OCONST && t != Z && t->type != T)
+ et = t->type->etype;
+ else
+ et = f1->type->etype;
+ }
+ if((typev[et] || t->type != T && typev[t->type->etype]) && o != OFUNC) {
+ gopcode64(o, f1, f2, t);
+ return;
+ }
+ uns = 0;
+ a = AGOK;
+ switch(o) {
+
+ case OASADD:
+ case OADD:
+ a = AADD;
+ if(et == TFLOAT)
+ a = AFADDS;
+ else
+ if(et == TDOUBLE)
+ a = AFADD;
+ break;
+
+ case OASSUB:
+ case OSUB:
+ a = ASUB;
+ if(et == TFLOAT)
+ a = AFSUBS;
+ else
+ if(et == TDOUBLE)
+ a = AFSUB;
+ break;
+
+ case OASOR:
+ case OOR:
+ a = AOR;
+ break;
+
+ case OASAND:
+ case OAND:
+ a = AAND;
+ if(f1->op == OCONST)
+ a = AANDCC;
+ break;
+
+ case OASXOR:
+ case OXOR:
+ a = AXOR;
+ break;
+
+ case OASLSHR:
+ case OLSHR:
+ a = ASRW;
+ break;
+
+ case OASASHR:
+ case OASHR:
+ a = ASRAW;
+ break;
+
+ case OASASHL:
+ case OASHL:
+ a = ASLW;
+ break;
+
+ case OFUNC:
+ a = ABL;
+ break;
+
+ case OASLMUL:
+ case OLMUL:
+ case OASMUL:
+ case OMUL:
+ if(et == TFLOAT) {
+ a = AFMULS;
+ break;
+ } else
+ if(et == TDOUBLE) {
+ a = AFMUL;
+ break;
+ }
+ a = AMULLW;
+ break;
+
+ case OASDIV:
+ case ODIV:
+ if(et == TFLOAT) {
+ a = AFDIVS;
+ break;
+ } else
+ if(et == TDOUBLE) {
+ a = AFDIV;
+ break;
+ }
+ a = ADIVW;
+ break;
+
+ case OASMOD:
+ case OMOD:
+ a = AREM;
+ break;
+
+ case OASLMOD:
+ case OLMOD:
+ a = AREMU;
+ break;
+
+ case OASLDIV:
+ case OLDIV:
+ a = ADIVWU;
+ break;
+
+ case OCOM:
+ a = ANOR;
+ break;
+
+ case ONEG:
+ a = ANEG;
+ if(et == TFLOAT || et == TDOUBLE)
+ a = AFNEG;
+ break;
+
+ case OEQ:
+ a = ABEQ;
+ if(t->op == OCONST && t->vconst >= (1<<15))
+ goto cmpu;
+ goto cmp;
+
+ case ONE:
+ a = ABNE;
+ if(t->op == OCONST && t->vconst >= (1<<15))
+ goto cmpu;
+ goto cmp;
+
+ case OLT:
+ a = ABLT;
+ goto cmp;
+
+ case OLE:
+ a = ABLE;
+ goto cmp;
+
+ case OGE:
+ a = ABGE;
+ goto cmp;
+
+ case OGT:
+ a = ABGT;
+ goto cmp;
+
+ case OLO:
+ a = ABLT;
+ goto cmpu;
+
+ case OLS:
+ a = ABLE;
+ goto cmpu;
+
+ case OHS:
+ a = ABGE;
+ goto cmpu;
+
+ case OHI:
+ a = ABGT;
+ goto cmpu;
+
+ cmpu:
+ uns = 1;
+ cmp:
+ nextpc();
+ p->as = uns? ACMPU: ACMP;
+ if(et == TFLOAT)
+ p->as = AFCMPU;
+ else
+ if(et == TDOUBLE)
+ p->as = AFCMPU;
+ if(f1 != Z)
+ naddr(f1, &p->from);
+ if(t != Z)
+ naddr(t, &p->to);
+ if(f1 == Z || t == Z || f2 != Z)
+ diag(Z, "bad cmp in gopcode %O", o);
+ if(debug['g'])
+ print("%P\n", p);
+ f1 = Z;
+ f2 = Z;
+ t = Z;
+ break;
+ }
+ if(a == AGOK)
+ diag(Z, "bad in gopcode %O", o);
+ gins3(a, f1, f2, t);
+}
+
+static void
+gopcode64(int o, Node *f1, Node *f2, Node *t)
+{
+ int a1, a2;
+ Node nod, nod1, nod2, sh;
+ ulong m;
+ Prog *p1;
+
+ if(t->op != OREGPAIR || f2 != Z && f2->op != OREGPAIR) {
+ diag(Z, "bad f2/dest in gopcode64 %O", o);
+ return;
+ }
+ if(f1->op != OCONST &&
+ (typev[f1->type->etype] && f1->op != OREGPAIR || !typev[f1->type->etype] && f1->op != OREGISTER)) {
+ diag(Z, "bad f1[%O] in gopcode64 %O", f1->op, o);
+ return;
+ }
+ /* a1 for low-order, a2 for high-order */
+ a1 = AGOK;
+ a2 = AGOK;
+ switch(o) {
+ case OASADD:
+ case OADD:
+ if(f1->op == OCONST && sconst(f1)) {
+ if(f2 == Z)
+ f2 = t;
+ gins3(AADDC, f1, f2->right, t->right);
+ if((f1->vconst>>32) == 0)
+ gins(AADDZE, f2->left, t->left);
+ else if((f1->vconst>>32) == -1)
+ gins(AADDME, f2->left, t->left);
+ else
+ diag(t, "odd vlong ADD: %lld", f1->vconst);
+ return;
+ }
+ a1 = AADDC;
+ a2 = AADDE;
+ break;
+
+ case OASSUB:
+ case OSUB:
+ a1 = ASUBC;
+ a2 = ASUBE;
+ break;
+
+ case OASOR:
+ case OOR:
+ if(f1->op == OCONST) {
+ gori64(AOR, f1, f2, t);
+ return;
+ }
+ a1 = a2 = AOR;
+ break;
+
+ case OASAND:
+ case OAND:
+ if(f1->op == OCONST) {
+ gandi64(AANDCC, f1, f2, t);
+ return;
+ }
+ a1 = a2 = AAND;
+ break;
+
+ case OASXOR:
+ case OXOR:
+ if(f1->op == OCONST) {
+ gori64(AXOR, f1, f2, t);
+ return;
+ }
+ a1 = a2 = AXOR;
+ break;
+
+ case OASLSHR:
+ case OLSHR:
+ if(f2 == Z)
+ f2 = t;
+ if(f1->op == OCONST) {
+ if(f1->vconst >= 32) {
+ if(f1->vconst == 32)
+ gmove(f2->left, t->right);
+ else if(f1->vconst < 64)
+ gins3(ASRW, nodconst(f1->vconst-32), f2->left, t->right);
+ else
+ gmove(nodconst(0), t->right);
+ gmove(nodconst(0), t->left);
+ return;
+ }
+ if(f1->vconst <= 0) {
+ if(f2 != t)
+ gmove(f2, t);
+ return;
+ }
+ sh = *nodconst(32 - f1->vconst);
+ m = 0xFFFFFFFFUL >> f1->vconst;
+ gins4(ARLWNM, &sh, f2->right, nodconst(m), t->right);
+ gins4(ARLWMI, &sh, f2->left, nodconst(~m), t->right);
+ gins4(ARLWNM, &sh, f2->left, nodconst(m), t->left);
+ return;
+ }
+ regalloc(&nod, &regnode, Z);
+ gins3(ASUBC, f1, nodconst(32), &nod);
+ gins3(ASRW, f1, f2->right, t->right);
+ regalloc(&nod1, &regnode, Z);
+ gins3(ASLW, &nod, f2->left, &nod1);
+ gins(AOR, &nod1, t->right);
+ gins3(AADD, nodconst(-32), f1, &nod);
+ gins3(ASRW, &nod, f2->left, &nod1);
+ gins(AOR, &nod1, t->right);
+ gins3(ASRW, f1, f2->left, t->left);
+ regfree(&nod);
+ regfree(&nod1);
+ return;
+
+ case OASASHR:
+ case OASHR:
+ if(f2 == Z)
+ f2 = t;
+ if(f1->op == OCONST) {
+ if(f1->vconst >= 32) {
+ if(f1->vconst == 32)
+ gmove(f2->left, t->right);
+ else if(f1->vconst < 64)
+ gins3(ASRAW, nodconst(f1->vconst-32), f2->left, t->right);
+ gins3(ASRAW, nodconst(31), f2->left, t->left);
+ if(f1->vconst >= 64) {
+ gmove(t->left, t->right);
+ return;
+ }
+ return;
+ }
+ if(f1->vconst <= 0) {
+ if(f2 != t)
+ gmove(f2, t);
+ return;
+ }
+ sh = *nodconst(32 - f1->vconst);
+ m = 0xFFFFFFFFUL >> f1->vconst;
+ gins4(ARLWNM, &sh, f2->right, nodconst(m), t->right);
+ gins4(ARLWMI, &sh, f2->left, nodconst(~m), t->right);
+ gins3(ASRAW, &sh, f2->left, t->left);
+ return;
+ }
+ regalloc(&nod, &regnode, Z);
+ gins3(ASUBC, f1, nodconst(32), &nod);
+ gins3(ASRW, f1, f2->right, t->right);
+ regalloc(&nod1, &regnode, Z);
+ gins3(ASLW, &nod, f2->left, &nod1);
+ gins(AOR, &nod1, t->right);
+ gins3(AADDCCC, nodconst(-32), f1, &nod);
+ gins3(ASRAW, &nod, f2->left, &nod1);
+ gins(ABLE, Z, Z);
+ p1 = p;
+ gins(AMOVW, &nod1, t->right);
+ patch(p1, pc);
+ gins3(ASRAW, f1, f2->left, t->left);
+ regfree(&nod);
+ regfree(&nod1);
+ return;
+
+ case OASASHL:
+ case OASHL:
+ if(f2 == Z)
+ f2 = t;
+ if(f1->op == OCONST) {
+ if(f1->vconst >= 32) {
+ if(f1->vconst == 32)
+ gmove(f2->right, t->left);
+ else if(f1->vconst >= 64)
+ gmove(nodconst(0), t->left);
+ else
+ gins3(ASLW, nodconst(f1->vconst-32), f2->right, t->left);
+ gmove(nodconst(0), t->right);
+ return;
+ }
+ if(f1->vconst <= 0) {
+ if(f2 != t)
+ gmove(f2, t);
+ return;
+ }
+ m = 0xFFFFFFFFUL << f1->vconst;
+ gins4(ARLWNM, f1, f2->left, nodconst(m), t->left);
+ gins4(ARLWMI, f1, f2->right, nodconst(~m), t->left);
+ gins4(ARLWNM, f1, f2->right, nodconst(m), t->right);
+ return;
+ }
+ regalloc(&nod, &regnode, Z);
+ gins3(ASUBC, f1, nodconst(32), &nod);
+ gins3(ASLW, f1, f2->left, t->left);
+ regalloc(&nod1, &regnode, Z);
+ gins3(ASRW, &nod, f2->right, &nod1);
+ gins(AOR, &nod1, t->left);
+ gins3(AADD, nodconst(-32), f1, &nod);
+ gins3(ASLW, &nod, f2->right, &nod1);
+ gins(AOR, &nod1, t->left);
+ gins3(ASLW, f1, f2->right, t->right);
+ regfree(&nod);
+ regfree(&nod1);
+ return;
+
+ case OASLMUL:
+ case OLMUL:
+ case OASMUL:
+ case OMUL:
+ if(f2 == Z)
+ f2 = t;
+ regalloc(&nod, &regnode, Z);
+ gins3(AMULLW, f1->right, f2->right, &nod); /* lo(f2.low*f1.low) */
+ regalloc(&nod1, &regnode, Z);
+ gins3(AMULHWU, f1->right, f2->right, &nod1); /* hi(f2.low*f1.low) */
+ regalloc(&nod2, &regnode, Z);
+ gins3(AMULLW, f2->right, f1->left, &nod2); /* lo(f2.low*f1.high) */
+ gins(AADD, &nod2, &nod1);
+ gins3(AMULLW, f1->right, f2->left, &nod2); /* lo(f2.high*f1.low) */
+ gins(AADD, &nod2, &nod1);
+ regfree(&nod2);
+ gmove(&nod, t->right);
+ gmove(&nod1, t->left);
+ regfree(&nod);
+ regfree(&nod1);
+ return;
+
+ case OCOM:
+ a1 = a2 = ANOR;
+ break;
+
+ case ONEG:
+ gins3(ASUBC, t->right, nodconst(0), t->right);
+ gins(ASUBZE, t->left, t->left);
+ return;
+ }
+ if(a1 == AGOK || a2 == AGOK)
+ diag(Z, "bad in gopcode64 %O", o);
+ if(f1->op == OCONST) {
+ if(f2 != Z & f2 != t)
+ diag(Z, "bad const in gopcode64 %O", o);
+ gins(a1, nod32const(f1->vconst), t->right);
+ gins(a2, nod32const(f1->vconst>>32), t->left);
+ } else {
+ if(f2 != Z && f2 != t) {
+ gins3(a1, f1->right, f2->right, t->right);
+ gins3(a2, f1->left, f2->left, t->left);
+ } else {
+ gins(a1, f1->right, t->right);
+ gins(a2, f1->left, t->left);
+ }
+ }
+}
+
+samaddr(Node *f, Node *t)
+{
+
+ if(f->op != t->op)
+ return 0;
+ switch(f->op) {
+
+ case OREGISTER:
+ if(f->reg != t->reg)
+ break;
+ return 1;
+
+ case OREGPAIR:
+ return samaddr(f->left, t->left) && samaddr(f->right, t->right);
+ }
+ return 0;
+}
+
+static void
+gori64(int a, Node *f1, Node *f2, Node *t)
+{
+ ulong lo, hi;
+
+ if(f2 == Z)
+ f2 = t;
+ lo = f1->vconst & MASK(32);
+ hi = (f1->vconst >> 32) & MASK(32);
+ if(lo & 0xFFFF)
+ gins3(a, nodconst(lo & 0xFFFF), f2->right, t->right);
+ if((lo >> 16) != 0)
+ gins3(a, nodconst(lo & 0xFFFF0000UL), f2->right, t->right);
+ if(hi & 0xFFFF)
+ gins3(a, nodconst(hi & 0xFFFF), f2->left, t->left);
+ if((hi >> 16) != 0)
+ gins3(a, nodconst(hi & 0xFFFF0000UL), f2->left, t->left);
+}
+
+static void
+gandi64(int a, Node *f1, Node *f2, Node *t)
+{
+ ulong lo, hi;
+
+ if(f2 == Z)
+ f2 = t;
+ lo = f1->vconst & MASK(32);
+ hi = (f1->vconst >> 32) & MASK(32);
+ if(lo == 0)
+ gins(AMOVW, nodconst(0), t->right);
+ else
+ gins3(a, nodconst(lo), f2->right, t->right);
+ if(hi == 0)
+ gins(AMOVW, nodconst(0), t->left);
+ else
+ gins3(a, nodconst(hi), f2->left, t->left);
+}
+
+void
+gbranch(int o)
+{
+ int a;
+
+ a = AGOK;
+ switch(o) {
+ case ORETURN:
+ a = ARETURN;
+ break;
+ case OGOTO:
+ a = ABR;
+ break;
+ }
+ nextpc();
+ if(a == AGOK) {
+ diag(Z, "bad in gbranch %O", o);
+ nextpc();
+ }
+ p->as = a;
+}
+
+void
+patch(Prog *op, long pc)
+{
+
+ op->to.offset = pc;
+ op->to.type = D_BRANCH;
+}
+
+void
+gpseudo(int a, Sym *s, Node *n)
+{
+
+ nextpc();
+ p->as = a;
+ p->from.type = D_OREG;
+ p->from.sym = s;
+ if(a == ATEXT)
+ p->reg = (profileflg ? 0 : NOPROF);
+ p->from.name = D_EXTERN;
+ if(s->class == CSTATIC)
+ p->from.name = D_STATIC;
+ naddr(n, &p->to);
+ if(a == ADATA || a == AGLOBL)
+ pc--;
+}
+
+int
+sval(long v)
+{
+
+ if(v >= -(1<<15) && v < (1<<15))
+ return 1;
+ return 0;
+}
+
+int
+sconst(Node *n)
+{
+ vlong vv;
+
+ if(n->op == OCONST) {
+ if(!typefd[n->type->etype]) {
+ vv = n->vconst;
+ if(vv >= -(((vlong)1)<<15) && vv < (((vlong)1)<<15))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+uconst(Node *n)
+{
+ vlong vv;
+
+ if(n->op == OCONST) {
+ if(!typefd[n->type->etype]) {
+ vv = n->vconst;
+ if(vv >= 0 && vv < (((vlong)1)<<16))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+long
+exreg(Type *t)
+{
+ long o;
+
+ if(typechlp[t->etype]) {
+ if(exregoffset <= 3)
+ return 0;
+ o = exregoffset;
+ exregoffset--;
+ return o;
+ }
+ if(typefd[t->etype]) {
+ if(exfregoffset <= 16)
+ return 0;
+ o = exfregoffset + NREG;
+ exfregoffset--;
+ return o;
+ }
+ return 0;
+}
+
+schar ewidth[NTYPE] =
+{
+ -1, /* [TXXX] */
+ SZ_CHAR, /* [TCHAR] */
+ SZ_CHAR, /* [TUCHAR] */
+ SZ_SHORT, /* [TSHORT] */
+ SZ_SHORT, /* [TUSHORT] */
+ SZ_INT, /* [TINT] */
+ SZ_INT, /* [TUINT] */
+ SZ_LONG, /* [TLONG] */
+ SZ_LONG, /* [TULONG] */
+ SZ_VLONG, /* [TVLONG] */
+ SZ_VLONG, /* [TUVLONG] */
+ SZ_FLOAT, /* [TFLOAT] */
+ SZ_DOUBLE, /* [TDOUBLE] */
+ SZ_IND, /* [TIND] */
+ 0, /* [TFUNC] */
+ -1, /* [TARRAY] */
+ 0, /* [TVOID] */
+ -1, /* [TSTRUCT] */
+ -1, /* [TUNION] */
+ SZ_INT, /* [TENUM] */
+};
+long ncast[NTYPE] =
+{
+ 0, /* [TXXX] */
+ BCHAR|BUCHAR, /* [TCHAR] */
+ BCHAR|BUCHAR, /* [TUCHAR] */
+ BSHORT|BUSHORT, /* [TSHORT] */
+ BSHORT|BUSHORT, /* [TUSHORT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TINT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TUINT] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TLONG] */
+ BINT|BUINT|BLONG|BULONG|BIND, /* [TULONG] */
+ BVLONG|BUVLONG, /* [TVLONG] */
+ BVLONG|BUVLONG, /* [TUVLONG] */
+ BFLOAT, /* [TFLOAT] */
+ BDOUBLE, /* [TDOUBLE] */
+ BLONG|BULONG|BIND, /* [TIND] */
+ 0, /* [TFUNC] */
+ 0, /* [TARRAY] */
+ 0, /* [TVOID] */
+ BSTRUCT, /* [TSTRUCT] */
+ BUNION, /* [TUNION] */
+ 0, /* [TENUM] */
+};