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