1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 package ssacompile
93
94 import (
95 "cmp"
96 "fmt"
97 "internal/buildcfg"
98 "math"
99 "math/bits"
100 "slices"
101 "unsafe"
102
103 "cmd/compile/internal/base"
104 "cmd/compile/internal/ir"
105 "cmd/compile/internal/ssa"
106 "cmd/compile/internal/ssa/ssabase"
107 "cmd/compile/internal/ssa/ssaop"
108 "cmd/compile/internal/types"
109 "cmd/internal/src"
110 "cmd/internal/sys"
111 )
112
113
114
115 const (
116 likelyDistance = 1
117 normalDistance = 10
118 unlikelyDistance = 100
119 )
120
121
122
123 func regalloc(f *ssa.Func) {
124 var s regAllocState
125 s.init(f)
126 s.regalloc(f)
127 s.close()
128 }
129
130 const noRegister ssaop.Register = 255
131
132
133 var noRegisters [32]ssaop.Register = [32]ssaop.Register{
134 noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
135 noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
136 noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
137 noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister, noRegister,
138 }
139
140 func (s *regAllocState) RegMaskString(m ssaop.RegMask) string {
141 str := ""
142 for r := ssaop.Register(0); !m.Empty(); r++ {
143 if !m.HasReg(r) {
144 continue
145 }
146 m = m.RemoveReg(r)
147 if str != "" {
148 str += " "
149 }
150 str += s.registers[r].String()
151 }
152 return str
153 }
154
155
156 func countRegs(r ssaop.RegMask) int {
157 return bits.OnesCount64(r.V1) + bits.OnesCount64(r.V2)
158 }
159
160
161 func (s *regAllocState) pickReg(rm ssaop.RegMask) ssaop.Register {
162 if s.f.Config.Ctxt.Arch.Arch == sys.ArchRISCV64 {
163
164 riscv64CompressedMask := rm.Intersect(ssaop.RegMask{V1: 0x0000ff000000ff00})
165 if !riscv64CompressedMask.Empty() {
166 rm = riscv64CompressedMask
167 }
168 }
169 return rm.PickReg()
170 }
171
172 type regState struct {
173 v *ssa.Value
174 c *ssa.Value
175
176 }
177
178 type regAllocState struct {
179 f *ssa.Func
180
181 sdom ssa.SparseTree
182 registers []ssabase.Register
183 numRegs ssaop.Register
184 SPReg ssaop.Register
185 SBReg ssaop.Register
186 GReg ssaop.Register
187 ZeroIntReg ssaop.Register
188 allocatable ssaop.RegMask
189
190
191
192
193 live [][]liveInfo
194
195
196
197
198 desired []desiredState
199
200
201 values []ssa.ValState
202
203
204 sp, sb ssa.ID
205
206
207
208 orig []*ssa.Value
209
210
211
212 regs []regState
213
214
215 nospill ssaop.RegMask
216
217
218 used ssaop.RegMask
219
220
221 usedSinceBlockStart ssaop.RegMask
222
223
224 tmpused ssaop.RegMask
225
226
227 curBlock *ssa.Block
228
229
230 freeUseRecords *ssa.Use
231
232
233
234 endRegs [][]endReg
235
236
237
238 startRegs [][]startReg
239
240
241
242
243 startRegsMask ssaop.RegMask
244
245
246 spillLive [][]ssa.ID
247
248 loopnest *ssa.LoopNest
249
250
251 visitOrder []*ssa.Block
252
253
254 blockOrder []int32
255
256
257 doClobber bool
258
259
260
261
262
263
264 nextCall []int32
265
266
267
268 curIdx int
269 }
270
271 type endReg struct {
272 r ssaop.Register
273 v *ssa.Value
274 c *ssa.Value
275 }
276
277 type startReg struct {
278 r ssaop.Register
279 v *ssa.Value
280 c *ssa.Value
281 pos src.XPos
282 }
283
284
285 func (s *regAllocState) freeReg(r ssaop.Register) {
286 if !s.allocatable.HasReg(r) && !s.isGReg(r) {
287 return
288 }
289 v := s.regs[r].v
290 if v == nil {
291 s.f.Fatalf("tried to free an already free register %d\n", r)
292 }
293
294
295 if s.f.Pass.Debug > ssa.RegDebug {
296 fmt.Printf("freeReg %s (dump %s/%s)\n", &s.registers[r], v, s.regs[r].c)
297 }
298 s.regs[r] = regState{}
299 s.values[v.ID].Regs = s.values[v.ID].Regs.RemoveReg(r)
300 s.used = s.used.RemoveReg(r)
301 }
302
303
304 func (s *regAllocState) freeRegs(m ssaop.RegMask) {
305 for !m.Intersect(s.used).Empty() {
306 s.freeReg(s.pickReg(m.Intersect(s.used)))
307 }
308 }
309
310
311 func (s *regAllocState) clobberRegs(m ssaop.RegMask) {
312 m = m.Intersect(s.allocatable.Intersect(s.f.Config.GpRegMask))
313 for !m.Empty() {
314 r := s.pickReg(m)
315 m = m.RemoveReg(r)
316 x := s.curBlock.NewValue0(src.NoXPos, ssaop.OpClobberReg, types.TypeVoid)
317 s.f.SetHome(x, &s.registers[r])
318 }
319 }
320
321
322
323 func (s *regAllocState) setOrig(c *ssa.Value, v *ssa.Value) {
324 if int(c.ID) >= cap(s.orig) {
325 x := s.f.Cache.AllocValueSlice(int(c.ID) + 1)
326 copy(x, s.orig)
327 s.f.Cache.FreeValueSlice(s.orig)
328 s.orig = x
329 }
330 for int(c.ID) >= len(s.orig) {
331 s.orig = append(s.orig, nil)
332 }
333 if s.orig[c.ID] != nil {
334 s.f.Fatalf("orig value set twice %s %s", c, v)
335 }
336 s.orig[c.ID] = s.orig[v.ID]
337 }
338
339
340
341 func (s *regAllocState) assignReg(r ssaop.Register, v *ssa.Value, c *ssa.Value) {
342 if s.f.Pass.Debug > ssa.RegDebug {
343 fmt.Printf("assignReg %s %s/%s\n", &s.registers[r], v, c)
344 }
345
346 s.values[v.ID].Regs = s.values[v.ID].Regs.AddReg(r)
347 s.f.SetHome(c, &s.registers[r])
348
349
350 if !s.allocatable.HasReg(r) && !s.isGReg(r) {
351 return
352 }
353 if s.regs[r].v != nil {
354 s.f.Fatalf("tried to assign register %d to %s/%s but it is already used by %s", r, v, c, s.regs[r].v)
355 }
356 s.regs[r] = regState{v, c}
357 s.used = s.used.AddReg(r)
358 }
359
360
361
362
363 func (s *regAllocState) allocReg(mask ssaop.RegMask, v *ssa.Value) ssaop.Register {
364 if v.OnWasmStack {
365 return noRegister
366 }
367
368 mask = mask.Intersect(s.allocatable)
369 mask = mask.Minus(s.nospill)
370 if mask.Empty() {
371 s.f.Fatalf("no register available for %s", v.LongString())
372 }
373
374
375 if !mask.Minus(s.used).Empty() {
376 r := s.pickReg(mask.Minus(s.used))
377 s.usedSinceBlockStart = s.usedSinceBlockStart.AddReg(r)
378 return r
379 }
380
381
382
383
384
385
386
387
388
389
390
391 var r ssaop.Register
392 maxuse := int32(-1)
393 for t := ssaop.Register(0); t < s.numRegs; t++ {
394 if !mask.HasReg(t) {
395 continue
396 }
397 v := s.regs[t].v
398 if n := s.values[v.ID].Uses.Dist; n > maxuse {
399
400
401 r = t
402 maxuse = n
403 }
404 }
405 if maxuse == -1 {
406 s.f.Fatalf("couldn't find register to spill")
407 }
408
409 if s.f.Config.Ctxt.Arch.Arch == sys.ArchWasm {
410
411
412
413 s.freeReg(r)
414 return r
415 }
416
417
418
419 v2 := s.regs[r].v
420 m := s.compatRegs(v2.Type).Minus(s.used).Minus(s.tmpused).RemoveReg(r)
421 if !m.Empty() && !s.values[v2.ID].Rematerializeable && countRegs(s.values[v2.ID].Regs) == 1 {
422 s.usedSinceBlockStart = s.usedSinceBlockStart.AddReg(r)
423 r2 := s.pickReg(m)
424 c := s.curBlock.NewValue1(v2.Pos, ssaop.OpCopy, v2.Type, s.regs[r].c)
425 if s.f.Pass.Debug > ssa.RegDebug {
426 fmt.Printf("copy %s to %s : %s\n", v2, c, &s.registers[r2])
427 }
428 s.setOrig(c, v2)
429 s.assignReg(r2, v2, c)
430 }
431
432
433
434
435 if !s.usedSinceBlockStart.HasReg(r) {
436 if s.startRegsMask.HasReg(r) {
437 if s.f.Pass.Debug > ssa.RegDebug {
438 fmt.Printf("dropped from startRegs: %s\n", &s.registers[r])
439 }
440 s.startRegsMask = s.startRegsMask.RemoveReg(r)
441 }
442 }
443
444 s.freeReg(r)
445 s.usedSinceBlockStart = s.usedSinceBlockStart.AddReg(r)
446 return r
447 }
448
449
450
451 func (s *regAllocState) makeSpill(v *ssa.Value, b *ssa.Block) *ssa.Value {
452 vi := &s.values[v.ID]
453 if vi.Spill != nil {
454
455 vi.RestoreMin = min(vi.RestoreMin, s.sdom[b.ID].Entry)
456 vi.RestoreMax = max(vi.RestoreMax, s.sdom[b.ID].Exit)
457 return vi.Spill
458 }
459
460
461 spill := s.f.NewValueNoBlock(ssaop.OpStoreReg, v.Type, v.Pos)
462
463
464 s.setOrig(spill, v)
465 vi.Spill = spill
466 vi.RestoreMin = s.sdom[b.ID].Entry
467 vi.RestoreMax = s.sdom[b.ID].Exit
468 return spill
469 }
470
471
472
473
474
475
476
477 func (s *regAllocState) allocValToReg(v *ssa.Value, mask ssaop.RegMask, nospill bool, pos src.XPos) *ssa.Value {
478 if s.f.Config.Ctxt.Arch.Arch == sys.ArchWasm && v.Rematerializeable() {
479 c := v.CopyIntoWithXPos(s.curBlock, pos)
480 c.OnWasmStack = true
481 s.setOrig(c, v)
482 return c
483 }
484 if v.OnWasmStack {
485 return v
486 }
487
488 vi := &s.values[v.ID]
489 pos = pos.WithNotStmt()
490
491 if !mask.Intersect(vi.Regs).Empty() {
492 mask = mask.Intersect(vi.Regs)
493 r := s.pickReg(mask)
494 if mask.HasReg(s.SPReg) {
495
496
497
498 r = s.SPReg
499 }
500 if !s.allocatable.HasReg(r) {
501 return v
502 }
503 if s.regs[r].v != v || s.regs[r].c == nil {
504 panic("bad register state")
505 }
506 if nospill {
507 s.nospill = s.nospill.AddReg(r)
508 }
509 s.usedSinceBlockStart = s.usedSinceBlockStart.AddReg(r)
510 return s.regs[r].c
511 }
512
513 var r ssaop.Register
514
515 onWasmStack := nospill && s.f.Config.Ctxt.Arch.Arch == sys.ArchWasm
516 if !onWasmStack {
517
518 r = s.allocReg(mask, v)
519 }
520
521
522 var c *ssa.Value
523 if !vi.Regs.Empty() {
524
525 var current *ssa.Value
526 if !vi.Regs.Minus(s.allocatable).Empty() {
527
528 current = v
529 } else {
530 r2 := s.pickReg(vi.Regs)
531 if s.regs[r2].v != v {
532 panic("bad register state")
533 }
534 current = s.regs[r2].c
535 s.usedSinceBlockStart = s.usedSinceBlockStart.AddReg(r2)
536 }
537 c = s.curBlock.NewValue1(pos, ssaop.OpCopy, v.Type, current)
538 } else if v.Rematerializeable() {
539
540 c = v.CopyIntoWithXPos(s.curBlock, pos)
541
542
543
544
545
546
547
548 sourceMask := s.regspec(c).Outputs[0].Regs
549 if mask.Intersect(sourceMask).Empty() && !onWasmStack {
550 s.setOrig(c, v)
551 s.assignReg(s.allocReg(sourceMask, v), v, c)
552
553
554
555
556
557
558
559
560
561
562 c = s.curBlock.NewValue1(pos, ssaop.OpCopy, v.Type, c)
563 }
564 } else {
565
566 spill := s.makeSpill(v, s.curBlock)
567 if s.f.Pass.Debug > ssa.LogSpills {
568 s.f.Warnl(vi.Spill.Pos, "load spill for %v from %v", v, spill)
569 }
570 c = s.curBlock.NewValue1(pos, ssaop.OpLoadReg, v.Type, spill)
571 sourceMask := s.compatRegs(v.Type)
572 if !sourceMask.HasReg(r) && !onWasmStack {
573
574
575
576 s.setOrig(c, v)
577 s.assignReg(s.allocReg(sourceMask, v), v, c)
578 c = s.curBlock.NewValue1(pos, ssaop.OpCopy, v.Type, c)
579 }
580 }
581
582 s.setOrig(c, v)
583
584 if onWasmStack {
585 c.OnWasmStack = true
586 return c
587 }
588
589 s.assignReg(r, v, c)
590 if c.Op == ssaop.OpLoadReg && s.isGReg(r) {
591 s.f.Fatalf("allocValToReg.OpLoadReg targeting g: " + c.LongString())
592 }
593 if nospill {
594 s.nospill = s.nospill.AddReg(r)
595 }
596 return c
597 }
598
599
600 func isLeaf(f *ssa.Func) bool {
601 for _, b := range f.Blocks {
602 for _, v := range b.Values {
603 if v.Op.IsCall() && !v.Op.IsTailCall() {
604
605 return false
606 }
607 }
608 }
609 return true
610 }
611
612 func (s *regAllocState) init(f *ssa.Func) {
613 s.f = f
614 s.f.RegAlloc = s.f.Cache.Locs[:0]
615 s.registers = f.Config.Registers
616 if nr := len(s.registers); nr == 0 || nr > int(noRegister) || nr > int(unsafe.Sizeof(ssaop.RegMask{})*8) {
617 s.f.Fatalf("bad number of registers: %d", nr)
618 } else {
619 s.numRegs = ssaop.Register(nr)
620 }
621
622 s.SPReg = noRegister
623 s.SBReg = noRegister
624 s.GReg = noRegister
625 s.ZeroIntReg = noRegister
626 for r := ssaop.Register(0); r < s.numRegs; r++ {
627 switch s.registers[r].String() {
628 case "SP":
629 s.SPReg = r
630 case "SB":
631 s.SBReg = r
632 case "g":
633 s.GReg = r
634 case "ZERO":
635 s.ZeroIntReg = r
636 }
637 }
638
639 switch noRegister {
640 case s.SPReg:
641 s.f.Fatalf("no SP register found")
642 case s.SBReg:
643 s.f.Fatalf("no SB register found")
644 case s.GReg:
645 if f.Config.HasGReg {
646 s.f.Fatalf("no g register found")
647 }
648 }
649
650
651 s.allocatable = s.f.Config.GpRegMask.Union(s.f.Config.FpRegMask).Union(s.f.Config.SpecialRegMask).Union(s.f.Config.SimdRegMask)
652 s.allocatable = s.allocatable.RemoveReg(s.SPReg)
653 s.allocatable = s.allocatable.RemoveReg(s.SBReg)
654 if s.f.Config.HasGReg {
655 s.allocatable = s.allocatable.RemoveReg(s.GReg)
656 }
657 if s.ZeroIntReg != noRegister {
658 s.allocatable = s.allocatable.RemoveReg(s.ZeroIntReg)
659 }
660 if buildcfg.FramePointerEnabled && s.f.Config.FPReg >= 0 {
661 s.allocatable = s.allocatable.RemoveReg(ssaop.Register(s.f.Config.FPReg))
662 }
663 if s.f.Config.LinkReg != -1 {
664 if isLeaf(f) {
665
666 s.allocatable = s.allocatable.RemoveReg(ssaop.Register(s.f.Config.LinkReg))
667 }
668 }
669 if s.f.Config.Ctxt.Flag_dynlink {
670 switch s.f.Config.Arch {
671 case "386":
672
673
674
675
676
677 case "amd64":
678 s.allocatable = s.allocatable.RemoveReg(15)
679 case "arm":
680 s.allocatable = s.allocatable.RemoveReg(9)
681 case "arm64":
682
683 case "loong64":
684
685 case "ppc64", "ppc64le":
686
687 case "riscv64":
688
689 case "s390x":
690 s.allocatable = s.allocatable.RemoveReg(11)
691 default:
692 s.f.Fe.Fatalf(src.NoXPos, "arch %s not implemented", s.f.Config.Arch)
693 }
694 }
695
696
697
698
699 s.visitOrder = layoutRegallocOrder(f)
700
701
702
703 s.blockOrder = make([]int32, f.NumBlocks())
704 for i, b := range s.visitOrder {
705 s.blockOrder[b.ID] = int32(i)
706 }
707
708 s.regs = make([]regState, s.numRegs)
709 nv := f.NumValues()
710 if cap(s.f.Cache.RegallocValues) >= nv {
711 s.f.Cache.RegallocValues = s.f.Cache.RegallocValues[:nv]
712 } else {
713 s.f.Cache.RegallocValues = make([]ssa.ValState, nv)
714 }
715 s.values = s.f.Cache.RegallocValues
716 s.orig = s.f.Cache.AllocValueSlice(nv)
717 for _, b := range s.visitOrder {
718 for _, v := range b.Values {
719 if v.NeedRegister() {
720 s.values[v.ID].NeedReg = true
721 s.values[v.ID].Rematerializeable = v.Rematerializeable()
722 s.orig[v.ID] = v
723 }
724
725
726 }
727 }
728 s.computeLive()
729
730 s.endRegs = make([][]endReg, f.NumBlocks())
731 s.startRegs = make([][]startReg, f.NumBlocks())
732 s.spillLive = make([][]ssa.ID, f.NumBlocks())
733 s.sdom = f.Sdom()
734
735
736 if f.Config.Ctxt.Arch.Arch == sys.ArchWasm {
737 canLiveOnStack := f.NewSparseSet(f.NumValues())
738 defer f.RetSparseSet(canLiveOnStack)
739 for _, b := range f.Blocks {
740
741 canLiveOnStack.Clear()
742 for _, c := range b.ControlValues() {
743 if c.Uses == 1 && !ssaop.OpcodeTable[c.Op].Generic {
744 canLiveOnStack.Add(c.ID)
745 }
746 }
747
748 for i := len(b.Values) - 1; i >= 0; i-- {
749 v := b.Values[i]
750 if canLiveOnStack.Contains(v.ID) {
751 v.OnWasmStack = true
752 } else {
753
754 canLiveOnStack.Clear()
755 }
756 for _, arg := range v.Args {
757
758
759
760
761
762 if arg.Uses == 1 && arg.Block == v.Block && !arg.Type.IsMemory() && !ssaop.OpcodeTable[arg.Op].Generic {
763 canLiveOnStack.Add(arg.ID)
764 }
765 }
766 }
767 }
768 }
769
770
771
772
773 if base.Flag.ClobberDeadReg && len(s.f.Blocks) <= 10000 {
774
775 s.doClobber = true
776 }
777 }
778
779 func (s *regAllocState) close() {
780 s.f.Cache.FreeValueSlice(s.orig)
781 }
782
783
784
785 func (s *regAllocState) addUse(id ssa.ID, dist int32, pos src.XPos) {
786 r := s.freeUseRecords
787 if r != nil {
788 s.freeUseRecords = r.Next
789 } else {
790 r = &ssa.Use{}
791 }
792 r.Dist = dist
793 r.Pos = pos
794 r.Next = s.values[id].Uses
795 s.values[id].Uses = r
796 if r.Next != nil && dist > r.Next.Dist {
797 s.f.Fatalf("uses added in wrong order")
798 }
799 }
800
801
802
803 func (s *regAllocState) advanceUses(v *ssa.Value) {
804 for _, a := range v.Args {
805 if !s.values[a.ID].NeedReg {
806 continue
807 }
808 ai := &s.values[a.ID]
809 r := ai.Uses
810 ai.Uses = r.Next
811 if r.Next == nil || (!ssaop.OpcodeTable[a.Op].FixedReg && r.Next.Dist > s.nextCall[s.curIdx]) {
812
813 s.freeRegs(ai.Regs)
814 }
815 r.Next = s.freeUseRecords
816 s.freeUseRecords = r
817 }
818 s.dropIfUnused(v)
819 }
820
821
822
823 func (s *regAllocState) dropIfUnused(v *ssa.Value) {
824 if !s.values[v.ID].NeedReg {
825 return
826 }
827 vi := &s.values[v.ID]
828 r := vi.Uses
829 nextCall := s.nextCall[s.curIdx]
830 if ssaop.OpcodeTable[v.Op].Call {
831 if s.curIdx == len(s.nextCall)-1 {
832 nextCall = math.MaxInt32
833 } else {
834 nextCall = s.nextCall[s.curIdx+1]
835 }
836 }
837 if r == nil || (!ssaop.OpcodeTable[v.Op].FixedReg && r.Dist > nextCall) {
838 s.freeRegs(vi.Regs)
839 }
840 }
841
842
843
844
845 func (s *regAllocState) liveAfterCurrentInstruction(v *ssa.Value) bool {
846 u := s.values[v.ID].Uses
847 if u == nil {
848 panic(fmt.Errorf("u is nil, v = %s, s.values[v.ID] = %v", v.LongString(), s.values[v.ID]))
849 }
850 d := u.Dist
851 for u != nil && u.Dist == d {
852 u = u.Next
853 }
854 return u != nil && u.Dist > d
855 }
856
857
858 func (s *regAllocState) setState(regs []endReg) {
859 s.freeRegs(s.used)
860 for _, x := range regs {
861 s.assignReg(x.r, x.v, x.c)
862 }
863 }
864
865
866 func (s *regAllocState) compatRegs(t *types.Type) ssaop.RegMask {
867 var m ssaop.RegMask
868 if t.IsTuple() || t.IsFlags() {
869 return ssaop.RegMask{}
870 }
871 if t.IsSIMD() {
872 if t.Size() > 8 {
873 return s.f.Config.SimdRegMask.Intersect(s.allocatable)
874 } else {
875 if !s.f.Config.SpecialRegMask.Empty() {
876
877
878 return s.f.Config.SpecialRegMask.Intersect(s.allocatable)
879 }
880
881
882 return s.f.Config.GpRegMask.Intersect(s.allocatable)
883 }
884 }
885 if t.IsFloat() || t == types.TypeInt128 {
886 if t.Kind() == types.TFLOAT32 && !s.f.Config.Fp32RegMask.Empty() {
887 m = s.f.Config.Fp32RegMask
888 } else if t.Kind() == types.TFLOAT64 && !s.f.Config.Fp64RegMask.Empty() {
889 m = s.f.Config.Fp64RegMask
890 } else {
891 m = s.f.Config.FpRegMask
892 }
893 } else {
894 m = s.f.Config.GpRegMask
895 }
896 return m.Intersect(s.allocatable)
897 }
898
899
900 func (s *regAllocState) regspec(v *ssa.Value) ssaop.RegInfo {
901 op := v.Op
902 if op == ssaop.OpConvert {
903
904
905
906 m := s.allocatable.Intersect(s.f.Config.GpRegMask)
907 return ssaop.RegInfo{Inputs: []ssaop.InputInfo{{Regs: m}}, Outputs: []ssaop.OutputInfo{{Regs: m}}}
908 }
909 if op == ssaop.OpArgIntReg {
910 reg := v.Block.Func.Config.IntParamRegs[v.AuxInt8()]
911 return ssaop.RegInfo{Outputs: []ssaop.OutputInfo{{Regs: ssa.RegMaskAt(ssaop.Register(reg))}}}
912 }
913 if op == ssaop.OpArgFloatReg {
914 reg := v.Block.Func.Config.FloatParamRegs[v.AuxInt8()]
915 return ssaop.RegInfo{Outputs: []ssaop.OutputInfo{{Regs: ssa.RegMaskAt(ssaop.Register(reg))}}}
916 }
917 if op.IsCall() {
918 if ac, ok := v.Aux.(*ssa.AuxCall); ok && ac.RegCache != nil {
919 return *ac.Reg(&ssaop.OpcodeTable[op].Reg, s.f.Config)
920 }
921 }
922 if op == ssaop.OpMakeResult && s.f.OwnAux.RegCache != nil {
923 return *s.f.OwnAux.ResultReg(s.f.Config)
924 }
925 return ssaop.OpcodeTable[op].Reg
926 }
927
928 func (s *regAllocState) isGReg(r ssaop.Register) bool {
929 return s.f.Config.HasGReg && s.GReg == r
930 }
931
932
933 var tmpVal ssa.Value
934
935 func (s *regAllocState) regalloc(f *ssa.Func) {
936 regValLiveSet := f.NewSparseSet(f.NumValues())
937 defer f.RetSparseSet(regValLiveSet)
938 var oldSched []*ssa.Value
939 var phis []*ssa.Value
940 var phiRegs []ssaop.Register
941 var args []*ssa.Value
942
943
944 var desired desiredState
945 desiredSecondReg := map[ssa.ID][4]ssaop.Register{}
946
947
948 type dentry struct {
949 out [4]ssaop.Register
950 in [3][4]ssaop.Register
951 }
952 var dinfo []dentry
953
954 if f.Entry != f.Blocks[0] {
955 f.Fatalf("entry block must be first")
956 }
957
958 for _, b := range s.visitOrder {
959 if s.f.Pass.Debug > ssa.RegDebug {
960 fmt.Printf("Begin processing block %v\n", b)
961 }
962 s.curBlock = b
963 s.startRegsMask = ssaop.RegMask{}
964 s.usedSinceBlockStart = ssaop.RegMask{}
965 clear(desiredSecondReg)
966
967
968
969 regValLiveSet.Clear()
970 if s.live != nil {
971 for _, e := range s.live[b.ID] {
972 s.addUse(e.ID, int32(len(b.Values))+e.dist, e.pos)
973 regValLiveSet.Add(e.ID)
974 }
975 }
976 for _, v := range b.ControlValues() {
977 if s.values[v.ID].NeedReg {
978 s.addUse(v.ID, int32(len(b.Values)), b.Pos)
979 regValLiveSet.Add(v.ID)
980 }
981 }
982 if cap(s.nextCall) < len(b.Values) {
983 c := cap(s.nextCall)
984 s.nextCall = append(s.nextCall[:c], make([]int32, len(b.Values)-c)...)
985 } else {
986 s.nextCall = s.nextCall[:len(b.Values)]
987 }
988 var nextCall int32 = math.MaxInt32
989 for i := len(b.Values) - 1; i >= 0; i-- {
990 v := b.Values[i]
991 regValLiveSet.Remove(v.ID)
992 if v.Op == ssaop.OpPhi {
993
994
995
996 s.nextCall[i] = nextCall
997 continue
998 }
999 if ssaop.OpcodeTable[v.Op].Call {
1000
1001 regValLiveSet.Clear()
1002 if s.sp != 0 && s.values[s.sp].Uses != nil {
1003 regValLiveSet.Add(s.sp)
1004 }
1005 if s.sb != 0 && s.values[s.sb].Uses != nil {
1006 regValLiveSet.Add(s.sb)
1007 }
1008 nextCall = int32(i)
1009 }
1010 for _, a := range v.Args {
1011 if !s.values[a.ID].NeedReg {
1012 continue
1013 }
1014 s.addUse(a.ID, int32(i), v.Pos)
1015 regValLiveSet.Add(a.ID)
1016 }
1017 s.nextCall[i] = nextCall
1018 }
1019 if s.f.Pass.Debug > ssa.RegDebug {
1020 fmt.Printf("use distances for %s\n", b)
1021 for i := range s.values {
1022 vi := &s.values[i]
1023 u := vi.Uses
1024 if u == nil {
1025 continue
1026 }
1027 fmt.Printf(" v%d:", i)
1028 for u != nil {
1029 fmt.Printf(" %d", u.Dist)
1030 u = u.Next
1031 }
1032 fmt.Println()
1033 }
1034 }
1035
1036
1037
1038 nphi := 0
1039 for _, v := range b.Values {
1040 if v.Op != ssaop.OpPhi {
1041 break
1042 }
1043 nphi++
1044 }
1045 phis = append(phis[:0], b.Values[:nphi]...)
1046 oldSched = append(oldSched[:0], b.Values[nphi:]...)
1047 b.Values = b.Values[:0]
1048
1049
1050 if b == f.Entry {
1051
1052 if nphi > 0 {
1053 f.Fatalf("phis in entry block")
1054 }
1055 } else if len(b.Preds) == 1 {
1056
1057 s.setState(s.endRegs[b.Preds[0].B.ID])
1058 if nphi > 0 {
1059 f.Fatalf("phis in single-predecessor block")
1060 }
1061
1062
1063
1064 for r := ssaop.Register(0); r < s.numRegs; r++ {
1065 v := s.regs[r].v
1066 if v != nil && !regValLiveSet.Contains(v.ID) {
1067 s.freeReg(r)
1068 }
1069 }
1070 } else {
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084 idx := -1
1085 for i, p := range b.Preds {
1086
1087
1088 pb := p.B
1089 if s.blockOrder[pb.ID] >= s.blockOrder[b.ID] {
1090 continue
1091 }
1092 if idx == -1 {
1093 idx = i
1094 continue
1095 }
1096 pSel := b.Preds[idx].B
1097 if len(s.spillLive[pb.ID]) < len(s.spillLive[pSel.ID]) {
1098 idx = i
1099 } else if len(s.spillLive[pb.ID]) == len(s.spillLive[pSel.ID]) {
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 if pb.LikelyBranch() && !pSel.LikelyBranch() || s.blockOrder[pb.ID] < s.blockOrder[pSel.ID] {
1111 idx = i
1112 }
1113 }
1114 }
1115 if idx < 0 {
1116 f.Fatalf("bad visitOrder, no predecessor of %s has been visited before it", b)
1117 }
1118 p := b.Preds[idx].B
1119 s.setState(s.endRegs[p.ID])
1120
1121 if s.f.Pass.Debug > ssa.RegDebug {
1122 fmt.Printf("starting merge block %s with end state of %s:\n", b, p)
1123 for _, x := range s.endRegs[p.ID] {
1124 fmt.Printf(" %s: orig:%s cache:%s\n", &s.registers[x.r], x.v, x.c)
1125 }
1126 }
1127
1128
1129
1130
1131
1132 phiRegs = phiRegs[:0]
1133 var phiUsed ssaop.RegMask
1134
1135 for _, v := range phis {
1136 if !s.values[v.ID].NeedReg {
1137 phiRegs = append(phiRegs, noRegister)
1138 continue
1139 }
1140 a := v.Args[idx]
1141
1142
1143 m := s.values[a.ID].Regs.Minus(phiUsed).Intersect(s.allocatable)
1144 if !m.Empty() {
1145 r := s.pickReg(m)
1146 phiUsed = phiUsed.AddReg(r)
1147 phiRegs = append(phiRegs, r)
1148 } else {
1149 phiRegs = append(phiRegs, noRegister)
1150 }
1151 }
1152
1153
1154 for i, v := range phis {
1155 if !s.values[v.ID].NeedReg {
1156 continue
1157 }
1158 a := v.Args[idx]
1159 r := phiRegs[i]
1160 if r == noRegister {
1161 continue
1162 }
1163 if regValLiveSet.Contains(a.ID) {
1164
1165
1166
1167
1168
1169
1170
1171
1172 m := s.compatRegs(a.Type).Minus(s.used).Minus(phiUsed)
1173 if !m.Empty() && !s.values[a.ID].Rematerializeable && countRegs(s.values[a.ID].Regs) == 1 {
1174 r2 := s.pickReg(m)
1175 c := p.NewValue1(a.Pos, ssaop.OpCopy, a.Type, s.regs[r].c)
1176 if s.f.Pass.Debug > ssa.RegDebug {
1177 fmt.Printf("copy %s to %s : %s\n", a, c, &s.registers[r2])
1178 }
1179 s.setOrig(c, a)
1180 s.assignReg(r2, a, c)
1181 s.endRegs[p.ID] = append(s.endRegs[p.ID], endReg{r2, a, c})
1182 }
1183 }
1184 s.freeReg(r)
1185 }
1186
1187
1188 b.Values = append(b.Values, phis...)
1189
1190
1191
1192 for i, v := range phis {
1193 if !s.values[v.ID].NeedReg {
1194 continue
1195 }
1196 if phiRegs[i] != noRegister {
1197 continue
1198 }
1199 m := s.compatRegs(v.Type).Minus(phiUsed).Minus(s.used)
1200
1201
1202 for i, pe := range b.Preds {
1203 if i == idx {
1204 continue
1205 }
1206 ri := noRegister
1207 for _, er := range s.endRegs[pe.B.ID] {
1208 if er.v == s.orig[v.Args[i].ID] {
1209 ri = er.r
1210 break
1211 }
1212 }
1213 if ri != noRegister && m.HasReg(ri) {
1214 m = ssa.RegMaskAt(ri)
1215 break
1216 }
1217 }
1218 if !m.Empty() {
1219 r := s.pickReg(m)
1220 phiRegs[i] = r
1221 phiUsed = phiUsed.AddReg(r)
1222 }
1223 }
1224
1225
1226 for i, v := range phis {
1227 if !s.values[v.ID].NeedReg {
1228 continue
1229 }
1230 r := phiRegs[i]
1231 if r == noRegister {
1232
1233
1234 s.values[v.ID].Spill = v
1235 continue
1236 }
1237
1238 s.assignReg(r, v, v)
1239 }
1240
1241
1242 for r := ssaop.Register(0); r < s.numRegs; r++ {
1243 if phiUsed.HasReg(r) {
1244 continue
1245 }
1246 v := s.regs[r].v
1247 if v != nil && !regValLiveSet.Contains(v.ID) {
1248 s.freeReg(r)
1249 }
1250 }
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264 doomDist := int32(math.MaxInt32)
1265 if l := s.loopnest.B2L[b.ID]; l != nil && l.Header == b && l.ContainsUnavoidableCall {
1266
1267
1268 doomDist = unlikelyDistance
1269 if len(s.nextCall) > 0 {
1270 doomDist = min(doomDist, s.nextCall[0])
1271 }
1272 }
1273
1274
1275
1276
1277
1278 regList := make([]startReg, 0, 32)
1279 for r := ssaop.Register(0); r < s.numRegs; r++ {
1280 v := s.regs[r].v
1281 if v == nil {
1282 continue
1283 }
1284 if phiUsed.HasReg(r) {
1285
1286
1287 continue
1288 }
1289
1290 if s.values[v.ID].Uses.Dist >= doomDist && s.allocatable.HasReg(r) && !ssaop.OpcodeTable[v.Op].FixedReg {
1291 s.freeReg(r)
1292 continue
1293 }
1294 regList = append(regList, startReg{r, v, s.regs[r].c, s.values[v.ID].Uses.Pos})
1295 s.startRegsMask = s.startRegsMask.AddReg(r)
1296 }
1297 s.startRegs[b.ID] = make([]startReg, len(regList))
1298 copy(s.startRegs[b.ID], regList)
1299
1300 if s.f.Pass.Debug > ssa.RegDebug {
1301 fmt.Printf("after phis\n")
1302 for _, x := range s.startRegs[b.ID] {
1303 fmt.Printf(" %s: v%d\n", &s.registers[x.r], x.v.ID)
1304 }
1305 }
1306 }
1307
1308
1309 for i, v := range phis {
1310 s.curIdx = i
1311 s.dropIfUnused(v)
1312 }
1313
1314
1315 if l := len(oldSched); cap(dinfo) < l {
1316 dinfo = make([]dentry, l)
1317 } else {
1318 dinfo = dinfo[:l]
1319 clear(dinfo)
1320 }
1321
1322
1323 if s.desired != nil {
1324 desired.copy(&s.desired[b.ID])
1325 }
1326
1327
1328
1329
1330
1331
1332 for _, e := range b.Succs {
1333 succ := e.B
1334
1335 for _, x := range s.startRegs[succ.ID] {
1336 desired.add(x.v.ID, x.r)
1337 }
1338
1339 pidx := e.I
1340 for _, v := range succ.Values {
1341 if v.Op != ssaop.OpPhi {
1342 break
1343 }
1344 if !s.values[v.ID].NeedReg {
1345 continue
1346 }
1347 rp, ok := s.f.GetHome(v.ID).(*ssabase.Register)
1348 if !ok {
1349
1350
1351
1352
1353 for _, a := range v.Args {
1354 rp, ok = s.f.GetHome(a.ID).(*ssabase.Register)
1355 if ok {
1356 break
1357 }
1358 }
1359 if !ok {
1360 continue
1361 }
1362 }
1363 desired.add(v.Args[pidx].ID, ssaop.Register(rp.Num))
1364 }
1365 }
1366
1367
1368 for i := len(oldSched) - 1; i >= 0; i-- {
1369 v := oldSched[i]
1370 prefs := desired.remove(v.ID)
1371 regspec := s.regspec(v)
1372 desired.clobber(regspec.Clobbers)
1373 for _, j := range regspec.Inputs {
1374 if countRegs(j.Regs) != 1 {
1375 continue
1376 }
1377 desired.clobber(j.Regs)
1378 desired.add(v.Args[j.Idx].ID, s.pickReg(j.Regs))
1379 }
1380 if ssaop.OpcodeTable[v.Op].ResultInArg0 || v.Op == ssaop.OpAMD64ADDQconst || v.Op == ssaop.OpAMD64ADDLconst || v.Op == ssaop.OpSelect0 {
1381 if ssaop.OpcodeTable[v.Op].Commutative {
1382 desired.addList(v.Args[1].ID, prefs)
1383 }
1384 desired.addList(v.Args[0].ID, prefs)
1385 }
1386
1387 dinfo[i].out = prefs
1388 for j, a := range v.Args {
1389 if j >= len(dinfo[i].in) {
1390 break
1391 }
1392 dinfo[i].in[j] = desired.get(a.ID)
1393 }
1394 if v.Op == ssaop.OpSelect1 && prefs[0] != noRegister {
1395
1396
1397 desiredSecondReg[v.Args[0].ID] = prefs
1398 }
1399 }
1400
1401
1402 for idx, v := range oldSched {
1403 s.curIdx = nphi + idx
1404 tmpReg := noRegister
1405 if s.f.Pass.Debug > ssa.RegDebug {
1406 fmt.Printf(" processing %s\n", v.LongString())
1407 }
1408 regspec := s.regspec(v)
1409 if v.Op == ssaop.OpPhi {
1410 f.Fatalf("phi %s not at start of block", v)
1411 }
1412 if ssaop.OpcodeTable[v.Op].FixedReg {
1413 switch v.Op {
1414 case ssaop.OpSP:
1415 s.assignReg(s.SPReg, v, v)
1416 s.sp = v.ID
1417 case ssaop.OpSB:
1418 s.assignReg(s.SBReg, v, v)
1419 s.sb = v.ID
1420 case ssaop.OpARM64ZERO, ssaop.OpLOONG64ZERO, ssaop.OpMIPS64ZERO:
1421 s.assignReg(s.ZeroIntReg, v, v)
1422 case ssaop.OpAMD64Zero128, ssaop.OpAMD64Zero256, ssaop.OpAMD64Zero512:
1423 regspec := s.regspec(v)
1424 m := regspec.Outputs[0].Regs
1425 if countRegs(m) != 1 {
1426 f.Fatalf("bad fixed-register op %s", v)
1427 }
1428 s.assignReg(s.pickReg(m), v, v)
1429 default:
1430 f.Fatalf("unknown fixed-register op %s", v)
1431 }
1432 b.Values = append(b.Values, v)
1433 s.advanceUses(v)
1434 continue
1435 }
1436 if v.Op == ssaop.OpSelect0 || v.Op == ssaop.OpSelect1 || v.Op == ssaop.OpSelectN {
1437 if s.values[v.ID].NeedReg {
1438 if v.Op == ssaop.OpSelectN {
1439 s.assignReg(ssaop.Register(s.f.GetHome(v.Args[0].ID).(ssa.LocResults)[int(v.AuxInt)].(*ssabase.Register).Num), v, v)
1440 } else {
1441 var i = 0
1442 if v.Op == ssaop.OpSelect1 {
1443 i = 1
1444 }
1445 s.assignReg(ssaop.Register(s.f.GetHome(v.Args[0].ID).(ssa.LocPair)[i].(*ssabase.Register).Num), v, v)
1446 }
1447 }
1448 b.Values = append(b.Values, v)
1449 s.advanceUses(v)
1450 continue
1451 }
1452 if v.Op == ssaop.OpGetG && s.f.Config.HasGReg {
1453
1454 if s.regs[s.GReg].v != nil {
1455 s.freeReg(s.GReg)
1456 }
1457 s.assignReg(s.GReg, v, v)
1458 b.Values = append(b.Values, v)
1459 s.advanceUses(v)
1460 continue
1461 }
1462 if v.Op == ssaop.OpArg {
1463
1464
1465
1466 s.values[v.ID].Spill = v
1467 b.Values = append(b.Values, v)
1468 s.advanceUses(v)
1469 continue
1470 }
1471 if v.Op == ssaop.OpKeepAlive {
1472
1473 s.advanceUses(v)
1474 a := v.Args[0]
1475 vi := &s.values[a.ID]
1476 if vi.Regs.Empty() && !vi.Rematerializeable {
1477
1478
1479
1480 v.SetArg(0, s.makeSpill(a, b))
1481 } else if _, ok := a.Aux.(*ir.Name); ok && vi.Rematerializeable {
1482
1483
1484
1485 v.Op = ssaop.OpVarLive
1486 v.SetArgs1(v.Args[1])
1487 v.Aux = a.Aux
1488 } else {
1489
1490
1491
1492 v.Op = ssaop.OpCopy
1493 v.SetArgs1(v.Args[1])
1494 }
1495 b.Values = append(b.Values, v)
1496 continue
1497 }
1498 if len(regspec.Inputs) == 0 && len(regspec.Outputs) == 0 {
1499
1500 if s.doClobber && v.Op.IsCall() {
1501 s.clobberRegs(regspec.Clobbers)
1502 }
1503 s.freeRegs(regspec.Clobbers)
1504 b.Values = append(b.Values, v)
1505 s.advanceUses(v)
1506 continue
1507 }
1508
1509 if s.values[v.ID].Rematerializeable {
1510
1511
1512
1513 for _, a := range v.Args {
1514 a.Uses--
1515 }
1516 s.advanceUses(v)
1517 continue
1518 }
1519
1520 if s.f.Pass.Debug > ssa.RegDebug {
1521 fmt.Printf("value %s\n", v.LongString())
1522 fmt.Printf(" out:")
1523 for _, r := range dinfo[idx].out {
1524 if r != noRegister {
1525 fmt.Printf(" %s", &s.registers[r])
1526 }
1527 }
1528 fmt.Println()
1529 for i := 0; i < len(v.Args) && i < 3; i++ {
1530 fmt.Printf(" in%d:", i)
1531 for _, r := range dinfo[idx].in[i] {
1532 if r != noRegister {
1533 fmt.Printf(" %s", &s.registers[r])
1534 }
1535 }
1536 fmt.Println()
1537 }
1538 }
1539
1540
1541
1542
1543 args = append(args[:0], make([]*ssa.Value, len(v.Args))...)
1544 for i, a := range v.Args {
1545 if !s.values[a.ID].NeedReg {
1546 args[i] = a
1547 }
1548 }
1549 for _, i := range regspec.Inputs {
1550 mask := i.Regs
1551 if countRegs(mask) == 1 && !mask.Intersect(s.values[v.Args[i.Idx].ID].Regs).Empty() {
1552 args[i.Idx] = s.allocValToReg(v.Args[i.Idx], mask, true, v.Pos)
1553 }
1554 }
1555
1556
1557
1558
1559
1560
1561 for {
1562 freed := false
1563 for _, i := range regspec.Inputs {
1564 if args[i.Idx] != nil {
1565 continue
1566 }
1567 mask := i.Regs
1568 if countRegs(mask) == 1 && !mask.Minus(s.used).Empty() {
1569 args[i.Idx] = s.allocValToReg(v.Args[i.Idx], mask, true, v.Pos)
1570
1571
1572
1573 oldregs := s.values[v.Args[i.Idx].ID].Regs
1574 if oldregs.Minus(regspec.Clobbers).Empty() || !s.liveAfterCurrentInstruction(v.Args[i.Idx]) {
1575 s.freeRegs(oldregs.Minus(mask).Minus(s.nospill))
1576 freed = true
1577 }
1578 }
1579 }
1580 if !freed {
1581 break
1582 }
1583 }
1584
1585
1586 for _, i := range regspec.Inputs {
1587 if args[i.Idx] != nil {
1588 continue
1589 }
1590 mask := i.Regs
1591 if mask.Intersect(s.values[v.Args[i.Idx].ID].Regs).Empty() {
1592
1593 mask = mask.Intersect(s.allocatable)
1594 mask = mask.Minus(s.nospill)
1595
1596 if i.Idx < 3 {
1597 for _, r := range dinfo[idx].in[i.Idx] {
1598 if r != noRegister && mask.Minus(s.used).HasReg(r) {
1599
1600 mask = ssa.RegMaskAt(r)
1601 break
1602 }
1603 }
1604 }
1605
1606 if !mask.Minus(desired.avoid).Empty() {
1607 mask = mask.Minus(desired.avoid)
1608 }
1609 }
1610 if mask.Intersect(s.values[v.Args[i.Idx].ID].Regs).HasReg(s.SPReg) {
1611
1612
1613
1614 mask = ssa.RegMaskAt(s.SPReg)
1615 }
1616 args[i.Idx] = s.allocValToReg(v.Args[i.Idx], mask, true, v.Pos)
1617 }
1618
1619
1620
1621
1622 if ssaop.OpcodeTable[v.Op].ResultInArg0 {
1623 var m ssaop.RegMask
1624 if !s.liveAfterCurrentInstruction(v.Args[0]) {
1625
1626 goto ok
1627 }
1628 if ssaop.OpcodeTable[v.Op].Commutative && !s.liveAfterCurrentInstruction(v.Args[1]) {
1629 args[0], args[1] = args[1], args[0]
1630 goto ok
1631 }
1632 if s.values[v.Args[0].ID].Rematerializeable {
1633
1634 goto ok
1635 }
1636 if ssaop.OpcodeTable[v.Op].Commutative && s.values[v.Args[1].ID].Rematerializeable {
1637 args[0], args[1] = args[1], args[0]
1638 goto ok
1639 }
1640 if countRegs(s.values[v.Args[0].ID].Regs) >= 2 {
1641
1642 goto ok
1643 }
1644 if ssaop.OpcodeTable[v.Op].Commutative && countRegs(s.values[v.Args[1].ID].Regs) >= 2 {
1645 args[0], args[1] = args[1], args[0]
1646 goto ok
1647 }
1648
1649
1650
1651
1652
1653 m = s.compatRegs(v.Args[0].Type).Minus(s.used)
1654 if m.Empty() {
1655
1656
1657
1658
1659 goto ok
1660 }
1661
1662
1663 for _, r := range dinfo[idx].out {
1664 if r != noRegister && m.Intersect(regspec.Outputs[0].Regs).HasReg(r) {
1665 m = ssa.RegMaskAt(r)
1666 args[0] = s.allocValToReg(v.Args[0], m, true, v.Pos)
1667
1668
1669 goto ok
1670 }
1671 }
1672
1673
1674 for _, r := range dinfo[idx].in[0] {
1675 if r != noRegister && m.HasReg(r) {
1676 m = ssa.RegMaskAt(r)
1677 s.allocValToReg(v.Args[0], m, true, v.Pos)
1678
1679
1680 goto ok
1681 }
1682 }
1683 if ssaop.OpcodeTable[v.Op].Commutative {
1684 for _, r := range dinfo[idx].in[1] {
1685 if r != noRegister && m.HasReg(r) {
1686 m = ssa.RegMaskAt(r)
1687 s.allocValToReg(v.Args[1], m, true, v.Pos)
1688 args[0], args[1] = args[1], args[0]
1689 goto ok
1690 }
1691 }
1692 }
1693
1694
1695 if !m.Minus(desired.avoid).Empty() {
1696 m = m.Minus(desired.avoid)
1697 }
1698
1699 c := s.allocValToReg(v.Args[0], m, true, v.Pos)
1700
1701
1702
1703
1704 if regspec.Outputs[0].Regs.HasReg(ssaop.Register(s.f.GetHome(c.ID).(*ssabase.Register).Num)) {
1705 if rp, ok := s.f.GetHome(args[0].ID).(*ssabase.Register); ok {
1706 r := ssaop.Register(rp.Num)
1707 for _, r2 := range dinfo[idx].in[0] {
1708 if r == r2 {
1709 args[0] = c
1710 break
1711 }
1712 }
1713 }
1714 }
1715 }
1716 ok:
1717 for i := 0; i < 2; i++ {
1718 if !(i == 0 && regspec.ClobbersArg0 || i == 1 && regspec.ClobbersArg1) {
1719 continue
1720 }
1721 if !s.liveAfterCurrentInstruction(v.Args[i]) {
1722
1723 continue
1724 }
1725 if s.values[v.Args[i].ID].Rematerializeable {
1726
1727 continue
1728 }
1729 if countRegs(s.values[v.Args[i].ID].Regs) >= 2 {
1730
1731 continue
1732 }
1733
1734 m := s.compatRegs(v.Args[i].Type).Minus(s.used)
1735 if m.Empty() {
1736
1737
1738
1739
1740 continue
1741 }
1742
1743 s.allocValToReg(v.Args[i], m, true, v.Pos)
1744 }
1745
1746
1747
1748
1749
1750
1751
1752 if ssaop.OpcodeTable[v.Op].NeedIntTemp {
1753 m := s.allocatable.Intersect(s.f.Config.GpRegMask)
1754 for _, out := range regspec.Outputs {
1755 if countRegs(out.Regs) == 1 {
1756 m = m.Minus(out.Regs)
1757 }
1758 }
1759 if !m.Minus(desired.avoid).Minus(s.nospill).Empty() {
1760 m = m.Minus(desired.avoid)
1761 }
1762 tmpReg = s.allocReg(m, &tmpVal)
1763 s.nospill = s.nospill.AddReg(tmpReg)
1764 s.tmpused = s.tmpused.AddReg(tmpReg)
1765 }
1766
1767 if regspec.ClobbersArg0 {
1768 s.freeReg(ssaop.Register(s.f.GetHome(args[0].ID).(*ssabase.Register).Num))
1769 }
1770 if regspec.ClobbersArg1 && !(regspec.ClobbersArg0 && s.f.GetHome(args[0].ID) == s.f.GetHome(args[1].ID)) {
1771 s.freeReg(ssaop.Register(s.f.GetHome(args[1].ID).(*ssabase.Register).Num))
1772 }
1773
1774
1775
1776
1777
1778 if !ssaop.OpcodeTable[v.Op].ResultNotInArgs {
1779 s.tmpused = s.nospill
1780 s.nospill = ssaop.RegMask{}
1781 s.advanceUses(v)
1782 }
1783
1784
1785 if s.doClobber && v.Op.IsCall() {
1786
1787
1788 s.clobberRegs(regspec.Clobbers.Minus(s.tmpused).Minus(s.nospill))
1789 }
1790 s.freeRegs(regspec.Clobbers)
1791 s.tmpused = s.tmpused.Union(regspec.Clobbers)
1792
1793
1794 {
1795 outRegs := noRegisters
1796 maxOutIdx := -1
1797 var used ssaop.RegMask
1798 if tmpReg != noRegister {
1799
1800
1801 used = used.AddReg(tmpReg)
1802 }
1803 for _, out := range regspec.Outputs {
1804 if out.Regs.Empty() {
1805 continue
1806 }
1807 mask := out.Regs.Intersect(s.allocatable).Minus(used)
1808 if mask.Empty() {
1809 s.f.Fatalf("can't find any output register %s", v.LongString())
1810 }
1811 if ssaop.OpcodeTable[v.Op].ResultInArg0 && out.Idx == 0 {
1812 if !ssaop.OpcodeTable[v.Op].Commutative {
1813
1814 r := ssaop.Register(s.f.GetHome(args[0].ID).(*ssabase.Register).Num)
1815 if !mask.HasReg(r) {
1816 s.f.Fatalf("resultInArg0 value's input %v cannot be an output of %s", s.f.GetHome(args[0].ID).(*ssabase.Register), v.LongString())
1817 }
1818 mask = ssa.RegMaskAt(r)
1819 } else {
1820
1821 r0 := ssaop.Register(s.f.GetHome(args[0].ID).(*ssabase.Register).Num)
1822 r1 := ssaop.Register(s.f.GetHome(args[1].ID).(*ssabase.Register).Num)
1823
1824 found := false
1825 for _, r := range dinfo[idx].out {
1826 if (r == r0 || r == r1) && mask.Minus(s.used).HasReg(r) {
1827 mask = ssa.RegMaskAt(r)
1828 found = true
1829 if r == r1 {
1830 args[0], args[1] = args[1], args[0]
1831 }
1832 break
1833 }
1834 }
1835 if !found {
1836
1837 mask = ssa.RegMaskAt(r0)
1838 }
1839 }
1840 }
1841 if out.Idx == 0 {
1842 for _, r := range dinfo[idx].out {
1843 if r != noRegister && mask.Minus(s.used).HasReg(r) {
1844
1845 mask = ssa.RegMaskAt(r)
1846 break
1847 }
1848 }
1849 }
1850 if out.Idx == 1 {
1851 if prefs, ok := desiredSecondReg[v.ID]; ok {
1852 for _, r := range prefs {
1853 if r != noRegister && mask.Minus(s.used).HasReg(r) {
1854
1855 mask = ssa.RegMaskAt(r)
1856 break
1857 }
1858 }
1859 }
1860 }
1861
1862 if !mask.Minus(desired.avoid).Minus(s.nospill).Minus(s.used).Empty() {
1863 mask = mask.Minus(desired.avoid)
1864 }
1865 r := s.allocReg(mask, v)
1866 if out.Idx > maxOutIdx {
1867 maxOutIdx = out.Idx
1868 }
1869 outRegs[out.Idx] = r
1870 used = used.AddReg(r)
1871 s.tmpused = s.tmpused.AddReg(r)
1872 }
1873
1874 if v.Type.IsTuple() {
1875 var outLocs ssa.LocPair
1876 if r := outRegs[0]; r != noRegister {
1877 outLocs[0] = &s.registers[r]
1878 }
1879 if r := outRegs[1]; r != noRegister {
1880 outLocs[1] = &s.registers[r]
1881 }
1882 s.f.SetHome(v, outLocs)
1883
1884 } else if v.Type.IsResults() {
1885
1886 outLocs := make(ssa.LocResults, maxOutIdx+1, maxOutIdx+1)
1887 for i := 0; i <= maxOutIdx; i++ {
1888 if r := outRegs[i]; r != noRegister {
1889 outLocs[i] = &s.registers[r]
1890 }
1891 }
1892 s.f.SetHome(v, outLocs)
1893 } else {
1894 if r := outRegs[0]; r != noRegister {
1895 s.assignReg(r, v, v)
1896 }
1897 }
1898 if tmpReg != noRegister {
1899
1900 if s.f.TempRegs == nil {
1901 s.f.TempRegs = map[ssa.ID]*ssabase.Register{}
1902 }
1903 s.f.TempRegs[v.ID] = &s.registers[tmpReg]
1904 }
1905 }
1906
1907
1908 if ssaop.OpcodeTable[v.Op].ResultNotInArgs {
1909 s.nospill = ssaop.RegMask{}
1910 s.advanceUses(v)
1911 }
1912 s.tmpused = ssaop.RegMask{}
1913
1914
1915 for i, a := range args {
1916 v.SetArg(i, a)
1917 }
1918 b.Values = append(b.Values, v)
1919 s.dropIfUnused(v)
1920 }
1921
1922
1923
1924 controls := append(make([]*ssa.Value, 0, 2), b.ControlValues()...)
1925
1926
1927 for i, v := range b.ControlValues() {
1928 if !s.values[v.ID].NeedReg {
1929 continue
1930 }
1931 if s.f.Pass.Debug > ssa.RegDebug {
1932 fmt.Printf(" processing control %s\n", v.LongString())
1933 }
1934
1935
1936
1937 b.ReplaceControl(i, s.allocValToReg(v, s.compatRegs(v.Type), false, b.Pos))
1938 }
1939
1940
1941
1942 for _, v := range controls {
1943 vi := &s.values[v.ID]
1944 if !vi.NeedReg {
1945 continue
1946 }
1947
1948 u := vi.Uses
1949 vi.Uses = u.Next
1950 if u.Next == nil {
1951 s.freeRegs(vi.Regs)
1952 }
1953 u.Next = s.freeUseRecords
1954 s.freeUseRecords = u
1955 }
1956
1957
1958
1959
1960 if len(b.Succs) == 1 {
1961 if s.f.Config.HasGReg && s.regs[s.GReg].v != nil {
1962 s.freeReg(s.GReg)
1963 }
1964 if s.blockOrder[b.ID] > s.blockOrder[b.Succs[0].B.ID] {
1965
1966 goto badloop
1967 }
1968
1969 top := b.Succs[0].B
1970 loop := s.loopnest.B2L[top.ID]
1971 if loop == nil || loop.Header != top || loop.ContainsUnavoidableCall {
1972 goto badloop
1973 }
1974
1975
1976 phiArgs := regValLiveSet
1977 phiArgs.Clear()
1978 for _, v := range b.Succs[0].B.Values {
1979 if v.Op == ssaop.OpPhi {
1980 phiArgs.Add(v.Args[b.Succs[0].I].ID)
1981 }
1982 }
1983
1984
1985
1986
1987 var likelyUsedRegs ssaop.RegMask
1988 for _, live := range s.live[b.ID] {
1989 if live.dist < unlikelyDistance {
1990 likelyUsedRegs = likelyUsedRegs.Union(s.values[live.ID].Regs)
1991 }
1992 }
1993
1994
1995
1996 for _, live := range s.live[b.ID] {
1997 if live.dist >= unlikelyDistance {
1998
1999 continue
2000 }
2001 vid := live.ID
2002 vi := &s.values[vid]
2003 v := s.orig[vid]
2004 if phiArgs.Contains(vid) {
2005
2006
2007
2008
2009 if !vi.Regs.Intersect(s.compatRegs(v.Type)).Empty() {
2010 continue
2011 }
2012 } else {
2013 if !vi.Regs.Empty() {
2014 continue
2015 }
2016 if vi.Rematerializeable {
2017
2018
2019
2020
2021
2022
2023
2024 continue
2025 }
2026 }
2027 if vi.Rematerializeable && s.f.Config.Ctxt.Arch.Arch == sys.ArchWasm {
2028 continue
2029 }
2030
2031
2032 m := s.compatRegs(v.Type).Minus(likelyUsedRegs)
2033 if m.Empty() {
2034
2035 continue
2036 }
2037
2038
2039 outerloop:
2040 for _, e := range desired.entries {
2041 if e.ID != v.ID {
2042 continue
2043 }
2044 for _, r := range e.regs {
2045 if r != noRegister && m.HasReg(r) {
2046 m = ssa.RegMaskAt(r)
2047 break outerloop
2048 }
2049 }
2050 }
2051 if !m.Minus(desired.avoid).Empty() {
2052 m = m.Minus(desired.avoid)
2053 }
2054 s.allocValToReg(v, m, false, b.Pos)
2055 likelyUsedRegs = likelyUsedRegs.Union(s.values[v.ID].Regs)
2056 }
2057 }
2058 badloop:
2059 ;
2060
2061
2062
2063 k := 0
2064 for r := ssaop.Register(0); r < s.numRegs; r++ {
2065 v := s.regs[r].v
2066 if v == nil {
2067 continue
2068 }
2069 k++
2070 }
2071 regList := make([]endReg, 0, k)
2072 for r := ssaop.Register(0); r < s.numRegs; r++ {
2073 v := s.regs[r].v
2074 if v == nil {
2075 continue
2076 }
2077 regList = append(regList, endReg{r, v, s.regs[r].c})
2078 }
2079 s.endRegs[b.ID] = regList
2080
2081 if checkEnabled {
2082 regValLiveSet.Clear()
2083 if s.live != nil {
2084 for _, x := range s.live[b.ID] {
2085 regValLiveSet.Add(x.ID)
2086 }
2087 }
2088 for r := ssaop.Register(0); r < s.numRegs; r++ {
2089 v := s.regs[r].v
2090 if v == nil {
2091 continue
2092 }
2093 if !regValLiveSet.Contains(v.ID) {
2094 s.f.Fatalf("val %s is in reg but not live at end of %s", v, b)
2095 }
2096 }
2097 }
2098
2099
2100
2101
2102
2103 if s.live != nil {
2104 for _, e := range s.live[b.ID] {
2105 vi := &s.values[e.ID]
2106 if !vi.Regs.Empty() {
2107
2108 continue
2109 }
2110 if vi.Rematerializeable {
2111
2112 continue
2113 }
2114 if s.f.Pass.Debug > ssa.RegDebug {
2115 fmt.Printf("live-at-end spill for %s at %s\n", s.orig[e.ID], b)
2116 }
2117 spill := s.makeSpill(s.orig[e.ID], b)
2118 s.spillLive[b.ID] = append(s.spillLive[b.ID], spill.ID)
2119 }
2120
2121
2122
2123
2124 for _, e := range s.live[b.ID] {
2125 u := s.values[e.ID].Uses
2126 if u == nil {
2127 f.Fatalf("live at end, no uses v%d", e.ID)
2128 }
2129 if u.Next != nil {
2130 f.Fatalf("live at end, too many uses v%d", e.ID)
2131 }
2132 s.values[e.ID].Uses = nil
2133 u.Next = s.freeUseRecords
2134 s.freeUseRecords = u
2135 }
2136 }
2137
2138
2139
2140
2141
2142
2143
2144 if c := countRegs(s.startRegsMask); c != len(s.startRegs[b.ID]) {
2145 regs := make([]startReg, 0, c)
2146 for _, sr := range s.startRegs[b.ID] {
2147 if !s.startRegsMask.HasReg(sr.r) {
2148 continue
2149 }
2150 regs = append(regs, sr)
2151 }
2152 s.startRegs[b.ID] = regs
2153 }
2154 }
2155
2156
2157 s.placeSpills()
2158
2159
2160
2161 stacklive := stackalloc(s.f, s.spillLive)
2162
2163
2164 s.shuffle(stacklive)
2165
2166
2167
2168
2169
2170 for {
2171 progress := false
2172 for _, b := range f.Blocks {
2173 for _, v := range b.Values {
2174 if v.Uses == 0 && (v.Op == ssaop.OpLoadReg || v.Op == ssaop.OpCopy) {
2175 if s.f.Pass.Debug > ssa.RegDebug {
2176 fmt.Printf("delete unused value %s\n", v.LongString())
2177 }
2178 v.ResetArgs()
2179 f.FreeValue(v)
2180 progress = true
2181 }
2182 }
2183 }
2184 if !progress {
2185 break
2186 }
2187 }
2188
2189 for _, b := range s.visitOrder {
2190 i := 0
2191 for _, v := range b.Values {
2192 if v.Op == ssaop.OpInvalid {
2193 continue
2194 }
2195 b.Values[i] = v
2196 i++
2197 }
2198 b.Values = b.Values[:i]
2199 }
2200 }
2201
2202 func (s *regAllocState) placeSpills() {
2203 mustBeFirst := func(op ssaop.Op) bool {
2204 return op.IsLoweredGetClosurePtr() || op == ssaop.OpPhi || op == ssaop.OpArgIntReg || op == ssaop.OpArgFloatReg
2205 }
2206
2207
2208
2209 start := map[ssa.ID][]*ssa.Value{}
2210
2211
2212 after := map[ssa.ID][]*ssa.Value{}
2213
2214 for i := range s.values {
2215 vi := s.values[i]
2216 spill := vi.Spill
2217 if spill == nil {
2218 continue
2219 }
2220 if spill.Block != nil {
2221
2222
2223 continue
2224 }
2225 v := s.orig[i]
2226
2227
2228
2229
2230
2231 if v == nil {
2232 panic(fmt.Errorf("nil v, s.orig[%d], vi = %v, spill = %s", i, vi, spill.LongString()))
2233 }
2234 best := v.Block
2235 bestArg := v
2236 var bestDepth int16
2237 if s.loopnest != nil && s.loopnest.B2L[best.ID] != nil {
2238 bestDepth = s.loopnest.B2L[best.ID].Depth
2239 }
2240 b := best
2241 const maxSpillSearch = 100
2242 for i := 0; i < maxSpillSearch; i++ {
2243
2244
2245 p := b
2246 b = nil
2247 for c := s.sdom.Child(p); c != nil && i < maxSpillSearch; c, i = s.sdom.Sibling(c), i+1 {
2248 if s.sdom[c.ID].Entry <= vi.RestoreMin && s.sdom[c.ID].Exit >= vi.RestoreMax {
2249
2250 b = c
2251 break
2252 }
2253 }
2254 if b == nil {
2255
2256 break
2257 }
2258
2259 var depth int16
2260 if s.loopnest != nil && s.loopnest.B2L[b.ID] != nil {
2261 depth = s.loopnest.B2L[b.ID].Depth
2262 }
2263 if depth > bestDepth {
2264
2265 continue
2266 }
2267
2268
2269
2270 if len(b.Preds) == 1 {
2271 for _, e := range s.endRegs[b.Preds[0].B.ID] {
2272 if e.v == v {
2273
2274 best = b
2275 bestArg = e.c
2276 bestDepth = depth
2277 break
2278 }
2279 }
2280 } else {
2281 for _, e := range s.startRegs[b.ID] {
2282 if e.v == v {
2283
2284 best = b
2285 bestArg = e.c
2286 bestDepth = depth
2287 break
2288 }
2289 }
2290 }
2291 }
2292
2293
2294 spill.Block = best
2295 spill.AddArg(bestArg)
2296 if best == v.Block && !mustBeFirst(v.Op) {
2297
2298 after[v.ID] = append(after[v.ID], spill)
2299 } else {
2300
2301 start[best.ID] = append(start[best.ID], spill)
2302 }
2303 }
2304
2305
2306 var oldSched []*ssa.Value
2307 for _, b := range s.visitOrder {
2308 nfirst := 0
2309 for _, v := range b.Values {
2310 if !mustBeFirst(v.Op) {
2311 break
2312 }
2313 nfirst++
2314 }
2315 oldSched = append(oldSched[:0], b.Values[nfirst:]...)
2316 b.Values = b.Values[:nfirst]
2317 b.Values = append(b.Values, start[b.ID]...)
2318 for _, v := range oldSched {
2319 b.Values = append(b.Values, v)
2320 b.Values = append(b.Values, after[v.ID]...)
2321 }
2322 }
2323 }
2324
2325
2326 func (s *regAllocState) shuffle(stacklive [][]ssa.ID) {
2327 var e edgeState
2328 e.s = s
2329 e.cache = map[ssa.ID][]*ssa.Value{}
2330 e.contents = map[ssa.Location]contentRecord{}
2331 if s.f.Pass.Debug > ssa.RegDebug {
2332 fmt.Printf("shuffle %s\n", s.f.Name)
2333 fmt.Println(s.f.String())
2334 }
2335
2336 e.exposedDownward = make(map[*ssa.Value]contentRecord)
2337 for _, b := range s.visitOrder {
2338 switch len(b.Preds) {
2339 case 0:
2340
2341 case 1:
2342
2343 p := b.Preds[0].B
2344 for _, r := range s.endRegs[p.ID] {
2345 _, ok := e.exposedDownward[r.c]
2346 if !ok {
2347 e.exposedDownward[r.c] = contentRecord{r.v.ID, r.c, true, src.NoXPos}
2348 }
2349 }
2350 default:
2351 e.b = b
2352 for i, edge := range b.Preds {
2353 p := edge.B
2354 e.p = p
2355 e.setup(i, s.endRegs[p.ID], s.startRegs[b.ID], stacklive[p.ID])
2356 e.process()
2357 }
2358 }
2359 }
2360
2361 ed := make([]contentRecord, 0, len(e.exposedDownward))
2362 for _, f := range e.exposedDownward {
2363 ed = append(ed, f)
2364 }
2365
2366
2367 slices.SortFunc(ed, func(a, b contentRecord) int {
2368 return cmp.Or(cmp.Compare(a.vid, b.vid), cmp.Compare(a.c.ID, b.c.ID))
2369 })
2370
2371 e.reestablishSSA(ed)
2372
2373 if s.f.Pass.Debug > ssa.RegDebug {
2374 fmt.Printf("post shuffle %s\n", s.f.Name)
2375 fmt.Println(s.f.String())
2376 }
2377 }
2378
2379
2380
2381 func (e *edgeState) reestablishSSA(exposedDownwards []contentRecord) {
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413 f := e.s.f
2414 if f.Pass.Debug > ssa.RegDebug {
2415 fmt.Printf("pre-reestablish %s\n", f.Name)
2416 fmt.Println(f.String())
2417 }
2418
2419
2420 type homedValue struct {
2421 orig *ssa.Value
2422 loc ssa.Location
2423 uses []useSpec
2424
2425
2426 defs []*ssa.Value
2427
2428
2429
2430 forceRename bool
2431 }
2432 type idLoc struct {
2433 id ssa.ID
2434 loc ssa.Location
2435 }
2436 var homedVals []*homedValue
2437 valsToHomed := make(map[idLoc]*homedValue)
2438 for _, c := range exposedDownwards {
2439 loc := f.GetHome(c.c.ID)
2440 homed := valsToHomed[idLoc{c.vid, loc}]
2441 if homed == nil {
2442 homed = new(homedValue)
2443 homed.orig = e.s.orig[c.vid]
2444 homed.loc = loc
2445 valsToHomed[idLoc{c.vid, loc}] = homed
2446 homedVals = append(homedVals, homed)
2447 }
2448 if c.final {
2449 homed.defs = append(homed.defs, c.c)
2450 } else {
2451 homed.forceRename = true
2452 }
2453 valsToHomed[idLoc{c.c.ID, loc}] = homed
2454 }
2455
2456
2457
2458 getHomed := func(b *ssa.Block, v *ssa.Value, arg *ssa.Value) *homedValue {
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468 if b == arg.Block && (v == nil || v.Op != ssaop.OpPhi) {
2469 return nil
2470 }
2471 loc := f.GetHome(arg.ID)
2472 homed := valsToHomed[idLoc{arg.ID, loc}]
2473 if homed == nil {
2474 return nil
2475 }
2476
2477 if len(homed.defs) == 1 && !homed.forceRename {
2478 return nil
2479 }
2480 return homed
2481 }
2482
2483 for _, b := range f.Blocks {
2484 for _, v := range b.Values {
2485 for i, a := range v.Args {
2486 homed := getHomed(b, v, a)
2487 if homed == nil {
2488 continue
2489 }
2490 homed.uses = append(homed.uses, useSpec{v, i})
2491 }
2492 }
2493 for i, c := range b.ControlValues() {
2494 homed := getHomed(b, nil, c)
2495 if homed == nil {
2496 continue
2497 }
2498 homed.uses = append(homed.uses, useSpec{b, i})
2499 }
2500 }
2501
2502
2503
2504
2505 downwardDef := f.Cache.AllocValueSlice(f.NumBlocks())
2506 defer f.Cache.FreeValueSlice(downwardDef)
2507
2508
2509
2510
2511
2512 topDef := f.Cache.AllocValueSlice(f.NumBlocks())
2513 defer f.Cache.FreeValueSlice(topDef)
2514
2515 varDF := f.NewSparseSet(f.NumBlocks())
2516 defer f.RetSparseSet(varDF)
2517 sdom := f.Sdom()
2518 for _, homed := range homedVals {
2519
2520
2521
2522
2523 if (len(homed.defs) == 1 && !homed.forceRename) || len(homed.uses) == 0 {
2524 continue
2525 }
2526 if f.Pass.Debug > ssa.RegDebug {
2527 defv := make([]string, len(homed.defs))
2528 for i, d := range homed.defs {
2529 defv[i] = d.String()
2530 }
2531 fmt.Printf("variable: %v(%s) defs: %v uses: %v\n", homed.orig, homed.loc, defv, homed.uses)
2532 }
2533 clear(downwardDef)
2534 clear(topDef)
2535 for _, d := range homed.defs {
2536 if downwardDef[d.Block.ID] != nil {
2537 f.Fatalf("already set a downward def for b%d\nprev: %s\nreplace: %s", d.Block.ID, downwardDef[d.Block.ID].LongString(), d.LongString())
2538 }
2539 downwardDef[d.Block.ID] = d
2540 }
2541 varDF.Clear()
2542 pluckBlocks := func(yield func(*ssa.Block) bool) {
2543 for _, d := range homed.defs {
2544 if !yield(d.Block) {
2545 return
2546 }
2547 }
2548 }
2549 for d := range f.IterDomFrontierPlus(pluckBlocks) {
2550 varDF.Add(d.ID)
2551 }
2552
2553 for i := 0; i < len(homed.uses); i++ {
2554 use := homed.uses[i]
2555
2556 search := use.block()
2557 if use.op() == ssaop.OpPhi {
2558 search = search.Preds[use.index].B
2559 if downwardDef[search.ID] != nil {
2560 use.replace(downwardDef[search.ID])
2561 continue
2562 }
2563 } else if topDef[search.ID] != nil {
2564 use.replace(topDef[search.ID])
2565 continue
2566 }
2567
2568 for {
2569 if varDF.Contains(search.ID) {
2570 phi := search.NewValue0(homed.orig.Pos, ssaop.OpPhi, homed.orig.Type)
2571 f.SetHome(phi, homed.loc)
2572 if downwardDef[search.ID] == nil {
2573 downwardDef[search.ID] = phi
2574 }
2575 if topDef[search.ID] != nil {
2576 f.Fatalf("double insert of phi")
2577 }
2578 topDef[search.ID] = phi
2579 if f.Pass.Debug > ssa.RegDebug {
2580 fmt.Printf("inserting phi v%d in b%d for %v use\n", phi.ID, search.ID, use)
2581 }
2582 args := make([]*ssa.Value, len(search.Preds))
2583
2584
2585
2586
2587 for i := range args {
2588 args[i] = phi
2589 homed.uses = append(homed.uses, useSpec{phi, i})
2590 }
2591 phi.AddArgs(args...)
2592 break
2593 }
2594 search = sdom.Parent(search)
2595 if search == nil {
2596 f.Fatalf("did not find reaching definition for %v in b%d", use, use.block().ID)
2597 }
2598 if downwardDef[search.ID] != nil {
2599 break
2600 }
2601 }
2602 if f.Pass.Debug > ssa.RegDebug && downwardDef[search.ID] != use.value() {
2603 fmt.Printf("replacing v%d with v%d in %#s\n", use.value().ID, downwardDef[search.ID].ID, use)
2604 }
2605 use.replace(downwardDef[search.ID])
2606 }
2607 }
2608
2609
2610
2611
2612 for _, b := range f.Blocks {
2613 if len(b.Values) == 0 {
2614 continue
2615 }
2616 if b.Values[len(b.Values)-1].Op == ssaop.OpPhi {
2617 slices.SortStableFunc(b.Values, func(a, b *ssa.Value) int {
2618 as, bs := 0, 0
2619 if a.Op == ssaop.OpPhi {
2620 as = -1
2621 }
2622 if b.Op == ssaop.OpPhi {
2623 bs = -1
2624 }
2625 return cmp.Compare(as, bs)
2626 })
2627 }
2628 }
2629 }
2630
2631
2632
2633
2634 type useSpec struct {
2635 x any
2636 index int
2637 }
2638
2639 func (u *useSpec) replace(v *ssa.Value) {
2640 var replace **ssa.Value
2641 switch x := u.x.(type) {
2642 case *ssa.Block:
2643 replace = &x.Controls[u.index]
2644 case *ssa.Value:
2645 replace = &x.Args[u.index]
2646 }
2647 (*replace).Uses--
2648 (*replace) = v
2649 v.Uses++
2650 }
2651
2652 func (u *useSpec) block() *ssa.Block {
2653 switch x := u.x.(type) {
2654 case *ssa.Block:
2655 return x
2656 case *ssa.Value:
2657 return x.Block
2658 }
2659 panic("unreachable")
2660 }
2661
2662 func (u *useSpec) op() ssaop.Op {
2663 switch x := u.x.(type) {
2664 case *ssa.Block:
2665 return ssaop.OpInvalid
2666 case *ssa.Value:
2667 return x.Op
2668 }
2669 panic("unreachable")
2670 }
2671
2672 func (u useSpec) Format(f fmt.State, verb rune) {
2673 switch x := u.x.(type) {
2674 case *ssa.Block:
2675 fmt.Fprintf(f, "b%d(%v)", x.ID, u.index)
2676 case *ssa.Value:
2677 if f.Flag('#') {
2678 fmt.Fprint(f, x.LongString())
2679 } else {
2680 fmt.Fprintf(f, "%s(%v)", x.String(), u.index)
2681 }
2682 }
2683 }
2684
2685 func (u *useSpec) value() *ssa.Value {
2686 switch x := u.x.(type) {
2687 case *ssa.Block:
2688 return x.Controls[u.index]
2689 case *ssa.Value:
2690 return x.Args[u.index]
2691 }
2692 panic("unreachable")
2693 }
2694
2695 type edgeState struct {
2696 s *regAllocState
2697 p, b *ssa.Block
2698
2699
2700 cache map[ssa.ID][]*ssa.Value
2701 cachedVals []ssa.ID
2702
2703
2704 contents map[ssa.Location]contentRecord
2705
2706
2707 destinations []dstRecord
2708 extra []dstRecord
2709
2710
2711
2712
2713
2714
2715 exposedDownward map[*ssa.Value]contentRecord
2716
2717 usedRegs ssaop.RegMask
2718 uniqueRegs ssaop.RegMask
2719 finalRegs ssaop.RegMask
2720 rematerializeableRegs ssaop.RegMask
2721 }
2722
2723 type contentRecord struct {
2724 vid ssa.ID
2725 c *ssa.Value
2726 final bool
2727 pos src.XPos
2728 }
2729
2730 type dstRecord struct {
2731 loc ssa.Location
2732 vid ssa.ID
2733 splice **ssa.Value
2734 pos src.XPos
2735 }
2736
2737
2738 func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive []ssa.ID) {
2739 if e.s.f.Pass.Debug > ssa.RegDebug {
2740 fmt.Printf("edge %s->%s\n", e.p, e.b)
2741 }
2742
2743
2744 clear(e.cache)
2745 e.cachedVals = e.cachedVals[:0]
2746 clear(e.contents)
2747 e.usedRegs = ssaop.RegMask{}
2748 e.uniqueRegs = ssaop.RegMask{}
2749 e.finalRegs = ssaop.RegMask{}
2750 e.rematerializeableRegs = ssaop.RegMask{}
2751
2752
2753 for _, x := range srcReg {
2754 e.set(&e.s.registers[x.r], x.v.ID, x.c, false, src.NoXPos)
2755 }
2756
2757 for _, spillID := range stacklive {
2758 v := e.s.orig[spillID]
2759 spill := e.s.values[v.ID].Spill
2760 if !e.s.sdom.IsAncestorEq(spill.Block, e.p) {
2761
2762
2763
2764
2765
2766
2767
2768
2769 continue
2770 }
2771 e.set(e.s.f.GetHome(spillID), v.ID, spill, false, src.NoXPos)
2772 }
2773
2774
2775 dsts := e.destinations[:0]
2776 for _, x := range dstReg {
2777 dsts = append(dsts, dstRecord{&e.s.registers[x.r], x.v.ID, nil, x.pos})
2778 }
2779
2780 for _, v := range e.b.Values {
2781 if v.Op != ssaop.OpPhi {
2782 break
2783 }
2784 loc := e.s.f.GetHome(v.ID)
2785 if loc == nil {
2786 continue
2787 }
2788 dsts = append(dsts, dstRecord{loc, v.Args[idx].ID, &v.Args[idx], v.Pos})
2789 }
2790 e.destinations = dsts
2791
2792 if e.s.f.Pass.Debug > ssa.RegDebug {
2793 for _, vid := range e.cachedVals {
2794 a := e.cache[vid]
2795 for _, c := range a {
2796 fmt.Printf("src %s: v%d cache=%s\n", e.s.f.GetHome(c.ID), vid, c)
2797 }
2798 }
2799 for _, d := range e.destinations {
2800 fmt.Printf("dst %s: v%d\n", d.loc, d.vid)
2801 }
2802 }
2803 }
2804
2805
2806 func (e *edgeState) process() {
2807 dsts := e.destinations
2808
2809
2810 for len(dsts) > 0 {
2811 i := 0
2812 for _, d := range dsts {
2813 if !e.processDest(d.loc, d.vid, d.splice, d.pos) {
2814
2815 dsts[i] = d
2816 i++
2817 }
2818 }
2819 if i < len(dsts) {
2820
2821 dsts = dsts[:i]
2822
2823
2824 dsts = append(dsts, e.extra...)
2825 e.extra = e.extra[:0]
2826 continue
2827 }
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851 d := dsts[0]
2852 loc := d.loc
2853 vid := e.contents[loc].vid
2854 c := e.contents[loc].c
2855 r := e.findRegFor(c.Type)
2856 if e.s.f.Pass.Debug > ssa.RegDebug {
2857 fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc, c)
2858 }
2859 e.erase(r)
2860 pos := d.pos.WithNotStmt()
2861 if _, isReg := loc.(*ssabase.Register); isReg {
2862 c = e.p.NewValue1(pos, ssaop.OpCopy, c.Type, c)
2863 } else {
2864 c = e.p.NewValue1(pos, ssaop.OpLoadReg, c.Type, c)
2865 }
2866 e.set(r, vid, c, false, pos)
2867 if c.Op == ssaop.OpLoadReg && e.s.isGReg(ssaop.Register(r.(*ssabase.Register).Num)) {
2868 e.s.f.Fatalf("process.OpLoadReg targeting g: " + c.LongString())
2869 }
2870 }
2871 }
2872
2873
2874
2875 func (e *edgeState) processDest(loc ssa.Location, vid ssa.ID, splice **ssa.Value, pos src.XPos) bool {
2876 pos = pos.WithNotStmt()
2877 occupant := e.contents[loc]
2878 if occupant.vid == vid {
2879
2880 cr := contentRecord{vid, occupant.c, true, pos}
2881 e.contents[loc] = cr
2882 _, ok := e.exposedDownward[occupant.c]
2883 if !ok {
2884 e.exposedDownward[occupant.c] = cr
2885 }
2886
2887 if splice != nil {
2888 (*splice).Uses--
2889 *splice = occupant.c
2890 occupant.c.Uses++
2891 }
2892 return true
2893 }
2894
2895
2896 if len(e.cache[occupant.vid]) == 1 && !e.s.values[occupant.vid].Rematerializeable && !ssaop.OpcodeTable[e.s.orig[occupant.vid].Op].FixedReg {
2897
2898
2899 return false
2900 }
2901
2902
2903 v := e.s.orig[vid]
2904 var c *ssa.Value
2905 var src ssa.Location
2906 if e.s.f.Pass.Debug > ssa.RegDebug {
2907 fmt.Printf("moving v%d to %s\n", vid, loc)
2908 fmt.Printf("sources of v%d:", vid)
2909 }
2910 if ssaop.OpcodeTable[v.Op].FixedReg {
2911 c = v
2912 src = e.s.f.GetHome(v.ID)
2913 } else {
2914 for _, w := range e.cache[vid] {
2915 h := e.s.f.GetHome(w.ID)
2916 if e.s.f.Pass.Debug > ssa.RegDebug {
2917 fmt.Printf(" %s:%s", h, w)
2918 }
2919 _, isreg := h.(*ssabase.Register)
2920 if src == nil || isreg {
2921 c = w
2922 src = h
2923 }
2924 }
2925 }
2926 if e.s.f.Pass.Debug > ssa.RegDebug {
2927 if src != nil {
2928 fmt.Printf(" [use %s]\n", src)
2929 } else {
2930 fmt.Printf(" [no source]\n")
2931 }
2932 }
2933 _, dstReg := loc.(*ssabase.Register)
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945 e.erase(loc)
2946 var x *ssa.Value
2947 if c == nil || e.s.values[vid].Rematerializeable {
2948 if !e.s.values[vid].Rematerializeable {
2949 e.s.f.Fatalf("can't find source for %s->%s: %s\n", e.p, e.b, v.LongString())
2950 }
2951 if dstReg {
2952
2953
2954
2955
2956 if !e.s.regspec(v).Outputs[0].Regs.HasReg(ssaop.Register(loc.(*ssabase.Register).Num)) {
2957 _, srcReg := src.(*ssabase.Register)
2958 if srcReg {
2959
2960
2961 x = e.p.NewValue1(pos, ssaop.OpCopy, c.Type, c)
2962 } else {
2963
2964 x = v.CopyInto(e.p)
2965 r := e.findRegFor(x.Type)
2966 e.erase(r)
2967
2968 e.set(r, vid, x, false, pos)
2969
2970 x = e.p.NewValue1(pos, ssaop.OpCopy, x.Type, x)
2971 }
2972 } else {
2973 x = v.CopyInto(e.p)
2974 }
2975 } else {
2976
2977
2978 r := e.findRegFor(v.Type)
2979 e.erase(r)
2980 x = v.CopyIntoWithXPos(e.p, pos)
2981 e.set(r, vid, x, false, pos)
2982
2983
2984
2985 x = e.p.NewValue1(pos, ssaop.OpStoreReg, loc.(ssa.LocalSlot).Type, x)
2986 }
2987 } else {
2988
2989 _, srcReg := src.(*ssabase.Register)
2990 if srcReg {
2991 if dstReg {
2992 x = e.p.NewValue1(pos, ssaop.OpCopy, c.Type, c)
2993 } else {
2994 x = e.p.NewValue1(pos, ssaop.OpStoreReg, loc.(ssa.LocalSlot).Type, c)
2995 }
2996 } else {
2997 if dstReg {
2998 x = e.p.NewValue1(pos, ssaop.OpLoadReg, c.Type, c)
2999 } else {
3000
3001 r := e.findRegFor(c.Type)
3002 e.erase(r)
3003 t := e.p.NewValue1(pos, ssaop.OpLoadReg, c.Type, c)
3004 e.set(r, vid, t, false, pos)
3005 x = e.p.NewValue1(pos, ssaop.OpStoreReg, loc.(ssa.LocalSlot).Type, t)
3006 }
3007 }
3008 }
3009 e.set(loc, vid, x, true, pos)
3010 if x.Op == ssaop.OpLoadReg && e.s.isGReg(ssaop.Register(loc.(*ssabase.Register).Num)) {
3011 e.s.f.Fatalf("processDest.OpLoadReg targeting g: " + x.LongString())
3012 }
3013 if splice != nil {
3014 (*splice).Uses--
3015 *splice = x
3016 x.Uses++
3017 }
3018 return true
3019 }
3020
3021
3022 func (e *edgeState) set(loc ssa.Location, vid ssa.ID, c *ssa.Value, final bool, pos src.XPos) {
3023 e.s.f.SetHome(c, loc)
3024 cr := contentRecord{vid, c, final, pos}
3025 e.contents[loc] = cr
3026 a := e.cache[vid]
3027 if final {
3028 _, ok := e.exposedDownward[c]
3029 if !ok {
3030 e.exposedDownward[c] = cr
3031 }
3032 }
3033 if len(a) == 0 {
3034 e.cachedVals = append(e.cachedVals, vid)
3035 }
3036 a = append(a, c)
3037 e.cache[vid] = a
3038 if r, ok := loc.(*ssabase.Register); ok {
3039 if e.usedRegs.HasReg(ssaop.Register(r.Num)) {
3040 e.s.f.Fatalf("%v is already set (v%d/%v)", r, vid, c)
3041 }
3042 e.usedRegs = e.usedRegs.AddReg(ssaop.Register(r.Num))
3043 if final {
3044 e.finalRegs = e.finalRegs.AddReg(ssaop.Register(r.Num))
3045 }
3046 if len(a) == 1 {
3047 e.uniqueRegs = e.uniqueRegs.AddReg(ssaop.Register(r.Num))
3048 }
3049 if len(a) == 2 {
3050 if t, ok := e.s.f.GetHome(a[0].ID).(*ssabase.Register); ok {
3051 e.uniqueRegs = e.uniqueRegs.RemoveReg(ssaop.Register(t.Num))
3052 }
3053 }
3054 if e.s.values[vid].Rematerializeable {
3055 e.rematerializeableRegs = e.rematerializeableRegs.AddReg(ssaop.Register(r.Num))
3056 }
3057 }
3058 if e.s.f.Pass.Debug > ssa.RegDebug {
3059 fmt.Printf("%s\n", c.LongString())
3060 fmt.Printf("v%d now available in %s:%s\n", vid, loc, c)
3061 }
3062 }
3063
3064
3065 func (e *edgeState) erase(loc ssa.Location) {
3066 cr := e.contents[loc]
3067 if cr.c == nil {
3068 return
3069 }
3070 vid := cr.vid
3071
3072 if cr.final {
3073
3074
3075
3076 e.extra = append(e.extra, dstRecord{loc, cr.vid, nil, cr.pos})
3077
3078
3079
3080
3081 if cr.c.Block == e.p {
3082 ed := cr
3083 ed.final = false
3084 e.exposedDownward[cr.c] = ed
3085 }
3086 }
3087
3088
3089 a := e.cache[vid]
3090 for i, c := range a {
3091 if e.s.f.GetHome(c.ID) == loc {
3092 if e.s.f.Pass.Debug > ssa.RegDebug {
3093 fmt.Printf("v%d no longer available in %s:%s\n", vid, loc, c)
3094 }
3095 a[i], a = a[len(a)-1], a[:len(a)-1]
3096 break
3097 }
3098 }
3099 e.cache[vid] = a
3100
3101
3102 if r, ok := loc.(*ssabase.Register); ok {
3103 e.usedRegs = e.usedRegs.RemoveReg(ssaop.Register(r.Num))
3104 if cr.final {
3105 e.finalRegs = e.finalRegs.RemoveReg(ssaop.Register(r.Num))
3106 }
3107 e.rematerializeableRegs = e.rematerializeableRegs.RemoveReg(ssaop.Register(r.Num))
3108 }
3109 if len(a) == 1 {
3110 if r, ok := e.s.f.GetHome(a[0].ID).(*ssabase.Register); ok {
3111 e.uniqueRegs = e.uniqueRegs.AddReg(ssaop.Register(r.Num))
3112 }
3113 }
3114 }
3115
3116
3117 func (e *edgeState) findRegFor(typ *types.Type) ssa.Location {
3118
3119 m := e.s.compatRegs(typ)
3120
3121
3122
3123
3124
3125
3126 x := m.Minus(e.usedRegs)
3127 if !x.Empty() {
3128 return &e.s.registers[e.s.pickReg(x)]
3129 }
3130 x = m.Minus(e.uniqueRegs).Minus(e.finalRegs)
3131 if !x.Empty() {
3132 return &e.s.registers[e.s.pickReg(x)]
3133 }
3134 x = m.Minus(e.uniqueRegs)
3135 if !x.Empty() {
3136 return &e.s.registers[e.s.pickReg(x)]
3137 }
3138 x = m.Intersect(e.rematerializeableRegs)
3139 if !x.Empty() {
3140 return &e.s.registers[e.s.pickReg(x)]
3141 }
3142
3143
3144
3145 for _, vid := range e.cachedVals {
3146 a := e.cache[vid]
3147 for _, c := range a {
3148 if r, ok := e.s.f.GetHome(c.ID).(*ssabase.Register); ok && m.HasReg(ssaop.Register(r.Num)) {
3149 if !c.Rematerializeable() {
3150 x := e.p.NewValue1(c.Pos, ssaop.OpStoreReg, c.Type, c)
3151
3152 t := ssa.LocalSlot{N: e.s.f.NewLocal(c.Pos, c.Type), Type: c.Type}
3153
3154 e.set(t, vid, x, false, c.Pos)
3155 if e.s.f.Pass.Debug > ssa.RegDebug {
3156 fmt.Printf(" SPILL %s->%s %s\n", r, t, x.LongString())
3157 }
3158 }
3159
3160
3161
3162 return r
3163 }
3164 }
3165 }
3166
3167 fmt.Printf("m:%d unique:%d final:%d rematerializable:%d\n", m, e.uniqueRegs, e.finalRegs, e.rematerializeableRegs)
3168 for _, vid := range e.cachedVals {
3169 a := e.cache[vid]
3170 for _, c := range a {
3171 fmt.Printf("v%d: %s %s\n", vid, c, e.s.f.GetHome(c.ID))
3172 }
3173 }
3174 e.s.f.Fatalf("can't find empty register on edge %s->%s", e.p, e.b)
3175 return nil
3176 }
3177
3178 type liveInfo struct {
3179 ID ssa.ID
3180 dist int32
3181 pos src.XPos
3182 }
3183
3184
3185
3186
3187 func (s *regAllocState) computeLive() {
3188 f := s.f
3189
3190
3191 if len(f.Blocks) == 1 {
3192 return
3193 }
3194 po := f.Postorder()
3195 s.live = make([][]liveInfo, f.NumBlocks())
3196 s.desired = make([]desiredState, f.NumBlocks())
3197 s.loopnest = f.Loopnest()
3198
3199 rematIDs := make([]ssa.ID, 0, 64)
3200
3201 live := f.NewSparseMapPos(f.NumValues())
3202 defer f.RetSparseMapPos(live)
3203 t := f.NewSparseMapPos(f.NumValues())
3204 defer f.RetSparseMapPos(t)
3205
3206 s.loopnest.ComputeUnavoidableCalls()
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222 var loopLiveIn map[*ssa.Loop][]liveInfo
3223 var numCalls []int32
3224 if len(s.loopnest.Loops) > 0 && !s.loopnest.HasIrreducible {
3225 loopLiveIn = make(map[*ssa.Loop][]liveInfo)
3226 numCalls = f.Cache.AllocInt32Slice(f.NumBlocks())
3227 defer f.Cache.FreeInt32Slice(numCalls)
3228 }
3229
3230 for {
3231 changed := false
3232
3233 for _, b := range po {
3234
3235 live.Clear()
3236 for _, e := range s.live[b.ID] {
3237 live.Set(e.ID, e.dist, e.pos)
3238 }
3239 update := false
3240
3241 for _, e := range b.Succs {
3242 succ := e.B
3243 delta := branchDistance(b, succ)
3244 for _, v := range succ.Values {
3245 if v.Op != ssaop.OpPhi {
3246 break
3247 }
3248 arg := v.Args[e.I]
3249 if s.values[arg.ID].NeedReg && (!live.Contains(arg.ID) || delta < live.Get(arg.ID)) {
3250 live.Set(arg.ID, delta, v.Pos)
3251 update = true
3252 }
3253 }
3254 }
3255 if update {
3256 s.live[b.ID] = updateLive(live, s.live[b.ID])
3257 }
3258
3259
3260 c := live.Contents()
3261 for i := range c {
3262 c[i].Val += int32(len(b.Values))
3263 }
3264
3265
3266 for _, c := range b.ControlValues() {
3267 if s.values[c.ID].NeedReg {
3268 live.Set(c.ID, int32(len(b.Values)), b.Pos)
3269 }
3270 }
3271
3272 for i := len(b.Values) - 1; i >= 0; i-- {
3273 v := b.Values[i]
3274 live.Remove(v.ID)
3275 if v.Op == ssaop.OpPhi {
3276 continue
3277 }
3278 if ssaop.OpcodeTable[v.Op].Call {
3279 if numCalls != nil {
3280 numCalls[b.ID]++
3281 }
3282 rematIDs = rematIDs[:0]
3283 c := live.Contents()
3284 for i := range c {
3285 c[i].Val += unlikelyDistance
3286 vid := c[i].Key
3287 if s.values[vid].Rematerializeable {
3288 rematIDs = append(rematIDs, vid)
3289 }
3290 }
3291
3292
3293
3294 for _, r := range rematIDs {
3295 live.Remove(r)
3296 }
3297 }
3298 for _, a := range v.Args {
3299 if s.values[a.ID].NeedReg {
3300 live.Set(a.ID, int32(i), v.Pos)
3301 }
3302 }
3303 }
3304
3305
3306 if loopLiveIn != nil {
3307 loop := s.loopnest.B2L[b.ID]
3308 if loop != nil && loop.Header.ID == b.ID {
3309 loopLiveIn[loop] = updateLive(live, nil)
3310 }
3311 }
3312
3313
3314 for _, e := range b.Preds {
3315 p := e.B
3316 delta := branchDistance(p, b)
3317
3318
3319 t.Clear()
3320 for _, e := range s.live[p.ID] {
3321 t.Set(e.ID, e.dist, e.pos)
3322 }
3323 update := false
3324
3325
3326 for _, e := range live.Contents() {
3327 d := e.Val + delta
3328 if !t.Contains(e.Key) || d < t.Get(e.Key) {
3329 update = true
3330 t.Set(e.Key, d, e.Pos)
3331 }
3332 }
3333
3334 if !update {
3335 continue
3336 }
3337 s.live[p.ID] = updateLive(t, s.live[p.ID])
3338 changed = true
3339 }
3340 }
3341
3342
3343
3344 if !changed {
3345 break
3346 }
3347
3348
3349
3350 if loopLiveIn != nil {
3351 break
3352 }
3353
3354
3355 if len(s.loopnest.Loops) == 0 {
3356 break
3357 }
3358 }
3359 if f.Pass.Debug > ssa.RegDebug {
3360 s.debugPrintLive("after dfs walk", f, s.live, s.desired)
3361 }
3362
3363
3364
3365 if loopLiveIn == nil {
3366 s.computeDesired()
3367 return
3368 }
3369
3370
3371
3372
3373
3374 loops := slices.Clone(s.loopnest.Loops)
3375 slices.SortFunc(loops, func(a, b *ssa.Loop) int {
3376 return cmp.Compare(a.Depth, b.Depth)
3377 })
3378
3379 loopset := f.NewSparseMapPos(f.NumValues())
3380 defer f.RetSparseMapPos(loopset)
3381 for _, loop := range loops {
3382 if loop.Outer == nil {
3383 continue
3384 }
3385 livein := loopLiveIn[loop]
3386 loopset.Clear()
3387 for _, l := range livein {
3388 loopset.Set(l.ID, l.dist, l.pos)
3389 }
3390 update := false
3391 for _, l := range loopLiveIn[loop.Outer] {
3392 if !loopset.Contains(l.ID) {
3393 loopset.Set(l.ID, l.dist, l.pos)
3394 update = true
3395 }
3396 }
3397 if update {
3398 loopLiveIn[loop] = updateLive(loopset, livein)
3399 }
3400 }
3401
3402
3403
3404 const unknownDistance = -1
3405
3406
3407
3408
3409 for _, b := range po {
3410 loop := s.loopnest.B2L[b.ID]
3411 if loop == nil {
3412 continue
3413 }
3414 headerLive := loopLiveIn[loop]
3415 loopset.Clear()
3416 for _, l := range s.live[b.ID] {
3417 loopset.Set(l.ID, l.dist, l.pos)
3418 }
3419 update := false
3420 for _, l := range headerLive {
3421 if !loopset.Contains(l.ID) {
3422 loopset.Set(l.ID, unknownDistance, src.NoXPos)
3423 update = true
3424 }
3425 }
3426 if update {
3427 s.live[b.ID] = updateLive(loopset, s.live[b.ID])
3428 }
3429 }
3430 if f.Pass.Debug > ssa.RegDebug {
3431 s.debugPrintLive("after live loop prop", f, s.live, s.desired)
3432 }
3433
3434
3435
3436
3437 unfinishedBlocks := f.Cache.AllocBlockSlice(len(po))
3438 defer f.Cache.FreeBlockSlice(unfinishedBlocks)
3439 copy(unfinishedBlocks, po)
3440
3441 for len(unfinishedBlocks) > 0 {
3442 n := 0
3443 for _, b := range unfinishedBlocks {
3444 live.Clear()
3445 unfinishedValues := 0
3446 for _, l := range s.live[b.ID] {
3447 if l.dist == unknownDistance {
3448 unfinishedValues++
3449 }
3450 live.Set(l.ID, l.dist, l.pos)
3451 }
3452 update := false
3453 for _, e := range b.Succs {
3454 succ := e.B
3455 for _, l := range s.live[succ.ID] {
3456 if !live.Contains(l.ID) || l.dist == unknownDistance {
3457 continue
3458 }
3459 dist := int32(len(succ.Values)) + l.dist + branchDistance(b, succ)
3460 dist += numCalls[succ.ID] * unlikelyDistance
3461 val := live.Get(l.ID)
3462 switch {
3463 case val == unknownDistance:
3464 unfinishedValues--
3465 fallthrough
3466 case dist < val:
3467 update = true
3468 live.Set(l.ID, dist, l.pos)
3469 }
3470 }
3471 }
3472 if update {
3473 s.live[b.ID] = updateLive(live, s.live[b.ID])
3474 }
3475 if unfinishedValues > 0 {
3476 unfinishedBlocks[n] = b
3477 n++
3478 }
3479 }
3480 unfinishedBlocks = unfinishedBlocks[:n]
3481 }
3482
3483
3484
3485 for _, b := range f.Blocks {
3486 slices.SortFunc(s.live[b.ID], func(a, b liveInfo) int {
3487 if a.dist != b.dist {
3488 return cmp.Compare(a.dist, b.dist)
3489 }
3490 return cmp.Compare(a.ID, b.ID)
3491 })
3492 }
3493
3494 s.computeDesired()
3495
3496 if f.Pass.Debug > ssa.RegDebug {
3497 s.debugPrintLive("final", f, s.live, s.desired)
3498 }
3499 }
3500
3501
3502
3503
3504 func (s *regAllocState) computeDesired() {
3505
3506
3507
3508 var desired desiredState
3509 f := s.f
3510 po := f.Postorder()
3511 maxPreds := 0
3512 for _, b := range f.Blocks {
3513 maxPreds = max(maxPreds, len(b.Preds))
3514 }
3515
3516 phiPrefs := make([]desiredState, maxPreds)
3517 for {
3518 changed := false
3519 for _, b := range po {
3520 desired.copy(&s.desired[b.ID])
3521 for i := range b.Preds {
3522 phiPrefs[i].reset()
3523 }
3524 var headerLoop *ssa.Loop
3525 if l := s.loopnest.B2L[b.ID]; l != nil && l.Header == b {
3526 headerLoop = l
3527 }
3528
3529 i := len(b.Values) - 1
3530 for ; i >= 0; i-- {
3531 v := b.Values[i]
3532 if v.Op == ssaop.OpPhi {
3533 break
3534 }
3535 prefs := desired.remove(v.ID)
3536 regspec := s.regspec(v)
3537
3538 desired.clobber(regspec.Clobbers)
3539
3540 for _, j := range regspec.Inputs {
3541 if countRegs(j.Regs) != 1 {
3542 continue
3543 }
3544 desired.clobber(j.Regs)
3545 desired.add(v.Args[j.Idx].ID, s.pickReg(j.Regs))
3546 }
3547
3548 if ssaop.OpcodeTable[v.Op].ResultInArg0 || v.Op == ssaop.OpAMD64ADDQconst || v.Op == ssaop.OpAMD64ADDLconst || v.Op == ssaop.OpSelect0 {
3549
3550
3551
3552
3553
3554 if ssaop.OpcodeTable[v.Op].Commutative {
3555 desired.addList(v.Args[1].ID, prefs)
3556 }
3557 desired.addList(v.Args[0].ID, prefs)
3558 }
3559 }
3560 for ; i >= 0; i-- {
3561 v := b.Values[i]
3562 prefs := desired.remove(v.ID)
3563 if prefs[0] == noRegister {
3564 continue
3565 }
3566
3567
3568 for _, r := range prefs {
3569 if r != noRegister {
3570 desired.avoid = desired.avoid.Minus(ssa.RegMaskAt(r))
3571 }
3572 }
3573
3574 for pidx, a := range v.Args {
3575 if headerLoop != nil && s.loopnest.B2L[b.Preds[pidx].B.ID] == headerLoop {
3576
3577
3578 continue
3579 }
3580 phiPrefs[pidx].addList(a.ID, prefs)
3581 }
3582 }
3583 for pidx, e := range b.Preds {
3584 p := e.B
3585 changed = s.desired[p.ID].merge(&desired) || changed
3586 changed = s.desired[p.ID].merge(&phiPrefs[pidx]) || changed
3587 }
3588 }
3589 if !changed || (!s.loopnest.HasIrreducible && len(s.loopnest.Loops) == 0) {
3590 break
3591 }
3592 }
3593 }
3594
3595
3596 func updateLive(t *ssa.SparseMapPos, live []liveInfo) []liveInfo {
3597 live = live[:0]
3598 if cap(live) < t.Size() {
3599 live = make([]liveInfo, 0, t.Size())
3600 }
3601 for _, e := range t.Contents() {
3602 live = append(live, liveInfo{e.Key, e.Val, e.Pos})
3603 }
3604 return live
3605 }
3606
3607
3608
3609
3610 func branchDistance(b *ssa.Block, s *ssa.Block) int32 {
3611 if len(b.Succs) == 2 {
3612 if b.Succs[0].B == s && b.Likely == ssa.BranchLikely ||
3613 b.Succs[1].B == s && b.Likely == ssa.BranchUnlikely {
3614 return likelyDistance
3615 }
3616 if b.Succs[0].B == s && b.Likely == ssa.BranchUnlikely ||
3617 b.Succs[1].B == s && b.Likely == ssa.BranchLikely {
3618 return unlikelyDistance
3619 }
3620 }
3621
3622
3623 return normalDistance
3624 }
3625
3626 func (s *regAllocState) debugPrintLive(stage string, f *ssa.Func, live [][]liveInfo, desired []desiredState) {
3627 fmt.Printf("%s: live values at end of each block: %s\n", stage, f.Name)
3628 for _, b := range f.Blocks {
3629 s.debugPrintLiveBlock(b, live[b.ID], &desired[b.ID])
3630 }
3631 }
3632
3633 func (s *regAllocState) debugPrintLiveBlock(b *ssa.Block, live []liveInfo, desired *desiredState) {
3634 fmt.Printf(" %s:", b)
3635 slices.SortFunc(live, func(a, b liveInfo) int {
3636 return cmp.Compare(a.ID, b.ID)
3637 })
3638 for _, x := range live {
3639 fmt.Printf(" v%d(%d)", x.ID, x.dist)
3640 for _, e := range desired.entries {
3641 if e.ID != x.ID {
3642 continue
3643 }
3644 fmt.Printf("[")
3645 first := true
3646 for _, r := range e.regs {
3647 if r == noRegister {
3648 continue
3649 }
3650 if !first {
3651 fmt.Printf(",")
3652 }
3653 fmt.Print(&s.registers[r])
3654 first = false
3655 }
3656 fmt.Printf("]")
3657 }
3658 }
3659 if avoid := desired.avoid; !avoid.Empty() {
3660 fmt.Printf(" avoid=%v", s.RegMaskString(avoid))
3661 }
3662 fmt.Println()
3663 }
3664
3665
3666 type desiredState struct {
3667
3668
3669 entries []desiredStateEntry
3670
3671
3672
3673
3674 avoid ssaop.RegMask
3675 }
3676 type desiredStateEntry struct {
3677
3678 ID ssa.ID
3679
3680
3681
3682
3683
3684 regs [4]ssaop.Register
3685 }
3686
3687
3688 func (d *desiredState) get(vid ssa.ID) [4]ssaop.Register {
3689 for _, e := range d.entries {
3690 if e.ID == vid {
3691 return e.regs
3692 }
3693 }
3694 return [4]ssaop.Register{noRegister, noRegister, noRegister, noRegister}
3695 }
3696
3697
3698 func (d *desiredState) add(vid ssa.ID, r ssaop.Register) {
3699 d.avoid = d.avoid.AddReg(r)
3700 for i := range d.entries {
3701 e := &d.entries[i]
3702 if e.ID != vid {
3703 continue
3704 }
3705 if e.regs[0] == r {
3706
3707 return
3708 }
3709 for j := 1; j < len(e.regs); j++ {
3710 if e.regs[j] == r {
3711
3712 copy(e.regs[1:], e.regs[:j])
3713 e.regs[0] = r
3714 return
3715 }
3716 }
3717 copy(e.regs[1:], e.regs[:])
3718 e.regs[0] = r
3719 return
3720 }
3721 d.entries = append(d.entries, desiredStateEntry{vid, [4]ssaop.Register{r, noRegister, noRegister, noRegister}})
3722 }
3723
3724 func (d *desiredState) addList(vid ssa.ID, regs [4]ssaop.Register) {
3725
3726 for i := len(regs) - 1; i >= 0; i-- {
3727 r := regs[i]
3728 if r != noRegister {
3729 d.add(vid, r)
3730 }
3731 }
3732 }
3733
3734
3735 func (d *desiredState) clobber(m ssaop.RegMask) {
3736 for i := 0; i < len(d.entries); {
3737 e := &d.entries[i]
3738 j := 0
3739 for _, r := range e.regs {
3740 if r != noRegister && !m.HasReg(r) {
3741 e.regs[j] = r
3742 j++
3743 }
3744 }
3745 if j == 0 {
3746
3747 d.entries[i] = d.entries[len(d.entries)-1]
3748 d.entries = d.entries[:len(d.entries)-1]
3749 continue
3750 }
3751 for ; j < len(e.regs); j++ {
3752 e.regs[j] = noRegister
3753 }
3754 i++
3755 }
3756 d.avoid = d.avoid.Minus(m)
3757 }
3758
3759
3760 func (d *desiredState) reset() {
3761 d.entries = d.entries[:0]
3762 d.avoid = ssaop.RegMask{}
3763 }
3764
3765
3766 func (d *desiredState) copy(x *desiredState) {
3767 d.entries = append(d.entries[:0], x.entries...)
3768 d.avoid = x.avoid
3769 }
3770
3771
3772 func (d *desiredState) remove(vid ssa.ID) [4]ssaop.Register {
3773 for i := range d.entries {
3774 if d.entries[i].ID == vid {
3775 regs := d.entries[i].regs
3776 d.entries[i] = d.entries[len(d.entries)-1]
3777 d.entries = d.entries[:len(d.entries)-1]
3778 return regs
3779 }
3780 }
3781 return [4]ssaop.Register{noRegister, noRegister, noRegister, noRegister}
3782 }
3783
3784
3785
3786 func (d *desiredState) merge(x *desiredState) bool {
3787 oldAvoid := d.avoid
3788 d.avoid = d.avoid.Union(x.avoid)
3789
3790
3791 for _, e := range x.entries {
3792 d.addList(e.ID, e.regs)
3793 }
3794 return oldAvoid != d.avoid
3795 }
3796
View as plain text