summaryrefslogtreecommitdiff
path: root/src/cmd/gc/cplx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/cplx.c')
-rw-r--r--src/cmd/gc/cplx.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
new file mode 100644
index 000000000..890cf7f10
--- /dev/null
+++ b/src/cmd/gc/cplx.c
@@ -0,0 +1,479 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "gg.h"
+
+static void subnode(Node *nr, Node *ni, Node *nc);
+static void minus(Node *nl, Node *res);
+ void complexminus(Node*, Node*);
+ void complexadd(int op, Node*, Node*, Node*);
+ void complexmul(Node*, Node*, Node*);
+
+#define CASE(a,b) (((a)<<16)|((b)<<0))
+
+static int
+overlap(Node *f, Node *t)
+{
+ // check whether f and t could be overlapping stack references.
+ // not exact, because it's hard to check for the stack register
+ // in portable code. close enough: worst case we will allocate
+ // an extra temporary and the registerizer will clean it up.
+ return f->op == OINDREG &&
+ t->op == OINDREG &&
+ f->xoffset+f->type->width >= t->xoffset &&
+ t->xoffset+t->type->width >= f->xoffset;
+}
+
+/*
+ * generate:
+ * res = n;
+ * simplifies and calls gmove.
+ */
+void
+complexmove(Node *f, Node *t)
+{
+ int ft, tt;
+ Node n1, n2, n3, n4;
+
+ if(debug['g']) {
+ dump("\ncomplexmove-f", f);
+ dump("complexmove-t", t);
+ }
+
+ if(!t->addable)
+ fatal("complexmove: to not addable");
+
+ ft = simsimtype(f->type);
+ tt = simsimtype(t->type);
+ switch(CASE(ft,tt)) {
+
+ default:
+ fatal("complexmove: unknown conversion: %T -> %T\n",
+ f->type, t->type);
+
+ case CASE(TCOMPLEX64,TCOMPLEX64):
+ case CASE(TCOMPLEX64,TCOMPLEX128):
+ case CASE(TCOMPLEX128,TCOMPLEX64):
+ case CASE(TCOMPLEX128,TCOMPLEX128):
+ // complex to complex move/convert.
+ // make f addable.
+ // also use temporary if possible stack overlap.
+ if(!f->addable || overlap(f, t)) {
+ tempname(&n1, f->type);
+ complexmove(f, &n1);
+ f = &n1;
+ }
+
+ subnode(&n1, &n2, f);
+ subnode(&n3, &n4, t);
+
+ cgen(&n1, &n3);
+ cgen(&n2, &n4);
+ break;
+ }
+}
+
+int
+complexop(Node *n, Node *res)
+{
+ if(n != N && n->type != T)
+ if(iscomplex[n->type->etype]) {
+ goto maybe;
+ }
+ if(res != N && res->type != T)
+ if(iscomplex[res->type->etype]) {
+ goto maybe;
+ }
+
+ if(n->op == OREAL || n->op == OIMAG)
+ goto yes;
+
+ goto no;
+
+maybe:
+ switch(n->op) {
+ case OCONV: // implemented ops
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OMINUS:
+ case OCOMPLEX:
+ case OREAL:
+ case OIMAG:
+ goto yes;
+
+ case ODOT:
+ case ODOTPTR:
+ case OINDEX:
+ case OIND:
+ case ONAME:
+ goto yes;
+ }
+
+no:
+//dump("\ncomplex-no", n);
+ return 0;
+yes:
+//dump("\ncomplex-yes", n);
+ return 1;
+}
+
+void
+complexgen(Node *n, Node *res)
+{
+ Node *nl, *nr;
+ Node tnl, tnr;
+ Node n1, n2, tmp;
+ int tl, tr;
+
+ if(debug['g']) {
+ dump("\ncomplexgen-n", n);
+ dump("complexgen-res", res);
+ }
+
+ // pick off float/complex opcodes
+ switch(n->op) {
+ case OCOMPLEX:
+ if(res->addable) {
+ subnode(&n1, &n2, res);
+ tempname(&tmp, n1.type);
+ cgen(n->left, &tmp);
+ cgen(n->right, &n2);
+ cgen(&tmp, &n1);
+ return;
+ }
+ break;
+
+ case OREAL:
+ case OIMAG:
+ nl = n->left;
+ if(!nl->addable) {
+ tempname(&tmp, nl->type);
+ complexgen(nl, &tmp);
+ nl = &tmp;
+ }
+ subnode(&n1, &n2, nl);
+ if(n->op == OREAL) {
+ cgen(&n1, res);
+ return;
+ }
+ cgen(&n2, res);
+ return;
+ }
+
+ // perform conversion from n to res
+ tl = simsimtype(res->type);
+ tl = cplxsubtype(tl);
+ tr = simsimtype(n->type);
+ tr = cplxsubtype(tr);
+ if(tl != tr) {
+ if(!n->addable) {
+ tempname(&n1, n->type);
+ complexmove(n, &n1);
+ n = &n1;
+ }
+ complexmove(n, res);
+ return;
+ }
+
+ if(!res->addable) {
+ igen(res, &n1, N);
+ cgen(n, &n1);
+ regfree(&n1);
+ return;
+ }
+ if(n->addable) {
+ complexmove(n, res);
+ return;
+ }
+
+ switch(n->op) {
+ default:
+ dump("complexgen: unknown op", n);
+ fatal("complexgen: unknown op %O", n->op);
+
+ case ODOT:
+ case ODOTPTR:
+ case OINDEX:
+ case OIND:
+ case ONAME: // PHEAP or PPARAMREF var
+ case OCALLFUNC:
+ igen(n, &n1, res);
+ complexmove(&n1, res);
+ regfree(&n1);
+ return;
+
+ case OCONV:
+ case OADD:
+ case OSUB:
+ case OMUL:
+ case OMINUS:
+ case OCOMPLEX:
+ case OREAL:
+ case OIMAG:
+ break;
+ }
+
+ nl = n->left;
+ if(nl == N)
+ return;
+ nr = n->right;
+
+ // make both sides addable in ullman order
+ if(nr != N) {
+ if(nl->ullman > nr->ullman && !nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+ if(!nr->addable) {
+ tempname(&tnr, nr->type);
+ cgen(nr, &tnr);
+ nr = &tnr;
+ }
+ }
+ if(!nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+
+ switch(n->op) {
+ default:
+ fatal("complexgen: unknown op %O", n->op);
+ break;
+
+ case OCONV:
+ complexmove(nl, res);
+ break;
+
+ case OMINUS:
+ complexminus(nl, res);
+ break;
+
+ case OADD:
+ case OSUB:
+ complexadd(n->op, nl, nr, res);
+ break;
+
+ case OMUL:
+ complexmul(nl, nr, res);
+ break;
+ }
+}
+
+void
+complexbool(int op, Node *nl, Node *nr, int true, Prog *to)
+{
+ Node tnl, tnr;
+ Node n1, n2, n3, n4;
+ Node na, nb, nc;
+
+ // make both sides addable in ullman order
+ if(nr != N) {
+ if(nl->ullman > nr->ullman && !nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+ if(!nr->addable) {
+ tempname(&tnr, nr->type);
+ cgen(nr, &tnr);
+ nr = &tnr;
+ }
+ }
+ if(!nl->addable) {
+ tempname(&tnl, nl->type);
+ cgen(nl, &tnl);
+ nl = &tnl;
+ }
+
+ // build tree
+ // real(l) == real(r) && imag(l) == imag(r)
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+
+ memset(&na, 0, sizeof(na));
+ na.op = OANDAND;
+ na.left = &nb;
+ na.right = &nc;
+ na.type = types[TBOOL];
+
+ memset(&nb, 0, sizeof(na));
+ nb.op = OEQ;
+ nb.left = &n1;
+ nb.right = &n3;
+ nb.type = types[TBOOL];
+
+ memset(&nc, 0, sizeof(na));
+ nc.op = OEQ;
+ nc.left = &n2;
+ nc.right = &n4;
+ nc.type = types[TBOOL];
+
+ if(op == ONE)
+ true = !true;
+
+ bgen(&na, true, to);
+}
+
+void
+nodfconst(Node *n, Type *t, Mpflt* fval)
+{
+ memset(n, 0, sizeof(*n));
+ n->op = OLITERAL;
+ n->addable = 1;
+ ullmancalc(n);
+ n->val.u.fval = fval;
+ n->val.ctype = CTFLT;
+ n->type = t;
+
+ if(!isfloat[t->etype])
+ fatal("nodfconst: bad type %T", t);
+}
+
+// break addable nc-complex into nr-real and ni-imaginary
+static void
+subnode(Node *nr, Node *ni, Node *nc)
+{
+ int tc;
+ Type *t;
+
+ if(!nc->addable)
+ fatal("subnode not addable");
+
+ tc = simsimtype(nc->type);
+ tc = cplxsubtype(tc);
+ t = types[tc];
+
+ if(nc->op == OLITERAL) {
+ nodfconst(nr, t, &nc->val.u.cval->real);
+ nodfconst(ni, t, &nc->val.u.cval->imag);
+ return;
+ }
+
+ *nr = *nc;
+ nr->type = t;
+
+ *ni = *nc;
+ ni->type = t;
+ ni->xoffset += t->width;
+}
+
+// generate code res = -nl
+static void
+minus(Node *nl, Node *res)
+{
+ Node ra;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OMINUS;
+ ra.left = nl;
+ ra.type = nl->type;
+ cgen(&ra, res);
+}
+
+// build and execute tree
+// real(res) = -real(nl)
+// imag(res) = -imag(nl)
+void
+complexminus(Node *nl, Node *res)
+{
+ Node n1, n2, n5, n6;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n5, &n6, res);
+
+ minus(&n1, &n5);
+ minus(&n2, &n6);
+}
+
+
+// build and execute tree
+// real(res) = real(nl) op real(nr)
+// imag(res) = imag(nl) op imag(nr)
+void
+complexadd(int op, Node *nl, Node *nr, Node *res)
+{
+ Node n1, n2, n3, n4, n5, n6;
+ Node ra;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+ subnode(&n5, &n6, res);
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = op;
+ ra.left = &n1;
+ ra.right = &n3;
+ ra.type = n1.type;
+ cgen(&ra, &n5);
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = op;
+ ra.left = &n2;
+ ra.right = &n4;
+ ra.type = n2.type;
+ cgen(&ra, &n6);
+}
+
+// build and execute tree
+// tmp = real(nl)*real(nr) - imag(nl)*imag(nr)
+// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr)
+// real(res) = tmp
+void
+complexmul(Node *nl, Node *nr, Node *res)
+{
+ Node n1, n2, n3, n4, n5, n6;
+ Node rm1, rm2, ra, tmp;
+
+ subnode(&n1, &n2, nl);
+ subnode(&n3, &n4, nr);
+ subnode(&n5, &n6, res);
+ tempname(&tmp, n5.type);
+
+ // real part -> tmp
+ memset(&rm1, 0, sizeof(ra));
+ rm1.op = OMUL;
+ rm1.left = &n1;
+ rm1.right = &n3;
+ rm1.type = n1.type;
+
+ memset(&rm2, 0, sizeof(ra));
+ rm2.op = OMUL;
+ rm2.left = &n2;
+ rm2.right = &n4;
+ rm2.type = n2.type;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OSUB;
+ ra.left = &rm1;
+ ra.right = &rm2;
+ ra.type = rm1.type;
+ cgen(&ra, &tmp);
+
+ // imag part
+ memset(&rm1, 0, sizeof(ra));
+ rm1.op = OMUL;
+ rm1.left = &n1;
+ rm1.right = &n4;
+ rm1.type = n1.type;
+
+ memset(&rm2, 0, sizeof(ra));
+ rm2.op = OMUL;
+ rm2.left = &n2;
+ rm2.right = &n3;
+ rm2.type = n2.type;
+
+ memset(&ra, 0, sizeof(ra));
+ ra.op = OADD;
+ ra.left = &rm1;
+ ra.right = &rm2;
+ ra.type = rm1.type;
+ cgen(&ra, &n6);
+
+ // tmp ->real part
+ cgen(&tmp, &n5);
+}