Source file src/cmd/compile/internal/ssacompile/copyelim.go
1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssacompile 6 7 import ( 8 "cmd/compile/internal/ssa" 9 "cmd/compile/internal/ssa/ssaop" 10 ) 11 12 // combine copyelim and phielim into a single pass. 13 // copyelim removes all uses of OpCopy values from f. 14 // A subsequent deadcode pass is needed to actually remove the copies. 15 func copyelim(f *ssa.Func) { 16 phielim(f) 17 18 // loop of copyelimValue(v) process has been done in phielim() pass. 19 // Update block control values. 20 for _, b := range f.Blocks { 21 for i, v := range b.ControlValues() { 22 if v.Op == ssaop.OpCopy { 23 b.ReplaceControl(i, v.Args[0]) 24 } 25 } 26 } 27 28 // Update named values. 29 for _, name := range f.Names { 30 values := f.NamedValues[name] 31 for i, v := range values { 32 if v.Op == ssaop.OpCopy { 33 values[i] = v.Args[0] 34 } 35 } 36 } 37 } 38 39 // copySource returns the (non-copy) op which is the 40 // ultimate source of v. v must be a copy op. 41 func copySource(v *ssa.Value) *ssa.Value { 42 w := v.Args[0] 43 44 // This loop is just: 45 // for w.Op == OpCopy { 46 // w = w.Args[0] 47 // } 48 // but we take some extra care to make sure we 49 // don't get stuck in an infinite loop. 50 // Infinite copy loops may happen in unreachable code. 51 // (TODO: or can they? Needs a test.) 52 slow := w 53 var advance bool 54 for w.Op == ssaop.OpCopy { 55 w = w.Args[0] 56 if w == slow { 57 w.Reset(ssaop.OpUnknown) 58 break 59 } 60 if advance { 61 slow = slow.Args[0] 62 } 63 advance = !advance 64 } 65 66 // The answer is w. Update all the copies we saw 67 // to point directly to w. Doing this update makes 68 // sure that we don't end up doing O(n^2) work 69 // for a chain of n copies. 70 for v != w { 71 x := v.Args[0] 72 v.SetArg(0, w) 73 v = x 74 } 75 return w 76 } 77 78 // copyelimValue ensures that no args of v are copies. 79 func copyelimValue(v *ssa.Value) { 80 for i, a := range v.Args { 81 if a.Op == ssaop.OpCopy { 82 v.SetArg(i, copySource(a)) 83 } 84 } 85 } 86 87 // phielim eliminates redundant phi values from f. 88 // A phi is redundant if its arguments are all equal. For 89 // purposes of counting, ignore the phi itself. Both of 90 // these phis are redundant: 91 // 92 // v = phi(x,x,x) 93 // v = phi(x,v,x,v) 94 // 95 // We repeat this process to also catch situations like: 96 // 97 // v = phi(x, phi(x, x), phi(x, v)) 98 // 99 // TODO: Can we also simplify cases like: 100 // 101 // v = phi(v, w, x) 102 // w = phi(v, w, x) 103 // 104 // and would that be useful? 105 func phielim(f *ssa.Func) { 106 for { 107 change := false 108 for _, b := range f.Blocks { 109 for _, v := range b.Values { 110 // This is an early place in SSA where all values are examined. 111 // Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc. 112 if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 { 113 v.Reset(ssaop.OpEmpty) 114 } 115 // Modify all values so no arg (including args 116 // of OpCopy) is a copy. 117 copyelimValue(v) 118 change = ssa.PhiElimValue(v) || change 119 } 120 } 121 if !change { 122 break 123 } 124 } 125 } 126