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