// Copyright 2015 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 // combine copyelim and phielim into a single pass. // copyelim removes all uses of OpCopy values from f. // A subsequent deadcode pass is needed to actually remove the copies. func copyelim(f *Func) { phielim(f) // loop of copyelimValue(v) process has been done in phielim() pass. // Update block control values. for _, b := range f.Blocks { for i, v := range b.ControlValues() { if v.Op == OpCopy { b.ReplaceControl(i, v.Args[0]) } } } // Update named values. for _, name := range f.Names { values := f.NamedValues[*name] for i, v := range values { if v.Op == OpCopy { values[i] = v.Args[0] } } } } // copySource returns the (non-copy) op which is the // ultimate source of v. v must be a copy op. func copySource(v *Value) *Value { w := v.Args[0] // This loop is just: // for w.Op == OpCopy { // w = w.Args[0] // } // but we take some extra care to make sure we // don't get stuck in an infinite loop. // Infinite copy loops may happen in unreachable code. // (TODO: or can they? Needs a test.) slow := w var advance bool for w.Op == OpCopy { w = w.Args[0] if w == slow { w.reset(OpUnknown) break } if advance { slow = slow.Args[0] } advance = !advance } // The answer is w. Update all the copies we saw // to point directly to w. Doing this update makes // sure that we don't end up doing O(n^2) work // for a chain of n copies. for v != w { x := v.Args[0] v.SetArg(0, w) v = x } return w } // copyelimValue ensures that no args of v are copies. func copyelimValue(v *Value) { for i, a := range v.Args { if a.Op == OpCopy { v.SetArg(i, copySource(a)) } } } // phielim eliminates redundant phi values from f. // A phi is redundant if its arguments are all equal. For // purposes of counting, ignore the phi itself. Both of // these phis are redundant: // // v = phi(x,x,x) // v = phi(x,v,x,v) // // We repeat this process to also catch situations like: // // v = phi(x, phi(x, x), phi(x, v)) // // TODO: Can we also simplify cases like: // // v = phi(v, w, x) // w = phi(v, w, x) // // and would that be useful? func phielim(f *Func) { for { change := false for _, b := range f.Blocks { for _, v := range b.Values { // This is an early place in SSA where all values are examined. // Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc. if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 { if t.IsStruct() { v.reset(OpStructMake) } else { v.reset(OpArrayMake0) } } // Modify all values so no arg (including args // of OpCopy) is a copy. copyelimValue(v) change = phielimValue(v) || change } } if !change { break } } } // phielimValue tries to convert the phi v to a copy. func phielimValue(v *Value) bool { if v.Op != OpPhi { return false } // If there are two distinct args of v which // are not v itself, then the phi must remain. // Otherwise, we can replace it with a copy. var w *Value for _, x := range v.Args { if x == v { continue } if x == w { continue } if w != nil { return false } w = x } if w == nil { // v references only itself. It must be in // a dead code loop. Don't bother modifying it. return false } v.Op = OpCopy v.SetArgs1(w) f := v.Block.Func if f.pass.debug > 0 { f.Warnl(v.Pos, "eliminated phi") } return true }