// 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] } } }