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