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