From 6826287c6b1ff2e3f23611472a9d81ac5e3aa89a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 24 Nov 2020 09:37:54 -0500 Subject: [PATCH] [dev.regabi] cmd/compile: replace evconst with non-mutating version evconst is one of the largest sources of Op rewrites, which prevent separating different kinds of nodes (in this case, arithmetic nodes and OLITERAL nodes). The change in swt.go is necessary because otherwise the syntax graph ends up containing that OLEN expression multiple times, which violates the invariant that it's a tree except for ONAME, OLITERAL, and OTYPE nodes. (Before, the OLEN was overwritten by an OLITERAL, so the invariant still held, but now that we don't overwrite it, we need a different copy for each instance.) Passes toolstash -cmp. Change-Id: Ia004774ab6852fb384805d0f9f9f234b40842811 Reviewed-on: https://go-review.googlesource.com/c/go/+/272869 Trust: Russ Cox Run-TryBot: Russ Cox TryBot-Result: Go Bot Reviewed-by: Matthew Dempsky --- src/cmd/compile/internal/gc/const.go | 115 +++++++++++++---------- src/cmd/compile/internal/gc/swt.go | 5 +- src/cmd/compile/internal/gc/typecheck.go | 15 ++- src/cmd/compile/internal/gc/walk.go | 4 +- 4 files changed, 76 insertions(+), 63 deletions(-) diff --git a/src/cmd/compile/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go index ebf3896a0a..18d5feb813 100644 --- a/src/cmd/compile/internal/gc/const.go +++ b/src/cmd/compile/internal/gc/const.go @@ -542,87 +542,105 @@ func Isconst(n *Node, ct constant.Kind) bool { return consttype(n) == ct } -// evconst rewrites constant expressions into OLITERAL nodes. -func evconst(n *Node) { +// evalConst returns a constant-evaluated expression equivalent to n. +// If n is not a constant, evalConst returns n. +// Otherwise, evalConst returns a new OLITERAL with the same value as n, +// and with .Orig pointing back to n. +func evalConst(n *Node) *Node { nl, nr := n.Left, n.Right // Pick off just the opcodes that can be constant evaluated. switch op := n.Op; op { case OPLUS, ONEG, OBITNOT, ONOT: if nl.Op == OLITERAL { - setconst(n, unaryOp(op, nl.Val(), n.Type)) + return origConst(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())) + return origConst(n, binaryOp(nl.Val(), op, nr.Val())) } case OEQ, ONE, OLT, OLE, OGT, OGE: if nl.Op == OLITERAL && nr.Op == OLITERAL { - setboolconst(n, compareOp(nl.Val(), op, nr.Val())) + return origBoolConst(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())) + return origConst(n, shiftOp(nl.Val(), op, nr.Val())) } case OCONV, ORUNESTR: if okforconst[n.Type.Etype] && nl.Op == OLITERAL { - setconst(n, convertVal(nl.Val(), n.Type, true)) + return origConst(n, convertVal(nl.Val(), n.Type, true)) } case OCONVNOP: if okforconst[n.Type.Etype] && nl.Op == OLITERAL { // set so n.Orig gets OCONV instead of OCONVNOP n.Op = OCONV - setconst(n, nl.Val()) + return origConst(n, nl.Val()) } case OADDSTR: // Merge adjacent constants in the argument list. s := n.List.Slice() - for i1 := 0; i1 < len(s); i1++ { - if Isconst(s[i1], constant.String) && i1+1 < len(s) && Isconst(s[i1+1], constant.String) { - // merge from i1 up to but not including i2 + need := 0 + for i := 0; i < len(s); i++ { + if i == 0 || !Isconst(s[i-1], constant.String) || !Isconst(s[i], constant.String) { + // Can't merge s[i] into s[i-1]; need a slot in the list. + need++ + } + } + if need == len(s) { + return n + } + if need == 1 { + var strs []string + for _, c := range s { + strs = append(strs, c.StringVal()) + } + return origConst(n, Val{U: strings.Join(strs, "")}) + } + newList := make([]*Node, 0, need) + for i := 0; i < len(s); i++ { + if Isconst(s[i], constant.String) && i+1 < len(s) && Isconst(s[i+1], constant.String) { + // merge from i up to but not including i2 var strs []string - i2 := i1 + i2 := i for i2 < len(s) && Isconst(s[i2], constant.String) { strs = append(strs, s[i2].StringVal()) i2++ } - nl := *s[i1] - nl.Orig = &nl - nl.SetVal(Val{strings.Join(strs, "")}) - s[i1] = &nl - s = append(s[:i1+1], s[i2:]...) + nl := origConst(s[i], Val{U: strings.Join(strs, "")}) + nl.Orig = nl // it's bigger than just s[i] + newList = append(newList, nl) + i = i2 - 1 + } else { + newList = append(newList, s[i]) } } - if len(s) == 1 && Isconst(s[0], constant.String) { - n.Op = OLITERAL - n.SetVal(s[0].Val()) - n.List.Set(nil) - } else { - n.List.Set(s) - } + n = n.copy() + n.List.Set(newList) + return n case OCAP, OLEN: switch nl.Type.Etype { case TSTRING: if Isconst(nl, constant.String) { - setintconst(n, int64(len(nl.StringVal()))) + return origIntConst(n, int64(len(nl.StringVal()))) } case TARRAY: if !hascallchan(nl) { - setintconst(n, nl.Type.NumElem()) + return origIntConst(n, nl.Type.NumElem()) } } case OALIGNOF, OOFFSETOF, OSIZEOF: - setintconst(n, evalunsafe(n)) + return origIntConst(n, evalunsafe(n)) case OREAL, OIMAG: if nl.Op == OLITERAL { @@ -647,7 +665,7 @@ func evconst(n *Node) { } re = im } - setconst(n, Val{re}) + return origConst(n, Val{re}) } case OCOMPLEX: @@ -656,9 +674,11 @@ func evconst(n *Node) { c := newMpcmplx() c.Real.Set(toflt(nl.Val()).U.(*Mpflt)) c.Imag.Set(toflt(nr.Val()).U.(*Mpflt)) - setconst(n, Val{c}) + return origConst(n, Val{c}) } } + + return n } func match(x, y Val) (Val, Val) { @@ -927,27 +947,21 @@ func shiftOp(x Val, op Op, y Val) Val { return Val{U: u} } -// setconst rewrites n as an OLITERAL with value v. -func setconst(n *Node, v Val) { - // If constant folding failed, mark n as broken and give up. +// origConst returns an OLITERAL with orig n and value v. +func origConst(n *Node, v Val) *Node { + // If constant folding was attempted (we were called) + // but it produced an invalid constant value, + // mark n as broken and give up. if v.U == nil { n.Type = nil - return - } - - // Ensure n.Orig still points to a semantically-equivalent - // expression after we rewrite n into a constant. - if n.Orig == n { - n.Orig = n.sepcopy() + return n } - *n = Node{ - Op: OLITERAL, - Pos: n.Pos, - Orig: n.Orig, - Type: n.Type, - Xoffset: BADWIDTH, - } + orig := n + n = nod(OLITERAL, nil, nil) + n.Orig = orig + n.Pos = orig.Pos + n.Type = orig.Type n.SetVal(v) // Check range. @@ -965,6 +979,7 @@ func setconst(n *Node, v Val) { n.SetVal(Val{trunccmplxlit(v.U.(*Mpcplx), n.Type)}) } } + return n } func assertRepresents(t *types.Type, v Val) { @@ -983,14 +998,14 @@ func represents(t *types.Type, v Val) bool { return t == vt || (t == types.UntypedRune && vt == types.UntypedInt) } -func setboolconst(n *Node, v bool) { - setconst(n, Val{U: v}) +func origBoolConst(n *Node, v bool) *Node { + return origConst(n, Val{U: v}) } -func setintconst(n *Node, v int64) { +func origIntConst(n *Node, v int64) *Node { u := new(Mpint) u.SetInt64(v) - setconst(n, Val{u}) + return origConst(n, Val{u}) } // nodlit returns a new untyped constant with value v. diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index 068f1a34e1..8459bd7c18 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -386,14 +386,13 @@ func (s *exprSwitch) flush() { runs = append(runs, cc[start:]) // Perform two-level binary search. - nlen := nod(OLEN, s.exprname, nil) binarySearch(len(runs), &s.done, func(i int) *Node { - return nod(OLE, nlen, nodintconst(runLen(runs[i-1]))) + return nod(OLE, nod(OLEN, s.exprname, nil), nodintconst(runLen(runs[i-1]))) }, func(i int, nif *Node) { run := runs[i] - nif.Left = nod(OEQ, nlen, nodintconst(runLen(run))) + nif.Left = nod(OEQ, nod(OLEN, s.exprname, nil), nodintconst(runLen(run))) s.search(run, &nif.Nbody) }, ) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 5cc7c8a34c..e014a0ba2d 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -776,7 +776,7 @@ func typecheck1(n *Node, top int) (res *Node) { } if iscmp[n.Op] { - evconst(n) + n = evalConst(n) t = types.UntypedBool if n.Op != OLITERAL { l, r = defaultlit2(l, r, true) @@ -786,12 +786,13 @@ func typecheck1(n *Node, top int) (res *Node) { } if et == TSTRING && n.Op == OADD { - // create OADDSTR node with list of strings in x + y + z + (w + v) + ... - n.Op = OADDSTR - + // create or update OADDSTR node with list of strings in x + y + z + (w + v) + ... if l.Op == OADDSTR { - n.List.Set(l.List.Slice()) + orig := n + n = l + n.Pos = orig.Pos } else { + n = nodl(n.Pos, OADDSTR, nil, nil) n.List.Set1(l) } if r.Op == OADDSTR { @@ -799,8 +800,6 @@ func typecheck1(n *Node, top int) (res *Node) { } else { n.List.Append(r) } - n.Left = nil - n.Right = nil } if (op == ODIV || op == OMOD) && Isconst(r, constant.Int) { @@ -2091,7 +2090,7 @@ func typecheck1(n *Node, top int) (res *Node) { } } - evconst(n) + n = evalConst(n) if n.Op == OTYPE && top&ctxType == 0 { if !n.Type.Broke() { yyerror("type %v is not an expression", n.Type) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 7bf5281a67..9971fb0c0d 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -513,7 +513,7 @@ opswitch: } if t.IsArray() { safeexpr(n.Left, init) - setintconst(n, t.NumElem()) + n = origIntConst(n, t.NumElem()) n.SetTypecheck(1) } @@ -1580,7 +1580,7 @@ opswitch: // walk of y%1 may have replaced it by 0. // Check whether n with its updated args is itself now a constant. t := n.Type - evconst(n) + n = evalConst(n) if n.Type != t { Fatalf("evconst changed Type: %v had type %v, now %v", n, t, n.Type) } -- 2.48.1