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