// 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 #include #include "go.h" #define TUP(x,y) (((x)<<16)|(y)) /*c2go int TUP(int, int); */ 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; Mpflt *fv; Val v; if(t == T) return oldv; memset(&v, 0, sizeof v); v.ctype = CTFLT; v.u.fval = oldv; overflow(v, t); fv = mal(sizeof *fv); *fv = *oldv; // convert large precision literal floating // into limited precision (float64 or float32) switch(t->etype) { case TFLOAT64: d = mpgetflt(fv); mpmovecflt(fv, d); break; case TFLOAT32: d = mpgetflt32(fv); 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 == idealbool) { if(t->etype == TBOOL) n->type = t; else n->type = types[TBOOL]; } 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; case OCOMPLEX: if(n->type->etype == TIDEAL) { switch(t->etype) { default: // If trying to convert to non-complex type, // leave as complex128 and let typechecker complain. t = types[TCOMPLEX128]; //fallthrough case TCOMPLEX128: n->type = t; convlit(&n->left, types[TFLOAT64]); convlit(&n->right, types[TFLOAT64]); break; case TCOMPLEX64: n->type = t; convlit(&n->left, types[TFLOAT32]); convlit(&n->right, types[TFLOAT32]); break; } } 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; case TUINTPTR: // A nil literal may be converted to uintptr // if it is an unsafe.Pointer if(n->type->etype == TUNSAFEPTR) { n->val.u.xval = mal(sizeof(*n->val.u.xval)); mpmovecfix(n->val.u.xval, 0); n->val.ctype = CTINT; } else goto bad; } break; case CTSTR: case CTBOOL: if(et != n->type->etype) goto bad; break; case CTINT: case CTRUNE: case CTFLT: case CTCPLX: ct = n->val.ctype; if(isint[et]) { switch(ct) { default: goto bad; case CTCPLX: case CTFLT: case CTRUNE: 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: case CTRUNE: n->val = toflt(n->val); // flowthrough case CTFLT: n->val.u.fval = truncfltlit(n->val.u.fval, t); break; } } else if(iscomplex[et]) { switch(ct) { default: goto bad; case CTFLT: case CTINT: case CTRUNE: n->val = tocplx(n->val); break; case CTCPLX: overflow(n->val, t); break; } } else if(et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit) n->val = tostr(n->val); else goto bad; break; } n->type = t; return; bad: if(!n->diag) { if(!t->broke) 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: case CTRUNE: 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: case CTRUNE: 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: case CTRUNE: 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 CTRUNE: v.ctype = CTINT; break; 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; } int doesoverflow(Val v, Type *t) { switch(v.ctype) { case CTINT: case CTRUNE: 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) return 1; 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) return 1; 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) return 1; break; } return 0; } void overflow(Val v, Type *t) { // v has already been converted // to appropriate form for t. if(t == T || t->etype == TIDEAL) return; if(!doesoverflow(v, t)) return; switch(v.ctype) { case CTINT: case CTRUNE: yyerror("constant %B overflows %T", v.u.xval, t); break; case CTFLT: yyerror("constant %#F overflows %T", v.u.fval, t); break; case CTCPLX: 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: case CTRUNE: 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) { int t; t = consttype(n); // If the caller is asking for CTINT, allow CTRUNE too. // Makes life easier for back ends. return t == ct || (ct == CTINT && t == CTRUNE); } static Node* saveorig(Node *n) { Node *n1; if(n == n->orig) { // duplicate node for n->orig. n1 = nod(OLITERAL, N, N); n->orig = n1; *n1 = *n; } return n->orig; } /* * if n is constant, rewrite as OLITERAL node. */ void evconst(Node *n) { Node *nl, *nr, *norig; int32 len; Strlit *str; int wl, wr, lno, et; Val v, rv; Mpint b; NodeList *l1, *l2; // pick off just the opcodes that can be // constant evaluated. switch(n->op) { default: return; case OADD: 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; case OADDSTR: // merge adjacent constants in the argument list. for(l1=n->list; l1 != nil; l1= l1->next) { if(isconst(l1->n, CTSTR) && l1->next != nil && isconst(l1->next->n, CTSTR)) { l2 = l1; len = 0; while(l2 != nil && isconst(l2->n, CTSTR)) { nr = l2->n; len += nr->val.u.sval->len; l2 = l2->next; } // merge from l1 up to but not including l2 str = mal(sizeof(*str) + len); str->len = len; len = 0; l2 = l1; while(l2 != nil && isconst(l2->n, CTSTR)) { nr = l2->n; memmove(str->s+len, nr->val.u.sval->s, nr->val.u.sval->len); len += nr->val.u.sval->len; l2 = l2->next; } nl = nod(OXXX, N, N); *nl = *l1->n; nl->orig = nl; nl->val.ctype = CTSTR; nl->val.u.sval = str; l1->n = nl; l1->next = l2; } } // fix list end pointer. for(l2=n->list; l2 != nil; l2=l2->next) n->list->end = l2; // collapse single-constant list to single constant. if(count(n->list) == 1 && isconst(n->list->n, CTSTR)) { n->op = OLITERAL; n->val = n->list->n->val; } return; } 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; if(nl->val.ctype != CTRUNE) 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); } // Rune and int turns into rune. if(v.ctype == CTRUNE && rv.ctype == CTINT) rv.ctype = CTRUNE; if(v.ctype == CTINT && rv.ctype == CTRUNE) { if(n->op == OLSH || n->op == ORSH) rv.ctype = CTINT; else v.ctype = CTRUNE; } 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): case TUP(OADD, CTRUNE): mpaddfixfix(v.u.xval, rv.u.xval, 0); break; case TUP(OSUB, CTINT): case TUP(OSUB, CTRUNE): mpsubfixfix(v.u.xval, rv.u.xval); break; case TUP(OMUL, CTINT): case TUP(OMUL, CTRUNE): mpmulfixfix(v.u.xval, rv.u.xval); break; case TUP(ODIV, CTINT): case TUP(ODIV, CTRUNE): 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): case TUP(OMOD, CTRUNE): 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): case TUP(OLSH, CTRUNE): mplshfixfix(v.u.xval, rv.u.xval); break; case TUP(ORSH, CTINT): case TUP(ORSH, CTRUNE): mprshfixfix(v.u.xval, rv.u.xval); break; case TUP(OOR, CTINT): case TUP(OOR, CTRUNE): mporfixfix(v.u.xval, rv.u.xval); break; case TUP(OAND, CTINT): case TUP(OAND, CTRUNE): mpandfixfix(v.u.xval, rv.u.xval); break; case TUP(OANDNOT, CTINT): case TUP(OANDNOT, CTRUNE): mpandnotfixfix(v.u.xval, rv.u.xval); break; case TUP(OXOR, CTINT): case TUP(OXOR, CTRUNE): 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(OMOD, CTFLT): // The default case above would print 'ideal % ideal', // which is not quite an ideal error. if(!n->diag) { yyerror("illegal constant expression: floating-point %% operation"); n->diag = 1; } return; 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): case TUP(OEQ, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0) goto settrue; goto setfalse; case TUP(ONE, CTINT): case TUP(ONE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0) goto settrue; goto setfalse; case TUP(OLT, CTINT): case TUP(OLT, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0) goto settrue; goto setfalse; case TUP(OLE, CTINT): case TUP(OLE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0) goto settrue; goto setfalse; case TUP(OGE, CTINT): case TUP(OGE, CTRUNE): if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0) goto settrue; goto setfalse; case TUP(OGT, CTINT): case TUP(OGT, CTRUNE): 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(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, CTRUNE): case TUP(OCONV, CTFLT): case TUP(OCONV, CTSTR): convlit1(&nl, n->type, 1); v = nl->val; break; case TUP(OPLUS, CTINT): case TUP(OPLUS, CTRUNE): break; case TUP(OMINUS, CTINT): case TUP(OMINUS, CTRUNE): mpnegfix(v.u.xval); break; case TUP(OCOM, CTINT): case TUP(OCOM, CTRUNE): 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: norig = saveorig(n); *n = *nl; // restore value of n->orig. n->orig = norig; 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: norig = saveorig(n); *n = *nodbool(1); n->orig = norig; return; setfalse: norig = saveorig(n); *n = *nodbool(0); n->orig = norig; 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 CTRUNE: 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; } // idealkind returns a constant kind like consttype // but for an arbitrary "ideal" (untyped constant) expression. static int idealkind(Node *n) { int k1, k2; if(n == N || !isideal(n->type)) return CTxxx; switch(n->op) { default: return CTxxx; case OLITERAL: return n->val.ctype; case OADD: case OAND: case OANDNOT: case OCOM: case ODIV: case OMINUS: case OMOD: case OMUL: case OSUB: case OXOR: case OOR: case OPLUS: // numeric kinds. k1 = idealkind(n->left); k2 = idealkind(n->right); if(k1 > k2) return k1; else return k2; case OREAL: case OIMAG: return CTFLT; case OCOMPLEX: return CTCPLX; case OADDSTR: return CTSTR; case OANDAND: case OEQ: case OGE: case OGT: case OLE: case OLT: case ONE: case ONOT: case OOROR: case OCMPSTR: case OCMPIFACE: return CTBOOL; case OLSH: case ORSH: // shifts (beware!). return idealkind(n->left); } } void defaultlit(Node **np, Type *t) { int lno; int ctype; Node *n, *nn; Type *t1; n = *np; if(n == N || !isideal(n->type)) return; if(n->op == OLITERAL) { nn = nod(OXXX, N, N); *nn = *n; n = nn; *np = n; } lno = setlineno(n); ctype = idealkind(n); switch(ctype) { default: if(t != T) { convlit(np, t); return; } if(n->val.ctype == CTNIL) { lineno = lno; if(!n->diag) { yyerror("use of untyped nil"); n->diag = 1; } n->type = T; break; } if(n->val.ctype == CTSTR) { t1 = types[TSTRING]; convlit(np, t1); break; } yyerror("defaultlit: unknown literal: %N", n); break; case CTxxx: fatal("defaultlit: idealkind is CTxxx: %+N", n); break; case CTBOOL: t1 = types[TBOOL]; if(t != T && t->etype == TBOOL) t1 = t; convlit(np, t1); break; case CTINT: t1 = types[TINT]; goto num; case CTRUNE: t1 = runetype; goto num; case CTFLT: t1 = types[TFLOAT64]; goto num; case CTCPLX: t1 = types[TCOMPLEX128]; goto num; num: if(t != T) { if(isint[t->etype]) { t1 = t; n->val = toint(n->val); } else if(isfloat[t->etype]) { t1 = t; n->val = toflt(n->val); } else if(iscomplex[t->etype]) { t1 = t; n->val = tocplx(n->val); } } overflow(n->val, t1); convlit(np, t1); 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; int lkind, rkind; 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(l->type->etype == TBOOL) { convlit(lp, types[TBOOL]); convlit(rp, types[TBOOL]); } lkind = idealkind(l); rkind = idealkind(r); if(lkind == CTCPLX || rkind == CTCPLX) { convlit(lp, types[TCOMPLEX128]); convlit(rp, types[TCOMPLEX128]); return; } if(lkind == CTFLT || rkind == CTFLT) { convlit(lp, types[TFLOAT64]); convlit(rp, types[TFLOAT64]); return; } if(lkind == CTRUNE || rkind == CTRUNE) { convlit(lp, runetype); convlit(rp, runetype); 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 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 && isconst(n, CTINT) && 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 TIDEAL: case TINT64: case TUINT64: case TPTR64: 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: case CTRUNE: 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) } static int hascallchan(Node*); // Is n a Go language constant (as opposed to a compile-time constant)? // Expressions derived from nil, like string([]byte(nil)), while they // may be known at compile time, are not Go language constants. // Only called for expressions known to evaluated to compile-time // constants. int isgoconst(Node *n) { Node *l; Type *t; if(n->orig != N) n = n->orig; switch(n->op) { case OADD: case OADDSTR: case OAND: case OANDAND: case OANDNOT: 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: case OIOTA: case OCOMPLEX: case OREAL: case OIMAG: if(isgoconst(n->left) && (n->right == N || isgoconst(n->right))) return 1; break; case OCONV: if(okforconst[n->type->etype] && isgoconst(n->left)) return 1; break; case OLEN: case OCAP: l = n->left; if(isgoconst(l)) return 1; // Special case: len/cap is constant when applied to array or // pointer to array when the expression does not contain // function calls or channel receive operations. t = l->type; if(t != T && isptr[t->etype]) t = t->type; if(isfixedarray(t) && !hascallchan(l)) return 1; break; case OLITERAL: if(n->val.ctype != CTNIL) return 1; break; case ONAME: l = n->sym->def; if(l && l->op == OLITERAL && n->val.ctype != CTNIL) return 1; break; case ONONAME: if(n->sym->def != N && n->sym->def->op == OIOTA) return 1; break; case OCALL: // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. l = n->left; while(l->op == OPAREN) l = l->left; if(l->op != ONAME || l->sym->pkg != unsafepkg) break; if(strcmp(l->sym->name, "Alignof") == 0 || strcmp(l->sym->name, "Offsetof") == 0 || strcmp(l->sym->name, "Sizeof") == 0) return 1; break; } //dump("nonconst", n); return 0; } static int hascallchan(Node *n) { NodeList *l; if(n == N) return 0; switch(n->op) { case OAPPEND: case OCALL: case OCALLFUNC: case OCALLINTER: case OCALLMETH: case OCAP: case OCLOSE: case OCOMPLEX: case OCOPY: case ODELETE: case OIMAG: case OLEN: case OMAKE: case ONEW: case OPANIC: case OPRINT: case OPRINTN: case OREAL: case ORECOVER: case ORECV: return 1; } if(hascallchan(n->left) || hascallchan(n->right)) return 1; for(l=n->list; l; l=l->next) if(hascallchan(l->n)) return 1; for(l=n->rlist; l; l=l->next) if(hascallchan(l->n)) return 1; return 0; }