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 _, arg := range v.Args {
517 if nc, ok := pendingUses[arg.ID]; ok {
518
519
520
521 if cs.trace > 2 {
522 fmt.Fprintf(os.Stderr, "=-= add indirectUE(%s) count=%d on %s\n", nc.n.Sym().Name, nc.count, v.LongString())
523 }
524 blockIndirectUE[v.ID] = append(blockIndirectUE[v.ID], nc.n)
525 nc.count--
526 if nc.count == 0 {
527
528
529 if cs.trace > 2 {
530 fmt.Fprintf(os.Stderr, "=-= last use of v%d\n",
531 arg.ID)
532 }
533 delete(pendingUses, arg.ID)
534 } else {
535
536
537 pendingUses[arg.ID] = nc
538 }
539 }
540 }
541 }
542
543
544
545
546
547
548
549
550 clear(evicted)
551 if len(pendingUses) != 0 {
552 for id, nc := range pendingUses {
553 if cs.trace > 2 {
554 fmt.Fprintf(os.Stderr, "=-= evicting %q due to pendingUse %d count %d\n", nc.n.Sym().Name, id, nc.count)
555 }
556 delete(rawcands, nc.n)
557 evicted[nc.n] = true
558 }
559 }
560
561
562 for id, sl := range blockIndirectUE {
563 for _, n := range sl {
564 if evicted[n] {
565 continue
566 }
567 indirectUE[id] = append(indirectUE[id], n)
568 if cs.trace > 2 {
569 fmt.Fprintf(os.Stderr, "=-= add final indUE v%d name %s\n", id, n.Sym().Name)
570 }
571 }
572 }
573 }
574 if len(rawcands) < 2 {
575 return nil, nil
576 }
577 cs.indirectUE = indirectUE
578 if cs.trace > 2 {
579 fmt.Fprintf(os.Stderr, "=-= iuetab:\n")
580 ids := make([]ssa.ID, 0, len(indirectUE))
581 for k := range indirectUE {
582 ids = append(ids, k)
583 }
584 slices.Sort(ids)
585 for _, id := range ids {
586 fmt.Fprintf(os.Stderr, " v%d:", id)
587 for _, n := range indirectUE[id] {
588 fmt.Fprintf(os.Stderr, " %s", n.Sym().Name)
589 }
590 fmt.Fprintf(os.Stderr, "\n")
591 }
592 }
593
594 pruned := cands[:0]
595 for k := range rawcands {
596 pruned = append(pruned, k)
597 }
598 sort.Slice(pruned, func(i, j int) bool {
599 return nameLess(pruned[i], pruned[j])
600 })
601 var regions []candRegion
602 pruned, regions = cs.genRegions(pruned)
603 if len(pruned) < 2 {
604 return nil, nil
605 }
606 return pruned, regions
607 }
608
609 type nameCount struct {
610 n *ir.Name
611 count int32
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629 func nameLess(ci, cj *ir.Name) bool {
630 if ci.Type().HasPointers() != cj.Type().HasPointers() {
631 return ci.Type().HasPointers()
632 }
633 if ci.Type().Alignment() != cj.Type().Alignment() {
634 return cj.Type().Alignment() < ci.Type().Alignment()
635 }
636 if ci.Type().Size() != cj.Type().Size() {
637 return cj.Type().Size() < ci.Type().Size()
638 }
639 if ci.Sym().Name != cj.Sym().Name {
640 return ci.Sym().Name < cj.Sym().Name
641 }
642 return fmt.Sprintf("%v", ci.Pos()) < fmt.Sprintf("%v", cj.Pos())
643 }
644
645
646
647
648
649
650 func nextRegion(cands []*ir.Name, idx int) int {
651 n := len(cands)
652 if idx >= n {
653 return -1
654 }
655 c0 := cands[idx]
656 szprev := c0.Type().Size()
657 alnprev := c0.Type().Alignment()
658 for j := idx + 1; j < n; j++ {
659 cj := cands[j]
660 szj := cj.Type().Size()
661 if szj > szprev {
662 return j - 1
663 }
664 alnj := cj.Type().Alignment()
665 if alnj > alnprev {
666 return j - 1
667 }
668 szprev = szj
669 alnprev = alnj
670 }
671 return n - 1
672 }
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688 func (cs *cstate) mergeVisitRegion(mls *MergeLocalsState, st, en int) {
689 if cs.trace > 1 {
690 fmt.Fprintf(os.Stderr, "=-= mergeVisitRegion(st=%d, en=%d)\n", st, en)
691 }
692 n := en - st + 1
693 used := bitvec.New(int32(n))
694
695 nxt := func(slot int) int {
696 for c := slot - st; c < n; c++ {
697 if used.Get(int32(c)) {
698 continue
699 }
700 return c + st
701 }
702 return -1
703 }
704
705 navail := n
706 cands := cs.cands
707 ivs := cs.ivs
708 if cs.trace > 1 {
709 fmt.Fprintf(os.Stderr, " =-= navail = %d\n", navail)
710 }
711 for navail >= 2 {
712 leader := nxt(st)
713 used.Set(int32(leader - st))
714 navail--
715
716 if cs.trace > 1 {
717 fmt.Fprintf(os.Stderr, " =-= begin leader %d used=%s\n", leader,
718 used.String())
719 }
720 elems := []int{leader}
721 lints := ivs[leader]
722
723 for succ := nxt(leader + 1); succ != -1; succ = nxt(succ + 1) {
724
725
726 if cs.hashDeselected != nil && cs.hashDeselected[cands[succ]] {
727 continue
728 }
729
730 if used.Get(int32(succ - st)) {
731 continue
732 }
733 if cs.trace > 1 {
734 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]))
735 }
736
737
738 if lints.Overlaps(ivs[succ]) {
739 continue
740 } else {
741
742 elems = append(elems, succ)
743 lints = lints.Merge(ivs[succ])
744 }
745 }
746 if len(elems) > 1 {
747
748
749 off := len(mls.vars)
750 sl := make([]int, len(elems))
751 for i, candslot := range elems {
752 sl[i] = off + i
753 mls.vars = append(mls.vars, cands[candslot])
754 mls.partition[cands[candslot]] = sl
755 }
756 navail -= (len(elems) - 1)
757 for i := range elems {
758 used.Set(int32(elems[i] - st))
759 }
760 if cs.trace > 1 {
761 fmt.Fprintf(os.Stderr, "=-= overlapping %+v:\n", sl)
762 for i := range sl {
763 dumpCand(mls.vars[sl[i]], sl[i])
764 }
765 for i, v := range elems {
766 fmt.Fprintf(os.Stderr, "=-= %d: sl=%d %s\n", i, v, ivs[v])
767 }
768 }
769 }
770 }
771 }
772
773
774
775
776 func (cs *cstate) performMerging() *MergeLocalsState {
777 cands := cs.cands
778
779 mls := &MergeLocalsState{
780 partition: make(map[*ir.Name][]int),
781 }
782
783
784 if cs.trace > 1 {
785 fmt.Fprintf(os.Stderr, "=-= cands live before overlap:\n")
786 for i := range cands {
787 c := cands[i]
788 fmt.Fprintf(os.Stderr, "%d: %v sz=%d ivs=%s\n",
789 i, c.Sym().Name, c.Type().Size(), cs.ivs[i].String())
790 }
791 fmt.Fprintf(os.Stderr, "=-= regions (%d): ", len(cs.regions))
792 for _, cr := range cs.regions {
793 fmt.Fprintf(os.Stderr, " [%d,%d]", cr.st, cr.en)
794 }
795 fmt.Fprintf(os.Stderr, "\n")
796 }
797
798
799
800 for _, cr := range cs.regions {
801 cs.mergeVisitRegion(mls, cr.st, cr.en)
802 }
803 if len(mls.vars) == 0 {
804 return nil
805 }
806 return mls
807 }
808
809
810
811
812
813 func (cs *cstate) computeIntervals() {
814 lv := cs.lv
815 ibuilders := make([]IntervalsBuilder, len(cs.cands))
816 nvars := int32(len(lv.vars))
817 liveout := bitvec.New(nvars)
818
819 cs.dumpFuncIfSelected()
820
821
822 ninstr := 0
823 for _, b := range lv.f.Blocks {
824 ninstr += len(b.Values)
825 }
826
827 iidx := ninstr - 1
828
829
830 for k := len(lv.f.Blocks) - 1; k >= 0; k-- {
831 b := lv.f.Blocks[k]
832 be := lv.blockEffects(b)
833
834 if cs.trace > 2 {
835 fmt.Fprintf(os.Stderr, "=-= liveout from tail of b%d: ", k)
836 for j := range lv.vars {
837 if be.liveout.Get(int32(j)) {
838 fmt.Fprintf(os.Stderr, " %q", lv.vars[j].Sym().Name)
839 }
840 }
841 fmt.Fprintf(os.Stderr, "\n")
842 }
843
844
845
846
847
848
849
850 for j := range lv.vars {
851 isLive := liveout.Get(int32(j))
852 blockLiveOut := be.liveout.Get(int32(j))
853 if isLive {
854 if !blockLiveOut {
855 if cs.trace > 2 {
856 fmt.Fprintf(os.Stderr, "=+= at instr %d block boundary kill of %v\n", iidx, lv.vars[j])
857 }
858 ibuilders[j].Kill(iidx)
859 }
860 } else if blockLiveOut {
861 if cs.trace > 2 {
862 fmt.Fprintf(os.Stderr, "=+= at block-end instr %d %v becomes live\n",
863 iidx, lv.vars[j])
864 }
865 ibuilders[j].Live(iidx)
866 }
867 }
868
869
870
871 liveout.Copy(be.liveout)
872
873
874 for i := len(b.Values) - 1; i >= 0; i-- {
875 v := b.Values[i]
876
877 if cs.trace > 2 {
878 fmt.Fprintf(os.Stderr, "=-= b%d instr %d: %s\n", k, iidx, v.LongString())
879 }
880
881
882
883 pos, e := lv.valueEffects(v)
884 becomeslive := e&uevar != 0
885 iskilled := e&varkill != 0
886 if becomeslive && iskilled {
887
888
889 panic("should never happen")
890 }
891 if iskilled && liveout.Get(pos) {
892 ibuilders[pos].Kill(iidx)
893 liveout.Unset(pos)
894 if cs.trace > 2 {
895 fmt.Fprintf(os.Stderr, "=+= at instr %d kill of %v\n",
896 iidx, lv.vars[pos])
897 }
898 } else if becomeslive && !liveout.Get(pos) {
899 ibuilders[pos].Live(iidx)
900 liveout.Set(pos)
901 if cs.trace > 2 {
902 fmt.Fprintf(os.Stderr, "=+= at instr %d upwards-exposed use of %v\n",
903 iidx, lv.vars[pos])
904 }
905 }
906
907 if cs.indirectUE != nil {
908
909 ues := cs.indirectUE[v.ID]
910 for _, n := range ues {
911 if pos, ok := lv.idx[n]; ok {
912 if !liveout.Get(pos) {
913 ibuilders[pos].Live(iidx)
914 liveout.Set(pos)
915 if cs.trace > 2 {
916 fmt.Fprintf(os.Stderr, "=+= at instr %d v%d indirect upwards-exposed use of %v\n", iidx, v.ID, lv.vars[pos])
917 }
918 }
919 }
920 }
921 }
922 iidx--
923 }
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
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 const checkLiveOnEntry = false
977 if checkLiveOnEntry && b == lv.f.Entry {
978 for j, v := range lv.vars {
979 if liveout.Get(int32(j)) {
980 lv.f.Fatalf("%v %L recorded as live on entry",
981 lv.fn.Nname, v)
982 }
983 }
984 }
985 }
986 if iidx != -1 {
987 panic("iidx underflow")
988 }
989
990
991 ivs := make([]Intervals, len(cs.cands))
992 for i := range cs.cands {
993 var err error
994 ivs[i], err = ibuilders[i].Finish()
995 if err != nil {
996 cs.dumpFunc()
997 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)
998 }
999 }
1000 cs.ivs = ivs
1001 }
1002
1003 func fmtFullPos(p src.XPos) string {
1004 var sb strings.Builder
1005 sep := ""
1006 base.Ctxt.AllPos(p, func(pos src.Pos) {
1007 sb.WriteString(sep)
1008 sep = "|"
1009 file := filepath.Base(pos.Filename())
1010 fmt.Fprintf(&sb, "%s:%d:%d", file, pos.Line(), pos.Col())
1011 })
1012 return sb.String()
1013 }
1014
1015 func dumpCand(c *ir.Name, i int) {
1016 fmt.Fprintf(os.Stderr, " %d: %s %q sz=%d hp=%v align=%d t=%v\n",
1017 i, fmtFullPos(c.Pos()), c.Sym().Name, c.Type().Size(),
1018 c.Type().HasPointers(), c.Type().Alignment(), c.Type())
1019 }
1020
1021
1022 func MakeMergeLocalsState(partition map[*ir.Name][]int, vars []*ir.Name) (*MergeLocalsState, error) {
1023 mls := &MergeLocalsState{partition: partition, vars: vars}
1024 if err := mls.check(); err != nil {
1025 return nil, err
1026 }
1027 return mls, nil
1028 }
1029
View as plain text