root/src/cmd/gc/cplx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. overlap
  2. complexmove
  3. complexop
  4. complexgen
  5. complexbool
  6. nodfconst
  7. subnode
  8. minus
  9. complexminus
  10. complexadd
  11. complexmul

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <u.h>
#include <libc.h>
#include "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, tmp;

        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(&tmp, f->type);
                        complexmove(f, &tmp);
                        f = &tmp;
                }

                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);
        }
        
        while(n->op == OCONVNOP)
                n = n->left;

        // 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:
        case OCALLMETH:
        case OCALLINTER:
                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, int likely, 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, likely, 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);
}

/* [<][>][^][v][top][bottom][index][help] */