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.
8 "cmd/compile/internal/ir"
9 "cmd/compile/internal/types"
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.
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.
31 for _, v := range b.Values {
33 // Ignore phis - they will always be first and can't be eliminated
36 if v.Type.IsMemory() {
37 stores = append(stores, v)
38 for _, a := range v.Args {
39 if a.Block == b && a.Type.IsMemory() {
41 if v.Op != OpStore && v.Op != OpZero && v.Op != OpVarDef {
42 // CALL, DUFFCOPY, etc. are both
49 for _, a := range v.Args {
50 if a.Block == b && a.Type.IsMemory() {
60 // find last store in the block
62 for _, v := range stores {
63 if storeUse.contains(v.ID) {
67 b.Fatalf("two final stores - simultaneous live stores %s %s", last.LongString(), v.LongString())
72 b.Fatalf("no last store found - cycle?")
75 // Walk backwards looking for dead stores. Keep track of shadowed addresses.
76 // A "shadowed address" is a pointer, offset, and size describing a memory region that
77 // is known to be written. We keep track of shadowed addresses in the shadowed map,
78 // mapping the ID of the address to a shadowRange where future writes will happen.
79 // Since we're walking backwards, writes to a shadowed region are useless,
80 // as they will be immediately overwritten.
85 if loadUse.contains(v.ID) {
86 // Someone might be reading this memory state.
87 // Clear all shadowed addresses.
90 if v.Op == OpStore || v.Op == OpZero {
93 for ptr.Op == OpOffPtr { // Walk to base pointer
99 sz = v.Aux.(*types.Type).Size()
103 sr := shadowRange(shadowed.get(ptr.ID))
104 if sr.contains(off, off+sz) {
105 // Modify the store/zero into a copy of the memory state,
106 // effectively eliding the store operation.
108 // store addr value mem
109 v.SetArgs1(v.Args[2])
112 v.SetArgs1(v.Args[1])
118 // Extend shadowed region.
119 shadowed.set(ptr.ID, int32(sr.merge(off, off+sz)))
122 // walk to previous store
124 // At start of block. Move on to next block.
125 // The memory phi, if it exists, is always
126 // the first logical store in the block.
127 // (Even if it isn't the first in the current b.Values order.)
130 for _, a := range v.Args {
131 if a.Block == b && a.Type.IsMemory() {
139 // A shadowRange encodes a set of byte offsets [lo():hi()] from
140 // a given pointer that will be written to later in the block.
141 // A zero shadowRange encodes an empty shadowed range (and so
142 // does a -1 shadowRange, which is what sparsemap.get returns
143 // on a failed lookup).
144 type shadowRange int32
146 func (sr shadowRange) lo() int64 {
147 return int64(sr & 0xffff)
149 func (sr shadowRange) hi() int64 {
150 return int64((sr >> 16) & 0xffff)
153 // contains reports whether [lo:hi] is completely within sr.
154 func (sr shadowRange) contains(lo, hi int64) bool {
155 return lo >= sr.lo() && hi <= sr.hi()
158 // merge returns the union of sr and [lo:hi].
159 // merge is allowed to return something smaller than the union.
160 func (sr shadowRange) merge(lo, hi int64) shadowRange {
161 if lo < 0 || hi > 0xffff {
162 // Ignore offsets that are too large or small.
165 if sr.lo() == sr.hi() {
166 // Old range is empty - use new one.
167 return shadowRange(lo + hi<<16)
169 if hi < sr.lo() || lo > sr.hi() {
170 // The two regions don't overlap or abut, so we would
171 // have to keep track of multiple disjoint ranges.
172 // Because we can only keep one, keep the larger one.
173 if sr.hi()-sr.lo() >= hi-lo {
176 return shadowRange(lo + hi<<16)
178 // Regions overlap or abut - compute the union.
179 return shadowRange(min(lo, sr.lo()) + max(hi, sr.hi())<<16)
182 // elimDeadAutosGeneric deletes autos that are never accessed. To achieve this
183 // we track the operations that the address of each auto reaches and if it only
184 // reaches stores then we delete all the stores. The other operations will then
185 // be eliminated by the dead code elimination pass.
186 func elimDeadAutosGeneric(f *Func) {
187 addr := make(map[*Value]*ir.Name) // values that the address of the auto reaches
188 elim := make(map[*Value]*ir.Name) // values that could be eliminated if the auto is
189 var used ir.NameSet // used autos that must be kept
191 // visit the value and report whether any of the maps are updated
192 visit := func(v *Value) (changed bool) {
195 case OpAddr, OpLocalAddr:
196 // Propagate the address if it points to an auto.
197 n, ok := v.Aux.(*ir.Name)
198 if !ok || n.Class != ir.PAUTO {
207 // v should be eliminated if we eliminate the auto.
208 n, ok := v.Aux.(*ir.Name)
209 if !ok || n.Class != ir.PAUTO {
218 // Don't delete the auto if it needs to be kept alive.
220 // We depend on this check to keep the autotmp stack slots
221 // for open-coded defers from being removed (since they
222 // may not be used by the inline code, but will be used by
223 // panic processing).
224 n, ok := v.Aux.(*ir.Name)
225 if !ok || n.Class != ir.PAUTO {
233 case OpStore, OpMove, OpZero:
234 // v should be eliminated if we eliminate the auto.
235 n, ok := addr[args[0]]
236 if ok && elim[v] == nil {
240 // Other args might hold pointers to autos.
244 // The code below assumes that we have handled all the ops
245 // with sym effects already. Sanity check that here.
246 // Ignore Args since they can't be autos.
247 if v.Op.SymEffect() != SymNone && v.Op != OpArg {
248 panic("unhandled op with sym effect")
251 if v.Uses == 0 && v.Op != OpNilCheck && !v.Op.IsCall() && !v.Op.HasSideEffects() || len(args) == 0 {
252 // Nil check has no use, but we need to keep it.
253 // Also keep calls and values that have side effects.
257 // If the address of the auto reaches a memory or control
258 // operation not covered above then we probably need to keep it.
259 // We also need to keep autos if they reach Phis (issue #26153).
260 if v.Type.IsMemory() || v.Type.IsFlags() || v.Op == OpPhi || v.MemoryArg() != nil {
261 for _, a := range args {
262 if n, ok := addr[a]; ok {
272 // Propagate any auto addresses through v.
274 for _, a := range args {
275 if n, ok := addr[a]; ok && !used.Has(n) {
278 } else if node != n {
279 // Most of the time we only see one pointer
280 // reaching an op, but some ops can take
281 // multiple pointers (e.g. NeqPtr, Phi etc.).
282 // This is rare, so just propagate the first
283 // value to keep things simple.
293 // The address of an auto reaches this op.
299 // This doesn't happen in practice, but catch it just in case.
314 for _, b := range f.Blocks {
315 for _, v := range b.Values {
316 changed = visit(v) || changed
318 // keep the auto if its address reaches a control value
319 for _, c := range b.ControlValues() {
320 if n, ok := addr[c]; ok && !used.Has(n) {
331 // Eliminate stores to unread autos.
332 for v, n := range elim {
336 // replace with OpCopy
337 v.SetArgs1(v.MemoryArg())
344 // elimUnreadAutos deletes stores (and associated bookkeeping ops VarDef and VarKill)
345 // to autos that are never read from.
346 func elimUnreadAutos(f *Func) {
347 // Loop over all ops that affect autos taking note of which
348 // autos we need and also stores that we might be able to
352 for _, b := range f.Blocks {
353 for _, v := range b.Values {
354 n, ok := v.Aux.(*ir.Name)
358 if n.Class != ir.PAUTO {
362 effect := v.Op.SymEffect()
364 case SymNone, SymWrite:
365 // If we haven't seen the auto yet
366 // then this might be a store we can
369 stores = append(stores, v)
372 // Assume the auto is needed (loaded,
373 // has its address taken, etc.).
374 // Note we have to check the uses
375 // because dead loads haven't been
384 // Eliminate stores to unread autos.
385 for _, store := range stores {
386 n, _ := store.Aux.(*ir.Name)
391 // replace store with OpCopy
392 store.SetArgs1(store.MemoryArg())