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