1
2
3
4
5 package ssacompile
6
7 import (
8 "cmp"
9 "container/heap"
10 "slices"
11 "sort"
12
13 "cmd/compile/internal/base"
14 "cmd/compile/internal/ssa"
15 "cmd/compile/internal/ssa/ssaop"
16 "cmd/compile/internal/types"
17 )
18
19 const (
20 ScorePhi = iota
21 ScoreArg
22 ScoreInitMem
23 ScoreReadTuple
24 ScoreNilCheck
25 ScoreMemory
26 ScoreReadFlags
27 ScoreDefault
28 ScoreFlags
29 ScoreInductionInc
30 ScoreControl
31 )
32
33 type ValHeap struct {
34 a []*ssa.Value
35 score []int8
36 inBlockUses []bool
37 }
38
39 func (h ValHeap) Len() int { return len(h.a) }
40 func (h ValHeap) Swap(i, j int) { a := h.a; a[i], a[j] = a[j], a[i] }
41
42 func (h *ValHeap) Push(x any) {
43
44
45 v := x.(*ssa.Value)
46 h.a = append(h.a, v)
47 }
48 func (h *ValHeap) Pop() any {
49 old := h.a
50 n := len(old)
51 x := old[n-1]
52 h.a = old[0 : n-1]
53 return x
54 }
55 func (h ValHeap) Less(i, j int) bool {
56 x := h.a[i]
57 y := h.a[j]
58 sx := h.score[x.ID]
59 sy := h.score[y.ID]
60 if c := sx - sy; c != 0 {
61 return c < 0
62 }
63
64
65
66 ix := h.inBlockUses[x.ID]
67 iy := h.inBlockUses[y.ID]
68 if ix != iy {
69 return ix
70 }
71
72 if x.Pos != y.Pos {
73 return x.Pos.Before(y.Pos)
74 }
75 if x.Op != ssaop.OpPhi {
76 if c := len(x.Args) - len(y.Args); c != 0 {
77 return c > 0
78 }
79 }
80 if c := x.Uses - y.Uses; c != 0 {
81 return c > 0
82 }
83
84
85
86 if c := x.AuxInt - y.AuxInt; c != 0 {
87 return c < 0
88 }
89 if cmp := x.Type.Compare(y.Type); cmp != types.CMPeq {
90 return cmp == types.CMPlt
91 }
92 return x.ID < y.ID
93 }
94
95
96
97
98
99
100 func schedule(f *ssa.Func) {
101
102 priq := new(ValHeap)
103
104
105 score := f.Cache.AllocInt8Slice(f.NumValues())
106 defer f.Cache.FreeInt8Slice(score)
107
108
109 nextMem := f.Cache.AllocValueSlice(f.NumValues())
110 defer f.Cache.FreeValueSlice(nextMem)
111
112
113
114 inBlockUses := f.Cache.AllocBoolSlice(f.NumValues())
115 defer f.Cache.FreeBoolSlice(inBlockUses)
116 if f.Config.Optimize {
117 for _, b := range f.Blocks {
118 for _, v := range b.Values {
119 for _, a := range v.Args {
120 if a.Block == b {
121 inBlockUses[a.ID] = true
122 }
123 }
124 }
125 }
126 }
127 priq.inBlockUses = inBlockUses
128
129 for _, b := range f.Blocks {
130
131 for _, v := range b.Values {
132 switch {
133 case v.Op.IsLoweredGetClosurePtr():
134
135
136
137
138 if b != f.Entry {
139 f.Fatalf("LoweredGetClosurePtr appeared outside of entry block, b=%s", b.String())
140 }
141 score[v.ID] = ScorePhi
142 case ssaop.OpcodeTable[v.Op].NilCheck:
143
144 score[v.ID] = ScoreNilCheck
145 case v.Op == ssaop.OpPhi:
146
147 score[v.ID] = ScorePhi
148 case v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg:
149
150
151
152
153 if b != f.Entry {
154 f.Fatalf("%s appeared outside of entry block, b=%s", v.Op, b.String())
155 }
156 score[v.ID] = ScorePhi
157 case v.Op == ssaop.OpArg || v.Op == ssaop.OpSP || v.Op == ssaop.OpSB:
158
159 score[v.ID] = ScoreArg
160 case v.Op == ssaop.OpInitMem:
161
162 score[v.ID] = ScoreInitMem
163 case v.Type.IsMemory():
164
165
166 score[v.ID] = ScoreMemory
167 case v.Op == ssaop.OpSelect0 || v.Op == ssaop.OpSelect1 || v.Op == ssaop.OpSelectN:
168
169
170 score[v.ID] = ScoreReadTuple
171 case v.HasFlagInput():
172
173
174 score[v.ID] = ScoreReadFlags
175 case v.IsFlagOp():
176
177
178
179
180
181 score[v.ID] = ScoreFlags
182 case (len(v.Args) == 1 &&
183 v.Args[0].Op == ssaop.OpPhi &&
184 v.Args[0].Uses > 1 &&
185 len(b.Succs) == 1 &&
186 b.Succs[0].B == v.Args[0].Block &&
187 v.Args[0].Args[b.Succs[0].I] == v):
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 score[v.ID] = ScoreInductionInc
203 default:
204 score[v.ID] = ScoreDefault
205 }
206 }
207 for _, c := range b.ControlValues() {
208
209
210 if c.Block != b || score[c.ID] < ScoreReadTuple {
211 continue
212 }
213 if score[c.ID] == ScoreReadTuple {
214 score[c.Args[0].ID] = ScoreControl
215 continue
216 }
217 score[c.ID] = ScoreControl
218 }
219 }
220 priq.score = score
221
222
223 type edge struct {
224 x, y *ssa.Value
225 }
226 edges := make([]edge, 0, 64)
227
228
229
230 inEdges := f.Cache.AllocInt32Slice(f.NumValues())
231 defer f.Cache.FreeInt32Slice(inEdges)
232
233 for _, b := range f.Blocks {
234 edges = edges[:0]
235
236 for _, v := range b.Values {
237 if v.Op == ssaop.OpPhi {
238
239
240
241 continue
242 }
243 for _, a := range v.Args {
244 if a.Block == b {
245 edges = append(edges, edge{a, v})
246 }
247 }
248 }
249
250
251
252
253 for _, v := range b.Values {
254 if v.Op != ssaop.OpPhi && v.Op != ssaop.OpInitMem && v.Type.IsMemory() {
255 nextMem[v.MemoryArg().ID] = v
256 }
257 }
258
259
260 for _, v := range b.Values {
261 if v.Op == ssaop.OpPhi || v.Type.IsMemory() {
262 continue
263 }
264 w := v.MemoryArg()
265 if w == nil {
266 continue
267 }
268 if s := nextMem[w.ID]; s != nil && s.Block == b {
269 edges = append(edges, edge{v, s})
270 }
271 }
272
273
274 slices.SortFunc(edges, func(a, b edge) int {
275 return cmp.Compare(a.x.ID, b.x.ID)
276 })
277
278 for _, e := range edges {
279 inEdges[e.y.ID]++
280 }
281
282
283 priq.a = priq.a[:0]
284 for _, v := range b.Values {
285 if inEdges[v.ID] == 0 {
286 heap.Push(priq, v)
287 }
288 }
289
290
291
292
293 nv := len(b.Values)
294 b.Values = b.Values[:0]
295 for priq.Len() > 0 {
296
297 v := heap.Pop(priq).(*ssa.Value)
298 b.Values = append(b.Values, v)
299
300
301 i := sort.Search(len(edges), func(i int) bool {
302 return edges[i].x.ID >= v.ID
303 })
304 j := sort.Search(len(edges), func(i int) bool {
305 return edges[i].x.ID > v.ID
306 })
307
308 for _, e := range edges[i:j] {
309 inEdges[e.y.ID]--
310 if inEdges[e.y.ID] == 0 {
311 heap.Push(priq, e.y)
312 }
313 }
314 }
315 if len(b.Values) != nv {
316 f.Fatalf("schedule does not include all values in block %s", b)
317 }
318 }
319
320
321
322
323 for _, b := range f.Blocks {
324 for _, v := range b.Values {
325 for i, a := range v.Args {
326 for a.Op == ssaop.OpSPanchored || ssaop.OpcodeTable[a.Op].NilCheck {
327 a = a.Args[0]
328 v.SetArg(i, a)
329 }
330 }
331 }
332 for i, c := range b.ControlValues() {
333 for c.Op == ssaop.OpSPanchored || ssaop.OpcodeTable[c.Op].NilCheck {
334 c = c.Args[0]
335 b.ReplaceControl(i, c)
336 }
337 }
338 }
339 for _, b := range f.Blocks {
340 i := 0
341 for _, v := range b.Values {
342 if v.Op == ssaop.OpSPanchored {
343
344 if v.Uses != 0 {
345 base.Fatalf("SPAnchored still has %d uses", v.Uses)
346 }
347 v.ResetArgs()
348 f.FreeValue(v)
349 } else {
350 if ssaop.OpcodeTable[v.Op].NilCheck {
351 if v.Uses != 0 {
352 base.Fatalf("nilcheck still has %d uses", v.Uses)
353 }
354
355
356
357 v.Type = types.TypeVoid
358 }
359 b.Values[i] = v
360 i++
361 }
362 }
363 b.TruncateValues(i)
364 }
365
366 f.Scheduled = true
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390 func storeOrder(values []*ssa.Value, sset *ssa.SparseSet, storeNumber []int32) []*ssa.Value {
391 if len(values) == 0 {
392 return values
393 }
394
395 f := values[0].Block.Func
396
397
398
399
400
401
402 stores := make([]*ssa.Value, 0, 64)
403 hasNilCheck := false
404 sset.Clear()
405 for _, v := range values {
406 if v.Type.IsMemory() {
407 stores = append(stores, v)
408 if v.Op == ssaop.OpInitMem || v.Op == ssaop.OpPhi {
409 continue
410 }
411 sset.Add(v.MemoryArg().ID)
412 }
413 if v.Op == ssaop.OpNilCheck {
414 hasNilCheck = true
415 }
416 }
417 if len(stores) == 0 || !hasNilCheck && f.Pass.Name == "nilcheckelim" {
418
419 return values
420 }
421
422
423 var last *ssa.Value
424 for _, v := range stores {
425 if !sset.Contains(v.ID) {
426 if last != nil {
427 f.Fatalf("two stores live simultaneously: %v and %v", v, last)
428 }
429 last = v
430 }
431 }
432
433
434
435
436
437
438
439
440
441
442 count := make([]int32, 3*(len(stores)+1))
443 sset.Clear()
444 for n, w := len(stores), last; n > 0; n-- {
445 storeNumber[w.ID] = int32(3 * n)
446 count[3*n]++
447 sset.Add(w.ID)
448 if w.Op == ssaop.OpInitMem || w.Op == ssaop.OpPhi {
449 if n != 1 {
450 f.Fatalf("store order is wrong: there are stores before %v", w)
451 }
452 break
453 }
454 w = w.MemoryArg()
455 }
456 var stack []*ssa.Value
457 for _, v := range values {
458 if sset.Contains(v.ID) {
459
460 continue
461 }
462 stack = append(stack, v)
463 sset.Add(v.ID)
464
465 for len(stack) > 0 {
466 w := stack[len(stack)-1]
467 if storeNumber[w.ID] != 0 {
468 stack = stack[:len(stack)-1]
469 continue
470 }
471 if w.Op == ssaop.OpPhi {
472
473
474 storeNumber[w.ID] = 2
475 count[2]++
476 stack = stack[:len(stack)-1]
477 continue
478 }
479
480 max := int32(0)
481 argsdone := true
482 for _, a := range w.Args {
483 if a.Block != w.Block {
484 continue
485 }
486 if !sset.Contains(a.ID) {
487 stack = append(stack, a)
488 sset.Add(a.ID)
489 argsdone = false
490 break
491 }
492 if storeNumber[a.ID]/3 > max {
493 max = storeNumber[a.ID] / 3
494 }
495 }
496 if !argsdone {
497 continue
498 }
499
500 n := 3*max + 2
501 if w.Op == ssaop.OpNilCheck {
502 n = 3*max + 1
503 }
504 storeNumber[w.ID] = n
505 count[n]++
506 stack = stack[:len(stack)-1]
507 }
508 }
509
510
511 for i := range count {
512 if i == 0 {
513 continue
514 }
515 count[i] += count[i-1]
516 }
517 if count[len(count)-1] != int32(len(values)) {
518 f.Fatalf("storeOrder: value is missing, total count = %d, values = %v", count[len(count)-1], values)
519 }
520
521
522 order := make([]*ssa.Value, len(values))
523 for _, v := range values {
524 s := storeNumber[v.ID]
525 order[count[s-1]] = v
526 count[s-1]++
527 }
528
529
530
531
532 if hasNilCheck {
533 start := -1
534 for i, v := range order {
535 if v.Op == ssaop.OpNilCheck {
536 if start == -1 {
537 start = i
538 }
539 } else {
540 if start != -1 {
541 slices.SortFunc(order[start:i], valuePosCmp)
542 start = -1
543 }
544 }
545 }
546 if start != -1 {
547 slices.SortFunc(order[start:], valuePosCmp)
548 }
549 }
550
551 return order
552 }
553
554 func valuePosCmp(a, b *ssa.Value) int {
555 if a.Pos.Before(b.Pos) {
556 return -1
557 }
558 if a.Pos.After(b.Pos) {
559 return +1
560 }
561 return 0
562 }
563
View as plain text