Source file src/cmd/compile/internal/ssa/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 ssa 6 7 // combine copyelim and phielim into a single pass. 8 // copyelim removes all uses of OpCopy values from f. 9 // A subsequent deadcode pass is needed to actually remove the copies. 10 func copyelim(f *Func) { 11 phielim(f) 12 13 // loop of copyelimValue(v) process has been done in phielim() pass. 14 // Update block control values. 15 for _, b := range f.Blocks { 16 for i, v := range b.ControlValues() { 17 if v.Op == OpCopy { 18 b.ReplaceControl(i, v.Args[0]) 19 } 20 } 21 } 22 23 // Update named values. 24 for _, name := range f.Names { 25 values := f.NamedValues[*name] 26 for i, v := range values { 27 if v.Op == OpCopy { 28 values[i] = v.Args[0] 29 } 30 } 31 } 32 } 33 34 // copySource returns the (non-copy) op which is the 35 // ultimate source of v. v must be a copy op. 36 func copySource(v *Value) *Value { 37 w := v.Args[0] 38 39 // This loop is just: 40 // for w.Op == OpCopy { 41 // w = w.Args[0] 42 // } 43 // but we take some extra care to make sure we 44 // don't get stuck in an infinite loop. 45 // Infinite copy loops may happen in unreachable code. 46 // (TODO: or can they? Needs a test.) 47 slow := w 48 var advance bool 49 for w.Op == OpCopy { 50 w = w.Args[0] 51 if w == slow { 52 w.reset(OpUnknown) 53 break 54 } 55 if advance { 56 slow = slow.Args[0] 57 } 58 advance = !advance 59 } 60 61 // The answer is w. Update all the copies we saw 62 // to point directly to w. Doing this update makes 63 // sure that we don't end up doing O(n^2) work 64 // for a chain of n copies. 65 for v != w { 66 x := v.Args[0] 67 v.SetArg(0, w) 68 v = x 69 } 70 return w 71 } 72 73 // copyelimValue ensures that no args of v are copies. 74 func copyelimValue(v *Value) { 75 for i, a := range v.Args { 76 if a.Op == OpCopy { 77 v.SetArg(i, copySource(a)) 78 } 79 } 80 } 81 82 // phielim eliminates redundant phi values from f. 83 // A phi is redundant if its arguments are all equal. For 84 // purposes of counting, ignore the phi itself. Both of 85 // these phis are redundant: 86 // 87 // v = phi(x,x,x) 88 // v = phi(x,v,x,v) 89 // 90 // We repeat this process to also catch situations like: 91 // 92 // v = phi(x, phi(x, x), phi(x, v)) 93 // 94 // TODO: Can we also simplify cases like: 95 // 96 // v = phi(v, w, x) 97 // w = phi(v, w, x) 98 // 99 // and would that be useful? 100 func phielim(f *Func) { 101 for { 102 change := false 103 for _, b := range f.Blocks { 104 for _, v := range b.Values { 105 // This is an early place in SSA where all values are examined. 106 // Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc. 107 if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 { 108 v.reset(OpEmpty) 109 } 110 // Modify all values so no arg (including args 111 // of OpCopy) is a copy. 112 copyelimValue(v) 113 change = phielimValue(v) || change 114 } 115 } 116 if !change { 117 break 118 } 119 } 120 } 121 122 // phielimValue tries to convert the phi v to a copy. 123 func phielimValue(v *Value) bool { 124 if v.Op != OpPhi { 125 return false 126 } 127 128 // If there are two distinct args of v which 129 // are not v itself, then the phi must remain. 130 // Otherwise, we can replace it with a copy. 131 var w *Value 132 for _, x := range v.Args { 133 if x == v { 134 continue 135 } 136 if x == w { 137 continue 138 } 139 if w != nil { 140 return false 141 } 142 w = x 143 } 144 145 if w == nil { 146 // v references only itself. It must be in 147 // a dead code loop. Don't bother modifying it. 148 return false 149 } 150 v.Op = OpCopy 151 v.SetArgs1(w) 152 f := v.Block.Func 153 if f.pass.debug > 0 { 154 f.Warnl(v.Pos, "eliminated phi") 155 } 156 return true 157 } 158