Commit 75ce89c2 authored by Keith Randall's avatar Keith Randall

cmd/compile: cache CFG-dependent computations

We compute a lot of stuff based off the CFG: postorder traversal,
dominators, dominator tree, loop nest.  Multiple phases use this
information and we end up recomputing some of it.  Add a cache
for this information so if the CFG hasn't changed, we can reuse
the previous computation.

Change-Id: I9b5b58af06830bd120afbee9cfab395a0a2f74b2
Reviewed-on: https://go-review.googlesource.com/29356Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 2679282d
...@@ -144,6 +144,7 @@ func (b *Block) AddEdgeTo(c *Block) { ...@@ -144,6 +144,7 @@ func (b *Block) AddEdgeTo(c *Block) {
j := len(c.Preds) j := len(c.Preds)
b.Succs = append(b.Succs, Edge{c, j}) b.Succs = append(b.Succs, Edge{c, j})
c.Preds = append(c.Preds, Edge{b, i}) c.Preds = append(c.Preds, Edge{b, i})
b.Func.invalidateCFG()
} }
// removePred removes the ith input edge from b. // removePred removes the ith input edge from b.
...@@ -159,6 +160,7 @@ func (b *Block) removePred(i int) { ...@@ -159,6 +160,7 @@ func (b *Block) removePred(i int) {
} }
b.Preds[n] = Edge{} b.Preds[n] = Edge{}
b.Preds = b.Preds[:n] b.Preds = b.Preds[:n]
b.Func.invalidateCFG()
} }
// removeSucc removes the ith output edge from b. // removeSucc removes the ith output edge from b.
...@@ -174,6 +176,7 @@ func (b *Block) removeSucc(i int) { ...@@ -174,6 +176,7 @@ func (b *Block) removeSucc(i int) {
} }
b.Succs[n] = Edge{} b.Succs[n] = Edge{}
b.Succs = b.Succs[:n] b.Succs = b.Succs[:n]
b.Func.invalidateCFG()
} }
func (b *Block) swapSuccessors() { func (b *Block) swapSuccessors() {
......
...@@ -255,8 +255,7 @@ func checkFunc(f *Func) { ...@@ -255,8 +255,7 @@ func checkFunc(f *Func) {
if f.RegAlloc == nil { if f.RegAlloc == nil {
// Note: regalloc introduces non-dominating args. // Note: regalloc introduces non-dominating args.
// See TODO in regalloc.go. // See TODO in regalloc.go.
idom := dominators(f) sdom := f.sdom()
sdom := newSparseTree(f, idom)
for _, b := range f.Blocks { for _, b := range f.Blocks {
for _, v := range b.Values { for _, v := range b.Values {
for i, arg := range v.Args { for i, arg := range v.Args {
......
...@@ -250,7 +250,6 @@ var passes = [...]pass{ ...@@ -250,7 +250,6 @@ var passes = [...]pass{
{name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules {name: "opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
{name: "zero arg cse", fn: zcse, required: true}, // required to merge OpSB values {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: "opt deadcode", fn: deadcode, required: true}, // remove any blocks orphaned during opt
{name: "generic domtree", fn: domTree},
{name: "generic cse", fn: cse}, {name: "generic cse", fn: cse},
{name: "phiopt", fn: phiopt}, {name: "phiopt", fn: phiopt},
{name: "nilcheckelim", fn: nilcheckelim}, {name: "nilcheckelim", fn: nilcheckelim},
...@@ -308,12 +307,6 @@ var passOrder = [...]constraint{ ...@@ -308,12 +307,6 @@ var passOrder = [...]constraint{
{"opt", "nilcheckelim"}, {"opt", "nilcheckelim"},
// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET // tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
{"tighten", "lower"}, {"tighten", "lower"},
// cse, phiopt, nilcheckelim, prove and loopbce share idom.
{"generic domtree", "generic cse"},
{"generic domtree", "phiopt"},
{"generic domtree", "nilcheckelim"},
{"generic domtree", "prove"},
{"generic domtree", "loopbce"},
// tighten will be most effective when as many values have been removed as possible // tighten will be most effective when as many values have been removed as possible
{"generic deadcode", "tighten"}, {"generic deadcode", "tighten"},
{"generic cse", "tighten"}, {"generic cse", "tighten"},
......
...@@ -131,13 +131,13 @@ func cse(f *Func) { ...@@ -131,13 +131,13 @@ func cse(f *Func) {
} }
} }
// Dominator tree (f.sdom) is computed by the generic domtree pass. sdom := f.sdom()
// Compute substitutions we would like to do. We substitute v for w // Compute substitutions we would like to do. We substitute v for w
// if v and w are in the same equivalence class and v dominates w. // if v and w are in the same equivalence class and v dominates w.
rewrite := make([]*Value, f.NumValues()) rewrite := make([]*Value, f.NumValues())
for _, e := range partition { for _, e := range partition {
sort.Sort(partitionByDom{e, f.sdom}) sort.Sort(partitionByDom{e, sdom})
for i := 0; i < len(e)-1; i++ { for i := 0; i < len(e)-1; i++ {
// e is sorted by domorder, so a maximal dominant element is first in the slice // e is sorted by domorder, so a maximal dominant element is first in the slice
v := e[i] v := e[i]
...@@ -152,7 +152,7 @@ func cse(f *Func) { ...@@ -152,7 +152,7 @@ func cse(f *Func) {
if w == nil { if w == nil {
continue continue
} }
if f.sdom.isAncestorEq(v.Block, w.Block) { if sdom.isAncestorEq(v.Block, w.Block) {
rewrite[w.ID] = v rewrite[w.ID] = v
e[j] = nil e[j] = nil
} else { } else {
......
...@@ -44,7 +44,6 @@ func TestCSEAuxPartitionBug(t *testing.T) { ...@@ -44,7 +44,6 @@ func TestCSEAuxPartitionBug(t *testing.T) {
Exit("rstore"))) Exit("rstore")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
cse(fun.f) cse(fun.f)
deadcode(fun.f) deadcode(fun.f)
CheckFunc(fun.f) CheckFunc(fun.f)
......
...@@ -247,7 +247,7 @@ func dominatorsSimple(f *Func) []*Block { ...@@ -247,7 +247,7 @@ func dominatorsSimple(f *Func) []*Block {
idom := make([]*Block, f.NumBlocks()) idom := make([]*Block, f.NumBlocks())
// Compute postorder walk // Compute postorder walk
post := postorder(f) post := f.postorder()
// Make map from block id to order index (for intersect call) // Make map from block id to order index (for intersect call)
postnum := make([]int, f.NumBlocks()) postnum := make([]int, f.NumBlocks())
...@@ -306,9 +306,3 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block { ...@@ -306,9 +306,3 @@ func intersect(b, c *Block, postnum []int, idom []*Block) *Block {
} }
return b return b
} }
// build immediate dominators.
func domTree(f *Func) {
f.idom = dominators(f)
f.sdom = newSparseTree(f, f.idom)
}
...@@ -11,14 +11,10 @@ func flagalloc(f *Func) { ...@@ -11,14 +11,10 @@ func flagalloc(f *Func) {
// Compute the in-register flag value we want at the end of // Compute the in-register flag value we want at the end of
// each block. This is basically a best-effort live variable // each block. This is basically a best-effort live variable
// analysis, so it can be much simpler than a full analysis. // analysis, so it can be much simpler than a full analysis.
// TODO: do we really need to keep flag values live across blocks?
// Could we force the flags register to be unused at basic block
// boundaries? Then we wouldn't need this computation.
end := make([]*Value, f.NumBlocks()) end := make([]*Value, f.NumBlocks())
po := f.postorder()
for n := 0; n < 2; n++ { for n := 0; n < 2; n++ {
// Walk blocks backwards. Poor-man's postorder traversal. for _, b := range po {
for i := len(f.Blocks) - 1; i >= 0; i-- {
b := f.Blocks[i]
// Walk values backwards to figure out what flag // Walk values backwards to figure out what flag
// value we want in the flag register at the start // value we want in the flag register at the start
// of the block. // of the block.
......
...@@ -36,8 +36,10 @@ type Func struct { ...@@ -36,8 +36,10 @@ type Func struct {
freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil. freeValues *Value // free Values linked by argstorage[0]. All other fields except ID are 0/nil.
freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil. freeBlocks *Block // free Blocks linked by succstorage[0].b. All other fields except ID are 0/nil.
idom []*Block // precomputed immediate dominators cachedPostorder []*Block // cached postorder traversal
sdom SparseTree // precomputed dominator tree cachedIdom []*Block // cached immediate dominators
cachedSdom SparseTree // cached dominator tree
cachedLoopnest *loopnest // cached loop nest information
constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type constants map[int64][]*Value // constants cache, keyed by constant value; users must check value's Op and Type
} }
...@@ -166,6 +168,7 @@ func (f *Func) NewBlock(kind BlockKind) *Block { ...@@ -166,6 +168,7 @@ func (f *Func) NewBlock(kind BlockKind) *Block {
b.Succs = b.succstorage[:0] b.Succs = b.succstorage[:0]
b.Values = b.valstorage[:0] b.Values = b.valstorage[:0]
f.Blocks = append(f.Blocks, b) f.Blocks = append(f.Blocks, b)
f.invalidateCFG()
return b return b
} }
...@@ -409,6 +412,9 @@ func (f *Func) Log() bool { return f.Config.Log() } ...@@ -409,6 +412,9 @@ func (f *Func) Log() bool { return f.Config.Log() }
func (f *Func) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(f.Entry.Line, msg, args...) } func (f *Func) Fatalf(msg string, args ...interface{}) { f.Config.Fatalf(f.Entry.Line, msg, args...) }
func (f *Func) Free() { func (f *Func) Free() {
// Clear cached CFG info.
f.invalidateCFG()
// Clear values. // Clear values.
n := f.vid.num() n := f.vid.num()
if n > len(f.Config.values) { if n > len(f.Config.values) {
...@@ -436,3 +442,45 @@ func (f *Func) Free() { ...@@ -436,3 +442,45 @@ func (f *Func) Free() {
f.Config.curFunc = nil f.Config.curFunc = nil
*f = Func{} // just in case *f = Func{} // just in case
} }
// postorder returns the reachable blocks in f in a postorder traversal.
func (f *Func) postorder() []*Block {
if f.cachedPostorder == nil {
f.cachedPostorder = postorder(f)
}
return f.cachedPostorder
}
// idom returns a map from block ID to the immediate dominator of that block.
// f.Entry.ID maps to nil. Unreachable blocks map to nil as well.
func (f *Func) idom() []*Block {
if f.cachedIdom == nil {
f.cachedIdom = dominators(f)
}
return f.cachedIdom
}
// sdom returns a sparse tree representing the dominator relationships
// among the blocks of f.
func (f *Func) sdom() SparseTree {
if f.cachedSdom == nil {
f.cachedSdom = newSparseTree(f, f.idom())
}
return f.cachedSdom
}
// loopnest returns the loop nest information for f.
func (f *Func) loopnest() *loopnest {
if f.cachedLoopnest == nil {
f.cachedLoopnest = loopnestfor(f)
}
return f.cachedLoopnest
}
// invalidateCFG tells f that its CFG has changed.
func (f *Func) invalidateCFG() {
f.cachedPostorder = nil
f.cachedIdom = nil
f.cachedSdom = nil
f.cachedLoopnest = nil
}
...@@ -120,8 +120,8 @@ func likelyadjust(f *Func) { ...@@ -120,8 +120,8 @@ func likelyadjust(f *Func) {
certain := make([]int8, f.NumBlocks()) // In the long run, all outcomes are at least this bad. Mainly for Exit certain := make([]int8, f.NumBlocks()) // In the long run, all outcomes are at least this bad. Mainly for Exit
local := make([]int8, f.NumBlocks()) // for our immediate predecessors. local := make([]int8, f.NumBlocks()) // for our immediate predecessors.
nest := loopnestfor(f) po := f.postorder()
po := nest.po nest := f.loopnest()
b2l := nest.b2l b2l := nest.b2l
for _, b := range po { for _, b := range po {
...@@ -260,9 +260,8 @@ func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop { ...@@ -260,9 +260,8 @@ func (l *loop) nearestOuterLoop(sdom SparseTree, b *Block) *loop {
} }
func loopnestfor(f *Func) *loopnest { func loopnestfor(f *Func) *loopnest {
po := postorder(f) po := f.postorder()
dom := dominators(f) sdom := f.sdom()
sdom := newSparseTree(f, dom)
b2l := make([]*loop, f.NumBlocks()) b2l := make([]*loop, f.NumBlocks())
loops := make([]*loop, 0) loops := make([]*loop, 0)
......
...@@ -33,6 +33,7 @@ type indVar struct { ...@@ -33,6 +33,7 @@ type indVar struct {
// TODO: handle 32 bit operations // TODO: handle 32 bit operations
func findIndVar(f *Func) []indVar { func findIndVar(f *Func) []indVar {
var iv []indVar var iv []indVar
sdom := f.sdom()
nextb: nextb:
for _, b := range f.Blocks { for _, b := range f.Blocks {
...@@ -110,7 +111,7 @@ nextb: ...@@ -110,7 +111,7 @@ nextb:
// Second condition: b.Succs[entry] dominates nxt so that // Second condition: b.Succs[entry] dominates nxt so that
// nxt is computed when inc < max, meaning nxt <= max. // nxt is computed when inc < max, meaning nxt <= max.
if !f.sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) { if !sdom.isAncestorEq(b.Succs[entry].b, nxt.Block) {
// inc+ind can only be reached through the branch that enters the loop. // inc+ind can only be reached through the branch that enters the loop.
continue continue
} }
...@@ -172,6 +173,7 @@ func loopbce(f *Func) { ...@@ -172,6 +173,7 @@ func loopbce(f *Func) {
// removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables. // removesBoundsChecks remove IsInBounds and IsSliceInBounds based on the induction variables.
func removeBoundsChecks(f *Func, m map[*Value]indVar) { func removeBoundsChecks(f *Func, m map[*Value]indVar) {
sdom := f.sdom()
for _, b := range f.Blocks { for _, b := range f.Blocks {
if b.Kind != BlockIf { if b.Kind != BlockIf {
continue continue
...@@ -200,7 +202,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) { ...@@ -200,7 +202,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
goto skip1 goto skip1
} }
if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) {
if v.Args[1] == iv.max { if v.Args[1] == iv.max {
if f.pass.debug > 0 { if f.pass.debug > 0 {
f.Config.Warnl(b.Line, "Found redundant %s", v.Op) f.Config.Warnl(b.Line, "Found redundant %s", v.Op)
...@@ -227,7 +229,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) { ...@@ -227,7 +229,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
goto skip2 goto skip2
} }
if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isNonNegative(iv.min) { if iv, has := m[ind]; has && 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 v.Args[1].Op == OpSliceCap && iv.max.Op == OpSliceLen && v.Args[1].Args[0] == iv.max.Args[0] {
if f.pass.debug > 0 { if f.pass.debug > 0 {
f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op) f.Config.Warnl(b.Line, "Found redundant %s (len promoted to cap)", v.Op)
...@@ -248,7 +250,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) { ...@@ -248,7 +250,7 @@ func removeBoundsChecks(f *Func, m map[*Value]indVar) {
} }
// ind + add >= 0 <-> min + add >= 0 <-> min >= -add // ind + add >= 0 <-> min + add >= 0 <-> min >= -add
if iv, has := m[ind]; has && f.sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) { if iv, has := m[ind]; has && sdom.isAncestorEq(iv.entry, b) && isGreaterOrEqualThan(iv.min, -add) {
if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() { if !v.Args[1].isGenericIntConst() || !iv.max.isGenericIntConst() {
goto skip3 goto skip3
} }
......
...@@ -10,7 +10,7 @@ func nilcheckelim(f *Func) { ...@@ -10,7 +10,7 @@ func nilcheckelim(f *Func) {
// A nil check is redundant if the same nil check was successful in a // 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 // dominating block. The efficacy of this pass depends heavily on the
// efficacy of the cse pass. // efficacy of the cse pass.
idom := f.idom idom := f.idom()
domTree := make([][]*Block, f.NumBlocks()) domTree := make([][]*Block, f.NumBlocks())
// Create a block ID -> [dominees] mapping // Create a block ID -> [dominees] mapping
......
...@@ -49,7 +49,6 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) { ...@@ -49,7 +49,6 @@ func benchmarkNilCheckDeep(b *testing.B, depth int) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
} }
} }
...@@ -84,7 +83,6 @@ func TestNilcheckSimple(t *testing.T) { ...@@ -84,7 +83,6 @@ func TestNilcheckSimple(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -122,7 +120,6 @@ func TestNilcheckDomOrder(t *testing.T) { ...@@ -122,7 +120,6 @@ func TestNilcheckDomOrder(t *testing.T) {
Goto("exit"))) Goto("exit")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -156,7 +153,6 @@ func TestNilcheckAddr(t *testing.T) { ...@@ -156,7 +153,6 @@ func TestNilcheckAddr(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -191,7 +187,6 @@ func TestNilcheckAddPtr(t *testing.T) { ...@@ -191,7 +187,6 @@ func TestNilcheckAddPtr(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -236,7 +231,6 @@ func TestNilcheckPhi(t *testing.T) { ...@@ -236,7 +231,6 @@ func TestNilcheckPhi(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -278,7 +272,6 @@ func TestNilcheckKeepRemove(t *testing.T) { ...@@ -278,7 +272,6 @@ func TestNilcheckKeepRemove(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -326,7 +319,6 @@ func TestNilcheckInFalseBranch(t *testing.T) { ...@@ -326,7 +319,6 @@ func TestNilcheckInFalseBranch(t *testing.T) {
Exit("mem"))) Exit("mem")))
CheckFunc(fun.f) CheckFunc(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -378,7 +370,6 @@ func TestNilcheckUser(t *testing.T) { ...@@ -378,7 +370,6 @@ func TestNilcheckUser(t *testing.T) {
CheckFunc(fun.f) CheckFunc(fun.f)
// we need the opt here to rewrite the user nilcheck // we need the opt here to rewrite the user nilcheck
opt(fun.f) opt(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
...@@ -423,7 +414,6 @@ func TestNilcheckBug(t *testing.T) { ...@@ -423,7 +414,6 @@ func TestNilcheckBug(t *testing.T) {
CheckFunc(fun.f) CheckFunc(fun.f)
// we need the opt here to rewrite the user nilcheck // we need the opt here to rewrite the user nilcheck
opt(fun.f) opt(fun.f)
domTree(fun.f)
nilcheckelim(fun.f) nilcheckelim(fun.f)
// clean up the removed nil check // clean up the removed nil check
......
...@@ -35,7 +35,6 @@ func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) { ...@@ -35,7 +35,6 @@ func benchFnPass(b *testing.B, fn passFunc, size int, bg blockGen) {
b.ReportAllocs() b.ReportAllocs()
c := NewConfig("amd64", DummyFrontend{b}, nil, true) c := NewConfig("amd64", DummyFrontend{b}, nil, true)
fun := Fun(c, "entry", bg(size)...) fun := Fun(c, "entry", bg(size)...)
domTree(fun.f)
CheckFunc(fun.f) CheckFunc(fun.f)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
...@@ -51,7 +50,6 @@ func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) { ...@@ -51,7 +50,6 @@ func benchFnBlock(b *testing.B, fn passFunc, bg blockGen) {
b.ReportAllocs() b.ReportAllocs()
c := NewConfig("amd64", DummyFrontend{b}, nil, true) c := NewConfig("amd64", DummyFrontend{b}, nil, true)
fun := Fun(c, "entry", bg(b.N)...) fun := Fun(c, "entry", bg(b.N)...)
domTree(fun.f)
CheckFunc(fun.f) CheckFunc(fun.f)
b.ResetTimer() b.ResetTimer()
for i := 0; i < passCount; i++ { for i := 0; i < passCount; i++ {
......
...@@ -24,6 +24,7 @@ package ssa ...@@ -24,6 +24,7 @@ package ssa
// //
// In this case we can replace x with a copy of b. // In this case we can replace x with a copy of b.
func phiopt(f *Func) { func phiopt(f *Func) {
sdom := f.sdom()
for _, b := range f.Blocks { for _, b := range f.Blocks {
if len(b.Preds) != 2 || len(b.Values) == 0 { if len(b.Preds) != 2 || len(b.Values) == 0 {
// TODO: handle more than 2 predecessors, e.g. a || b || c. // TODO: handle more than 2 predecessors, e.g. a || b || c.
...@@ -92,7 +93,7 @@ func phiopt(f *Func) { ...@@ -92,7 +93,7 @@ func phiopt(f *Func) {
// value is always computed. This guarantees that the side effects // value is always computed. This guarantees that the side effects
// of value are not seen if a is false. // of value are not seen if a is false.
if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 { if v.Args[reverse].Op == OpConstBool && v.Args[reverse].AuxInt == 1 {
if tmp := v.Args[1-reverse]; f.sdom.isAncestorEq(tmp.Block, b) { if tmp := v.Args[1-reverse]; sdom.isAncestorEq(tmp.Block, b) {
v.reset(OpOrB) v.reset(OpOrB)
v.SetArgs2(b0.Control, tmp) v.SetArgs2(b0.Control, tmp)
if f.pass.debug > 0 { if f.pass.debug > 0 {
...@@ -108,7 +109,7 @@ func phiopt(f *Func) { ...@@ -108,7 +109,7 @@ func phiopt(f *Func) {
// value is always computed. This guarantees that the side effects // value is always computed. This guarantees that the side effects
// of value are not seen if a is false. // of value are not seen if a is false.
if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 { if v.Args[1-reverse].Op == OpConstBool && v.Args[1-reverse].AuxInt == 0 {
if tmp := v.Args[reverse]; f.sdom.isAncestorEq(tmp.Block, b) { if tmp := v.Args[reverse]; sdom.isAncestorEq(tmp.Block, b) {
v.reset(OpAndB) v.reset(OpAndB)
v.SetArgs2(b0.Control, tmp) v.SetArgs2(b0.Control, tmp)
if f.pass.debug > 0 { if f.pass.debug > 0 {
......
...@@ -463,13 +463,15 @@ func prove(f *Func) { ...@@ -463,13 +463,15 @@ func prove(f *Func) {
}) })
ft := newFactsTable() ft := newFactsTable()
idom := f.idom()
sdom := f.sdom()
// DFS on the dominator tree. // DFS on the dominator tree.
for len(work) > 0 { for len(work) > 0 {
node := work[len(work)-1] node := work[len(work)-1]
work = work[:len(work)-1] work = work[:len(work)-1]
parent := f.idom[node.block.ID] parent := idom[node.block.ID]
branch := getBranch(f.sdom, parent, node.block) branch := getBranch(sdom, parent, node.block)
switch node.state { switch node.state {
case descend: case descend:
...@@ -488,7 +490,7 @@ func prove(f *Func) { ...@@ -488,7 +490,7 @@ func prove(f *Func) {
block: node.block, block: node.block,
state: simplify, state: simplify,
}) })
for s := f.sdom.Child(node.block); s != nil; s = f.sdom.Sibling(s) { for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {
work = append(work, bp{ work = append(work, bp{
block: s, block: s,
state: descend, state: descend,
......
...@@ -2195,8 +2195,8 @@ func (s *regAllocState) computeLive() { ...@@ -2195,8 +2195,8 @@ func (s *regAllocState) computeLive() {
// Walk the dominator tree from end to beginning, just once, treating SCC // Walk the dominator tree from end to beginning, just once, treating SCC
// components as single blocks, duplicated calculated liveness information // components as single blocks, duplicated calculated liveness information
// out to all of them. // out to all of them.
s.loopnest = loopnestfor(f) po := f.postorder()
po := s.loopnest.po s.loopnest = f.loopnest()
for { for {
changed := false changed := false
......
...@@ -57,7 +57,7 @@ type SparseTreeHelper struct { ...@@ -57,7 +57,7 @@ type SparseTreeHelper struct {
// NewSparseTreeHelper returns a SparseTreeHelper for use // NewSparseTreeHelper returns a SparseTreeHelper for use
// in the gc package, for example in phi-function placement. // in the gc package, for example in phi-function placement.
func NewSparseTreeHelper(f *Func) *SparseTreeHelper { func NewSparseTreeHelper(f *Func) *SparseTreeHelper {
dom := dominators(f) dom := f.idom()
ponums := make([]int32, f.NumBlocks()) ponums := make([]int32, f.NumBlocks())
po := postorderWithNumbering(f, ponums) po := postorderWithNumbering(f, ponums)
return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums) return makeSparseTreeHelper(newSparseTree(f, dom), dom, po, ponums)
......
...@@ -273,7 +273,7 @@ func (s *stackAllocState) computeLive(spillLive [][]ID) { ...@@ -273,7 +273,7 @@ func (s *stackAllocState) computeLive(spillLive [][]ID) {
// Instead of iterating over f.Blocks, iterate over their postordering. // Instead of iterating over f.Blocks, iterate over their postordering.
// Liveness information flows backward, so starting at the end // Liveness information flows backward, so starting at the end
// increases the probability that we will stabilize quickly. // increases the probability that we will stabilize quickly.
po := postorder(s.f) po := s.f.postorder()
for { for {
changed := false changed := false
for _, b := range po { for _, b := range po {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment