diff options
Diffstat (limited to 'src/cmd/gc/cplx.c')
-rw-r--r-- | src/cmd/gc/cplx.c | 479 |
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); +} |