summaryrefslogtreecommitdiff
path: root/src/cmd/gc/const.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/const.c')
-rw-r--r--src/cmd/gc/const.c1299
1 files changed, 1299 insertions, 0 deletions
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
new file mode 100644
index 000000000..36a64cb97
--- /dev/null
+++ b/src/cmd/gc/const.c
@@ -0,0 +1,1299 @@
+// 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 "go.h"
+#define TUP(x,y) (((x)<<16)|(y))
+
+static Val tocplx(Val);
+static Val toflt(Val);
+static Val tostr(Val);
+static Val copyval(Val);
+static void cmplxmpy(Mpcplx*, Mpcplx*);
+static void cmplxdiv(Mpcplx*, Mpcplx*);
+
+/*
+ * truncate float literal fv to 32-bit or 64-bit precision
+ * according to type; return truncated value.
+ */
+Mpflt*
+truncfltlit(Mpflt *oldv, Type *t)
+{
+ double d;
+ float f;
+ Mpflt *fv;
+
+ if(t == T)
+ return oldv;
+
+ fv = mal(sizeof *fv);
+ *fv = *oldv;
+
+ // convert large precision literal floating
+ // into limited precision (float64 or float32)
+ // botch -- this assumes that compiler fp
+ // has same precision as runtime fp
+ switch(t->etype) {
+ case TFLOAT64:
+ d = mpgetflt(fv);
+ mpmovecflt(fv, d);
+ break;
+
+ case TFLOAT32:
+ d = mpgetflt(fv);
+ f = d;
+ d = f;
+ mpmovecflt(fv, d);
+ break;
+ }
+ return fv;
+}
+
+/*
+ * convert n, if literal, to type t.
+ * implicit conversion.
+ */
+void
+convlit(Node **np, Type *t)
+{
+ convlit1(np, t, 0);
+}
+
+/*
+ * convert n, if literal, to type t.
+ * return a new node if necessary
+ * (if n is a named constant, can't edit n->type directly).
+ */
+void
+convlit1(Node **np, Type *t, int explicit)
+{
+ int ct, et;
+ Node *n, *nn;
+
+ n = *np;
+ if(n == N || t == T || n->type == T || isideal(t) || n->type == t)
+ return;
+ if(!explicit && !isideal(n->type))
+ return;
+
+ if(n->op == OLITERAL) {
+ nn = nod(OXXX, N, N);
+ *nn = *n;
+ n = nn;
+ *np = n;
+ }
+
+ switch(n->op) {
+ default:
+ if(n->type->etype == TIDEAL) {
+ convlit(&n->left, t);
+ convlit(&n->right, t);
+ n->type = t;
+ }
+ return;
+ case OLITERAL:
+ // target is invalid type for a constant? leave alone.
+ if(!okforconst[t->etype] && n->type->etype != TNIL) {
+ defaultlit(&n, T);
+ *np = n;
+ return;
+ }
+ break;
+ case OLSH:
+ case ORSH:
+ convlit1(&n->left, t, explicit && isideal(n->left->type));
+ t = n->left->type;
+ if(t != T && t->etype == TIDEAL && n->val.ctype != CTINT)
+ n->val = toint(n->val);
+ if(t != T && !isint[t->etype]) {
+ yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ t = T;
+ }
+ n->type = t;
+ return;
+ }
+
+ // avoided repeated calculations, errors
+ if(eqtype(n->type, t))
+ return;
+
+ ct = consttype(n);
+ if(ct < 0)
+ goto bad;
+
+ et = t->etype;
+ if(et == TINTER) {
+ if(ct == CTNIL && n->type == types[TNIL]) {
+ n->type = t;
+ return;
+ }
+ defaultlit(np, T);
+ return;
+ }
+
+ switch(ct) {
+ default:
+ goto bad;
+
+ case CTNIL:
+ switch(et) {
+ default:
+ n->type = T;
+ goto bad;
+
+ case TSTRING:
+ // let normal conversion code handle it
+ return;
+
+ case TARRAY:
+ if(!isslice(t))
+ goto bad;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TINTER:
+ case TMAP:
+ case TCHAN:
+ case TFUNC:
+ case TUNSAFEPTR:
+ break;
+ }
+ break;
+
+ case CTSTR:
+ case CTBOOL:
+ if(et != n->type->etype)
+ goto bad;
+ break;
+
+ case CTINT:
+ case CTFLT:
+ case CTCPLX:
+ ct = n->val.ctype;
+ if(isint[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTCPLX:
+ case CTFLT:
+ n->val = toint(n->val);
+ // flowthrough
+ case CTINT:
+ overflow(n->val, t);
+ break;
+ }
+ } else
+ if(isfloat[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTCPLX:
+ case CTINT:
+ n->val = toflt(n->val);
+ // flowthrough
+ case CTFLT:
+ overflow(n->val, t);
+ n->val.u.fval = truncfltlit(n->val.u.fval, t);
+ break;
+ }
+ } else
+ if(iscomplex[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTFLT:
+ case CTINT:
+ n->val = tocplx(n->val);
+ break;
+ case CTCPLX:
+ overflow(n->val, t);
+ break;
+ }
+ } else
+ if(et == TSTRING && ct == CTINT && explicit)
+ n->val = tostr(n->val);
+ else
+ goto bad;
+ break;
+ }
+ n->type = t;
+ return;
+
+bad:
+ if(!n->diag) {
+ yyerror("cannot convert %#N to type %T", n, t);
+ n->diag = 1;
+ }
+ if(isideal(n->type)) {
+ defaultlit(&n, T);
+ *np = n;
+ }
+ return;
+}
+
+static Val
+copyval(Val v)
+{
+ Mpint *i;
+ Mpflt *f;
+ Mpcplx *c;
+
+ switch(v.ctype) {
+ case CTINT:
+ i = mal(sizeof(*i));
+ mpmovefixfix(i, v.u.xval);
+ v.u.xval = i;
+ break;
+ case CTFLT:
+ f = mal(sizeof(*f));
+ mpmovefltflt(f, v.u.fval);
+ v.u.fval = f;
+ break;
+ case CTCPLX:
+ c = mal(sizeof(*c));
+ mpmovefltflt(&c->real, &v.u.cval->real);
+ mpmovefltflt(&c->imag, &v.u.cval->imag);
+ v.u.cval = c;
+ break;
+ }
+ return v;
+}
+
+static Val
+tocplx(Val v)
+{
+ Mpcplx *c;
+
+ switch(v.ctype) {
+ case CTINT:
+ c = mal(sizeof(*c));
+ mpmovefixflt(&c->real, v.u.xval);
+ mpmovecflt(&c->imag, 0.0);
+ v.ctype = CTCPLX;
+ v.u.cval = c;
+ break;
+ case CTFLT:
+ c = mal(sizeof(*c));
+ mpmovefltflt(&c->real, v.u.fval);
+ mpmovecflt(&c->imag, 0.0);
+ v.ctype = CTCPLX;
+ v.u.cval = c;
+ break;
+ }
+ return v;
+}
+
+static Val
+toflt(Val v)
+{
+ Mpflt *f;
+
+ switch(v.ctype) {
+ case CTINT:
+ f = mal(sizeof(*f));
+ mpmovefixflt(f, v.u.xval);
+ v.ctype = CTFLT;
+ v.u.fval = f;
+ break;
+ case CTCPLX:
+ f = mal(sizeof(*f));
+ mpmovefltflt(f, &v.u.cval->real);
+ if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+ yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+ v.ctype = CTFLT;
+ v.u.fval = f;
+ break;
+ }
+ return v;
+}
+
+Val
+toint(Val v)
+{
+ Mpint *i;
+
+ switch(v.ctype) {
+ case CTFLT:
+ i = mal(sizeof(*i));
+ if(mpmovefltfix(i, v.u.fval) < 0)
+ yyerror("constant %#F truncated to integer", v.u.fval);
+ v.ctype = CTINT;
+ v.u.xval = i;
+ break;
+ case CTCPLX:
+ i = mal(sizeof(*i));
+ if(mpmovefltfix(i, &v.u.cval->real) < 0)
+ yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag);
+ if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+ yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+ v.ctype = CTINT;
+ v.u.xval = i;
+ break;
+ }
+ return v;
+}
+
+void
+overflow(Val v, Type *t)
+{
+ // v has already been converted
+ // to appropriate form for t.
+ if(t == T || t->etype == TIDEAL)
+ return;
+ switch(v.ctype) {
+ case CTINT:
+ if(!isint[t->etype])
+ fatal("overflow: %T integer constant", t);
+ if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 ||
+ mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0)
+ yyerror("constant %B overflows %T", v.u.xval, t);
+ break;
+ case CTFLT:
+ if(!isfloat[t->etype])
+ fatal("overflow: %T floating-point constant", t);
+ if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0)
+ yyerror("constant %#F overflows %T", v.u.fval, t);
+ break;
+ case CTCPLX:
+ if(!iscomplex[t->etype])
+ fatal("overflow: %T complex constant", t);
+ if(mpcmpfltflt(&v.u.cval->real, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 ||
+ mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 ||
+ mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0)
+ yyerror("constant %#F overflows %T", v.u.fval, t);
+ break;
+ }
+}
+
+static Val
+tostr(Val v)
+{
+ Rune rune;
+ int l;
+ Strlit *s;
+
+ switch(v.ctype) {
+ case CTINT:
+ if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 ||
+ mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0)
+ yyerror("overflow in int -> string");
+ rune = mpgetfix(v.u.xval);
+ l = runelen(rune);
+ s = mal(sizeof(*s)+l);
+ s->len = l;
+ runetochar((char*)s->s, &rune);
+ memset(&v, 0, sizeof v);
+ v.ctype = CTSTR;
+ v.u.sval = s;
+ break;
+
+ case CTFLT:
+ yyerror("no float -> string");
+
+ case CTNIL:
+ memset(&v, 0, sizeof v);
+ v.ctype = CTSTR;
+ v.u.sval = mal(sizeof *s);
+ break;
+ }
+ return v;
+}
+
+int
+consttype(Node *n)
+{
+ if(n == N || n->op != OLITERAL)
+ return -1;
+ return n->val.ctype;
+}
+
+int
+isconst(Node *n, int ct)
+{
+ return consttype(n) == ct;
+}
+
+/*
+ * if n is constant, rewrite as OLITERAL node.
+ */
+void
+evconst(Node *n)
+{
+ Node *nl, *nr;
+ int32 len;
+ Strlit *str;
+ int wl, wr, lno, et;
+ Val v, rv;
+ Mpint b;
+
+ // pick off just the opcodes that can be
+ // constant evaluated.
+ switch(n->op) {
+ default:
+ return;
+ case OADD:
+ case OADDSTR:
+ case OAND:
+ case OANDAND:
+ case OANDNOT:
+ case OARRAYBYTESTR:
+ case OCOM:
+ case ODIV:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLSH:
+ case OLT:
+ case OMINUS:
+ case OMOD:
+ case OMUL:
+ case ONE:
+ case ONOT:
+ case OOR:
+ case OOROR:
+ case OPLUS:
+ case ORSH:
+ case OSUB:
+ case OXOR:
+ break;
+ case OCONV:
+ if(n->type == T)
+ return;
+ if(!okforconst[n->type->etype] && n->type->etype != TNIL)
+ return;
+ break;
+ }
+
+ nl = n->left;
+ if(nl == N || nl->type == T)
+ return;
+ if(consttype(nl) < 0)
+ return;
+ wl = nl->type->etype;
+ if(isint[wl] || isfloat[wl] || iscomplex[wl])
+ wl = TIDEAL;
+
+ nr = n->right;
+ if(nr == N)
+ goto unary;
+ if(nr->type == T)
+ return;
+ if(consttype(nr) < 0)
+ return;
+ wr = nr->type->etype;
+ if(isint[wr] || isfloat[wr] || iscomplex[wr])
+ wr = TIDEAL;
+
+ // check for compatible general types (numeric, string, etc)
+ if(wl != wr)
+ goto illegal;
+
+ // check for compatible types.
+ switch(n->op) {
+ default:
+ // ideal const mixes with anything but otherwise must match.
+ if(nl->type->etype != TIDEAL) {
+ defaultlit(&nr, nl->type);
+ n->right = nr;
+ }
+ if(nr->type->etype != TIDEAL) {
+ defaultlit(&nl, nr->type);
+ n->left = nl;
+ }
+ if(nl->type->etype != nr->type->etype)
+ goto illegal;
+ break;
+
+ case OLSH:
+ case ORSH:
+ // right must be unsigned.
+ // left can be ideal.
+ defaultlit(&nr, types[TUINT]);
+ n->right = nr;
+ if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype]))
+ goto illegal;
+ nl->val = toint(nl->val);
+ nr->val = toint(nr->val);
+ break;
+ }
+
+ // copy numeric value to avoid modifying
+ // n->left, in case someone still refers to it (e.g. iota).
+ v = nl->val;
+ if(wl == TIDEAL)
+ v = copyval(v);
+
+ rv = nr->val;
+
+ // convert to common ideal
+ if(v.ctype == CTCPLX || rv.ctype == CTCPLX) {
+ v = tocplx(v);
+ rv = tocplx(rv);
+ }
+ if(v.ctype == CTFLT || rv.ctype == CTFLT) {
+ v = toflt(v);
+ rv = toflt(rv);
+ }
+ if(v.ctype != rv.ctype) {
+ // Use of undefined name as constant?
+ if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0)
+ return;
+ fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype);
+ }
+
+ // run op
+ switch(TUP(n->op, v.ctype)) {
+ default:
+ illegal:
+ if(!n->diag) {
+ yyerror("illegal constant expression: %T %O %T",
+ nl->type, n->op, nr->type);
+ n->diag = 1;
+ }
+ return;
+
+ case TUP(OADD, CTINT):
+ mpaddfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OSUB, CTINT):
+ mpsubfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OMUL, CTINT):
+ mpmulfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ODIV, CTINT):
+ if(mpcmpfixc(rv.u.xval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecfix(v.u.xval, 1);
+ break;
+ }
+ mpdivfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OMOD, CTINT):
+ if(mpcmpfixc(rv.u.xval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecfix(v.u.xval, 1);
+ break;
+ }
+ mpmodfixfix(v.u.xval, rv.u.xval);
+ break;
+
+ case TUP(OLSH, CTINT):
+ mplshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ORSH, CTINT):
+ mprshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OOR, CTINT):
+ mporfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OAND, CTINT):
+ mpandfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OANDNOT, CTINT):
+ mpandnotfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OXOR, CTINT):
+ mpxorfixfix(v.u.xval, rv.u.xval);
+ break;
+
+ case TUP(OADD, CTFLT):
+ mpaddfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(OSUB, CTFLT):
+ mpsubfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(OMUL, CTFLT):
+ mpmulfltflt(v.u.fval, rv.u.fval);
+ break;
+ case TUP(ODIV, CTFLT):
+ if(mpcmpfltc(rv.u.fval, 0) == 0) {
+ yyerror("division by zero");
+ mpmovecflt(v.u.fval, 1.0);
+ break;
+ }
+ mpdivfltflt(v.u.fval, rv.u.fval);
+ break;
+
+ case TUP(OADD, CTCPLX):
+ mpaddfltflt(&v.u.cval->real, &rv.u.cval->real);
+ mpaddfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+ break;
+ case TUP(OSUB, CTCPLX):
+ mpsubfltflt(&v.u.cval->real, &rv.u.cval->real);
+ mpsubfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+ break;
+ case TUP(OMUL, CTCPLX):
+ cmplxmpy(v.u.cval, rv.u.cval);
+ break;
+ case TUP(ODIV, CTCPLX):
+ if(mpcmpfltc(&rv.u.cval->real, 0) == 0 &&
+ mpcmpfltc(&rv.u.cval->imag, 0) == 0) {
+ yyerror("complex division by zero");
+ mpmovecflt(&rv.u.cval->real, 1.0);
+ mpmovecflt(&rv.u.cval->imag, 0.0);
+ break;
+ }
+ cmplxdiv(v.u.cval, rv.u.cval);
+ break;
+
+ case TUP(OEQ, CTNIL):
+ goto settrue;
+ case TUP(ONE, CTNIL):
+ goto setfalse;
+
+ case TUP(OEQ, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTINT):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) >= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTFLT):
+ if(mpcmpfltflt(v.u.fval, rv.u.fval) > 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTCPLX):
+ if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) == 0 &&
+ mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTCPLX):
+ if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) != 0 ||
+ mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) != 0)
+ goto settrue;
+ goto setfalse;
+
+ case TUP(OEQ, CTSTR):
+ if(cmpslit(nl, nr) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTSTR):
+ if(cmpslit(nl, nr) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTSTR):
+ if(cmpslit(nl, nr) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTSTR):
+ if(cmpslit(nl, nr) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTSTR):
+ if(cmpslit(nl, nr) >= 0l)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTSTR):
+ if(cmpslit(nl, nr) > 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OADDSTR, CTSTR):
+ len = v.u.sval->len + rv.u.sval->len;
+ str = mal(sizeof(*str) + len);
+ str->len = len;
+ memcpy(str->s, v.u.sval->s, v.u.sval->len);
+ memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len);
+ str->len = len;
+ v.u.sval = str;
+ break;
+
+ case TUP(OOROR, CTBOOL):
+ if(v.u.bval || rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(OANDAND, CTBOOL):
+ if(v.u.bval && rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(OEQ, CTBOOL):
+ if(v.u.bval == rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTBOOL):
+ if(v.u.bval != rv.u.bval)
+ goto settrue;
+ goto setfalse;
+ }
+ goto ret;
+
+unary:
+ // copy numeric value to avoid modifying
+ // nl, in case someone still refers to it (e.g. iota).
+ v = nl->val;
+ if(wl == TIDEAL)
+ v = copyval(v);
+
+ switch(TUP(n->op, v.ctype)) {
+ default:
+ if(!n->diag) {
+ yyerror("illegal constant expression %O %T", n->op, nl->type);
+ n->diag = 1;
+ }
+ return;
+
+ case TUP(OCONV, CTNIL):
+ case TUP(OARRAYBYTESTR, CTNIL):
+ if(n->type->etype == TSTRING) {
+ v = tostr(v);
+ nl->type = n->type;
+ break;
+ }
+ // fall through
+ case TUP(OCONV, CTINT):
+ case TUP(OCONV, CTFLT):
+ case TUP(OCONV, CTSTR):
+ convlit1(&nl, n->type, 1);
+ break;
+
+ case TUP(OPLUS, CTINT):
+ break;
+ case TUP(OMINUS, CTINT):
+ mpnegfix(v.u.xval);
+ break;
+ case TUP(OCOM, CTINT):
+ et = Txxx;
+ if(nl->type != T)
+ et = nl->type->etype;
+
+ // calculate the mask in b
+ // result will be (a ^ mask)
+ switch(et) {
+ default:
+ // signed guys change sign
+ mpmovecfix(&b, -1);
+ break;
+
+ case TUINT8:
+ case TUINT16:
+ case TUINT32:
+ case TUINT64:
+ case TUINT:
+ case TUINTPTR:
+ // unsigned guys invert their bits
+ mpmovefixfix(&b, maxintval[et]);
+ break;
+ }
+ mpxorfixfix(v.u.xval, &b);
+ break;
+
+ case TUP(OPLUS, CTFLT):
+ break;
+ case TUP(OMINUS, CTFLT):
+ mpnegflt(v.u.fval);
+ break;
+
+ case TUP(OPLUS, CTCPLX):
+ break;
+ case TUP(OMINUS, CTCPLX):
+ mpnegflt(&v.u.cval->real);
+ mpnegflt(&v.u.cval->imag);
+ break;
+
+ case TUP(ONOT, CTBOOL):
+ if(!v.u.bval)
+ goto settrue;
+ goto setfalse;
+ }
+
+ret:
+ // rewrite n in place.
+ *n = *nl;
+ n->val = v;
+
+ // check range.
+ lno = setlineno(n);
+ overflow(v, n->type);
+ lineno = lno;
+
+ // truncate precision for non-ideal float.
+ if(v.ctype == CTFLT && n->type->etype != TIDEAL)
+ n->val.u.fval = truncfltlit(v.u.fval, n->type);
+ return;
+
+settrue:
+ *n = *nodbool(1);
+ return;
+
+setfalse:
+ *n = *nodbool(0);
+ return;
+}
+
+Node*
+nodlit(Val v)
+{
+ Node *n;
+
+ n = nod(OLITERAL, N, N);
+ n->val = v;
+ switch(v.ctype) {
+ default:
+ fatal("nodlit ctype %d", v.ctype);
+ case CTSTR:
+ n->type = idealstring;
+ break;
+ case CTBOOL:
+ n->type = idealbool;
+ break;
+ case CTINT:
+ case CTFLT:
+ case CTCPLX:
+ n->type = types[TIDEAL];
+ break;
+ case CTNIL:
+ n->type = types[TNIL];
+ break;
+ }
+ return n;
+}
+
+Node*
+nodcplxlit(Val r, Val i)
+{
+ Node *n;
+ Mpcplx *c;
+
+ r = toflt(r);
+ i = toflt(i);
+
+ c = mal(sizeof(*c));
+ n = nod(OLITERAL, N, N);
+ n->type = types[TIDEAL];
+ n->val.u.cval = c;
+ n->val.ctype = CTCPLX;
+
+ if(r.ctype != CTFLT || i.ctype != CTFLT)
+ fatal("nodcplxlit ctype %d/%d", r.ctype, i.ctype);
+
+ mpmovefltflt(&c->real, r.u.fval);
+ mpmovefltflt(&c->imag, i.u.fval);
+ return n;
+}
+
+// TODO(rsc): combine with convlit
+void
+defaultlit(Node **np, Type *t)
+{
+ int lno;
+ Node *n, *nn;
+
+ n = *np;
+ if(n == N || !isideal(n->type))
+ return;
+
+ switch(n->op) {
+ case OLITERAL:
+ nn = nod(OXXX, N, N);
+ *nn = *n;
+ n = nn;
+ *np = n;
+ break;
+ case OLSH:
+ case ORSH:
+ defaultlit(&n->left, t);
+ t = n->left->type;
+ if(t != T && !isint[t->etype]) {
+ yyerror("invalid operation: %#N (shift of type %T)", n, t);
+ t = T;
+ }
+ n->type = t;
+ return;
+ default:
+ if(n->left == N) {
+ dump("defaultlit", n);
+ fatal("defaultlit");
+ }
+ // n is ideal, so left and right must both be ideal.
+ // n has not been computed as a constant value,
+ // so either left or right must not be constant.
+ // The only 'ideal' non-constant expressions are shifts. Ugh.
+ // If one of these is a shift and the other is not, use that type.
+ // When compiling x := 1<<i + 3.14, this means we try to push
+ // the float64 down into the 1<<i, producing the correct error
+ // (cannot shift float64).
+ if(t == T && (n->right->op == OLSH || n->right->op == ORSH)) {
+ defaultlit(&n->left, T);
+ defaultlit(&n->right, n->left->type);
+ } else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) {
+ defaultlit(&n->right, T);
+ defaultlit(&n->left, n->right->type);
+ } else {
+ defaultlit(&n->left, t);
+ defaultlit(&n->right, t);
+ }
+ if(n->type == idealbool || n->type == idealstring)
+ n->type = types[n->type->etype];
+ else
+ n->type = n->left->type;
+ return;
+ }
+
+ lno = setlineno(n);
+ switch(n->val.ctype) {
+ default:
+ if(t != T) {
+ convlit(np, t);
+ break;
+ }
+ if(n->val.ctype == CTNIL) {
+ lineno = lno;
+ yyerror("use of untyped nil");
+ n->type = T;
+ break;
+ }
+ if(n->val.ctype == CTSTR) {
+ n->type = types[TSTRING];
+ break;
+ }
+ yyerror("defaultlit: unknown literal: %#N", n);
+ break;
+ case CTBOOL:
+ n->type = types[TBOOL];
+ if(t != T && t->etype == TBOOL)
+ n->type = t;
+ break;
+ case CTINT:
+ n->type = types[TINT];
+ goto num;
+ case CTFLT:
+ n->type = types[TFLOAT64];
+ goto num;
+ case CTCPLX:
+ n->type = types[TCOMPLEX128];
+ goto num;
+ num:
+ if(t != T) {
+ if(isint[t->etype]) {
+ n->type = t;
+ n->val = toint(n->val);
+ }
+ else
+ if(isfloat[t->etype]) {
+ n->type = t;
+ n->val = toflt(n->val);
+ }
+ else
+ if(iscomplex[t->etype]) {
+ n->type = t;
+ n->val = tocplx(n->val);
+ }
+ }
+ overflow(n->val, n->type);
+ break;
+ }
+ lineno = lno;
+}
+
+/*
+ * defaultlit on both nodes simultaneously;
+ * if they're both ideal going in they better
+ * get the same type going out.
+ * force means must assign concrete (non-ideal) type.
+ */
+void
+defaultlit2(Node **lp, Node **rp, int force)
+{
+ Node *l, *r;
+
+ l = *lp;
+ r = *rp;
+ if(l->type == T || r->type == T)
+ return;
+ if(!isideal(l->type)) {
+ convlit(rp, l->type);
+ return;
+ }
+ if(!isideal(r->type)) {
+ convlit(lp, r->type);
+ return;
+ }
+ if(!force)
+ return;
+ if(isconst(l, CTCPLX) || isconst(r, CTCPLX)) {
+ convlit(lp, types[TCOMPLEX128]);
+ convlit(rp, types[TCOMPLEX128]);
+ return;
+ }
+ if(isconst(l, CTFLT) || isconst(r, CTFLT)) {
+ convlit(lp, types[TFLOAT64]);
+ convlit(rp, types[TFLOAT64]);
+ return;
+ }
+ convlit(lp, types[TINT]);
+ convlit(rp, types[TINT]);
+}
+
+int
+cmpslit(Node *l, Node *r)
+{
+ int32 l1, l2, i, m;
+ uchar *s1, *s2;
+
+ l1 = l->val.u.sval->len;
+ l2 = r->val.u.sval->len;
+ s1 = (uchar*)l->val.u.sval->s;
+ s2 = (uchar*)r->val.u.sval->s;
+
+ m = l1;
+ if(l2 < m)
+ m = l2;
+
+ for(i=0; i<m; i++) {
+ if(s1[i] == s2[i])
+ continue;
+ if(s1[i] > s2[i])
+ return +1;
+ return -1;
+ }
+ if(l1 == l2)
+ return 0;
+ if(l1 > l2)
+ return +1;
+ return -1;
+}
+
+int
+smallintconst(Node *n)
+{
+ if(n->op == OLITERAL && n->type != T)
+ switch(simtype[n->type->etype]) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TBOOL:
+ case TPTR32:
+ return 1;
+ case TINT64:
+ case TUINT64:
+ if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0
+ || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+ break;
+ return 1;
+ }
+ return 0;
+}
+
+long
+nonnegconst(Node *n)
+{
+ if(n->op == OLITERAL && n->type != T)
+ switch(simtype[n->type->etype]) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TIDEAL:
+ // check negative and 2^31
+ if(mpcmpfixfix(n->val.u.xval, minintval[TUINT32]) < 0
+ || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+ break;
+ return mpgetfix(n->val.u.xval);
+ }
+ return -1;
+}
+
+/*
+ * convert x to type et and back to int64
+ * for sign extension and truncation.
+ */
+static int64
+iconv(int64 x, int et)
+{
+ switch(et) {
+ case TINT8:
+ x = (int8)x;
+ break;
+ case TUINT8:
+ x = (uint8)x;
+ break;
+ case TINT16:
+ x = (int16)x;
+ break;
+ case TUINT16:
+ x = (uint64)x;
+ break;
+ case TINT32:
+ x = (int32)x;
+ break;
+ case TUINT32:
+ x = (uint32)x;
+ break;
+ case TINT64:
+ case TUINT64:
+ break;
+ }
+ return x;
+}
+
+/*
+ * convert constant val to type t; leave in con.
+ * for back end.
+ */
+void
+convconst(Node *con, Type *t, Val *val)
+{
+ int64 i;
+ int tt;
+
+ tt = simsimtype(t);
+
+ // copy the constant for conversion
+ nodconst(con, types[TINT8], 0);
+ con->type = t;
+ con->val = *val;
+
+ if(isint[tt]) {
+ con->val.ctype = CTINT;
+ con->val.u.xval = mal(sizeof *con->val.u.xval);
+ switch(val->ctype) {
+ default:
+ fatal("convconst ctype=%d %lT", val->ctype, t);
+ case CTINT:
+ i = mpgetfix(val->u.xval);
+ break;
+ case CTBOOL:
+ i = val->u.bval;
+ break;
+ case CTNIL:
+ i = 0;
+ break;
+ }
+ i = iconv(i, tt);
+ mpmovecfix(con->val.u.xval, i);
+ return;
+ }
+
+ if(isfloat[tt]) {
+ con->val = toflt(con->val);
+ if(con->val.ctype != CTFLT)
+ fatal("convconst ctype=%d %T", con->val.ctype, t);
+ if(tt == TFLOAT32)
+ con->val.u.fval = truncfltlit(con->val.u.fval, t);
+ return;
+ }
+
+ if(iscomplex[tt]) {
+ con->val = tocplx(con->val);
+ if(tt == TCOMPLEX64) {
+ con->val.u.cval->real = *truncfltlit(&con->val.u.cval->real, types[TFLOAT32]);
+ con->val.u.cval->imag = *truncfltlit(&con->val.u.cval->imag, types[TFLOAT32]);
+ }
+ return;
+ }
+
+ fatal("convconst %lT constant", t);
+
+}
+
+// complex multiply v *= rv
+// (a, b) * (c, d) = (a*c - b*d, b*c + a*d)
+static void
+cmplxmpy(Mpcplx *v, Mpcplx *rv)
+{
+ Mpflt ac, bd, bc, ad;
+
+ mpmovefltflt(&ac, &v->real);
+ mpmulfltflt(&ac, &rv->real); // ac
+
+ mpmovefltflt(&bd, &v->imag);
+ mpmulfltflt(&bd, &rv->imag); // bd
+
+ mpmovefltflt(&bc, &v->imag);
+ mpmulfltflt(&bc, &rv->real); // bc
+
+ mpmovefltflt(&ad, &v->real);
+ mpmulfltflt(&ad, &rv->imag); // ad
+
+ mpmovefltflt(&v->real, &ac);
+ mpsubfltflt(&v->real, &bd); // ac-bd
+
+ mpmovefltflt(&v->imag, &bc);
+ mpaddfltflt(&v->imag, &ad); // bc+ad
+}
+
+// complex divide v /= rv
+// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d)
+static void
+cmplxdiv(Mpcplx *v, Mpcplx *rv)
+{
+ Mpflt ac, bd, bc, ad, cc_plus_dd;
+
+ mpmovefltflt(&cc_plus_dd, &rv->real);
+ mpmulfltflt(&cc_plus_dd, &rv->real); // cc
+
+ mpmovefltflt(&ac, &rv->imag);
+ mpmulfltflt(&ac, &rv->imag); // dd
+
+ mpaddfltflt(&cc_plus_dd, &ac); // cc+dd
+
+ mpmovefltflt(&ac, &v->real);
+ mpmulfltflt(&ac, &rv->real); // ac
+
+ mpmovefltflt(&bd, &v->imag);
+ mpmulfltflt(&bd, &rv->imag); // bd
+
+ mpmovefltflt(&bc, &v->imag);
+ mpmulfltflt(&bc, &rv->real); // bc
+
+ mpmovefltflt(&ad, &v->real);
+ mpmulfltflt(&ad, &rv->imag); // ad
+
+ mpmovefltflt(&v->real, &ac);
+ mpaddfltflt(&v->real, &bd); // ac+bd
+ mpdivfltflt(&v->real, &cc_plus_dd); // (ac+bd)/(cc+dd)
+
+ mpmovefltflt(&v->imag, &bc);
+ mpsubfltflt(&v->imag, &ad); // bc-ad
+ mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd)
+}