1
2
3
4
5 package liveness
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/bitvec"
10 "cmd/compile/internal/ir"
11 "cmd/compile/internal/ssa"
12 "cmd/compile/internal/ssa/ssaop"
13 "cmd/internal/src"
14 "fmt"
15 "os"
16 "path/filepath"
17 "slices"
18 "sort"
19 "strings"
20 )
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 type MergeLocalsState struct {
42
43 vars []*ir.Name
44
45 partition map[*ir.Name][]int
46 }
47
48
49
50 type candRegion struct {
51 st, en int
52 }
53
54
55
56
57 type cstate struct {
58 fn *ir.Func
59 f *ssa.Func
60 lv *Liveness
61 cands []*ir.Name
62 nameToSlot map[*ir.Name]int32
63 regions []candRegion
64 indirectUE map[ssa.ID][]*ir.Name
65 ivs []Intervals
66 hashDeselected map[*ir.Name]bool
67 trace int
68 }
69
70
71
72
73 func MergeLocals(fn *ir.Func, f *ssa.Func) *MergeLocalsState {
74
75
76
77
78 cs := &cstate{
79 fn: fn,
80 f: f,
81 trace: base.Debug.MergeLocalsTrace,
82 }
83 cs.collectMergeCandidates()
84 if len(cs.regions) == 0 {
85 return nil
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 cs.lv = newliveness(fn, f, cs.cands, cs.nameToSlot, 0)
107 cs.lv.conservativeWrites = true
108 cs.lv.prologue()
109 cs.lv.solve()
110
111
112
113 cs.computeIntervals()
114
115
116 rv := cs.performMerging()
117 if err := rv.check(); err != nil {
118 base.FatalfAt(fn.Pos(), "invalid mergelocals state: %v", err)
119 }
120 return rv
121 }
122
123
124
125 func (mls *MergeLocalsState) Subsumed(n *ir.Name) bool {
126 if sl, ok := mls.partition[n]; ok && mls.vars[sl[0]] != n {
127 return true
128 }
129 return false
130 }
131
132
133
134 func (mls *MergeLocalsState) IsLeader(n *ir.Name) bool {
135 if sl, ok := mls.partition[n]; ok && mls.vars[sl[0]] == n {
136 return true
137 }
138 return false
139 }
140
141
142 func (mls *MergeLocalsState) Leader(n *ir.Name) *ir.Name {
143 if sl, ok := mls.partition[n]; ok {
144 if mls.vars[sl[0]] == n {
145 panic("variable is not subsumed")
146 }
147 return mls.vars[sl[0]]
148 }
149 panic("not a merge candidate")
150 }
151
152
153 func (mls *MergeLocalsState) Followers(n *ir.Name, tmp []*ir.Name) []*ir.Name {
154 tmp = tmp[:0]
155 sl, ok := mls.partition[n]
156 if !ok {
157 panic("no entry for leader")
158 }
159 if mls.vars[sl[0]] != n {
160 panic("followers invoked on subsumed var")
161 }
162 for _, k := range sl[1:] {
163 tmp = append(tmp, mls.vars[k])
164 }
165 slices.SortStableFunc(tmp, func(a, b *ir.Name) int {
166 return strings.Compare(a.Sym().Name, b.Sym().Name)
167 })
168 return tmp
169 }
170
171
172
173 func (mls *MergeLocalsState) EstSavings() (int, int) {
174 totnp := 0
175 totp := 0
176 for n := range mls.partition {
177 if mls.Subsumed(n) {
178 sz := int(n.Type().Size())
179 if n.Type().HasPointers() {
180 totp += sz
181 } else {
182 totnp += sz
183 }
184 }
185 }
186 return totnp, totp
187 }
188
189
190
191 func (mls *MergeLocalsState) check() error {
192 if mls == nil {
193 return nil
194 }
195 used := make(map[int]bool)
196 seenv := make(map[*ir.Name]int)
197 for ii, v := range mls.vars {
198 if prev, ok := seenv[v]; ok {
199 return fmt.Errorf("duplicate var %q in vslots: %d and %d\n",
200 v.Sym().Name, ii, prev)
201 }
202 seenv[v] = ii
203 }
204 for k, sl := range mls.partition {
205
206 if len(sl) < 2 {
207 return fmt.Errorf("k=%q v=%+v slice len %d invalid",
208 k.Sym().Name, sl, len(sl))
209 }
210
211 for i, v := range sl {
212 if v < 0 || v > len(mls.vars)-1 {
213 return fmt.Errorf("k=%q v=+%v slpos %d vslot %d out of range of m.v", k.Sym().Name, sl, i, v)
214 }
215 }
216 }
217 for k, sl := range mls.partition {
218 foundk := false
219 for i, v := range sl {
220 vv := mls.vars[v]
221 if i == 0 {
222 if !mls.IsLeader(vv) {
223 return fmt.Errorf("k=%s v=+%v slpos 0 vslot %d IsLeader(%q) is false should be true", k.Sym().Name, sl, v, vv.Sym().Name)
224 }
225 } else {
226 if !mls.Subsumed(vv) {
227 return fmt.Errorf("k=%s v=+%v slpos %d vslot %d Subsumed(%q) is false should be true", k.Sym().Name, sl, i, v, vv.Sym().Name)
228 }
229 if mls.Leader(vv) != mls.vars[sl[0]] {
230 return fmt.Errorf("k=%s v=+%v slpos %d vslot %d Leader(%q) got %v want %v", k.Sym().Name, sl, i, v, vv.Sym().Name, mls.Leader(vv), mls.vars[sl[0]])
231 }
232 }
233 if vv == k {
234 foundk = true
235 if used[v] {
236 return fmt.Errorf("k=%s v=+%v val slice used violation at slpos %d vslot %d", k.Sym().Name, sl, i, v)
237 }
238 used[v] = true
239 }
240 }
241 if !foundk {
242 return fmt.Errorf("k=%s v=+%v slice value missing k", k.Sym().Name, sl)
243 }
244 vl := mls.vars[sl[0]]
245 for _, v := range sl[1:] {
246 vv := mls.vars[v]
247 if vv.Type().Size() > vl.Type().Size() {
248 return fmt.Errorf("k=%s v=+%v follower %s size %d larger than leader %s size %d", k.Sym().Name, sl, vv.Sym().Name, vv.Type().Size(), vl.Sym().Name, vl.Type().Size())
249 }
250 if vv.Type().HasPointers() && !vl.Type().HasPointers() {
251 return fmt.Errorf("k=%s v=+%v follower %s hasptr=true but leader %s hasptr=false", k.Sym().Name, sl, vv.Sym().Name, vl.Sym().Name)
252 }
253 if vv.Type().Alignment() > vl.Type().Alignment() {
254 return fmt.Errorf("k=%s v=+%v follower %s align %d greater than leader %s align %d", k.Sym().Name, sl, vv.Sym().Name, vv.Type().Alignment(), vl.Sym().Name, vl.Type().Alignment())
255 }
256 }
257 }
258 for i := range used {
259 if !used[i] {
260 return fmt.Errorf("pos %d var %q unused", i, mls.vars[i])
261 }
262 }
263 return nil
264 }
265
266 func (mls *MergeLocalsState) String() string {
267 var leaders []*ir.Name
268 for n, sl := range mls.partition {
269 if n == mls.vars[sl[0]] {
270 leaders = append(leaders, n)
271 }
272 }
273 slices.SortFunc(leaders, func(a, b *ir.Name) int {
274 return strings.Compare(a.Sym().Name, b.Sym().Name)
275 })
276 var sb strings.Builder
277 for _, n := range leaders {
278 sb.WriteString(n.Sym().Name + ":")
279 sl := mls.partition[n]
280 for _, k := range sl[1:] {
281 n := mls.vars[k]
282 sb.WriteString(" " + n.Sym().Name)
283 }
284 sb.WriteString("\n")
285 }
286 return sb.String()
287 }
288
289
290
291
292
293
294
295
296
297 func (cs *cstate) collectMergeCandidates() {
298 var cands []*ir.Name
299
300
301
302
303 for _, n := range cs.fn.Dcl {
304 if !n.Used() {
305 continue
306 }
307 if !ssa.IsMergeCandidate(n) {
308 continue
309 }
310 cands = append(cands, n)
311 }
312 if len(cands) < 2 {
313 return
314 }
315
316
317 sort.SliceStable(cands, func(i, j int) bool {
318 return nameLess(cands[i], cands[j])
319 })
320
321 if cs.trace > 1 {
322 fmt.Fprintf(os.Stderr, "=-= raw cand list for func %v:\n", cs.fn)
323 for i := range cands {
324 dumpCand(cands[i], i)
325 }
326 }
327
328
329
330 initial, _ := cs.genRegions(cands)
331 if len(initial) < 2 {
332 return
333 }
334
335
336 cs.setupHashBisection(initial)
337
338
339
340
341
342 cs.cands, cs.regions = cs.populateIndirectUseTable(initial)
343 if len(cs.cands) < 2 {
344 return
345 }
346
347
348
349
350 cs.nameToSlot = make(map[*ir.Name]int32)
351 for i, n := range cs.cands {
352 cs.nameToSlot[n] = int32(i)
353 }
354
355 if cs.trace > 1 {
356 fmt.Fprintf(os.Stderr, "=-= pruned candidate list for fn %v:\n", cs.fn)
357 for i := range cs.cands {
358 dumpCand(cs.cands[i], i)
359 }
360 }
361 }
362
363
364
365 func (cs *cstate) genRegions(cands []*ir.Name) ([]*ir.Name, []candRegion) {
366 var pruned []*ir.Name
367 var regions []candRegion
368 st := 0
369 for {
370 en := nextRegion(cands, st)
371 if en == -1 {
372 break
373 }
374 if st == en {
375
376 st++
377 continue
378 }
379 pst := len(pruned)
380 pen := pst + (en - st)
381 if cs.trace > 1 {
382 fmt.Fprintf(os.Stderr, "=-= addregion st=%d en=%d: add part %d -> %d\n", st, en, pst, pen)
383 }
384
385
386 pruned = append(pruned, cands[st:en+1]...)
387 regions = append(regions, candRegion{st: pst, en: pen})
388 st = en + 1
389 }
390 if len(pruned) < 2 {
391 return nil, nil
392 }
393 return pruned, regions
394 }
395
396 func (cs *cstate) dumpFunc() {
397 fmt.Fprintf(os.Stderr, "=-= mergelocalsdumpfunc %v:\n", cs.fn)
398 ii := 0
399 for k, b := range cs.f.Blocks {
400 fmt.Fprintf(os.Stderr, "b%d:\n", k)
401 for _, v := range b.Values {
402 pos := base.Ctxt.PosTable.Pos(v.Pos)
403 fmt.Fprintf(os.Stderr, "=-= %d L%d|C%d %s\n", ii, pos.RelLine(), pos.RelCol(), v.LongString())
404 ii++
405 }
406 }
407 }
408
409 func (cs *cstate) dumpFuncIfSelected() {
410 if base.Debug.MergeLocalsDumpFunc == "" {
411 return
412 }
413 if !strings.HasSuffix(fmt.Sprintf("%v", cs.fn),
414 base.Debug.MergeLocalsDumpFunc) {
415 return
416 }
417 cs.dumpFunc()
418 }
419
420
421
422
423
424
425 func (cs *cstate) setupHashBisection(cands []*ir.Name) {
426 if base.Debug.MergeLocalsHash == "" {
427 return
428 }
429 deselected := make(map[*ir.Name]bool)
430 selCount := 0
431 for _, cand := range cands {
432 if !base.MergeLocalsHash.MatchPosWithInfo(cand.Pos(), "mergelocals", nil) {
433 deselected[cand] = true
434 } else {
435 deselected[cand] = false
436 selCount++
437 }
438 }
439 if selCount < len(cands) {
440 cs.hashDeselected = deselected
441 }
442 if base.Debug.MergeLocalsHTrace != 0 && selCount >= 2 {
443 cs.trace = base.Debug.MergeLocalsHTrace
444 }
445 }
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471 func (cs *cstate) populateIndirectUseTable(cands []*ir.Name) ([]*ir.Name, []candRegion) {
472
473
474 indirectUE := make(map[ssa.ID][]*ir.Name)
475
476
477
478 rawcands := make(map[*ir.Name]struct{})
479
480
481
482 pendingUses := make(map[ssa.ID]nameCount)
483
484
485
486 blockIndirectUE := make(map[ssa.ID][]*ir.Name)
487
488
489 evicted := make(map[*ir.Name]bool)
490 for _, n := range cands {
491 rawcands[n] = struct{}{}
492 }
493 for k := 0; k < len(cs.f.Blocks); k++ {
494 clear(pendingUses)
495 clear(blockIndirectUE)
496 b := cs.f.Blocks[k]
497 for _, v := range b.Values {
498 if n, e := affectedVar(v); n != nil {
499 if _, ok := rawcands[n]; ok {
500 if e&ssaop.SymAddr != 0 && v.Uses != 0 {
501
502 if _, ok := pendingUses[v.ID]; ok {
503
504 base.FatalfAt(v.Pos, "internal error: apparent multiple defs for SSA value %d", v.ID)
505 }
506
507
508
509 pendingUses[v.ID] = nameCount{n: n, count: v.Uses}
510 if cs.trace > 2 {
511 fmt.Fprintf(os.Stderr, "=-= SymAddr(%s) on %s\n",
512 n.Sym().Name, v.LongString())
513 }
514 }
515 }
516 }
517 for idx, arg := range v.Args {
518 if nc, ok := pendingUses[arg.ID]; ok {
519 if !v.AddrSinkArg(idx) {
520
521
522
523 if v.Type.IsMemory() {
524
525 continue
526 }
527
528 if _, ok := pendingUses[v.ID]; ok {
529
530
531 continue
532 }
533
534
535 pendingUses[v.ID] = nameCount{n: nc.n, count: v.Uses}
536 }
537
538
539
540 if cs.trace > 2 {
541 fmt.Fprintf(os.Stderr, "=-= add indirectUE(%s) count=%d on %s\n", nc.n.Sym().Name, nc.count, v.LongString())
542 }
543 blockIndirectUE[v.ID] = append(blockIndirectUE[v.ID], nc.n)
544 nc.count--
545 if nc.count == 0 {
546
547
548 if cs.trace > 2 {
549 fmt.Fprintf(os.Stderr, "=-= last use of v%d\n",
550 arg.ID)
551 }
552 delete(pendingUses, arg.ID)
553 } else {
554
555
556 pendingUses[arg.ID] = nc
557 }
558 }
559 }
560 }
561
562
563
564
565
566
567
568
569 clear(evicted)
570 if len(pendingUses) != 0 {
571 for id, nc := range pendingUses {
572 if cs.trace > 2 {
573 fmt.Fprintf(os.Stderr, "=-= evicting %q due to pendingUse %d count %d\n", nc.n.Sym().Name, id, nc.count)
574 }
575 delete(rawcands, nc.n)
576 evicted[nc.n] = true
577 }
578 }
579
580
581 for id, sl := range blockIndirectUE {
582 for _, n := range sl {
583 if evicted[n] {
584 continue
585 }
586 indirectUE[id] = append(indirectUE[id], n)
587 if cs.trace > 2 {
588 fmt.Fprintf(os.Stderr, "=-= add final indUE v%d name %s\n", id, n.Sym().Name)
589 }
590 }
591 }
592 }
593 if len(rawcands) < 2 {
594 return nil, nil
595 }
596 cs.indirectUE = indirectUE
597 if cs.trace > 2 {
598 fmt.Fprintf(os.Stderr, "=-= iuetab:\n")
599 ids := make([]ssa.ID, 0, len(indirectUE))
600 for k := range indirectUE {
601 ids = append(ids, k)
602 }
603 slices.Sort(ids)
604 for _, id := range ids {
605 fmt.Fprintf(os.Stderr, " v%d:", id)
606 for _, n := range indirectUE[id] {
607 fmt.Fprintf(os.Stderr, " %s", n.Sym().Name)
608 }
609 fmt.Fprintf(os.Stderr, "\n")
610 }
611 }
612
613 pruned := cands[:0]
614 for k := range rawcands {
615 pruned = append(pruned, k)
616 }
617 sort.Slice(pruned, func(i, j int) bool {
618 return nameLess(pruned[i], pruned[j])
619 })
620 var regions []candRegion
621 pruned, regions = cs.genRegions(pruned)
622 if len(pruned) < 2 {
623 return nil, nil
624 }
625 return pruned, regions
626 }
627
628 type nameCount struct {
629 n *ir.Name
630 count int32
631 }
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648 func nameLess(ci, cj *ir.Name) bool {
649 if ci.Type().HasPointers() != cj.Type().HasPointers() {
650 return ci.Type().HasPointers()
651 }
652 if ci.Type().Alignment() != cj.Type().Alignment() {
653 return cj.Type().Alignment() < ci.Type().Alignment()
654 }
655 if ci.Type().Size() != cj.Type().Size() {
656 return cj.Type().Size() < ci.Type().Size()
657 }
658 if ci.Sym().Name != cj.Sym().Name {
659 return ci.Sym().Name < cj.Sym().Name
660 }
661 return fmt.Sprintf("%v", ci.Pos()) < fmt.Sprintf("%v", cj.Pos())
662 }
663
664
665
666
667
668
669 func nextRegion(cands []*ir.Name, idx int) int {
670 n := len(cands)
671 if idx >= n {
672 return -1
673 }
674 c0 := cands[idx]
675 szprev := c0.Type().Size()
676 alnprev := c0.Type().Alignment()
677 for j := idx + 1; j < n; j++ {
678 cj := cands[j]
679 szj := cj.Type().Size()
680 if szj > szprev {
681 return j - 1
682 }
683 alnj := cj.Type().Alignment()
684 if alnj > alnprev {
685 return j - 1
686 }
687 szprev = szj
688 alnprev = alnj
689 }
690 return n - 1
691 }
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707 func (cs *cstate) mergeVisitRegion(mls *MergeLocalsState, st, en int) {
708 if cs.trace > 1 {
709 fmt.Fprintf(os.Stderr, "=-= mergeVisitRegion(st=%d, en=%d)\n", st, en)
710 }
711 n := en - st + 1
712 used := bitvec.New(int32(n))
713
714 nxt := func(slot int) int {
715 for c := slot - st; c < n; c++ {
716 if used.Get(int32(c)) {
717 continue
718 }
719 return c + st
720 }
721 return -1
722 }
723
724 navail := n
725 cands := cs.cands
726 ivs := cs.ivs
727 if cs.trace > 1 {
728 fmt.Fprintf(os.Stderr, " =-= navail = %d\n", navail)
729 }
730 for navail >= 2 {
731 leader := nxt(st)
732 used.Set(int32(leader - st))
733 navail--
734
735 if cs.trace > 1 {
736 fmt.Fprintf(os.Stderr, " =-= begin leader %d used=%s\n", leader,
737 used.String())
738 }
739 elems := []int{leader}
740 lints := ivs[leader]
741
742 for succ := nxt(leader + 1); succ != -1; succ = nxt(succ + 1) {
743
744
745 if cs.hashDeselected != nil && cs.hashDeselected[cands[succ]] {
746 continue
747 }
748
749 if used.Get(int32(succ - st)) {
750 continue
751 }
752 if cs.trace > 1 {
753 fmt.Fprintf(os.Stderr, " =-= overlap of %d[%v] {%s} with %d[%v] {%s} is: %v\n", leader, cands[leader], lints.String(), succ, cands[succ], ivs[succ].String(), lints.Overlaps(ivs[succ]))
754 }
755
756
757 if lints.Overlaps(ivs[succ]) {
758 continue
759 } else {
760
761 elems = append(elems, succ)
762 lints = lints.Merge(ivs[succ])
763 }
764 }
765 if len(elems) > 1 {
766
767
768 off := len(mls.vars)
769 sl := make([]int, len(elems))
770 for i, candslot := range elems {
771 sl[i] = off + i
772 mls.vars = append(mls.vars, cands[candslot])
773 mls.partition[cands[candslot]] = sl
774 }
775 navail -= (len(elems) - 1)
776 for i := range elems {
777 used.Set(int32(elems[i] - st))
778 }
779 if cs.trace > 1 {
780 fmt.Fprintf(os.Stderr, "=-= overlapping %+v:\n", sl)
781 for i := range sl {
782 dumpCand(mls.vars[sl[i]], sl[i])
783 }
784 for i, v := range elems {
785 fmt.Fprintf(os.Stderr, "=-= %d: sl=%d %s\n", i, v, ivs[v])
786 }
787 }
788 }
789 }
790 }
791
792
793
794
795 func (cs *cstate) performMerging() *MergeLocalsState {
796 cands := cs.cands
797
798 mls := &MergeLocalsState{
799 partition: make(map[*ir.Name][]int),
800 }
801
802
803 if cs.trace > 1 {
804 fmt.Fprintf(os.Stderr, "=-= cands live before overlap:\n")
805 for i := range cands {
806 c := cands[i]
807 fmt.Fprintf(os.Stderr, "%d: %v sz=%d ivs=%s\n",
808 i, c.Sym().Name, c.Type().Size(), cs.ivs[i].String())
809 }
810 fmt.Fprintf(os.Stderr, "=-= regions (%d): ", len(cs.regions))
811 for _, cr := range cs.regions {
812 fmt.Fprintf(os.Stderr, " [%d,%d]", cr.st, cr.en)
813 }
814 fmt.Fprintf(os.Stderr, "\n")
815 }
816
817
818
819 for _, cr := range cs.regions {
820 cs.mergeVisitRegion(mls, cr.st, cr.en)
821 }
822 if len(mls.vars) == 0 {
823 return nil
824 }
825 return mls
826 }
827
828
829
830
831
832 func (cs *cstate) computeIntervals() {
833 lv := cs.lv
834 ibuilders := make([]IntervalsBuilder, len(cs.cands))
835 nvars := int32(len(lv.vars))
836 liveout := bitvec.New(nvars)
837
838 cs.dumpFuncIfSelected()
839
840
841 ninstr := 0
842 for _, b := range lv.f.Blocks {
843 ninstr += len(b.Values)
844 }
845
846 iidx := ninstr - 1
847
848
849 for k := len(lv.f.Blocks) - 1; k >= 0; k-- {
850 b := lv.f.Blocks[k]
851 be := lv.blockEffects(b)
852
853 if cs.trace > 2 {
854 fmt.Fprintf(os.Stderr, "=-= liveout from tail of b%d: ", k)
855 for j := range lv.vars {
856 if be.liveout.Get(int32(j)) {
857 fmt.Fprintf(os.Stderr, " %q", lv.vars[j].Sym().Name)
858 }
859 }
860 fmt.Fprintf(os.Stderr, "\n")
861 }
862
863
864
865
866
867
868
869 for j := range lv.vars {
870 isLive := liveout.Get(int32(j))
871 blockLiveOut := be.liveout.Get(int32(j))
872 if isLive {
873 if !blockLiveOut {
874 if cs.trace > 2 {
875 fmt.Fprintf(os.Stderr, "=+= at instr %d block boundary kill of %v\n", iidx, lv.vars[j])
876 }
877 ibuilders[j].Kill(iidx)
878 }
879 } else if blockLiveOut {
880 if cs.trace > 2 {
881 fmt.Fprintf(os.Stderr, "=+= at block-end instr %d %v becomes live\n",
882 iidx, lv.vars[j])
883 }
884 ibuilders[j].Live(iidx)
885 }
886 }
887
888
889
890 liveout.Copy(be.liveout)
891
892
893 for i := len(b.Values) - 1; i >= 0; i-- {
894 v := b.Values[i]
895
896 if cs.trace > 2 {
897 fmt.Fprintf(os.Stderr, "=-= b%d instr %d: %s\n", k, iidx, v.LongString())
898 }
899
900
901
902 pos, e := lv.valueEffects(v)
903 becomeslive := e&uevar != 0
904 iskilled := e&varkill != 0
905 if becomeslive && iskilled {
906
907
908 panic("should never happen")
909 }
910 if iskilled && liveout.Get(pos) {
911 ibuilders[pos].Kill(iidx)
912 liveout.Unset(pos)
913 if cs.trace > 2 {
914 fmt.Fprintf(os.Stderr, "=+= at instr %d kill of %v\n",
915 iidx, lv.vars[pos])
916 }
917 } else if becomeslive && !liveout.Get(pos) {
918 ibuilders[pos].Live(iidx)
919 liveout.Set(pos)
920 if cs.trace > 2 {
921 fmt.Fprintf(os.Stderr, "=+= at instr %d upwards-exposed use of %v\n",
922 iidx, lv.vars[pos])
923 }
924 }
925
926 if cs.indirectUE != nil {
927
928 ues := cs.indirectUE[v.ID]
929 for _, n := range ues {
930 if pos, ok := lv.idx[n]; ok {
931 if !liveout.Get(pos) {
932 ibuilders[pos].Live(iidx)
933 liveout.Set(pos)
934 if cs.trace > 2 {
935 fmt.Fprintf(os.Stderr, "=+= at instr %d v%d indirect upwards-exposed use of %v\n", iidx, v.ID, lv.vars[pos])
936 }
937 }
938 }
939 }
940 }
941 iidx--
942 }
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995 const checkLiveOnEntry = false
996 if checkLiveOnEntry && b == lv.f.Entry {
997 for j, v := range lv.vars {
998 if liveout.Get(int32(j)) {
999 lv.f.Fatalf("%v %L recorded as live on entry",
1000 lv.fn.Nname, v)
1001 }
1002 }
1003 }
1004 }
1005 if iidx != -1 {
1006 panic("iidx underflow")
1007 }
1008
1009
1010 ivs := make([]Intervals, len(cs.cands))
1011 for i := range cs.cands {
1012 var err error
1013 ivs[i], err = ibuilders[i].Finish()
1014 if err != nil {
1015 cs.dumpFunc()
1016 base.FatalfAt(cs.cands[i].Pos(), "interval construct error for var %q in func %q (%d instrs): %v", cs.cands[i].Sym().Name, ir.FuncName(cs.fn), ninstr, err)
1017 }
1018 }
1019 cs.ivs = ivs
1020 }
1021
1022 func fmtFullPos(p src.XPos) string {
1023 var sb strings.Builder
1024 sep := ""
1025 base.Ctxt.AllPos(p, func(pos src.Pos) {
1026 sb.WriteString(sep)
1027 sep = "|"
1028 file := filepath.Base(pos.Filename())
1029 fmt.Fprintf(&sb, "%s:%d:%d", file, pos.Line(), pos.Col())
1030 })
1031 return sb.String()
1032 }
1033
1034 func dumpCand(c *ir.Name, i int) {
1035 fmt.Fprintf(os.Stderr, " %d: %s %q sz=%d hp=%v align=%d t=%v\n",
1036 i, fmtFullPos(c.Pos()), c.Sym().Name, c.Type().Size(),
1037 c.Type().HasPointers(), c.Type().Alignment(), c.Type())
1038 }
1039
1040
1041 func MakeMergeLocalsState(partition map[*ir.Name][]int, vars []*ir.Name) (*MergeLocalsState, error) {
1042 mls := &MergeLocalsState{partition: partition, vars: vars}
1043 if err := mls.check(); err != nil {
1044 return nil, err
1045 }
1046 return mls, nil
1047 }
1048
View as plain text