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 for a.Op == OpSPanchored || opcodeTable[a.Op].nilCheck {
318 a = a.Args[0]
319 v.SetArg(i, a)
320 }
321 }
322 }
323 for i, c := range b.ControlValues() {
324 for c.Op == OpSPanchored || opcodeTable[c.Op].nilCheck {
325 c = c.Args[0]
326 b.ReplaceControl(i, c)
327 }
328 }
329 }
330 for _, b := range f.Blocks {
331 i := 0
332 for _, v := range b.Values {
333 if v.Op == OpSPanchored {
334
335 if v.Uses != 0 {
336 base.Fatalf("SPAnchored still has %d uses", v.Uses)
337 }
338 v.resetArgs()
339 f.freeValue(v)
340 } else {
341 if opcodeTable[v.Op].nilCheck {
342 if v.Uses != 0 {
343 base.Fatalf("nilcheck still has %d uses", v.Uses)
344 }
345
346
347
348 v.Type = types.TypeVoid
349 }
350 b.Values[i] = v
351 i++
352 }
353 }
354 b.truncateValues(i)
355 }
356
357 f.scheduled = true
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 func storeOrder(values []*Value, sset *sparseSet, storeNumber []int32) []*Value {
382 if len(values) == 0 {
383 return values
384 }
385
386 f := values[0].Block.Func
387
388
389
390
391
392
393 stores := make([]*Value, 0, 64)
394 hasNilCheck := false
395 sset.clear()
396 for _, v := range values {
397 if v.Type.IsMemory() {
398 stores = append(stores, v)
399 if v.Op == OpInitMem || v.Op == OpPhi {
400 continue
401 }
402 sset.add(v.MemoryArg().ID)
403 }
404 if v.Op == OpNilCheck {
405 hasNilCheck = true
406 }
407 }
408 if len(stores) == 0 || !hasNilCheck && f.pass.name == "nilcheckelim" {
409
410 return values
411 }
412
413
414 var last *Value
415 for _, v := range stores {
416 if !sset.contains(v.ID) {
417 if last != nil {
418 f.Fatalf("two stores live simultaneously: %v and %v", v, last)
419 }
420 last = v
421 }
422 }
423
424
425
426
427
428
429
430
431
432
433 count := make([]int32, 3*(len(stores)+1))
434 sset.clear()
435 for n, w := len(stores), last; n > 0; n-- {
436 storeNumber[w.ID] = int32(3 * n)
437 count[3*n]++
438 sset.add(w.ID)
439 if w.Op == OpInitMem || w.Op == OpPhi {
440 if n != 1 {
441 f.Fatalf("store order is wrong: there are stores before %v", w)
442 }
443 break
444 }
445 w = w.MemoryArg()
446 }
447 var stack []*Value
448 for _, v := range values {
449 if sset.contains(v.ID) {
450
451 continue
452 }
453 stack = append(stack, v)
454 sset.add(v.ID)
455
456 for len(stack) > 0 {
457 w := stack[len(stack)-1]
458 if storeNumber[w.ID] != 0 {
459 stack = stack[:len(stack)-1]
460 continue
461 }
462 if w.Op == OpPhi {
463
464
465 storeNumber[w.ID] = 2
466 count[2]++
467 stack = stack[:len(stack)-1]
468 continue
469 }
470
471 max := int32(0)
472 argsdone := true
473 for _, a := range w.Args {
474 if a.Block != w.Block {
475 continue
476 }
477 if !sset.contains(a.ID) {
478 stack = append(stack, a)
479 sset.add(a.ID)
480 argsdone = false
481 break
482 }
483 if storeNumber[a.ID]/3 > max {
484 max = storeNumber[a.ID] / 3
485 }
486 }
487 if !argsdone {
488 continue
489 }
490
491 n := 3*max + 2
492 if w.Op == OpNilCheck {
493 n = 3*max + 1
494 }
495 storeNumber[w.ID] = n
496 count[n]++
497 stack = stack[:len(stack)-1]
498 }
499 }
500
501
502 for i := range count {
503 if i == 0 {
504 continue
505 }
506 count[i] += count[i-1]
507 }
508 if count[len(count)-1] != int32(len(values)) {
509 f.Fatalf("storeOrder: value is missing, total count = %d, values = %v", count[len(count)-1], values)
510 }
511
512
513 order := make([]*Value, len(values))
514 for _, v := range values {
515 s := storeNumber[v.ID]
516 order[count[s-1]] = v
517 count[s-1]++
518 }
519
520
521
522
523 if hasNilCheck {
524 start := -1
525 for i, v := range order {
526 if v.Op == OpNilCheck {
527 if start == -1 {
528 start = i
529 }
530 } else {
531 if start != -1 {
532 slices.SortFunc(order[start:i], valuePosCmp)
533 start = -1
534 }
535 }
536 }
537 if start != -1 {
538 slices.SortFunc(order[start:], valuePosCmp)
539 }
540 }
541
542 return order
543 }
544
545
546 func (v *Value) isFlagOp() bool {
547 if v.Type.IsFlags() || v.Type.IsTuple() && v.Type.FieldType(1).IsFlags() {
548 return true
549 }
550
551
552 switch v.Op {
553 case OpPPC64SUBC, OpPPC64ADDC, OpPPC64SUBCconst, OpPPC64ADDCconst:
554 return true
555 }
556 return false
557 }
558
559
560 func (v *Value) hasFlagInput() bool {
561 for _, a := range v.Args {
562 if a.isFlagOp() {
563 return true
564 }
565 }
566
567
568 switch v.Op {
569 case OpPPC64SUBE, OpPPC64ADDE, OpPPC64SUBZEzero, OpPPC64ADDZE, OpPPC64ADDZEzero:
570 return true
571 }
572 return false
573 }
574
575 func valuePosCmp(a, b *Value) int {
576 if a.Pos.Before(b.Pos) {
577 return -1
578 }
579 if a.Pos.After(b.Pos) {
580 return +1
581 }
582 return 0
583 }
584
View as plain text