]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: share dominator tree among many passes
authorAlexandru Moșoi <mosoi@google.com>
Mon, 11 Apr 2016 19:51:29 +0000 (21:51 +0200)
committerAlexandru Moșoi <alexandru@mosoi.ro>
Tue, 12 Apr 2016 14:44:26 +0000 (14:44 +0000)
These passes do not modify the dominator tree too much.

% benchstat old.txt new.txt
name       old time/op     new time/op     delta
Template       335ms ± 3%      325ms ± 8%    ~             (p=0.074 n=8+9)
GoTypes        1.05s ± 1%      1.05s ± 3%    ~            (p=0.095 n=9+10)
Compiler       5.37s ± 4%      5.29s ± 1%  -1.42%         (p=0.022 n=9+10)
MakeBash       34.9s ± 3%      34.4s ± 2%    ~            (p=0.095 n=9+10)

name       old alloc/op    new alloc/op    delta
Template      55.4MB ± 0%     54.9MB ± 0%  -0.81%        (p=0.000 n=10+10)
GoTypes        179MB ± 0%      178MB ± 0%  -0.89%        (p=0.000 n=10+10)
Compiler       807MB ± 0%      798MB ± 0%  -1.10%        (p=0.000 n=10+10)

name       old allocs/op   new allocs/op   delta
Template        498k ± 0%       496k ± 0%  -0.29%          (p=0.000 n=9+9)
GoTypes        1.42M ± 0%      1.41M ± 0%  -0.24%        (p=0.000 n=10+10)
Compiler       5.61M ± 0%      5.60M ± 0%  -0.12%        (p=0.000 n=10+10)

Change-Id: I4cd20cfba3f132ebf371e16046ab14d7e42799ec
Reviewed-on: https://go-review.googlesource.com/21806
Run-TryBot: Alexandru Moșoi <alexandru@mosoi.ro>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/ssa/compile.go
src/cmd/compile/internal/ssa/dom.go
src/cmd/compile/internal/ssa/func.go
src/cmd/compile/internal/ssa/loopbce.go
src/cmd/compile/internal/ssa/nilcheck.go
src/cmd/compile/internal/ssa/nilcheck_test.go
src/cmd/compile/internal/ssa/prove.go

index b4215f119ef586e24604a3e82a6fc89db4e5d296..f4f0d8cab2879e1174dcef4ab6ec3ba7262cbb77 100644 (file)
@@ -234,6 +234,7 @@ var passes = [...]pass{
        {name: "zero arg cse", fn: zcse, required: true},     // required to merge OpSB values
        {name: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
        {name: "generic cse", fn: cse},
+       {name: "generic domtree", fn: domTree},
        {name: "phiopt", fn: phiopt},
        {name: "nilcheckelim", fn: nilcheckelim},
        {name: "prove", fn: prove},
@@ -288,6 +289,10 @@ var passOrder = [...]constraint{
        {"opt", "nilcheckelim"},
        // tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
        {"tighten", "lower"},
+       // nilcheckelim, prove and loopbce share idom.
+       {"generic domtree", "nilcheckelim"},
+       {"generic domtree", "prove"},
+       {"generic domtree", "loopbce"},
        // tighten will be most effective when as many values have been removed as possible
        {"generic deadcode", "tighten"},
        {"generic cse", "tighten"},
index 0fffcdc2af2c0ed14cc8aa56454a3a80516765a1..fedaf602e4a7ecf276f4e839aac83c2c007172c4 100644 (file)
@@ -364,3 +364,9 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
        }
        return b
 }
+
+// build immediate dominators.
+func domTree(f *Func) {
+       f.idom = dominators(f)
+       f.sdom = newSparseTree(f, f.idom)
+}
index 8dd75f6093ce948532da17eab0848784d1f44bc6..da44f26106e1d8a6ef168c91b74088d88aae4079 100644 (file)
@@ -36,6 +36,9 @@ type Func struct {
        freeValues *Value // free Values linked by argstorage[0].  All other fields except ID are 0/nil.
        freeBlocks *Block // free Blocks linked by succstorage[0].  All other fields except ID are 0/nil.
 
+       idom []*Block   // precomputed immediate dominators
+       sdom sparseTree // precomputed dominator tree
+
        constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
 }
 
index c937ead1b226d4610e43fe06a1243fd7720e576a..9bd2d3f0de97d5a8dc5afe8e60f0fa957be0ef7c 100644 (file)
@@ -31,7 +31,7 @@ type indVar struct {
 //
 //
 // TODO: handle 32 bit operations
-func findIndVar(f *Func, sdom sparseTree) []indVar {
+func findIndVar(f *Func) []indVar {
        var iv []indVar
 
 nextb:
@@ -110,7 +110,7 @@ nextb:
 
                // Second condition: b.Succs[entry] dominates nxt so that
                // nxt is computed when inc < max, meaning nxt <= max.
-               if !sdom.isAncestorEq(b.Succs[entry], nxt.Block) {
+               if !f.sdom.isAncestorEq(b.Succs[entry], nxt.Block) {
                        // inc+ind can only be reached through the branch that enters the loop.
                        continue
                }
@@ -160,20 +160,18 @@ nextb:
 
 // loopbce performs loop based bounds check elimination.
 func loopbce(f *Func) {
-       idom := dominators(f)
-       sdom := newSparseTree(f, idom)
-       ivList := findIndVar(f, sdom)
+       ivList := findIndVar(f)
 
        m := make(map[*Value]indVar)
        for _, iv := range ivList {
                m[iv.ind] = iv
        }
 
-       removeBoundsChecks(f, sdom, m)
+       removeBoundsChecks(f, m)
 }
 
 // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables.
-func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
+func removeBoundsChecks(f *Func, m map[*Value]indVar) {
        for _, b := range f.Blocks {
                if b.Kind != BlockIf {
                        continue
@@ -202,7 +200,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
                                goto skip1
                        }
 
-                       if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+                       if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
                                if v.Args[1] == iv.max {
                                        if f.pass.debug > 0 {
                                                f.Config.Warnl(b.Line, "Found redundant %s", v.Op)
@@ -229,7 +227,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
                                goto skip2
                        }
 
-                       if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
+                       if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
                                if v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] {
                                        if f.pass.debug > 0 {
                                                f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op)
@@ -250,7 +248,7 @@ func removeBoundsChecks(f *Func, sdom sparseTree, m map[*Value]indVar) {
                        }
 
                        // ind + add >= 0 <-> min + add >= 0 <-> min >= -add
-                       if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
+                       if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
                                if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() {
                                        goto skip3
                                }
index 881e3b2eff9d00afa5a4a67d3f6e8d552d583063..753e48aad5773db5cb8aa21d759134244ba79835 100644 (file)
@@ -11,7 +11,7 @@ func nilcheckelim(f *Func) {
        // A nil check is redundant if the same nil check was successful in a
        // dominating block. The efficacy of this pass depends heavily on the
        // efficacy of the cse pass.
-       idom := dominators(f)
+       idom := f.idom
        domTree := make([][]*Block, f.NumBlocks())
 
        // Create a block ID -> [dominees] mapping
index d1f38b6951e797be66c049094d33ac67e885bdbc..c1c8f94767b310d5d9832733efa07dda779e1bad 100644 (file)
@@ -49,6 +49,7 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) {
        b.ReportAllocs()
 
        for i := 0; i < b.N; i++ {
+               domTree(fun.f)
                nilcheckelim(fun.f)
        }
 }
@@ -83,6 +84,7 @@ func TestNilcheckSimple(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -120,6 +122,7 @@ func TestNilcheckDomOrder(t *testing.T) {
                        Goto("exit")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -153,6 +156,7 @@ func TestNilcheckAddr(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -187,6 +191,7 @@ func TestNilcheckAddPtr(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -231,6 +236,7 @@ func TestNilcheckPhi(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -272,6 +278,7 @@ func TestNilcheckKeepRemove(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -319,6 +326,7 @@ func TestNilcheckInFalseBranch(t *testing.T) {
                        Exit("mem")))
 
        CheckFunc(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -370,6 +378,7 @@ func TestNilcheckUser(t *testing.T) {
        CheckFunc(fun.f)
        // we need the opt here to rewrite the user nilcheck
        opt(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
@@ -414,6 +423,7 @@ func TestNilcheckBug(t *testing.T) {
        CheckFunc(fun.f)
        // we need the opt here to rewrite the user nilcheck
        opt(fun.f)
+       domTree(fun.f)
        nilcheckelim(fun.f)
 
        // clean up the removed nil check
index a12a99626365ee9ffbbf9583ff435f14a969c4c2..f4a10b508a6b3b9654baf55fff009dba1c76285e 100644 (file)
@@ -445,9 +445,6 @@ var (
 // else branch of the first comparison is executed, we already know that i < len(a).
 // The code for the second panic can be removed.
 func prove(f *Func) {
-       idom := dominators(f)
-       sdom := newSparseTree(f, idom)
-
        // current node state
        type walkState int
        const (
@@ -471,8 +468,8 @@ func prove(f *Func) {
        for len(work) > 0 {
                node := work[len(work)-1]
                work = work[:len(work)-1]
-               parent := idom[node.block.ID]
-               branch := getBranch(sdom, parent, node.block)
+               parent := f.idom[node.block.ID]
+               branch := getBranch(f.sdom, parent, node.block)
 
                switch node.state {
                case descend:
@@ -491,7 +488,7 @@ func prove(f *Func) {
                                block: node.block,
                                state: simplify,
                        })
-                       for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {
+                       for s := f.sdom.Child(node.block); s != nil; s = f.sdom.Sibling(s) {
                                work = append(work, bp{
                                        block: s,
                                        state: descend,