| // 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 { |
| if opcodeTable[v.Op].earlyOk { |
| // Double check we didn't mark the wrong ops as earlyOk |
| if v.Type.IsMemory() || opcodeTable[v.Op].nilCheck || 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 == OpSelect0 || v.Op == 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 != 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] |
| } |
| } |
| } |