]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: avoid runtime call during switch string(byteslice)
authorJosh Bleecher Snyder <josharian@gmail.com>
Thu, 19 Apr 2018 01:28:34 +0000 (18:28 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Sat, 21 Apr 2018 00:50:50 +0000 (00:50 +0000)
This triggers three times while building std,
once in image/png and twice in go/internal/gccgoimporter.

There are no instances in std in which a more aggressive
optimization would have triggered.

This doesn't necessarily avoid an allocation,
because escape analysis is already able in many cases
to use a temporary backing for the string,
but it does at a minimum avoid the runtime call and copy.

Fixes #24937

Change-Id: I7019e85638ba8cd7e2f03890e672558b858579bc
Reviewed-on: https://go-review.googlesource.com/108035
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/gc/swt.go
test/fixedbugs/issue24937.go [new file with mode: 0644]

index 8dbbb553de0e60ebb8d178867252dda1530ddb36..433d38544ec3953e138287d52e82505c3e11f777 100644 (file)
@@ -254,6 +254,34 @@ func (s *exprSwitch) walk(sw *Node) {
                }
        }
 
+       // Given "switch string(byteslice)",
+       // with all cases being constants (or the default case),
+       // use a zero-cost alias of the byte slice.
+       // In theory, we could be more aggressive,
+       // allowing any side-effect-free expressions in cases,
+       // but it's a bit tricky because some of that information
+       // is unavailable due to the introduction of temporaries during order.
+       // Restricting to constants is simple and probably powerful enough.
+       // Do this before calling walkexpr on cond,
+       // because walkexpr will lower the string
+       // conversion into a runtime call.
+       // See issue 24937 for more discussion.
+       if cond.Op == OARRAYBYTESTR {
+               ok := true
+               for _, cas := range sw.List.Slice() {
+                       if cas.Op != OCASE {
+                               Fatalf("switch string(byteslice) bad op: %v", cas.Op)
+                       }
+                       if cas.Left != nil && !Isconst(cas.Left, CTSTR) {
+                               ok = false
+                               break
+                       }
+               }
+               if ok {
+                       cond.Op = OARRAYBYTESTRTMP
+               }
+       }
+
        cond = walkexpr(cond, &sw.Ninit)
        t := sw.Type
        if t == nil {
diff --git a/test/fixedbugs/issue24937.go b/test/fixedbugs/issue24937.go
new file mode 100644 (file)
index 0000000..7d8460f
--- /dev/null
@@ -0,0 +1,15 @@
+// run
+
+// Copyright 2018 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.
+
+package main
+
+func main() {
+       x := []byte{'a'}
+       switch string(x) {
+       case func() string { x[0] = 'b'; return "b" }():
+               panic("FAIL")
+       }
+}