]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add automated rewrite cycle detection
authorJosh Bleecher Snyder <josharian@gmail.com>
Sun, 5 Sep 2021 02:29:08 +0000 (19:29 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Wed, 8 Sep 2021 22:47:57 +0000 (22:47 +0000)
A common bug during development is to introduce rewrite rule cycles.
This is annoying because it takes a while to notice that
make.bash is a bit too slow this time, and to remember why.
And then you have to manually arrange to debug.

Make this all easier by automating it.
Detect cycles, and when we detect one, print the sequence
of rewrite rules that occur within a single cycle before crashing.

Change-Id: I8dadda13990ab925a81940d4833c9e5243368435
Reviewed-on: https://go-review.googlesource.com/c/go/+/347829
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/cmd/compile/internal/ssa/html.go
src/cmd/compile/internal/ssa/print.go
src/cmd/compile/internal/ssa/rewrite.go

index 4d191199fbafe1b3f5f61882a40aced0e836cf36..6fd898636cd6ceff0eb015a2261e3a7048020d12 100644 (file)
@@ -1221,7 +1221,7 @@ func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
        }
 }
 
-func (p htmlFuncPrinter) endBlock(b *Block) {
+func (p htmlFuncPrinter) endBlock(b *Block, reachable bool) {
        if len(b.Values) > 0 { // end list of values
                io.WriteString(p.w, "</ul>")
                io.WriteString(p.w, "</li>")
index d917183c70f5e8f23ff904a6421983e8a1770b25..81c64a76920df38510b375f5b3c6fc99c7df2f86 100644 (file)
@@ -17,22 +17,30 @@ func printFunc(f *Func) {
 
 func hashFunc(f *Func) []byte {
        h := sha256.New()
-       p := stringFuncPrinter{w: h}
+       p := stringFuncPrinter{w: h, printDead: true}
        fprintFunc(p, f)
        return h.Sum(nil)
 }
 
 func (f *Func) String() string {
        var buf bytes.Buffer
-       p := stringFuncPrinter{w: &buf}
+       p := stringFuncPrinter{w: &buf, printDead: true}
        fprintFunc(p, f)
        return buf.String()
 }
 
+// rewriteHash returns a hash of f suitable for detecting rewrite cycles.
+func (f *Func) rewriteHash() string {
+       h := sha256.New()
+       p := stringFuncPrinter{w: h, printDead: false}
+       fprintFunc(p, f)
+       return fmt.Sprintf("%x", h.Sum(nil))
+}
+
 type funcPrinter interface {
        header(f *Func)
        startBlock(b *Block, reachable bool)
-       endBlock(b *Block)
+       endBlock(b *Block, reachable bool)
        value(v *Value, live bool)
        startDepCycle()
        endDepCycle()
@@ -40,7 +48,8 @@ type funcPrinter interface {
 }
 
 type stringFuncPrinter struct {
-       w io.Writer
+       w         io.Writer
+       printDead bool
 }
 
 func (p stringFuncPrinter) header(f *Func) {
@@ -50,6 +59,9 @@ func (p stringFuncPrinter) header(f *Func) {
 }
 
 func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
+       if !p.printDead && !reachable {
+               return
+       }
        fmt.Fprintf(p.w, "  b%d:", b.ID)
        if len(b.Preds) > 0 {
                io.WriteString(p.w, " <-")
@@ -64,11 +76,17 @@ func (p stringFuncPrinter) startBlock(b *Block, reachable bool) {
        io.WriteString(p.w, "\n")
 }
 
-func (p stringFuncPrinter) endBlock(b *Block) {
+func (p stringFuncPrinter) endBlock(b *Block, reachable bool) {
+       if !p.printDead && !reachable {
+               return
+       }
        fmt.Fprintln(p.w, "    "+b.LongString())
 }
 
 func (p stringFuncPrinter) value(v *Value, live bool) {
+       if !p.printDead && !live {
+               return
+       }
        fmt.Fprint(p.w, "    ")
        //fmt.Fprint(p.w, v.Block.Func.fe.Pos(v.Pos))
        //fmt.Fprint(p.w, ": ")
@@ -103,7 +121,7 @@ func fprintFunc(p funcPrinter, f *Func) {
                                p.value(v, live[v.ID])
                                printed[v.ID] = true
                        }
-                       p.endBlock(b)
+                       p.endBlock(b, reachable[b.ID])
                        continue
                }
 
@@ -151,7 +169,7 @@ func fprintFunc(p funcPrinter, f *Func) {
                        }
                }
 
-               p.endBlock(b)
+               p.endBlock(b, reachable[b.ID])
        }
        for _, name := range f.Names {
                p.named(*name, f.NamedValues[*name])
index 5d468768b6a13b9a0acd886e98d7ae94e1a66672..a997050ee2c0868483c413ad21135d1fd3e9732f 100644 (file)
@@ -36,6 +36,8 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
        if debug > 1 {
                fmt.Printf("%s: rewriting for %s\n", f.pass.name, f.Name)
        }
+       var iters int
+       var states map[string]bool
        for {
                change := false
                for _, b := range f.Blocks {
@@ -146,6 +148,30 @@ func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter, deadcode deadValu
                if !change {
                        break
                }
+               iters++
+               if iters > 1000 || debug >= 2 {
+                       // We've done a suspiciously large number of rewrites (or we're in debug mode).
+                       // As of Sep 2021, 90% of rewrites complete in 4 iterations or fewer
+                       // and the maximum value encountered during make.bash is 12.
+                       // Start checking for cycles. (This is too expensive to do routinely.)
+                       if states == nil {
+                               states = make(map[string]bool)
+                       }
+                       h := f.rewriteHash()
+                       if _, ok := states[h]; ok {
+                               // We've found a cycle.
+                               // To diagnose it, set debug to 2 and start again,
+                               // so that we'll print all rules applied until we complete another cycle.
+                               // If debug is already >= 2, we've already done that, so it's time to crash.
+                               if debug < 2 {
+                                       debug = 2
+                                       states = make(map[string]bool)
+                               } else {
+                                       f.Fatalf("rewrite cycle detected")
+                               }
+                       }
+                       states[h] = true
+               }
        }
        // remove clobbered values
        for _, b := range f.Blocks {