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