]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: make DCE remove nodes after terminating if
authorisharipo <iskander.sharipov@intel.com>
Thu, 1 Feb 2018 17:37:23 +0000 (20:37 +0300)
committerIlya Tocar <ilya.tocar@intel.com>
Tue, 3 Apr 2018 15:19:41 +0000 (15:19 +0000)
This change makes compiler frontend dead code elimination of const expr if
statements introduced in https://golang.org/cl/38773 treat both
if constCondTrue { ...; returnStmt } toBeRemoved...
if constCondFalse { ...; } else { returnStmt } toBeRemoved...
identically to:
if constCondTrue { ...; returnStmt } else { toBeRemoved... }

Where "constCondTrue" is a an expression that can be evaluated
to "true" during compile time.

The additional checks are only triggered for const expr
if conditions that evaluate to true.

name       old time/op       new time/op       delta
Template         431ms ± 2%        429ms ± 1%    ~     (p=0.491 n=8+6)
Unicode          198ms ± 4%        201ms ± 2%    ~     (p=0.234 n=7+6)
GoTypes          1.40s ± 1%        1.41s ± 2%    ~     (p=0.053 n=7+7)
Compiler         6.72s ± 2%        6.81s ± 1%  +1.35%  (p=0.011 n=7+7)
SSA              17.3s ± 1%        17.3s ± 2%    ~     (p=0.731 n=6+7)
Flate            275ms ± 2%        275ms ± 2%    ~     (p=0.902 n=7+7)
GoParser         340ms ± 2%        339ms ± 2%    ~     (p=0.902 n=7+7)
Reflect          910ms ± 2%        905ms ± 1%    ~     (p=0.310 n=6+6)
Tar              403ms ± 1%        403ms ± 2%    ~     (p=0.366 n=7+6)
XML              486ms ± 1%        490ms ± 1%    ~     (p=0.065 n=6+6)
StdCmd           56.2s ± 1%        56.6s ± 2%    ~     (p=0.620 n=7+7)

name       old user-time/op  new user-time/op  delta
Template         559ms ± 8%        557ms ± 7%    ~     (p=0.713 n=8+7)
Unicode          266ms ±13%        277ms ± 9%    ~     (p=0.157 n=8+7)
GoTypes          1.83s ± 2%        1.84s ± 1%    ~     (p=0.522 n=8+7)
Compiler         8.67s ± 4%        8.89s ± 4%    ~     (p=0.077 n=7+7)
SSA              23.9s ± 1%        24.2s ± 1%  +1.31%  (p=0.005 n=7+7)
Flate            351ms ± 4%        342ms ± 5%    ~     (p=0.105 n=7+7)
GoParser         437ms ± 2%        423ms ± 5%  -3.14%  (p=0.016 n=7+7)
Reflect          1.16s ± 3%        1.15s ± 2%    ~     (p=0.362 n=7+7)
Tar              517ms ± 4%        511ms ± 3%    ~     (p=0.538 n=7+7)
XML              619ms ± 3%        617ms ± 4%    ~     (p=0.483 n=7+7)

Fixes #23521

Change-Id: I165a7827d869aeb93ce6047d026ff873d039a4f3
Reviewed-on: https://go-review.googlesource.com/91056
Run-TryBot: Iskander Sharipov <iskander.sharipov@intel.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
src/cmd/compile/internal/gc/typecheck.go
test/fixedbugs/issue23521.go [new file with mode: 0644]

index 1bff3431a0a474b63ebc918cd862c9f1548c3d70..02cac2e86c45461779bb4c5a6857b50aa23adba1 100644 (file)
@@ -3926,24 +3926,45 @@ func deadcode(fn *Node) {
 }
 
 func deadcodeslice(nn Nodes) {
-       for _, n := range nn.Slice() {
+       for i, n := range nn.Slice() {
+               // Cut is set to true when all nodes after i'th position
+               // should be removed.
+               // In other words, it marks whole slice "tail" as dead.
+               cut := false
                if n == nil {
                        continue
                }
                if n.Op == OIF {
                        n.Left = deadcodeexpr(n.Left)
                        if Isconst(n.Left, CTBOOL) {
+                               var body Nodes
                                if n.Left.Bool() {
                                        n.Rlist = Nodes{}
+                                       body = n.Nbody
                                } else {
                                        n.Nbody = Nodes{}
+                                       body = n.Rlist
+                               }
+                               // If "then" or "else" branch ends with panic or return statement,
+                               // it is safe to remove all statements after this node.
+                               // isterminating is not used to avoid goto-related complications.
+                               if body := body.Slice(); len(body) != 0 {
+                                       switch body[(len(body) - 1)].Op {
+                                       case ORETURN, ORETJMP, OPANIC:
+                                               cut = true
+                                       }
                                }
                        }
                }
+
                deadcodeslice(n.Ninit)
                deadcodeslice(n.Nbody)
                deadcodeslice(n.List)
                deadcodeslice(n.Rlist)
+               if cut {
+                       *nn.slice = nn.Slice()[:i+1]
+                       break
+               }
        }
 }
 
diff --git a/test/fixedbugs/issue23521.go b/test/fixedbugs/issue23521.go
new file mode 100644 (file)
index 0000000..159e032
--- /dev/null
@@ -0,0 +1,43 @@
+// errorcheck -0 -m
+
+// 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.
+
+// Issue 23521: improve early DCE for if without explicit else.
+
+package p
+
+//go:noinline
+func nonleaf() {}
+
+const truth = true
+
+func f() int { // ERROR "can inline f"
+       if truth {
+               return 0
+       }
+       // If everything below is removed, as it should,
+       // function f should be inlineable.
+       nonleaf()
+       for {
+               panic("")
+       }
+}
+
+func g() int { // ERROR "can inline g"
+       return f() // ERROR "inlining call to f"
+}
+
+func f2() int { // ERROR "can inline f2"
+       if !truth {
+               nonleaf()
+       } else {
+               return 0
+       }
+       panic("")
+}
+
+func g2() int { // ERROR "can inline g2"
+       return f2() // ERROR "inlining call to f2"
+}