@@ -133,6 +133,153 @@ func dse(f *Func) {
133
133
}
134
134
}
135
135
136
+ // elimDeadAutosGeneric deletes autos that are never accessed. To acheive 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 :
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
+ n , ok := v .Aux .(GCNode )
174
+ if ! ok || n .StorageClass () != ClassAuto {
175
+ return
176
+ }
177
+ if ! used [n ] {
178
+ used [n ] = true
179
+ changed = true
180
+ }
181
+ return
182
+ case OpStore , OpMove , OpZero :
183
+ // v should be elimated if we eliminate the auto.
184
+ n , ok := addr [args [0 ]]
185
+ if ok && elim [v ] == nil {
186
+ elim [v ] = n
187
+ changed = true
188
+ }
189
+ // Other args might hold pointers to autos.
190
+ args = args [1 :]
191
+ }
192
+
193
+ // The code below assumes that we have handled all the ops
194
+ // with sym effects already. Sanity check that here.
195
+ // Ignore Args since they can't be autos.
196
+ if v .Op .SymEffect () != SymNone && v .Op != OpArg {
197
+ panic ("unhandled op with sym effect" )
198
+ }
199
+
200
+ if v .Uses == 0 || len (args ) == 0 {
201
+ return
202
+ }
203
+
204
+ // If the address of the auto reaches a memory or control
205
+ // operation not covered above then we probably need to keep it.
206
+ if v .Type .IsMemory () || v .Type .IsFlags () || (v .Op != OpPhi && v .MemoryArg () != nil ) {
207
+ for _ , a := range args {
208
+ if n , ok := addr [a ]; ok {
209
+ if ! used [n ] {
210
+ used [n ] = true
211
+ changed = true
212
+ }
213
+ }
214
+ }
215
+ return
216
+ }
217
+
218
+ // Propagate any auto addresses through v.
219
+ node := GCNode (nil )
220
+ for _ , a := range args {
221
+ if n , ok := addr [a ]; ok && ! used [n ] {
222
+ if node == nil {
223
+ node = n
224
+ } else if node != n {
225
+ // Most of the time we only see one pointer
226
+ // reaching an op, but some ops can take
227
+ // multiple pointers (e.g. NeqPtr, Phi etc.).
228
+ // This is rare, so just propagate the first
229
+ // value to keep things simple.
230
+ used [n ] = true
231
+ changed = true
232
+ }
233
+ }
234
+ }
235
+ if node == nil {
236
+ return
237
+ }
238
+ if addr [v ] == nil {
239
+ // The address of an auto reaches this op.
240
+ addr [v ] = node
241
+ changed = true
242
+ return
243
+ }
244
+ if addr [v ] != node {
245
+ // This doesn't happen in practice, but catch it just in case.
246
+ used [node ] = true
247
+ changed = true
248
+ }
249
+ return
250
+ }
251
+
252
+ iterations := 0
253
+ for {
254
+ if iterations == 4 {
255
+ // give up
256
+ return
257
+ }
258
+ iterations ++
259
+ changed := false
260
+ for _ , b := range f .Blocks {
261
+ for _ , v := range b .Values {
262
+ changed = visit (v ) || changed
263
+ }
264
+ }
265
+ if ! changed {
266
+ break
267
+ }
268
+ }
269
+
270
+ // Eliminate stores to unread autos.
271
+ for v , n := range elim {
272
+ if used [n ] {
273
+ continue
274
+ }
275
+ // replace with OpCopy
276
+ v .SetArgs1 (v .MemoryArg ())
277
+ v .Aux = nil
278
+ v .AuxInt = 0
279
+ v .Op = OpCopy
280
+ }
281
+ }
282
+
136
283
// elimUnreadAutos deletes stores (and associated bookkeeping ops VarDef and VarKill)
137
284
// to autos that are never read from.
138
285
func elimUnreadAutos (f * Func ) {
0 commit comments