github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/cse.go (about) 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 import ( 8 "fmt" 9 "sort" 10 ) 11 12 const ( 13 cmpDepth = 4 14 ) 15 16 // cse does common-subexpression elimination on the Function. 17 // Values are just relinked, nothing is deleted. A subsequent deadcode 18 // pass is required to actually remove duplicate expressions. 19 func cse(f *Func) { 20 // Two values are equivalent if they satisfy the following definition: 21 // equivalent(v, w): 22 // v.op == w.op 23 // v.type == w.type 24 // v.aux == w.aux 25 // v.auxint == w.auxint 26 // len(v.args) == len(w.args) 27 // v.block == w.block if v.op == OpPhi 28 // equivalent(v.args[i], w.args[i]) for i in 0..len(v.args)-1 29 30 // The algorithm searches for a partition of f's values into 31 // equivalence classes using the above definition. 32 // It starts with a coarse partition and iteratively refines it 33 // until it reaches a fixed point. 34 35 // Make initial coarse partitions by using a subset of the conditions above. 36 a := make([]*Value, 0, f.NumValues()) 37 auxIDs := auxmap{} 38 for _, b := range f.Blocks { 39 for _, v := range b.Values { 40 if auxIDs[v.Aux] == 0 { 41 auxIDs[v.Aux] = int32(len(auxIDs)) + 1 42 } 43 if v.Type.IsMemory() { 44 continue // memory values can never cse 45 } 46 if opcodeTable[v.Op].commutative && len(v.Args) == 2 && v.Args[1].ID < v.Args[0].ID { 47 // Order the arguments of binary commutative operations. 48 v.Args[0], v.Args[1] = v.Args[1], v.Args[0] 49 } 50 a = append(a, v) 51 } 52 } 53 partition := partitionValues(a, auxIDs) 54 55 // map from value id back to eqclass id 56 valueEqClass := make([]ID, f.NumValues()) 57 for _, b := range f.Blocks { 58 for _, v := range b.Values { 59 // Use negative equivalence class #s for unique values. 60 valueEqClass[v.ID] = -v.ID 61 } 62 } 63 for i, e := range partition { 64 if f.pass.debug > 1 && len(e) > 500 { 65 fmt.Printf("CSE.large partition (%d): ", len(e)) 66 for j := 0; j < 3; j++ { 67 fmt.Printf("%s ", e[j].LongString()) 68 } 69 fmt.Println() 70 } 71 72 for _, v := range e { 73 valueEqClass[v.ID] = ID(i) 74 } 75 if f.pass.debug > 2 && len(e) > 1 { 76 fmt.Printf("CSE.partition #%d:", i) 77 for _, v := range e { 78 fmt.Printf(" %s", v.String()) 79 } 80 fmt.Printf("\n") 81 } 82 } 83 84 // Find an equivalence class where some members of the class have 85 // non-equivalent arguments. Split the equivalence class appropriately. 86 // Repeat until we can't find any more splits. 87 for { 88 changed := false 89 90 // partition can grow in the loop. By not using a range loop here, 91 // we process new additions as they arrive, avoiding O(n^2) behavior. 92 for i := 0; i < len(partition); i++ { 93 e := partition[i] 94 v := e[0] 95 // all values in this equiv class that are not equivalent to v get moved 96 // into another equiv class. 97 // To avoid allocating while building that equivalence class, 98 // move the values equivalent to v to the beginning of e 99 // and other values to the end of e. 100 allvals := e 101 eqloop: 102 for j := 1; j < len(e); { 103 w := e[j] 104 equivalent := true 105 for i := 0; i < len(v.Args); i++ { 106 if valueEqClass[v.Args[i].ID] != valueEqClass[w.Args[i].ID] { 107 equivalent = false 108 break 109 } 110 } 111 if !equivalent || v.Type.Compare(w.Type) != CMPeq { 112 // w is not equivalent to v. 113 // move it to the end and shrink e. 114 e[j], e[len(e)-1] = e[len(e)-1], e[j] 115 e = e[:len(e)-1] 116 valueEqClass[w.ID] = ID(len(partition)) 117 changed = true 118 continue eqloop 119 } 120 // v and w are equivalent. Keep w in e. 121 j++ 122 } 123 partition[i] = e 124 if len(e) < len(allvals) { 125 partition = append(partition, allvals[len(e):]) 126 } 127 } 128 129 if !changed { 130 break 131 } 132 } 133 134 // Dominator tree (f.sdom) is computed by the generic domtree pass. 135 136 // Compute substitutions we would like to do. We substitute v for w 137 // if v and w are in the same equivalence class and v dominates w. 138 rewrite := make([]*Value, f.NumValues()) 139 for _, e := range partition { 140 sort.Sort(partitionByDom{e, f.sdom}) 141 for i := 0; i < len(e)-1; i++ { 142 // e is sorted by domorder, so a maximal dominant element is first in the slice 143 v := e[i] 144 if v == nil { 145 continue 146 } 147 148 e[i] = nil 149 // Replace all elements of e which v dominates 150 for j := i + 1; j < len(e); j++ { 151 w := e[j] 152 if w == nil { 153 continue 154 } 155 if f.sdom.isAncestorEq(v.Block, w.Block) { 156 rewrite[w.ID] = v 157 e[j] = nil 158 } else { 159 // e is sorted by domorder, so v.Block doesn't dominate any subsequent blocks in e 160 break 161 } 162 } 163 } 164 } 165 166 // if we rewrite a tuple generator to a new one in a different block, 167 // copy its selectors to the new generator's block, so tuple generator 168 // and selectors stay together. 169 // be careful not to copy same selectors more than once (issue 16741). 170 copiedSelects := make(map[ID][]*Value) 171 for _, b := range f.Blocks { 172 out: 173 for _, v := range b.Values { 174 if rewrite[v.ID] != nil { 175 continue 176 } 177 if v.Op != OpSelect0 && v.Op != OpSelect1 { 178 continue 179 } 180 if !v.Args[0].Type.IsTuple() { 181 f.Fatalf("arg of tuple selector %s is not a tuple: %s", v.String(), v.Args[0].LongString()) 182 } 183 t := rewrite[v.Args[0].ID] 184 if t != nil && t.Block != b { 185 // v.Args[0] is tuple generator, CSE'd into a different block as t, v is left behind 186 for _, c := range copiedSelects[t.ID] { 187 if v.Op == c.Op { 188 // an equivalent selector is already copied 189 rewrite[v.ID] = c 190 continue out 191 } 192 } 193 c := v.copyInto(t.Block) 194 rewrite[v.ID] = c 195 copiedSelects[t.ID] = append(copiedSelects[t.ID], c) 196 } 197 } 198 } 199 200 rewrites := int64(0) 201 202 // Apply substitutions 203 for _, b := range f.Blocks { 204 for _, v := range b.Values { 205 for i, w := range v.Args { 206 if x := rewrite[w.ID]; x != nil { 207 v.SetArg(i, x) 208 rewrites++ 209 } 210 } 211 } 212 if v := b.Control; v != nil { 213 if x := rewrite[v.ID]; x != nil { 214 if v.Op == OpNilCheck { 215 // nilcheck pass will remove the nil checks and log 216 // them appropriately, so don't mess with them here. 217 continue 218 } 219 b.SetControl(x) 220 } 221 } 222 } 223 if f.pass.stats > 0 { 224 f.LogStat("CSE REWRITES", rewrites) 225 } 226 } 227 228 // An eqclass approximates an equivalence class. During the 229 // algorithm it may represent the union of several of the 230 // final equivalence classes. 231 type eqclass []*Value 232 233 // partitionValues partitions the values into equivalence classes 234 // based on having all the following features match: 235 // - opcode 236 // - type 237 // - auxint 238 // - aux 239 // - nargs 240 // - block # if a phi op 241 // - first two arg's opcodes and auxint 242 // - NOT first two arg's aux; that can break CSE. 243 // partitionValues returns a list of equivalence classes, each 244 // being a sorted by ID list of *Values. The eqclass slices are 245 // backed by the same storage as the input slice. 246 // Equivalence classes of size 1 are ignored. 247 func partitionValues(a []*Value, auxIDs auxmap) []eqclass { 248 sort.Sort(sortvalues{a, auxIDs}) 249 250 var partition []eqclass 251 for len(a) > 0 { 252 v := a[0] 253 j := 1 254 for ; j < len(a); j++ { 255 w := a[j] 256 if cmpVal(v, w, auxIDs, cmpDepth) != CMPeq { 257 break 258 } 259 } 260 if j > 1 { 261 partition = append(partition, a[:j]) 262 } 263 a = a[j:] 264 } 265 266 return partition 267 } 268 func lt2Cmp(isLt bool) Cmp { 269 if isLt { 270 return CMPlt 271 } 272 return CMPgt 273 } 274 275 type auxmap map[interface{}]int32 276 277 func cmpVal(v, w *Value, auxIDs auxmap, depth int) Cmp { 278 // Try to order these comparison by cost (cheaper first) 279 if v.Op != w.Op { 280 return lt2Cmp(v.Op < w.Op) 281 } 282 if v.AuxInt != w.AuxInt { 283 return lt2Cmp(v.AuxInt < w.AuxInt) 284 } 285 if len(v.Args) != len(w.Args) { 286 return lt2Cmp(len(v.Args) < len(w.Args)) 287 } 288 if v.Op == OpPhi && v.Block != w.Block { 289 return lt2Cmp(v.Block.ID < w.Block.ID) 290 } 291 if v.Type.IsMemory() { 292 // We will never be able to CSE two values 293 // that generate memory. 294 return lt2Cmp(v.ID < w.ID) 295 } 296 297 if tc := v.Type.Compare(w.Type); tc != CMPeq { 298 return tc 299 } 300 301 if v.Aux != w.Aux { 302 if v.Aux == nil { 303 return CMPlt 304 } 305 if w.Aux == nil { 306 return CMPgt 307 } 308 return lt2Cmp(auxIDs[v.Aux] < auxIDs[w.Aux]) 309 } 310 311 if depth > 0 { 312 for i := range v.Args { 313 if v.Args[i] == w.Args[i] { 314 // skip comparing equal args 315 continue 316 } 317 if ac := cmpVal(v.Args[i], w.Args[i], auxIDs, depth-1); ac != CMPeq { 318 return ac 319 } 320 } 321 } 322 323 return CMPeq 324 } 325 326 // Sort values to make the initial partition. 327 type sortvalues struct { 328 a []*Value // array of values 329 auxIDs auxmap // aux -> aux ID map 330 } 331 332 func (sv sortvalues) Len() int { return len(sv.a) } 333 func (sv sortvalues) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] } 334 func (sv sortvalues) Less(i, j int) bool { 335 v := sv.a[i] 336 w := sv.a[j] 337 if cmp := cmpVal(v, w, sv.auxIDs, cmpDepth); cmp != CMPeq { 338 return cmp == CMPlt 339 } 340 341 // Sort by value ID last to keep the sort result deterministic. 342 return v.ID < w.ID 343 } 344 345 type partitionByDom struct { 346 a []*Value // array of values 347 sdom SparseTree 348 } 349 350 func (sv partitionByDom) Len() int { return len(sv.a) } 351 func (sv partitionByDom) Swap(i, j int) { sv.a[i], sv.a[j] = sv.a[j], sv.a[i] } 352 func (sv partitionByDom) Less(i, j int) bool { 353 v := sv.a[i] 354 w := sv.a[j] 355 return sv.sdom.domorder(v.Block) < sv.sdom.domorder(w.Block) 356 }