mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: fix containsUnavoidableCall computation
The previous algorithm was incorrect, as it reused the dominatedByCall slice without resetting it. It also used the depth fields even though they were not yet calculated. Also, clean up a lot of the loop detector code that we never use. Always compute depths. It is cheap. Update #71868 Not really sure how to test this. As it is just an advisory bit, nothing goes really wrong when the result is incorrect. Change-Id: Ic0ae87a4d3576554831252d88b05b058ca68af41 Reviewed-on: https://go-review.googlesource.com/c/go/+/680775 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
d28b27cd8e
commit
5045fdd8ff
4 changed files with 105 additions and 180 deletions
|
|
@ -2735,7 +2735,7 @@ func (s *regAllocState) computeLive() {
|
|||
// out to all of them.
|
||||
po := f.postorder()
|
||||
s.loopnest = f.loopnest()
|
||||
s.loopnest.calculateDepths()
|
||||
s.loopnest.computeUnavoidableCalls()
|
||||
for {
|
||||
changed := false
|
||||
|
||||
|
|
@ -3042,3 +3042,72 @@ func (d *desiredState) merge(x *desiredState) {
|
|||
d.addList(e.ID, e.regs)
|
||||
}
|
||||
}
|
||||
|
||||
// computeUnavoidableCalls computes the containsUnavoidableCall fields in the loop nest.
|
||||
func (loopnest *loopnest) computeUnavoidableCalls() {
|
||||
f := loopnest.f
|
||||
|
||||
hasCall := f.Cache.allocBoolSlice(f.NumBlocks())
|
||||
defer f.Cache.freeBoolSlice(hasCall)
|
||||
for _, b := range f.Blocks {
|
||||
if b.containsCall() {
|
||||
hasCall[b.ID] = true
|
||||
}
|
||||
}
|
||||
found := f.Cache.allocSparseSet(f.NumBlocks())
|
||||
defer f.Cache.freeSparseSet(found)
|
||||
// Run dfs to find path through the loop that avoids all calls.
|
||||
// Such path either escapes the loop or returns back to the header.
|
||||
// It isn't enough to have exit not dominated by any call, for example:
|
||||
// ... some loop
|
||||
// call1 call2
|
||||
// \ /
|
||||
// block
|
||||
// ...
|
||||
// block is not dominated by any single call, but we don't have call-free path to it.
|
||||
loopLoop:
|
||||
for _, l := range loopnest.loops {
|
||||
found.clear()
|
||||
tovisit := make([]*Block, 0, 8)
|
||||
tovisit = append(tovisit, l.header)
|
||||
for len(tovisit) > 0 {
|
||||
cur := tovisit[len(tovisit)-1]
|
||||
tovisit = tovisit[:len(tovisit)-1]
|
||||
if hasCall[cur.ID] {
|
||||
continue
|
||||
}
|
||||
for _, s := range cur.Succs {
|
||||
nb := s.Block()
|
||||
if nb == l.header {
|
||||
// Found a call-free path around the loop.
|
||||
continue loopLoop
|
||||
}
|
||||
if found.contains(nb.ID) {
|
||||
// Already found via another path.
|
||||
continue
|
||||
}
|
||||
nl := loopnest.b2l[nb.ID]
|
||||
if nl == nil || (nl.depth <= l.depth && nl != l) {
|
||||
// Left the loop.
|
||||
continue
|
||||
}
|
||||
tovisit = append(tovisit, nb)
|
||||
found.add(nb.ID)
|
||||
}
|
||||
}
|
||||
// No call-free path was found.
|
||||
l.containsUnavoidableCall = true
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) containsCall() bool {
|
||||
if b.Kind == BlockDefer {
|
||||
return true
|
||||
}
|
||||
for _, v := range b.Values {
|
||||
if opcodeTable[v.Op].call {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue