]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/gc: major evconst cleanup
authorMatthew Dempsky <mdempsky@google.com>
Thu, 4 Oct 2018 23:49:53 +0000 (16:49 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 9 Oct 2018 19:57:59 +0000 (19:57 +0000)
Major cleanup to structure the code more similarly to go/constant.

Passes "toolstash -cmp" on std cmd.

Change-Id: I3ec7a7a24e313f119b0da4095001aad02e317894
Reviewed-on: https://go-review.googlesource.com/c/139901
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/gc/const.go

index effed1f4ac152c541ac9dec026194dd98c78db41..e60e05df04530d5d3604d4e43adc9b6d8b3370f8 100644 (file)
@@ -40,7 +40,7 @@ func (v Val) Ctype() Ctype {
        switch x := v.U.(type) {
        default:
                Fatalf("unexpected Ctype for %T", v.U)
-               panic("not reached")
+               panic("unreachable")
        case nil:
                return 0
        case *NilVal:
@@ -68,7 +68,7 @@ func eqval(a, b Val) bool {
        switch x := a.U.(type) {
        default:
                Fatalf("unexpected Ctype for %T", a.U)
-               panic("not reached")
+               panic("unreachable")
        case *NilVal:
                return true
        case bool:
@@ -96,7 +96,7 @@ func (v Val) Interface() interface{} {
        switch x := v.U.(type) {
        default:
                Fatalf("unexpected Interface for %T", v.U)
-               panic("not reached")
+               panic("unreachable")
        case *NilVal:
                return nil
        case bool, string:
@@ -424,29 +424,6 @@ bad:
        return n
 }
 
-func copyval(v Val) Val {
-       switch u := v.U.(type) {
-       case *Mpint:
-               i := new(Mpint)
-               i.Set(u)
-               i.Rune = u.Rune
-               v.U = i
-
-       case *Mpflt:
-               f := newMpflt()
-               f.Set(u)
-               v.U = f
-
-       case *Mpcplx:
-               c := new(Mpcplx)
-               c.Real.Set(&u.Real)
-               c.Imag.Set(&u.Imag)
-               v.U = c
-       }
-
-       return v
-}
-
 func tocplx(v Val) Val {
        switch u := v.U.(type) {
        case *Mpint:
@@ -585,10 +562,6 @@ func tostr(v Val) Val {
                        i = u.Int64()
                }
                v.U = string(i)
-
-       case *NilVal:
-               // Can happen because of string([]byte(nil)).
-               v.U = ""
        }
 
        return v
@@ -609,50 +582,55 @@ func Isconst(n *Node, ct Ctype) bool {
        return t == ct || (ct == CTINT && t == CTRUNE)
 }
 
-// if n is constant, rewrite as OLITERAL node.
+// evconst rewrites constant expressions into OLITERAL nodes.
 func evconst(n *Node) {
-       // pick off just the opcodes that can be
-       // constant evaluated.
-       switch n.Op {
-       default:
-               return
+       nl, nr := n.Left, n.Right
 
-       case OADD,
-               OAND,
-               OANDAND,
-               OANDNOT,
-               OARRAYBYTESTR,
-               OCOM,
-               ODIV,
-               OEQ,
-               OGE,
-               OGT,
-               OLE,
-               OLSH,
-               OLT,
-               OMINUS,
-               OMOD,
-               OMUL,
-               ONE,
-               ONOT,
-               OOR,
-               OOROR,
-               OPLUS,
-               ORSH,
-               OSUB,
-               OXOR:
-               break
+       // Pick off just the opcodes that can be constant evaluated.
+       switch op := n.Op; op {
+       case OPLUS, OMINUS, OCOM, ONOT:
+               if nl.Op == OLITERAL {
+                       setconst(n, unaryOp(op, nl.Val(), n.Type))
+               }
+
+       case OADD, OSUB, OMUL, ODIV, OMOD, OOR, OXOR, OAND, OANDNOT, OOROR, OANDAND:
+               if nl.Op == OLITERAL && nr.Op == OLITERAL {
+                       setconst(n, binaryOp(nl.Val(), op, nr.Val()))
+               }
+
+       case OEQ, ONE, OLT, OLE, OGT, OGE:
+               if nl.Op == OLITERAL && nr.Op == OLITERAL {
+                       if nl.Type.IsInterface() != nr.Type.IsInterface() {
+                               // Mixed interface/non-interface
+                               // constant comparison means comparing
+                               // nil interface with some typed
+                               // constant, which is always unequal.
+                               // E.g., interface{}(nil) == (*int)(nil).
+                               setboolconst(n, op == ONE)
+                       } else {
+                               setboolconst(n, compareOp(nl.Val(), op, nr.Val()))
+                       }
+               }
+
+       case OLSH, ORSH:
+               if nl.Op == OLITERAL && nr.Op == OLITERAL {
+                       setconst(n, shiftOp(nl.Val(), op, nr.Val()))
+               }
 
        case OCONV:
-               if n.Type == nil {
-                       return
+               if n.Type != nil && okforconst[n.Type.Etype] && nl.Op == OLITERAL {
+                       // TODO(mdempsky): There should be a convval function.
+                       setconst(n, convlit1(nl, n.Type, true, false).Val())
                }
-               if !okforconst[n.Type.Etype] && n.Type.Etype != TNIL {
-                       return
+
+       case OARRAYBYTESTR:
+               // string([]byte(nil)) or string([]rune(nil))
+               if nl.Op == OLITERAL && nl.Val().Ctype() == CTNIL {
+                       setconst(n, Val{U: ""})
                }
 
-               // merge adjacent constants in the argument list.
        case OADDSTR:
+               // Merge adjacent constants in the argument list.
                s := n.List.Slice()
                for i1 := 0; i1 < len(s); i1++ {
                        if Isconst(s[i1], CTSTR) && i1+1 < len(s) && Isconst(s[i1+1], CTSTR) {
@@ -678,521 +656,292 @@ func evconst(n *Node) {
                } else {
                        n.List.Set(s)
                }
-
-               return
-       }
-
-       nl := n.Left
-       if nl == nil || nl.Type == nil {
-               return
-       }
-       if consttype(nl) == 0 {
-               return
-       }
-       wl := nl.Type.Etype
-       if isInt[wl] || isFloat[wl] || isComplex[wl] {
-               wl = TIDEAL
-       }
-
-       // avoid constant conversions in switches below
-       const (
-               CTINT_         = uint32(CTINT)
-               CTRUNE_        = uint32(CTRUNE)
-               CTFLT_         = uint32(CTFLT)
-               CTCPLX_        = uint32(CTCPLX)
-               CTSTR_         = uint32(CTSTR)
-               CTBOOL_        = uint32(CTBOOL)
-               CTNIL_         = uint32(CTNIL)
-               OCONV_         = uint32(OCONV) << 16
-               OARRAYBYTESTR_ = uint32(OARRAYBYTESTR) << 16
-               OPLUS_         = uint32(OPLUS) << 16
-               OMINUS_        = uint32(OMINUS) << 16
-               OCOM_          = uint32(OCOM) << 16
-               ONOT_          = uint32(ONOT) << 16
-               OLSH_          = uint32(OLSH) << 16
-               ORSH_          = uint32(ORSH) << 16
-               OADD_          = uint32(OADD) << 16
-               OSUB_          = uint32(OSUB) << 16
-               OMUL_          = uint32(OMUL) << 16
-               ODIV_          = uint32(ODIV) << 16
-               OMOD_          = uint32(OMOD) << 16
-               OOR_           = uint32(OOR) << 16
-               OAND_          = uint32(OAND) << 16
-               OANDNOT_       = uint32(OANDNOT) << 16
-               OXOR_          = uint32(OXOR) << 16
-               OEQ_           = uint32(OEQ) << 16
-               ONE_           = uint32(ONE) << 16
-               OLT_           = uint32(OLT) << 16
-               OLE_           = uint32(OLE) << 16
-               OGE_           = uint32(OGE) << 16
-               OGT_           = uint32(OGT) << 16
-               OOROR_         = uint32(OOROR) << 16
-               OANDAND_       = uint32(OANDAND) << 16
-       )
-
-       nr := n.Right
-       var rv Val
-       var wr types.EType
-       var ctype uint32
-       var v Val
-       if nr == nil {
-               // copy numeric value to avoid modifying
-               // nl, in case someone still refers to it (e.g. iota).
-               v = copyval(nl.Val())
-
-               // rune values are int values for the purpose of constant folding.
-               ctype = uint32(v.Ctype())
-               if ctype == CTRUNE_ {
-                       ctype = CTINT_
-               }
-
-               switch uint32(n.Op)<<16 | ctype {
-               default:
-                       if !n.Diag() {
-                               yyerror("illegal constant expression %v %v", n.Op, nl.Type)
-                               n.SetDiag(true)
-                       }
-                       return
-
-               case OCONV_ | CTNIL_,
-                       OARRAYBYTESTR_ | CTNIL_:
-                       if n.Type.IsString() {
-                               v = tostr(v)
-                               nl.Type = n.Type
-                               break
-                       }
-                       fallthrough
-               case OCONV_ | CTINT_,
-                       OCONV_ | CTFLT_,
-                       OCONV_ | CTCPLX_,
-                       OCONV_ | CTSTR_,
-                       OCONV_ | CTBOOL_:
-                       nl = convlit1(nl, n.Type, true, false)
-                       v = nl.Val()
-
-               case OPLUS_ | CTINT_:
-                       break
-
-               case OMINUS_ | CTINT_:
-                       v.U.(*Mpint).Neg()
-
-               case OCOM_ | CTINT_:
-                       et := Txxx
-                       if nl.Type != nil {
-                               et = nl.Type.Etype
-                       }
-
-                       // calculate the mask in b
-                       // result will be (a ^ mask)
-                       var b Mpint
-                       switch et {
-                       // signed guys change sign
-                       default:
-                               b.SetInt64(-1)
-
-                               // unsigned guys invert their bits
-                       case TUINT8,
-                               TUINT16,
-                               TUINT32,
-                               TUINT64,
-                               TUINT,
-                               TUINTPTR:
-                               b.Set(maxintval[et])
-                       }
-
-                       v.U.(*Mpint).Xor(&b)
-
-               case OPLUS_ | CTFLT_:
-                       break
-
-               case OMINUS_ | CTFLT_:
-                       v.U.(*Mpflt).Neg()
-
-               case OPLUS_ | CTCPLX_:
-                       break
-
-               case OMINUS_ | CTCPLX_:
-                       v.U.(*Mpcplx).Real.Neg()
-                       v.U.(*Mpcplx).Imag.Neg()
-
-               case ONOT_ | CTBOOL_:
-                       if !v.U.(bool) {
-                               goto settrue
-                       }
-                       goto setfalse
-               }
-               goto ret
        }
-       if nr.Type == nil {
-               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 {
-               if wl == TINTER || wr == TINTER {
-                       if n.Op == ONE {
-                               goto settrue
-                       }
-                       goto setfalse
-               }
-               goto illegal
-       }
-
-       // check for compatible types.
-       switch n.Op {
-       // ideal const mixes with anything but otherwise must match.
-       default:
-               if nl.Type.Etype != TIDEAL {
-                       nr = defaultlit(nr, nl.Type)
-                       n.Right = nr
-               }
-
-               if nr.Type.Etype != TIDEAL {
-                       nl = defaultlit(nl, nr.Type)
-                       n.Left = nl
-               }
-
-               if nl.Type.Etype != nr.Type.Etype {
-                       goto illegal
-               }
-
-       // right must be unsigned.
-       // left can be ideal.
-       case OLSH, ORSH:
-               nr = defaultlit(nr, types.Types[TUINT])
+}
 
-               n.Right = nr
-               if nr.Type != nil && (nr.Type.IsSigned() || !nr.Type.IsInteger()) {
-                       goto illegal
-               }
-               if nl.Val().Ctype() != CTRUNE {
-                       nl.SetVal(toint(nl.Val()))
-               }
-               nr.SetVal(toint(nr.Val()))
+func match(x, y Val) (Val, Val) {
+       switch {
+       case x.Ctype() == CTCPLX || y.Ctype() == CTCPLX:
+               return tocplx(x), tocplx(y)
+       case x.Ctype() == CTFLT || y.Ctype() == CTFLT:
+               return toflt(x), toflt(y)
        }
 
-       // copy numeric value to avoid modifying
-       // n->left, in case someone still refers to it (e.g. iota).
-       v = copyval(nl.Val())
-       rv = nr.Val()
-
-       // convert to common ideal
-       if v.Ctype() == CTCPLX || rv.Ctype() == CTCPLX {
-               v = tocplx(v)
-               rv = tocplx(rv)
-       }
+       // Mixed int/rune are fine.
+       return x, y
+}
 
-       if v.Ctype() == CTFLT || rv.Ctype() == CTFLT {
-               v = toflt(v)
-               rv = toflt(rv)
-       }
+func compareOp(x Val, op Op, y Val) bool {
+       x, y = match(x, y)
 
-       // Rune and int turns into rune.
-       if v.Ctype() == CTRUNE && rv.Ctype() == CTINT {
-               i := new(Mpint)
-               i.Set(rv.U.(*Mpint))
-               i.Rune = true
-               rv.U = i
-       }
-       if v.Ctype() == CTINT && rv.Ctype() == CTRUNE {
-               if n.Op == OLSH || n.Op == ORSH {
-                       i := new(Mpint)
-                       i.Set(rv.U.(*Mpint))
-                       rv.U = i
-               } else {
-                       i := new(Mpint)
-                       i.Set(v.U.(*Mpint))
-                       i.Rune = true
-                       v.U = i
+       switch x.Ctype() {
+       case CTNIL:
+               _, _ = x.U.(*NilVal), y.U.(*NilVal) // assert dynamic types match
+               switch op {
+               case OEQ:
+                       return true
+               case ONE:
+                       return false
                }
-       }
 
-       if v.Ctype() != rv.Ctype() {
-               // Use of undefined name as constant?
-               if (v.Ctype() == 0 || rv.Ctype() == 0) && nerrors > 0 {
-                       return
+       case CTBOOL:
+               x, y := x.U.(bool), y.U.(bool)
+               switch op {
+               case OEQ:
+                       return x == y
+               case ONE:
+                       return x != y
                }
-               Fatalf("constant type mismatch %v(%d) %v(%d)", nl.Type, v.Ctype(), nr.Type, rv.Ctype())
-       }
-
-       // rune values are int values for the purpose of constant folding.
-       ctype = uint32(v.Ctype())
-       if ctype == CTRUNE_ {
-               ctype = CTINT_
-       }
-
-       // run op
-       switch uint32(n.Op)<<16 | ctype {
-       default:
-               goto illegal
-
-       case OADD_ | CTINT_:
-               v.U.(*Mpint).Add(rv.U.(*Mpint))
-
-       case OSUB_ | CTINT_:
-               v.U.(*Mpint).Sub(rv.U.(*Mpint))
 
-       case OMUL_ | CTINT_:
-               v.U.(*Mpint).Mul(rv.U.(*Mpint))
+       case CTINT, CTRUNE:
+               x, y := x.U.(*Mpint), y.U.(*Mpint)
+               return cmpZero(x.Cmp(y), op)
 
-       case ODIV_ | CTINT_:
-               if rv.U.(*Mpint).CmpInt64(0) == 0 {
-                       yyerror("division by zero")
-                       v.U.(*Mpint).SetOverflow()
-                       break
-               }
-
-               v.U.(*Mpint).Quo(rv.U.(*Mpint))
+       case CTFLT:
+               x, y := x.U.(*Mpflt), y.U.(*Mpflt)
+               return cmpZero(x.Cmp(y), op)
 
-       case OMOD_ | CTINT_:
-               if rv.U.(*Mpint).CmpInt64(0) == 0 {
-                       yyerror("division by zero")
-                       v.U.(*Mpint).SetOverflow()
-                       break
+       case CTCPLX:
+               x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
+               eq := x.Real.Cmp(&y.Real) == 0 && x.Imag.Cmp(&y.Imag) == 0
+               switch op {
+               case OEQ:
+                       return eq
+               case ONE:
+                       return !eq
                }
 
-               v.U.(*Mpint).Rem(rv.U.(*Mpint))
-
-       case OLSH_ | CTINT_:
-               v.U.(*Mpint).Lsh(rv.U.(*Mpint))
-
-       case ORSH_ | CTINT_:
-               v.U.(*Mpint).Rsh(rv.U.(*Mpint))
-
-       case OOR_ | CTINT_:
-               v.U.(*Mpint).Or(rv.U.(*Mpint))
-
-       case OAND_ | CTINT_:
-               v.U.(*Mpint).And(rv.U.(*Mpint))
-
-       case OANDNOT_ | CTINT_:
-               v.U.(*Mpint).AndNot(rv.U.(*Mpint))
-
-       case OXOR_ | CTINT_:
-               v.U.(*Mpint).Xor(rv.U.(*Mpint))
-
-       case OADD_ | CTFLT_:
-               v.U.(*Mpflt).Add(rv.U.(*Mpflt))
+       case CTSTR:
+               x, y := x.U.(string), y.U.(string)
+               switch op {
+               case OEQ:
+                       return x == y
+               case ONE:
+                       return x != y
+               case OLT:
+                       return x < y
+               case OLE:
+                       return x <= y
+               case OGT:
+                       return x > y
+               case OGE:
+                       return x >= y
+               }
+       }
+
+       Fatalf("compareOp: bad comparison: %v %v %v", x, op, y)
+       panic("unreachable")
+}
 
-       case OSUB_ | CTFLT_:
-               v.U.(*Mpflt).Sub(rv.U.(*Mpflt))
+func cmpZero(x int, op Op) bool {
+       switch op {
+       case OEQ:
+               return x == 0
+       case ONE:
+               return x != 0
+       case OLT:
+               return x < 0
+       case OLE:
+               return x <= 0
+       case OGT:
+               return x > 0
+       case OGE:
+               return x >= 0
+       }
+
+       Fatalf("cmpZero: want comparison operator, got %v", op)
+       panic("unreachable")
+}
 
-       case OMUL_ | CTFLT_:
-               v.U.(*Mpflt).Mul(rv.U.(*Mpflt))
+func binaryOp(x Val, op Op, y Val) Val {
+       x, y = match(x, y)
 
-       case ODIV_ | CTFLT_:
-               if rv.U.(*Mpflt).CmpFloat64(0) == 0 {
-                       yyerror("division by zero")
-                       v.U.(*Mpflt).SetFloat64(1.0)
-                       break
+Outer:
+       switch x.Ctype() {
+       case CTBOOL:
+               x, y := x.U.(bool), y.U.(bool)
+               switch op {
+               case OANDAND:
+                       return Val{U: x && y}
+               case OOROR:
+                       return Val{U: x || y}
+               }
+
+       case CTINT, CTRUNE:
+               x, y := x.U.(*Mpint), y.U.(*Mpint)
+
+               u := new(Mpint)
+               u.Rune = x.Rune || y.Rune
+               u.Set(x)
+               switch op {
+               case OADD:
+                       u.Add(y)
+               case OSUB:
+                       u.Sub(y)
+               case OMUL:
+                       u.Mul(y)
+               case ODIV:
+                       if y.CmpInt64(0) == 0 {
+                               yyerror("division by zero")
+                               u.SetOverflow()
+                               break
+                       }
+                       u.Quo(y)
+               case OMOD:
+                       if y.CmpInt64(0) == 0 {
+                               yyerror("division by zero")
+                               u.SetOverflow()
+                               break
+                       }
+                       u.Rem(y)
+               case OOR:
+                       u.Or(y)
+               case OAND:
+                       u.And(y)
+               case OANDNOT:
+                       u.AndNot(y)
+               case OXOR:
+                       u.Xor(y)
+               default:
+                       break Outer
                }
+               return Val{U: u}
 
-               v.U.(*Mpflt).Quo(rv.U.(*Mpflt))
-
-       // The default case above would print 'ideal % ideal',
-       // which is not quite an ideal error.
-       case OMOD_ | CTFLT_:
-               if !n.Diag() {
+       case CTFLT:
+               x, y := x.U.(*Mpflt), y.U.(*Mpflt)
+
+               u := newMpflt()
+               u.Set(x)
+               switch op {
+               case OADD:
+                       u.Add(y)
+               case OSUB:
+                       u.Sub(y)
+               case OMUL:
+                       u.Mul(y)
+               case ODIV:
+                       if y.CmpFloat64(0) == 0 {
+                               yyerror("division by zero")
+                               u.SetFloat64(1)
+                               break
+                       }
+                       u.Quo(y)
+               case OMOD:
+                       // TODO(mdempsky): Move to typecheck.
                        yyerror("illegal constant expression: floating-point %% operation")
-                       n.SetDiag(true)
-               }
-
-               return
-
-       case OADD_ | CTCPLX_:
-               v.U.(*Mpcplx).Real.Add(&rv.U.(*Mpcplx).Real)
-               v.U.(*Mpcplx).Imag.Add(&rv.U.(*Mpcplx).Imag)
-
-       case OSUB_ | CTCPLX_:
-               v.U.(*Mpcplx).Real.Sub(&rv.U.(*Mpcplx).Real)
-               v.U.(*Mpcplx).Imag.Sub(&rv.U.(*Mpcplx).Imag)
-
-       case OMUL_ | CTCPLX_:
-               v.U.(*Mpcplx).Mul(rv.U.(*Mpcplx))
-
-       case ODIV_ | CTCPLX_:
-               if !v.U.(*Mpcplx).Div(rv.U.(*Mpcplx)) {
-                       yyerror("complex division by zero")
-                       rv.U.(*Mpcplx).Real.SetFloat64(1.0)
-                       rv.U.(*Mpcplx).Imag.SetFloat64(0.0)
-                       break
-               }
-
-       case OEQ_ | CTNIL_:
-               goto settrue
-
-       case ONE_ | CTNIL_:
-               goto setfalse
-
-       case OEQ_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) == 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case ONE_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) != 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLT_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) < 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLE_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) <= 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGE_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) >= 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGT_ | CTINT_:
-               if v.U.(*Mpint).Cmp(rv.U.(*Mpint)) > 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OEQ_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) == 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case ONE_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) != 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLT_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) < 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLE_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) <= 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGE_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) >= 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGT_ | CTFLT_:
-               if v.U.(*Mpflt).Cmp(rv.U.(*Mpflt)) > 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OEQ_ | CTCPLX_:
-               if v.U.(*Mpcplx).Real.Cmp(&rv.U.(*Mpcplx).Real) == 0 && v.U.(*Mpcplx).Imag.Cmp(&rv.U.(*Mpcplx).Imag) == 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case ONE_ | CTCPLX_:
-               if v.U.(*Mpcplx).Real.Cmp(&rv.U.(*Mpcplx).Real) != 0 || v.U.(*Mpcplx).Imag.Cmp(&rv.U.(*Mpcplx).Imag) != 0 {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OEQ_ | CTSTR_:
-               if strlit(nl) == strlit(nr) {
-                       goto settrue
-               }
-               goto setfalse
-
-       case ONE_ | CTSTR_:
-               if strlit(nl) != strlit(nr) {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLT_ | CTSTR_:
-               if strlit(nl) < strlit(nr) {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OLE_ | CTSTR_:
-               if strlit(nl) <= strlit(nr) {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGE_ | CTSTR_:
-               if strlit(nl) >= strlit(nr) {
-                       goto settrue
-               }
-               goto setfalse
-
-       case OGT_ | CTSTR_:
-               if strlit(nl) > strlit(nr) {
-                       goto settrue
+               default:
+                       break Outer
                }
-               goto setfalse
+               return Val{U: u}
 
-       case OOROR_ | CTBOOL_:
-               if v.U.(bool) || rv.U.(bool) {
-                       goto settrue
+       case CTCPLX:
+               x, y := x.U.(*Mpcplx), y.U.(*Mpcplx)
+
+               u := new(Mpcplx)
+               u.Real.Set(&x.Real)
+               u.Imag.Set(&x.Imag)
+               switch op {
+               case OADD:
+                       u.Real.Add(&y.Real)
+                       u.Imag.Add(&y.Imag)
+               case OSUB:
+                       u.Real.Sub(&y.Real)
+                       u.Imag.Sub(&y.Imag)
+               case OMUL:
+                       u.Mul(y)
+               case ODIV:
+                       if !u.Div(y) {
+                               yyerror("complex division by zero")
+                               u.Real.SetFloat64(1)
+                               u.Imag.SetFloat64(0)
+                       }
+               default:
+                       break Outer
                }
-               goto setfalse
+               return Val{U: u}
+       }
 
-       case OANDAND_ | CTBOOL_:
-               if v.U.(bool) && rv.U.(bool) {
-                       goto settrue
-               }
-               goto setfalse
+       Fatalf("binaryOp: bad operation: %v %v %v", x, op, y)
+       panic("unreachable")
+}
 
-       case OEQ_ | CTBOOL_:
-               if v.U.(bool) == rv.U.(bool) {
-                       goto settrue
+func unaryOp(op Op, x Val, t *types.Type) Val {
+       switch op {
+       case OPLUS:
+               switch x.Ctype() {
+               case CTINT, CTRUNE, CTFLT, CTCPLX:
+                       return x
+               }
+
+       case OMINUS:
+               switch x.Ctype() {
+               case CTINT, CTRUNE:
+                       x := x.U.(*Mpint)
+                       u := new(Mpint)
+                       u.Rune = x.Rune
+                       u.Set(x)
+                       u.Neg()
+                       return Val{U: u}
+
+               case CTFLT:
+                       x := x.U.(*Mpflt)
+                       u := newMpflt()
+                       u.Set(x)
+                       u.Neg()
+                       return Val{U: u}
+
+               case CTCPLX:
+                       x := x.U.(*Mpcplx)
+                       u := new(Mpcplx)
+                       u.Real.Set(&x.Real)
+                       u.Imag.Set(&x.Imag)
+                       u.Real.Neg()
+                       u.Imag.Neg()
+                       return Val{U: u}
+               }
+
+       case OCOM:
+               x := x.U.(*Mpint)
+
+               u := new(Mpint)
+               u.Rune = x.Rune
+               if t.IsSigned() || t.IsUntyped() {
+                       // Signed values change sign.
+                       u.SetInt64(-1)
+               } else {
+                       // Unsigned values invert their bits.
+                       u.Set(maxintval[t.Etype])
                }
-               goto setfalse
+               u.Xor(x)
+               return Val{U: u}
 
-       case ONE_ | CTBOOL_:
-               if v.U.(bool) != rv.U.(bool) {
-                       goto settrue
-               }
-               goto setfalse
+       case ONOT:
+               return Val{U: !x.U.(bool)}
        }
 
-ret:
-       setconst(n, v)
-       return
-
-settrue:
-       setconst(n, Val{true})
-       return
+       Fatalf("unaryOp: bad operation: %v %v", op, x)
+       panic("unreachable")
+}
 
-setfalse:
-       setconst(n, Val{false})
-       return
+func shiftOp(x Val, op Op, y Val) Val {
+       if x.Ctype() != CTRUNE {
+               x = toint(x)
+       }
+       y = toint(y)
 
-illegal:
-       if !n.Diag() {
-               yyerror("illegal constant expression: %v %v %v", nl.Type, n.Op, nr.Type)
-               n.SetDiag(true)
+       u := new(Mpint)
+       u.Set(x.U.(*Mpint))
+       u.Rune = x.U.(*Mpint).Rune
+       switch op {
+       case OLSH:
+               u.Lsh(y.U.(*Mpint))
+       case ORSH:
+               u.Rsh(y.U.(*Mpint))
+       default:
+               Fatalf("shiftOp: bad operator: %v", op)
+               panic("unreachable")
        }
+       return Val{U: u}
 }
 
 // setconst rewrites n as an OLITERAL with value v.
@@ -1223,6 +972,10 @@ func setconst(n *Node, v Val) {
        }
 }
 
+func setboolconst(n *Node, v bool) {
+       setconst(n, Val{U: v})
+}
+
 func setintconst(n *Node, v int64) {
        u := new(Mpint)
        u.SetInt64(v)