blob: 890cf7f10c29d6daaf2c8804e55785da0e2f7d91 [file] [log] [blame]
// 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);
}