]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: avoid infinite loops in dead blocks during phi insertion
authorJosh Bleecher Snyder <josharian@gmail.com>
Thu, 30 Mar 2017 13:45:36 +0000 (06:45 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Thu, 30 Mar 2017 17:06:08 +0000 (17:06 +0000)
Now that we no longer generate dead code,
it is possible to follow block predecessors
into infinite loops with no variable definitions,
causing an infinite loop during phi insertion.

To fix that, check explicitly whether the predecessor
is dead in lookupVarOutgoing, and if so, bail.

The loop in lookupVarOutgoing is very hot code,
so I am wary of adding anything to it.
However, a long, CPU-only benchmarking run shows no
performance impact at all.

Fixes #19783

Change-Id: I8ef8d267e0b20a29b5cb0fecd7084f76c6f98e47
Reviewed-on: https://go-review.googlesource.com/38913
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/phi.go
test/fixedbugs/issue19783.go [new file with mode: 0644]

index 0ba2de80e2ce4c6b05e57f448fbeee44223c944e..013bb0164f423f9aa4ed3712713f7b3b895a5588 100644 (file)
@@ -430,14 +430,15 @@ func (s *sparseSet) clear() {
 
 // Variant to use for small functions.
 type simplePhiState struct {
-       s       *state                 // SSA state
-       f       *ssa.Func              // function to work on
-       fwdrefs []*ssa.Value           // list of FwdRefs to be processed
-       defvars []map[*Node]*ssa.Value // defined variables at end of each block
+       s         *state                 // SSA state
+       f         *ssa.Func              // function to work on
+       fwdrefs   []*ssa.Value           // list of FwdRefs to be processed
+       defvars   []map[*Node]*ssa.Value // defined variables at end of each block
+       reachable []bool                 // which blocks are reachable
 }
 
 func (s *simplePhiState) insertPhis() {
-       reachable := ssa.ReachableBlocks(s.f)
+       s.reachable = ssa.ReachableBlocks(s.f)
 
        // Find FwdRef ops.
        for _, b := range s.f.Blocks {
@@ -465,7 +466,7 @@ loop:
                        // No variable should be live at entry.
                        s.s.Fatalf("Value live at entry. It shouldn't be. func %s, node %v, value %v", s.f.Name, var_, v)
                }
-               if !reachable[b.ID] {
+               if !s.reachable[b.ID] {
                        // This block is dead.
                        // It doesn't matter what we use here as long as it is well-formed.
                        v.Op = ssa.OpUnknown
@@ -520,6 +521,11 @@ func (s *simplePhiState) lookupVarOutgoing(b *ssa.Block, t ssa.Type, var_ *Node,
                        break
                }
                b = b.Preds[0].Block()
+               if !s.reachable[b.ID] {
+                       // This is rare; it happens with oddly interleaved infinite loops in dead code.
+                       // See issue 19783.
+                       break
+               }
        }
        // Generate a FwdRef for the variable and return that.
        v := b.NewValue0A(line, ssa.OpFwdRef, t, var_)
diff --git a/test/fixedbugs/issue19783.go b/test/fixedbugs/issue19783.go
new file mode 100644 (file)
index 0000000..8d6494e
--- /dev/null
@@ -0,0 +1,18 @@
+// compile
+
+// Copyright 2017 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 p
+
+func Spin() {
+l1:
+       for true {
+               goto l1
+       l2:
+               if true {
+                       goto l2
+               }
+       }
+}