blob: 1cdb4b5186b137031347b37082a1185ad30d0606 [file] [log] [blame]
// Copyright 2016 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 ssa
// We are looking for loops with following structure
// (loop bodies may have control flow inside):
//
// +--------------+
// | |
// | preheader |
// | |
// +-------+------+
// |
// |
// +-------v------+
// | |
// +------> header |
// | | |
// | +-------+------+
// | |
// | |
// | +-------v------+
// | | |
// +------+ loop body |
// | |
// +--------------+
//
//
// We consider all phis and memory operations as initial loop dependent set.
// So loop independent values are all loop values,
// minus transitive closure of initial loop dependent values.
// We remove those values from their BBs and move them to preheader.
func licm(f *Func) {
// See likelyadjust.go for details about loop info.
nest := loopnestfor(f)
if len(nest.loops) == 0 || nest.hasIrreducible {
return
}
uses := uses(f)
defer uses.free(f)
loopDependent := f.Cache.allocBoolSlice(f.NumValues())
defer f.Cache.freeBoolSlice(loopDependent)
queue := f.Cache.allocValueSlice(f.NumValues())
defer f.Cache.freeValueSlice(queue)
queue = queue[:0]
// Start with all values we can't move out of loops.
for _, b := range f.Blocks {
if loop := nest.b2l[b.ID]; loop == nil || !loop.isInner {
// Values outside any loop we don't care about.
// Values not in a leaf loop we can't handle.
continue
}
for _, v := range b.Values {
loopDep := false
if v.Op == OpPhi {
loopDep = true
} else if v.Type.IsMemory() {
// We can't move state-modifying code.
// (TODO: but maybe this is handled by memory Phis anyway?)
loopDep = true
} else if v.Type.IsFlags() || v.Type.IsTuple() && (v.Type.FieldType(0).IsFlags() || v.Type.FieldType(1).IsFlags()) {
// This is not required for correctness. It is just to
// keep the live range of flag values low.
loopDep = true
} else if opcodeTable[v.Op].nilCheck {
// NilCheck in case loop executes 0 times. (It has a memory arg anyway?)
loopDep = true
} else if v.MemoryArg() != nil {
// Because the state of memory might be different at the loop start. (Also handled by Phi?)
loopDep = true
} else if v.Type.IsPtr() {
// Can't move pointer arithmetic, as it may be guarded by conditionals
// and this could materialize a bad pointer across a safepoint.
loopDep = true
}
if loopDep {
loopDependent[v.ID] = true
queue = append(queue, v)
}
}
}
// If a value can't be moved out of a loop, neither can its users.
// The queue contains values which are loop dependent, but their users
// have not been marked as loop dependent yet.
for len(queue) > 0 {
v := queue[len(queue)-1]
queue = queue[:len(queue)-1]
for _, u := range uses.get(v) {
if loop := nest.b2l[u.Block.ID]; loop == nil || !loop.isInner {
continue // see above
}
if loopDependent[u.ID] {
continue
}
loopDependent[u.ID] = true
queue = append(queue, u)
}
}
// Anything not marked as loop-dependent can be moved out of its loop.
for _, b := range f.Blocks {
loop := nest.b2l[b.ID]
if loop == nil || !loop.isInner {
// loopDependent check is wrong for loops containing other loops,
// because then a value might have an argument computed inside
// a nested loop.
continue
}
if len(loop.header.Preds) != 2 {
continue // is never true?
}
anyMoved := false
for i, v := range b.Values {
if loopDependent[v.ID] {
continue
}
// Figure out where to move loop-independent values.
h := loop.header
var inIdx int
if int(h.Preds[0].b.ID) >= len(nest.b2l) || nest.b2l[h.Preds[0].b.ID] != loop {
inIdx = 0
} else {
inIdx = 1
}
dest := h.Preds[inIdx].b
if dest.Kind != BlockPlain {
outIdx := h.Preds[inIdx].i
// Introduce a new block between the loop
// header predecessor and the loop header itself.
mid := f.NewBlock(BlockPlain)
mid.Pos = dest.Pos
// Splice into graph.
mid.Preds = append(mid.Preds, Edge{dest, outIdx})
mid.Succs = append(mid.Succs, Edge{h, inIdx})
h.Preds[inIdx] = Edge{mid, 0}
dest.Succs[outIdx] = Edge{mid, 0}
dest = mid
}
b.Values[i] = nil
v.Block = dest
dest.Values = append(dest.Values, v)
anyMoved = true
}
if anyMoved {
// We just nil'd entries in b.Values above. Compact out the nils.
i := 0
for _, v := range b.Values {
if v == nil {
continue
}
b.Values[i] = v
i++
}
b.Values = b.Values[:i]
}
}
}