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