1
2
3
4
5
6
7 package ssa
8
9 import (
10 "cmd/compile/internal/ir"
11 "cmd/compile/internal/types"
12 "cmd/internal/src"
13 "fmt"
14 )
15
16 type stackAllocState struct {
17 f *Func
18
19
20
21 live [][]ID
22
23
24
25 values []stackValState
26 interfere [][]ID
27 names []LocalSlot
28
29 nArgSlot,
30 nNotNeed,
31 nNamedSlot,
32 nReuse,
33 nAuto,
34 nSelfInterfere int32
35 }
36
37 func newStackAllocState(f *Func) *stackAllocState {
38 s := f.Cache.stackAllocState
39 if s == nil {
40 return new(stackAllocState)
41 }
42 if s.f != nil {
43 f.fe.Fatalf(src.NoXPos, "newStackAllocState called without previous free")
44 }
45 return s
46 }
47
48 func putStackAllocState(s *stackAllocState) {
49 for i := range s.values {
50 s.values[i] = stackValState{}
51 }
52 for i := range s.interfere {
53 s.interfere[i] = nil
54 }
55 for i := range s.names {
56 s.names[i] = LocalSlot{}
57 }
58 s.f.Cache.stackAllocState = s
59 s.f = nil
60 s.live = nil
61 s.nArgSlot, s.nNotNeed, s.nNamedSlot, s.nReuse, s.nAuto, s.nSelfInterfere = 0, 0, 0, 0, 0, 0
62 }
63
64 type stackValState struct {
65 typ *types.Type
66 spill *Value
67 needSlot bool
68 isArg bool
69 }
70
71
72
73
74 func stackalloc(f *Func, spillLive [][]ID) [][]ID {
75 if f.pass.debug > stackDebug {
76 fmt.Println("before stackalloc")
77 fmt.Println(f.String())
78 }
79 s := newStackAllocState(f)
80 s.init(f, spillLive)
81 defer putStackAllocState(s)
82
83 s.stackalloc()
84 if f.pass.stats > 0 {
85 f.LogStat("stack_alloc_stats",
86 s.nArgSlot, "arg_slots", s.nNotNeed, "slot_not_needed",
87 s.nNamedSlot, "named_slots", s.nAuto, "auto_slots",
88 s.nReuse, "reused_slots", s.nSelfInterfere, "self_interfering")
89 }
90
91 return s.live
92 }
93
94 func (s *stackAllocState) init(f *Func, spillLive [][]ID) {
95 s.f = f
96
97
98 if n := f.NumValues(); cap(s.values) >= n {
99 s.values = s.values[:n]
100 } else {
101 s.values = make([]stackValState, n)
102 }
103 for _, b := range f.Blocks {
104 for _, v := range b.Values {
105 s.values[v.ID].typ = v.Type
106 s.values[v.ID].needSlot = !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && f.getHome(v.ID) == nil && !v.rematerializeable() && !v.OnWasmStack
107 s.values[v.ID].isArg = hasAnyArgOp(v)
108 if f.pass.debug > stackDebug && s.values[v.ID].needSlot {
109 fmt.Printf("%s needs a stack slot\n", v)
110 }
111 if v.Op == OpStoreReg {
112 s.values[v.Args[0].ID].spill = v
113 }
114 }
115 }
116
117
118 s.computeLive(spillLive)
119
120
121 s.buildInterferenceGraph()
122 }
123
124 func (s *stackAllocState) stackalloc() {
125 f := s.f
126
127
128
129
130 if n := f.NumValues(); cap(s.names) >= n {
131 s.names = s.names[:n]
132 } else {
133 s.names = make([]LocalSlot, n)
134 }
135 names := s.names
136 empty := LocalSlot{}
137 for _, name := range f.Names {
138
139
140 for _, v := range f.NamedValues[*name] {
141 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
142 aux := v.Aux.(*AuxNameOffset)
143
144 if name.N != aux.Name || name.Off != aux.Offset {
145 if f.pass.debug > stackDebug {
146 fmt.Printf("stackalloc register arg %s skipping name %s\n", v, name)
147 }
148 continue
149 }
150 } else if name.N.Class == ir.PPARAM && v.Op != OpArg {
151
152 if f.pass.debug > stackDebug {
153 fmt.Printf("stackalloc PPARAM name %s skipping non-Arg %s\n", name, v)
154 }
155 continue
156 }
157
158 if names[v.ID] == empty {
159 if f.pass.debug > stackDebug {
160 fmt.Printf("stackalloc value %s to name %s\n", v, *name)
161 }
162 names[v.ID] = *name
163 }
164 }
165 }
166
167
168 for _, v := range f.Entry.Values {
169 if !hasAnyArgOp(v) {
170 continue
171 }
172 if v.Aux == nil {
173 f.Fatalf("%s has nil Aux\n", v.LongString())
174 }
175 if v.Op == OpArg {
176 loc := LocalSlot{N: v.Aux.(*ir.Name), Type: v.Type, Off: v.AuxInt}
177 if f.pass.debug > stackDebug {
178 fmt.Printf("stackalloc OpArg %s to %s\n", v, loc)
179 }
180 f.setHome(v, loc)
181 continue
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 }
203
204
205
206
207
208
209 locations := map[*types.Type][]LocalSlot{}
210
211
212
213 slots := f.Cache.allocIntSlice(f.NumValues())
214 defer f.Cache.freeIntSlice(slots)
215 for i := range slots {
216 slots[i] = -1
217 }
218
219
220 used := f.Cache.allocBoolSlice(f.NumValues())
221 defer f.Cache.freeBoolSlice(used)
222 for _, b := range f.Blocks {
223 for _, v := range b.Values {
224 if !s.values[v.ID].needSlot {
225 s.nNotNeed++
226 continue
227 }
228 if hasAnyArgOp(v) {
229 s.nArgSlot++
230 continue
231 }
232
233
234
235 var name LocalSlot
236 if v.Op == OpStoreReg {
237 name = names[v.Args[0].ID]
238 } else {
239 name = names[v.ID]
240 }
241 if name.N != nil && v.Type.Compare(name.Type) == types.CMPeq {
242 for _, id := range s.interfere[v.ID] {
243 h := f.getHome(id)
244 if h != nil && h.(LocalSlot).N == name.N && h.(LocalSlot).Off == name.Off {
245
246
247 s.nSelfInterfere++
248 goto noname
249 }
250 }
251 if f.pass.debug > stackDebug {
252 fmt.Printf("stackalloc %s to %s\n", v, name)
253 }
254 s.nNamedSlot++
255 f.setHome(v, name)
256 continue
257 }
258
259 noname:
260
261 locs := locations[v.Type]
262
263 for i := 0; i < len(locs); i++ {
264 used[i] = false
265 }
266 for _, xid := range s.interfere[v.ID] {
267 slot := slots[xid]
268 if slot >= 0 {
269 used[slot] = true
270 }
271 }
272
273 var i int
274 for i = 0; i < len(locs); i++ {
275 if !used[i] {
276 s.nReuse++
277 break
278 }
279 }
280
281 if i == len(locs) {
282 s.nAuto++
283 locs = append(locs, LocalSlot{N: f.NewLocal(v.Pos, v.Type), Type: v.Type, Off: 0})
284 locations[v.Type] = locs
285 }
286
287 loc := locs[i]
288 if f.pass.debug > stackDebug {
289 fmt.Printf("stackalloc %s to %s\n", v, loc)
290 }
291 f.setHome(v, loc)
292 slots[v.ID] = i
293 }
294 }
295 }
296
297
298
299
300
301
302 func (s *stackAllocState) computeLive(spillLive [][]ID) {
303 s.live = make([][]ID, s.f.NumBlocks())
304 var phis []*Value
305 live := s.f.newSparseSet(s.f.NumValues())
306 defer s.f.retSparseSet(live)
307 t := s.f.newSparseSet(s.f.NumValues())
308 defer s.f.retSparseSet(t)
309
310
311
312
313 po := s.f.postorder()
314 for {
315 changed := false
316 for _, b := range po {
317
318 live.clear()
319 live.addAll(s.live[b.ID])
320
321
322 phis = phis[:0]
323 for i := len(b.Values) - 1; i >= 0; i-- {
324 v := b.Values[i]
325 live.remove(v.ID)
326 if v.Op == OpPhi {
327
328
329
330 if !v.Type.IsMemory() && !v.Type.IsVoid() {
331 phis = append(phis, v)
332 }
333 continue
334 }
335 for _, a := range v.Args {
336 if s.values[a.ID].needSlot {
337 live.add(a.ID)
338 }
339 }
340 }
341
342
343
344 for i, e := range b.Preds {
345 p := e.b
346 t.clear()
347 t.addAll(s.live[p.ID])
348 t.addAll(live.contents())
349 t.addAll(spillLive[p.ID])
350 for _, v := range phis {
351 a := v.Args[i]
352 if s.values[a.ID].needSlot {
353 t.add(a.ID)
354 }
355 if spill := s.values[a.ID].spill; spill != nil {
356
357 t.add(spill.ID)
358 }
359 }
360 if t.size() == len(s.live[p.ID]) {
361 continue
362 }
363
364 s.live[p.ID] = append(s.live[p.ID][:0], t.contents()...)
365 changed = true
366 }
367 }
368
369 if !changed {
370 break
371 }
372 }
373 if s.f.pass.debug > stackDebug {
374 for _, b := range s.f.Blocks {
375 fmt.Printf("stacklive %s %v\n", b, s.live[b.ID])
376 }
377 }
378 }
379
380 func (f *Func) getHome(vid ID) Location {
381 if int(vid) >= len(f.RegAlloc) {
382 return nil
383 }
384 return f.RegAlloc[vid]
385 }
386
387 func (f *Func) setHome(v *Value, loc Location) {
388 for v.ID >= ID(len(f.RegAlloc)) {
389 f.RegAlloc = append(f.RegAlloc, nil)
390 }
391 f.RegAlloc[v.ID] = loc
392 }
393
394 func (s *stackAllocState) buildInterferenceGraph() {
395 f := s.f
396 if n := f.NumValues(); cap(s.interfere) >= n {
397 s.interfere = s.interfere[:n]
398 } else {
399 s.interfere = make([][]ID, n)
400 }
401 live := f.newSparseSet(f.NumValues())
402 defer f.retSparseSet(live)
403 for _, b := range f.Blocks {
404
405
406 live.clear()
407 live.addAll(s.live[b.ID])
408 for i := len(b.Values) - 1; i >= 0; i-- {
409 v := b.Values[i]
410 if s.values[v.ID].needSlot {
411 live.remove(v.ID)
412 for _, id := range live.contents() {
413
414
415 if s.values[v.ID].typ.Compare(s.values[id].typ) == types.CMPeq || hasAnyArgOp(v) || s.values[id].isArg {
416 s.interfere[v.ID] = append(s.interfere[v.ID], id)
417 s.interfere[id] = append(s.interfere[id], v.ID)
418 }
419 }
420 }
421 for _, a := range v.Args {
422 if s.values[a.ID].needSlot {
423 live.add(a.ID)
424 }
425 }
426 if hasAnyArgOp(v) && s.values[v.ID].needSlot {
427
428
429
430
431
432
433
434
435 live.add(v.ID)
436 }
437 }
438 }
439 if f.pass.debug > stackDebug {
440 for vid, i := range s.interfere {
441 if len(i) > 0 {
442 fmt.Printf("v%d interferes with", vid)
443 for _, x := range i {
444 fmt.Printf(" v%d", x)
445 }
446 fmt.Println()
447 }
448 }
449 }
450 }
451
452 func hasAnyArgOp(v *Value) bool {
453 return v.Op == OpArg || v.Op == OpArgIntReg || v.Op == OpArgFloatReg
454 }
455
View as plain text