github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/ssa/deadstore.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 "github.com/gagliardetto/golang-go/cmd/compile/internal/types" 9 "github.com/gagliardetto/golang-go/cmd/internal/src" 10 ) 11 12 // dse does dead-store elimination on the Function. 13 // Dead stores are those which are unconditionally followed by 14 // another store to the same location, with no intervening load. 15 // This implementation only works within a basic block. TODO: use something more global. 16 func dse(f *Func) { 17 var stores []*Value 18 loadUse := f.newSparseSet(f.NumValues()) 19 defer f.retSparseSet(loadUse) 20 storeUse := f.newSparseSet(f.NumValues()) 21 defer f.retSparseSet(storeUse) 22 shadowed := f.newSparseMap(f.NumValues()) 23 defer f.retSparseMap(shadowed) 24 for _, b := range f.Blocks { 25 // Find all the stores in this block. Categorize their uses: 26 // loadUse contains stores which are used by a subsequent load. 27 // storeUse contains stores which are used by a subsequent store. 28 loadUse.clear() 29 storeUse.clear() 30 stores = stores[:0] 31 for _, v := range b.Values { 32 if v.Op == OpPhi { 33 // Ignore phis - they will always be first and can't be eliminated 34 continue 35 } 36 if v.Type.IsMemory() { 37 stores = append(stores, v) 38 for _, a := range v.Args { 39 if a.Block == b && a.Type.IsMemory() { 40 storeUse.add(a.ID) 41 if v.Op != OpStore && v.Op != OpZero && v.Op != OpVarDef && v.Op != OpVarKill { 42 // CALL, DUFFCOPY, etc. are both 43 // reads and writes. 44 loadUse.add(a.ID) 45 } 46 } 47 } 48 } else { 49 for _, a := range v.Args { 50 if a.Block == b && a.Type.IsMemory() { 51 loadUse.add(a.ID) 52 } 53 } 54 } 55 } 56 if len(stores) == 0 { 57 continue 58 } 59 60 // find last store in the block 61 var last *Value 62 for _, v := range stores { 63 if storeUse.contains(v.ID) { 64 continue 65 } 66 if last != nil { 67 b.Fatalf("two final stores - simultaneous live stores %s %s", last.LongString(), v.LongString()) 68 } 69 last = v 70 } 71 if last == nil { 72 b.Fatalf("no last store found - cycle?") 73 } 74 75 // Walk backwards looking for dead stores. Keep track of shadowed addresses. 76 // An "address" is an SSA Value which encodes both the address and size of 77 // the write. This code will not remove dead stores to the same address 78 // of different types. 79 shadowed.clear() 80 v := last 81 82 walkloop: 83 if loadUse.contains(v.ID) { 84 // Someone might be reading this memory state. 85 // Clear all shadowed addresses. 86 shadowed.clear() 87 } 88 if v.Op == OpStore || v.Op == OpZero { 89 var sz int64 90 if v.Op == OpStore { 91 sz = v.Aux.(*types.Type).Size() 92 } else { // OpZero 93 sz = v.AuxInt 94 } 95 if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz { 96 // Modify store into a copy 97 if v.Op == OpStore { 98 // store addr value mem 99 v.SetArgs1(v.Args[2]) 100 } else { 101 // zero addr mem 102 typesz := v.Args[0].Type.Elem().Size() 103 if sz != typesz { 104 f.Fatalf("mismatched zero/store sizes: %d and %d [%s]", 105 sz, typesz, v.LongString()) 106 } 107 v.SetArgs1(v.Args[1]) 108 } 109 v.Aux = nil 110 v.AuxInt = 0 111 v.Op = OpCopy 112 } else { 113 if sz > 0x7fffffff { // work around sparseMap's int32 value type 114 sz = 0x7fffffff 115 } 116 shadowed.set(v.Args[0].ID, int32(sz), src.NoXPos) 117 } 118 } 119 // walk to previous store 120 if v.Op == OpPhi { 121 // At start of block. Move on to next block. 122 // The memory phi, if it exists, is always 123 // the first logical store in the block. 124 // (Even if it isn't the first in the current b.Values order.) 125 continue 126 } 127 for _, a := range v.Args { 128 if a.Block == b && a.Type.IsMemory() { 129 v = a 130 goto walkloop 131 } 132 } 133 } 134 } 135 136 // elimDeadAutosGeneric deletes autos that are never accessed. To achieve this 137 // we track the operations that the address of each auto reaches and if it only 138 // reaches stores then we delete all the stores. The other operations will then 139 // be eliminated by the dead code elimination pass. 140 func elimDeadAutosGeneric(f *Func) { 141 addr := make(map[*Value]GCNode) // values that the address of the auto reaches 142 elim := make(map[*Value]GCNode) // values that could be eliminated if the auto is 143 used := make(map[GCNode]bool) // used autos that must be kept 144 145 // visit the value and report whether any of the maps are updated 146 visit := func(v *Value) (changed bool) { 147 args := v.Args 148 switch v.Op { 149 case OpAddr, OpLocalAddr: 150 // Propagate the address if it points to an auto. 151 n, ok := v.Aux.(GCNode) 152 if !ok || n.StorageClass() != ClassAuto { 153 return 154 } 155 if addr[v] == nil { 156 addr[v] = n 157 changed = true 158 } 159 return 160 case OpVarDef, OpVarKill: 161 // v should be eliminated if we eliminate the auto. 162 n, ok := v.Aux.(GCNode) 163 if !ok || n.StorageClass() != ClassAuto { 164 return 165 } 166 if elim[v] == nil { 167 elim[v] = n 168 changed = true 169 } 170 return 171 case OpVarLive: 172 // Don't delete the auto if it needs to be kept alive. 173 174 // We depend on this check to keep the autotmp stack slots 175 // for open-coded defers from being removed (since they 176 // may not be used by the inline code, but will be used by 177 // panic processing). 178 n, ok := v.Aux.(GCNode) 179 if !ok || n.StorageClass() != ClassAuto { 180 return 181 } 182 if !used[n] { 183 used[n] = true 184 changed = true 185 } 186 return 187 case OpStore, OpMove, OpZero: 188 // v should be eliminated if we eliminate the auto. 189 n, ok := addr[args[0]] 190 if ok && elim[v] == nil { 191 elim[v] = n 192 changed = true 193 } 194 // Other args might hold pointers to autos. 195 args = args[1:] 196 } 197 198 // The code below assumes that we have handled all the ops 199 // with sym effects already. Sanity check that here. 200 // Ignore Args since they can't be autos. 201 if v.Op.SymEffect() != SymNone && v.Op != OpArg { 202 panic("unhandled op with sym effect") 203 } 204 205 if v.Uses == 0 && v.Op != OpNilCheck || len(args) == 0 { 206 // Nil check has no use, but we need to keep it. 207 return 208 } 209 210 // If the address of the auto reaches a memory or control 211 // operation not covered above then we probably need to keep it. 212 // We also need to keep autos if they reach Phis (issue #26153). 213 if v.Type.IsMemory() || v.Type.IsFlags() || v.Op == OpPhi || v.MemoryArg() != nil { 214 for _, a := range args { 215 if n, ok := addr[a]; ok { 216 if !used[n] { 217 used[n] = true 218 changed = true 219 } 220 } 221 } 222 return 223 } 224 225 // Propagate any auto addresses through v. 226 node := GCNode(nil) 227 for _, a := range args { 228 if n, ok := addr[a]; ok && !used[n] { 229 if node == nil { 230 node = n 231 } else if node != n { 232 // Most of the time we only see one pointer 233 // reaching an op, but some ops can take 234 // multiple pointers (e.g. NeqPtr, Phi etc.). 235 // This is rare, so just propagate the first 236 // value to keep things simple. 237 used[n] = true 238 changed = true 239 } 240 } 241 } 242 if node == nil { 243 return 244 } 245 if addr[v] == nil { 246 // The address of an auto reaches this op. 247 addr[v] = node 248 changed = true 249 return 250 } 251 if addr[v] != node { 252 // This doesn't happen in practice, but catch it just in case. 253 used[node] = true 254 changed = true 255 } 256 return 257 } 258 259 iterations := 0 260 for { 261 if iterations == 4 { 262 // give up 263 return 264 } 265 iterations++ 266 changed := false 267 for _, b := range f.Blocks { 268 for _, v := range b.Values { 269 changed = visit(v) || changed 270 } 271 // keep the auto if its address reaches a control value 272 for _, c := range b.ControlValues() { 273 if n, ok := addr[c]; ok && !used[n] { 274 used[n] = true 275 changed = true 276 } 277 } 278 } 279 if !changed { 280 break 281 } 282 } 283 284 // Eliminate stores to unread autos. 285 for v, n := range elim { 286 if used[n] { 287 continue 288 } 289 // replace with OpCopy 290 v.SetArgs1(v.MemoryArg()) 291 v.Aux = nil 292 v.AuxInt = 0 293 v.Op = OpCopy 294 } 295 } 296 297 // elimUnreadAutos deletes stores (and associated bookkeeping ops VarDef and VarKill) 298 // to autos that are never read from. 299 func elimUnreadAutos(f *Func) { 300 // Loop over all ops that affect autos taking note of which 301 // autos we need and also stores that we might be able to 302 // eliminate. 303 seen := make(map[GCNode]bool) 304 var stores []*Value 305 for _, b := range f.Blocks { 306 for _, v := range b.Values { 307 n, ok := v.Aux.(GCNode) 308 if !ok { 309 continue 310 } 311 if n.StorageClass() != ClassAuto { 312 continue 313 } 314 315 effect := v.Op.SymEffect() 316 switch effect { 317 case SymNone, SymWrite: 318 // If we haven't seen the auto yet 319 // then this might be a store we can 320 // eliminate. 321 if !seen[n] { 322 stores = append(stores, v) 323 } 324 default: 325 // Assume the auto is needed (loaded, 326 // has its address taken, etc.). 327 // Note we have to check the uses 328 // because dead loads haven't been 329 // eliminated yet. 330 if v.Uses > 0 { 331 seen[n] = true 332 } 333 } 334 } 335 } 336 337 // Eliminate stores to unread autos. 338 for _, store := range stores { 339 n, _ := store.Aux.(GCNode) 340 if seen[n] { 341 continue 342 } 343 344 // replace store with OpCopy 345 store.SetArgs1(store.MemoryArg()) 346 store.Aux = nil 347 store.AuxInt = 0 348 store.Op = OpCopy 349 } 350 }