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