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