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