From: Russ Cox Date: Fri, 1 May 2015 00:35:47 +0000 (-0400) Subject: cmd/internal/gc: avoid turning 'x = f()' into 'tmp = f(); x = tmp' for simple x X-Git-Tag: go1.5beta1~631 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=18d98bc9cb34df680ae3dac89712366a9883789f;p=gostls13.git cmd/internal/gc: avoid turning 'x = f()' into 'tmp = f(); x = tmp' for simple x This slows down more things than I expected, but it also speeds things up, and it reduces stack frame sizes and the load on the optimizer, so it's still likely a net win. name old mean new mean delta BenchmarkBinaryTree17 13.2s × (0.98,1.03) 13.2s × (0.98,1.02) ~ (p=0.795) BenchmarkFannkuch11 4.41s × (1.00,1.00) 4.45s × (0.99,1.01) +0.88% (p=0.000) BenchmarkFmtFprintfEmpty 86.4ns × (0.99,1.01) 90.1ns × (0.95,1.05) +4.31% (p=0.000) BenchmarkFmtFprintfString 318ns × (0.96,1.07) 337ns × (0.98,1.03) +6.05% (p=0.000) BenchmarkFmtFprintfInt 332ns × (0.97,1.04) 320ns × (0.97,1.02) -3.42% (p=0.000) BenchmarkFmtFprintfIntInt 562ns × (0.96,1.04) 574ns × (0.96,1.06) +2.00% (p=0.013) BenchmarkFmtFprintfPrefixedInt 442ns × (0.96,1.06) 450ns × (0.97,1.05) +1.73% (p=0.039) BenchmarkFmtFprintfFloat 640ns × (0.99,1.02) 659ns × (0.99,1.03) +3.01% (p=0.000) BenchmarkFmtManyArgs 2.19µs × (0.97,1.06) 2.21µs × (0.98,1.02) ~ (p=0.104) BenchmarkGobDecode 20.0ms × (0.98,1.03) 19.7ms × (0.97,1.04) -1.35% (p=0.035) BenchmarkGobEncode 17.8ms × (0.96,1.04) 18.0ms × (0.96,1.06) ~ (p=0.131) BenchmarkGzip 653ms × (0.99,1.02) 652ms × (0.99,1.01) ~ (p=0.572) BenchmarkGunzip 143ms × (0.99,1.02) 142ms × (1.00,1.01) -0.52% (p=0.005) BenchmarkHTTPClientServer 110µs × (0.98,1.03) 108µs × (0.99,1.02) -1.90% (p=0.000) BenchmarkJSONEncode 40.0ms × (0.98,1.05) 41.5ms × (0.97,1.06) +3.89% (p=0.000) BenchmarkJSONDecode 118ms × (0.99,1.01) 118ms × (0.98,1.01) +0.69% (p=0.010) BenchmarkMandelbrot200 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.924) BenchmarkGoParse 8.43ms × (0.92,1.11) 8.56ms × (0.93,1.05) ~ (p=0.242) BenchmarkRegexpMatchEasy0_32 180ns × (0.91,1.07) 163ns × (1.00,1.00) -9.33% (p=0.000) BenchmarkRegexpMatchEasy0_1K 550ns × (0.98,1.02) 558ns × (0.99,1.01) +1.44% (p=0.000) BenchmarkRegexpMatchEasy1_32 152ns × (0.94,1.05) 139ns × (0.98,1.02) -8.51% (p=0.000) BenchmarkRegexpMatchEasy1_1K 909ns × (0.98,1.06) 868ns × (0.99,1.02) -4.52% (p=0.000) BenchmarkRegexpMatchMedium_32 262ns × (0.97,1.03) 253ns × (0.99,1.02) -3.31% (p=0.000) BenchmarkRegexpMatchMedium_1K 73.8µs × (0.98,1.04) 72.7µs × (1.00,1.01) -1.61% (p=0.001) BenchmarkRegexpMatchHard_32 3.87µs × (0.99,1.02) 3.87µs × (1.00,1.01) ~ (p=0.791) BenchmarkRegexpMatchHard_1K 118µs × (0.98,1.04) 117µs × (0.99,1.02) ~ (p=0.110) BenchmarkRevcomp 1.00s × (0.94,1.10) 0.99s × (0.94,1.09) ~ (p=0.433) BenchmarkTemplate 140ms × (0.97,1.04) 140ms × (0.99,1.01) ~ (p=0.303) BenchmarkTimeParse 622ns × (0.99,1.02) 625ns × (0.99,1.01) +0.51% (p=0.001) BenchmarkTimeFormat 731ns × (0.98,1.04) 719ns × (0.99,1.01) -1.66% (p=0.000) Change-Id: Ibc3edb59a178adafda50156f46a341f69a17d83f Reviewed-on: https://go-review.googlesource.com/9721 Reviewed-by: David Chase --- diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go index f08f5f20fe..82876f81bc 100644 --- a/src/cmd/internal/gc/order.go +++ b/src/cmd/internal/gc/order.go @@ -264,7 +264,7 @@ func orderblock(l **NodeList) { func orderexprinplace(np **Node, outer *Order) { n := *np var order Order - orderexpr(&n, &order) + orderexpr(&n, &order, nil) addinit(&n, order.out) // insert new temporaries from order @@ -358,8 +358,8 @@ func ordercallargs(l **NodeList, order *Order) { // Ordercall orders the call expression n. // n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. func ordercall(n *Node, order *Order) { - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) // ODDDARG temp + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) // ODDDARG temp ordercallargs(&n.List, order) } @@ -447,8 +447,14 @@ func orderstmt(n *Node, order *Order) { case OVARKILL: order.out = list(order.out, n) - case OAS, - OAS2, + case OAS: + t := marktemp(order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, n.Left) + ordermapassign(n, order) + cleantemp(t, order) + + case OAS2, OCLOSE, OCOPY, OPRINT, @@ -456,29 +462,27 @@ func orderstmt(n *Node, order *Order) { ORECOVER, ORECV: t := marktemp(order) - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) switch n.Op { - case OAS, OAS2, OAS2DOTTYPE: + case OAS2, OAS2DOTTYPE: ordermapassign(n, order) - default: order.out = list(order.out, n) } - cleantemp(t, order) - // Special: rewrite l op= r into l = l op r. - // This simplies quite a few operations; - // most important is that it lets us separate - // out map read from map write when l is - // a map index expression. case OASOP: + // Special: rewrite l op= r into l = l op r. + // This simplies quite a few operations; + // most important is that it lets us separate + // out map read from map write when l is + // a map index expression. t := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) n.Left = ordersafeexpr(n.Left, order) tmp1 := treecopy(n.Left) if tmp1.Op == OINDEXMAP { @@ -487,7 +491,7 @@ func orderstmt(n *Node, order *Order) { tmp1 = ordercopyexpr(tmp1, n.Left.Type, order, 0) n.Right = Nod(int(n.Etype), tmp1, n.Right) typecheck(&n.Right, Erv) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) n.Etype = 0 n.Op = OAS ordermapassign(n, order) @@ -500,8 +504,8 @@ func orderstmt(n *Node, order *Order) { orderexprlist(n.List, order) r := n.Rlist.N - orderexpr(&r.Left, order) - orderexpr(&r.Right, order) + orderexpr(&r.Left, order, nil) + orderexpr(&r.Right, order, nil) // See case OINDEXMAP below. if r.Right.Op == OARRAYBYTESTR { @@ -527,7 +531,7 @@ func orderstmt(n *Node, order *Order) { t := marktemp(order) orderexprlist(n.List, order) - orderexpr(&n.Rlist.N.Left, order) // i in i.(T) + orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T) if isblank(n.List.N) { order.out = list(order.out, n) } else { @@ -548,7 +552,7 @@ func orderstmt(n *Node, order *Order) { t := marktemp(order) orderexprlist(n.List, order) - orderexpr(&n.Rlist.N.Left, order) // arg to recv + orderexpr(&n.Rlist.N.Left, order, nil) // arg to recv ch := n.Rlist.N.Left.Type tmp1 := ordertemp(ch.Type, order, haspointers(ch.Type)) var tmp2 *Node @@ -617,8 +621,8 @@ func orderstmt(n *Node, order *Order) { case ODELETE: t := marktemp(order) - orderexpr(&n.List.N, order) - orderexpr(&n.List.Next.N, order) + orderexpr(&n.List.N, order, nil) + orderexpr(&n.List.Next.N, order, nil) orderaddrtemp(&n.List.Next.N, order) // map key order.out = list(order.out, n) cleantemp(t, order) @@ -659,7 +663,7 @@ func orderstmt(n *Node, order *Order) { case OPANIC: t := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) if !Isinter(n.Left.Type) { orderaddrtemp(&n.Left, order) } @@ -677,7 +681,7 @@ func orderstmt(n *Node, order *Order) { case ORANGE: t := marktemp(order) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) switch n.Type.Etype { default: Fatal("orderstmt range %v", n.Type) @@ -793,7 +797,7 @@ func orderstmt(n *Node, order *Order) { // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c. // r->left == N means 'case <-c'. // c is always evaluated; x and ok are only evaluated when assigned. - orderexpr(&r.Right.Left, order) + orderexpr(&r.Right.Left, order, nil) if r.Right.Left.Op != ONAME { r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0) @@ -853,12 +857,12 @@ func orderstmt(n *Node, order *Order) { // case c <- x // r->left is c, r->right is x, both are always evaluated. - orderexpr(&r.Left, order) + orderexpr(&r.Left, order, nil) if !istemp(r.Left) { r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) } - orderexpr(&r.Right, order) + orderexpr(&r.Right, order, nil) if !istemp(r.Right) { r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) } @@ -884,8 +888,8 @@ func orderstmt(n *Node, order *Order) { case OSEND: t := marktemp(order) - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderaddrtemp(&n.Right, order) order.out = list(order.out, n) cleantemp(t, order) @@ -900,7 +904,7 @@ func orderstmt(n *Node, order *Order) { case OSWITCH: t := marktemp(order) - orderexpr(&n.Ntest, order) + orderexpr(&n.Ntest, order, nil) for l := n.List; l != nil; l = l.Next { if l.N.Op != OXCASE { Fatal("order switch case %v", Oconv(int(l.N.Op), 0)) @@ -919,7 +923,7 @@ func orderstmt(n *Node, order *Order) { // Orderexprlist orders the expression list l into order. func orderexprlist(l *NodeList, order *Order) { for ; l != nil; l = l.Next { - orderexpr(&l.N, order) + orderexpr(&l.N, order, nil) } } @@ -933,7 +937,10 @@ func orderexprlistinplace(l *NodeList, order *Order) { // Orderexpr orders a single expression, appending side // effects to order->out as needed. -func orderexpr(np **Node, order *Order) { +// If this is part of an assignment lhs = *np, lhs is given. +// Otherwise lhs == nil. (When lhs != nil it may be possible +// to avoid copying the result of the expression to a temporary.) +func orderexpr(np **Node, order *Order, lhs *Node) { n := *np if n == nil { return @@ -944,8 +951,8 @@ func orderexpr(np **Node, order *Order) { switch n.Op { default: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) @@ -986,8 +993,8 @@ func orderexpr(np **Node, order *Order) { } case OCMPSTR: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) // Mark string(byteSlice) arguments to reuse byteSlice backing // buffer during conversion. String comparison does not @@ -1001,9 +1008,9 @@ func orderexpr(np **Node, order *Order) { // key must be addressable case OINDEXMAP: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) // For x = m[string(k)] where k is []byte, the allocation of // backing bytes for the string can be avoided by reusing @@ -1029,7 +1036,7 @@ func orderexpr(np **Node, order *Order) { // concrete type (not interface) argument must be addressable // temporary to pass to runtime. case OCONVIFACE: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) if !Isinter(n.Left.Type) { orderaddrtemp(&n.Left, order) @@ -1037,7 +1044,7 @@ func orderexpr(np **Node, order *Order) { case OANDAND, OOROR: mark := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) // Clean temporaries from first branch at beginning of second. // Leave them on the stack so that they can be killed in the outer @@ -1064,7 +1071,9 @@ func orderexpr(np **Node, order *Order) { OREAL, ORECOVER: ordercall(n, order) - n = ordercopyexpr(n, n.Type, order, 0) + if lhs == nil || lhs.Op != ONAME || flag_race != 0 { + n = ordercopyexpr(n, n.Type, order, 0) + } case OCLOSURE: if n.Noescape && n.Func.Cvars != nil { @@ -1072,8 +1081,8 @@ func orderexpr(np **Node, order *Order) { } case OARRAYLIT, OCALLPART: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) if n.Noescape { @@ -1090,7 +1099,7 @@ func orderexpr(np **Node, order *Order) { } case ODOTTYPE, ODOTTYPE2: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) // TODO(rsc): The Isfat is for consistency with componentgen and walkexpr. // It needs to be removed in all three places. // That would allow inlining x.(struct{*int}) the same as x.(*int). @@ -1099,12 +1108,12 @@ func orderexpr(np **Node, order *Order) { } case ORECV: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) n = ordercopyexpr(n, n.Type, order, 1) case OEQ, ONE: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) t := n.Left.Type if t.Etype == TSTRUCT || Isfixedarray(t) { // for complex comparisons, we need both args to be