blob: a7d3ff248db78a3635753233cf6e6c752a15e46a [file]
// 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 ssacompile
import (
"cmd/compile/internal/ssa"
"cmd/compile/internal/ssa/block"
"cmd/compile/internal/ssa/ssaop"
)
// 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 *ssa.Func) {
// See likelyadjust.go for details about loop info.
nest := ssa.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 {
if ssaop.OpcodeTable[v.Op].EarlyOk {
// Double check we didn't mark the wrong ops as earlyOk
if v.Type.IsMemory() || ssaop.OpcodeTable[v.Op].NilCheck || ssaop.OpcodeTable[v.Op].HasSideEffects || v.MemoryArg() != nil {
v.Fatalf("op %s has bad earlyOk mark", v.Op)
}
if !v.Type.IsPtr() {
// Note: can't move pointer arithmetic, as it may be guarded by conditionals
// and thus could materialize a bad pointer across a safepoint.
continue // Ok to lift out of loop.
}
}
if v.Op == ssaop.OpSelect0 || v.Op == ssaop.OpSelect1 {
// These ops can (and must) move with the op they are selecting from.
continue
}
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 != block.BlockPlain {
outIdx := h.Preds[inIdx].I
// Introduce a new block between the loop
// header predecessor and the loop header itself.
mid := f.NewBlock(block.BlockPlain)
mid.Pos = dest.Pos
// Splice into graph.
mid.Preds = append(mid.Preds, ssa.Edge{B: dest, I: outIdx})
mid.Succs = append(mid.Succs, ssa.Edge{B: h, I: inIdx})
h.Preds[inIdx] = ssa.Edge{B: mid, I: 0}
dest.Succs[outIdx] = ssa.Edge{B: mid, I: 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]
}
}
}