1
2
3
4
5 package ssa
6
7 import (
8 "cmd/compile/internal/abi"
9 "cmd/compile/internal/abt"
10 "cmd/compile/internal/ir"
11 "cmd/compile/internal/types"
12 "cmd/internal/dwarf"
13 "cmd/internal/obj"
14 "cmd/internal/src"
15 "cmp"
16 "encoding/hex"
17 "fmt"
18 "internal/buildcfg"
19 "math/bits"
20 "slices"
21 "strings"
22 )
23
24 type SlotID int32
25 type VarID int32
26
27
28
29
30 type FuncDebug struct {
31
32 Slots []LocalSlot
33
34 Vars []*ir.Name
35
36 VarSlots [][]SlotID
37
38 LocationLists [][]byte
39
40
41 RegOutputParams []*ir.Name
42
43 OptDcl []*ir.Name
44
45
46
47
48
49 GetPC func(block, value ID) int64
50 }
51
52 type BlockDebug struct {
53
54
55 startState, endState abt.T
56
57
58
59 lastCheckedTime, lastChangedTime int32
60
61 relevant bool
62
63
64
65 everProcessed bool
66 }
67
68
69 type liveSlot struct {
70 VarLoc
71 }
72
73 func (ls *liveSlot) String() string {
74 return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1)
75 }
76
77 func (ls liveSlot) absent() bool {
78 return ls.Registers == 0 && !ls.onStack()
79 }
80
81
82
83
84 type StackOffset int32
85
86 func (s StackOffset) onStack() bool {
87 return s != 0
88 }
89
90 func (s StackOffset) stackOffsetValue() int32 {
91 return int32(s) >> 1
92 }
93
94
95 type stateAtPC struct {
96
97 slots []VarLoc
98
99 registers [][]SlotID
100 }
101
102
103 func (state *stateAtPC) reset(live abt.T) {
104 slots, registers := state.slots, state.registers
105 for i := range slots {
106 slots[i] = VarLoc{}
107 }
108 for i := range registers {
109 registers[i] = registers[i][:0]
110 }
111 for it := live.Iterator(); !it.Done(); {
112 k, d := it.Next()
113 live := d.(*liveSlot)
114 slots[k] = live.VarLoc
115 if live.VarLoc.Registers == 0 {
116 continue
117 }
118
119 mask := uint64(live.VarLoc.Registers)
120 for {
121 if mask == 0 {
122 break
123 }
124 reg := uint8(bits.TrailingZeros64(mask))
125 mask &^= 1 << reg
126
127 registers[reg] = append(registers[reg], SlotID(k))
128 }
129 }
130 state.slots, state.registers = slots, registers
131 }
132
133 func (s *debugState) LocString(loc VarLoc) string {
134 if loc.absent() {
135 return "<nil>"
136 }
137
138 var storage []string
139 if loc.onStack() {
140 storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue()))
141 }
142
143 mask := uint64(loc.Registers)
144 for {
145 if mask == 0 {
146 break
147 }
148 reg := uint8(bits.TrailingZeros64(mask))
149 mask &^= 1 << reg
150
151 storage = append(storage, s.registers[reg].String())
152 }
153 return strings.Join(storage, ",")
154 }
155
156
157 type VarLoc struct {
158
159
160 Registers RegisterSet
161
162 StackOffset
163 }
164
165 func (loc VarLoc) absent() bool {
166 return loc.Registers == 0 && !loc.onStack()
167 }
168
169 func (loc VarLoc) intersect(other VarLoc) VarLoc {
170 if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset {
171 loc.StackOffset = 0
172 }
173 loc.Registers &= other.Registers
174 return loc
175 }
176
177 var BlockStart = &Value{
178 ID: -10000,
179 Op: OpInvalid,
180 Aux: StringToAux("BlockStart"),
181 }
182
183 var BlockEnd = &Value{
184 ID: -20000,
185 Op: OpInvalid,
186 Aux: StringToAux("BlockEnd"),
187 }
188
189 var FuncEnd = &Value{
190 ID: -30000,
191 Op: OpInvalid,
192 Aux: StringToAux("FuncEnd"),
193 }
194
195
196 type RegisterSet uint64
197
198
199
200
201 func (s *debugState) logf(msg string, args ...interface{}) {
202 if s.f.PrintOrHtmlSSA {
203 fmt.Printf(msg, args...)
204 }
205 }
206
207 type debugState struct {
208
209 slots []LocalSlot
210 vars []*ir.Name
211 varSlots [][]SlotID
212 lists [][]byte
213
214
215 slotVars []VarID
216
217 f *Func
218 loggingLevel int
219 convergeCount int
220 registers []Register
221 stackOffset func(LocalSlot) int32
222 ctxt *obj.Link
223
224
225 valueNames [][]SlotID
226
227
228 currentState stateAtPC
229 changedVars *sparseSet
230 changedSlots *sparseSet
231
232
233 pendingEntries []pendingEntry
234
235 varParts map[*ir.Name][]SlotID
236 blockDebug []BlockDebug
237 pendingSlotLocs []VarLoc
238 }
239
240 func (state *debugState) initializeCache(f *Func, numVars, numSlots int) {
241
242 if cap(state.blockDebug) < f.NumBlocks() {
243 state.blockDebug = make([]BlockDebug, f.NumBlocks())
244 } else {
245
246
247 b := state.blockDebug[:f.NumBlocks()]
248 for i := range b {
249 b[i] = BlockDebug{}
250 }
251 }
252
253
254 if cap(state.valueNames) < f.NumValues() {
255 old := state.valueNames
256 state.valueNames = make([][]SlotID, f.NumValues())
257 copy(state.valueNames, old)
258 }
259 vn := state.valueNames[:f.NumValues()]
260 for i := range vn {
261 vn[i] = vn[i][:0]
262 }
263
264
265 if cap(state.currentState.slots) < numSlots {
266 state.currentState.slots = make([]VarLoc, numSlots)
267 } else {
268 state.currentState.slots = state.currentState.slots[:numSlots]
269 }
270 if cap(state.currentState.registers) < len(state.registers) {
271 state.currentState.registers = make([][]SlotID, len(state.registers))
272 } else {
273 state.currentState.registers = state.currentState.registers[:len(state.registers)]
274 }
275
276
277 state.changedVars = newSparseSet(numVars)
278 state.changedSlots = newSparseSet(numSlots)
279
280
281 numPieces := 0
282 for i := range state.varSlots {
283 numPieces += len(state.varSlots[i])
284 }
285 if cap(state.pendingSlotLocs) < numPieces {
286 state.pendingSlotLocs = make([]VarLoc, numPieces)
287 } else {
288 psl := state.pendingSlotLocs[:numPieces]
289 for i := range psl {
290 psl[i] = VarLoc{}
291 }
292 }
293 if cap(state.pendingEntries) < numVars {
294 state.pendingEntries = make([]pendingEntry, numVars)
295 }
296 pe := state.pendingEntries[:numVars]
297 freePieceIdx := 0
298 for varID, slots := range state.varSlots {
299 pe[varID] = pendingEntry{
300 pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)],
301 }
302 freePieceIdx += len(slots)
303 }
304 state.pendingEntries = pe
305
306 if cap(state.lists) < numVars {
307 state.lists = make([][]byte, numVars)
308 } else {
309 state.lists = state.lists[:numVars]
310 for i := range state.lists {
311 state.lists[i] = nil
312 }
313 }
314 }
315
316 func (state *debugState) allocBlock(b *Block) *BlockDebug {
317 return &state.blockDebug[b.ID]
318 }
319
320 func (s *debugState) blockEndStateString(b *BlockDebug) string {
321 endState := stateAtPC{slots: make([]VarLoc, len(s.slots)), registers: make([][]SlotID, len(s.registers))}
322 endState.reset(b.endState)
323 return s.stateString(endState)
324 }
325
326 func (s *debugState) stateString(state stateAtPC) string {
327 var strs []string
328 for slotID, loc := range state.slots {
329 if !loc.absent() {
330 strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.slots[slotID], s.LocString(loc)))
331 }
332 }
333
334 strs = append(strs, "\n")
335 for reg, slots := range state.registers {
336 if len(slots) != 0 {
337 var slotStrs []string
338 for _, slot := range slots {
339 slotStrs = append(slotStrs, s.slots[slot].String())
340 }
341 strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.registers[reg], slotStrs))
342 }
343 }
344
345 if len(strs) == 1 {
346 return "(no vars)\n"
347 }
348 return strings.Join(strs, "")
349 }
350
351
352
353
354
355 type slotCanonicalizer struct {
356 slmap map[slotKey]SlKeyIdx
357 slkeys []LocalSlot
358 }
359
360 func newSlotCanonicalizer() *slotCanonicalizer {
361 return &slotCanonicalizer{
362 slmap: make(map[slotKey]SlKeyIdx),
363 slkeys: []LocalSlot{LocalSlot{N: nil}},
364 }
365 }
366
367 type SlKeyIdx uint32
368
369 const noSlot = SlKeyIdx(0)
370
371
372
373 type slotKey struct {
374 name *ir.Name
375 offset int64
376 width int64
377 splitOf SlKeyIdx
378 splitOffset int64
379 }
380
381
382
383
384
385 func (sc *slotCanonicalizer) lookup(ls LocalSlot) (SlKeyIdx, bool) {
386 split := noSlot
387 if ls.SplitOf != nil {
388 split, _ = sc.lookup(*ls.SplitOf)
389 }
390 k := slotKey{
391 name: ls.N, offset: ls.Off, width: ls.Type.Size(),
392 splitOf: split, splitOffset: ls.SplitOffset,
393 }
394 if idx, ok := sc.slmap[k]; ok {
395 return idx, true
396 }
397 rv := SlKeyIdx(len(sc.slkeys))
398 sc.slkeys = append(sc.slkeys, ls)
399 sc.slmap[k] = rv
400 return rv, false
401 }
402
403 func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
404 return sc.slkeys[idx]
405 }
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438 func PopulateABIInRegArgOps(f *Func) {
439 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
440
441
442
443
444
445
446
447 sc := newSlotCanonicalizer()
448 for _, sl := range f.Names {
449 sc.lookup(*sl)
450 }
451
452
453 addToNV := func(v *Value, sl LocalSlot) {
454 values, ok := f.NamedValues[sl]
455 if !ok {
456
457 sla := f.localSlotAddr(sl)
458 f.Names = append(f.Names, sla)
459 } else {
460 for _, ev := range values {
461 if v == ev {
462 return
463 }
464 }
465 }
466 values = append(values, v)
467 f.NamedValues[sl] = values
468 }
469
470 newValues := []*Value{}
471
472 abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
473 i := f.ABISelf.FloatIndexFor(reg)
474 if i >= 0 {
475 return f.Config.floatParamRegs[i]
476 } else {
477 return f.Config.intParamRegs[reg]
478 }
479 }
480
481
482 var pos src.XPos
483 if len(f.Entry.Values) != 0 {
484 pos = f.Entry.Values[0].Pos
485 }
486 synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl LocalSlot) *Value {
487 aux := &AuxNameOffset{n, sl.Off}
488 op, auxInt := ArgOpAndRegisterFor(reg, f.ABISelf)
489 v := f.newValueNoBlock(op, t, pos)
490 v.AuxInt = auxInt
491 v.Aux = aux
492 v.Args = nil
493 v.Block = f.Entry
494 newValues = append(newValues, v)
495 addToNV(v, sl)
496 f.setHome(v, &f.Config.registers[abiRegIndexToRegister(reg)])
497 return v
498 }
499
500
501
502
503
504
505 for _, v := range f.Entry.Values {
506 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
507 aux := v.Aux.(*AuxNameOffset)
508 sl := LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
509
510 idx, _ := sc.lookup(sl)
511
512 addToNV(v, sc.canonSlot(idx))
513 } else if v.Op.IsCall() {
514
515 break
516 }
517 }
518
519
520
521 for _, inp := range pri.InParams() {
522 if !isNamedRegParam(inp) {
523 continue
524 }
525 n := inp.Name
526
527
528
529 types, offsets := inp.RegisterTypesAndOffsets()
530 for k, t := range types {
531
532
533
534
535
536
537
538
539
540 pieceSlot := LocalSlot{N: n, Type: t, Off: offsets[k]}
541
542
543
544 _, found := sc.lookup(pieceSlot)
545 if !found {
546
547
548
549
550 synthesizeOpIntFloatArg(n, t, inp.Registers[k],
551 pieceSlot)
552 }
553 }
554 }
555
556
557 f.Entry.Values = append(newValues, f.Entry.Values...)
558 }
559
560
561
562
563 func BuildFuncDebug(ctxt *obj.Link, f *Func, loggingLevel int, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
564 if f.RegAlloc == nil {
565 f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
566 }
567 state := &f.Cache.debugState
568 state.loggingLevel = loggingLevel % 1000
569
570
571
572
573
574 state.convergeCount = loggingLevel / 1000
575 state.f = f
576 state.registers = f.Config.registers
577 state.stackOffset = stackOffset
578 state.ctxt = ctxt
579
580 if buildcfg.Experiment.RegabiArgs {
581 PopulateABIInRegArgOps(f)
582 }
583
584 if state.loggingLevel > 0 {
585 state.logf("Generating location lists for function %q\n", f.Name)
586 }
587
588 if state.varParts == nil {
589 state.varParts = make(map[*ir.Name][]SlotID)
590 } else {
591 clear(state.varParts)
592 }
593
594
595
596
597 state.slots = state.slots[:0]
598 state.vars = state.vars[:0]
599 for i, slot := range f.Names {
600 state.slots = append(state.slots, *slot)
601 if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
602 continue
603 }
604
605 topSlot := slot
606 for topSlot.SplitOf != nil {
607 topSlot = topSlot.SplitOf
608 }
609 if _, ok := state.varParts[topSlot.N]; !ok {
610 state.vars = append(state.vars, topSlot.N)
611 }
612 state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i))
613 }
614
615
616
617 for _, b := range f.Blocks {
618 for _, v := range b.Values {
619 if v.Op == OpVarDef {
620 n := v.Aux.(*ir.Name)
621 if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
622 continue
623 }
624
625 if _, ok := state.varParts[n]; !ok {
626 slot := LocalSlot{N: n, Type: v.Type, Off: 0}
627 state.slots = append(state.slots, slot)
628 state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)}
629 state.vars = append(state.vars, n)
630 }
631 }
632 }
633 }
634
635
636 if cap(state.varSlots) < len(state.vars) {
637 state.varSlots = make([][]SlotID, len(state.vars))
638 } else {
639 state.varSlots = state.varSlots[:len(state.vars)]
640 for i := range state.varSlots {
641 state.varSlots[i] = state.varSlots[i][:0]
642 }
643 }
644 if cap(state.slotVars) < len(state.slots) {
645 state.slotVars = make([]VarID, len(state.slots))
646 } else {
647 state.slotVars = state.slotVars[:len(state.slots)]
648 }
649
650 for varID, n := range state.vars {
651 parts := state.varParts[n]
652 slices.SortFunc(parts, func(a, b SlotID) int {
653 return cmp.Compare(varOffset(state.slots[a]), varOffset(state.slots[b]))
654 })
655
656 state.varSlots[varID] = parts
657 for _, slotID := range parts {
658 state.slotVars[slotID] = VarID(varID)
659 }
660 }
661
662 state.initializeCache(f, len(state.varParts), len(state.slots))
663
664 for i, slot := range f.Names {
665 if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
666 continue
667 }
668 for _, value := range f.NamedValues[*slot] {
669 state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
670 }
671 }
672
673 blockLocs := state.liveness()
674 state.buildLocationLists(blockLocs)
675
676
677 rval.Slots = state.slots
678 rval.VarSlots = state.varSlots
679 rval.Vars = state.vars
680 rval.LocationLists = state.lists
681 }
682
683
684
685 func (state *debugState) liveness() []*BlockDebug {
686 blockLocs := make([]*BlockDebug, state.f.NumBlocks())
687 counterTime := int32(1)
688
689
690
691 po := state.f.Postorder()
692 converged := false
693
694
695
696
697
698
699 keepGoing := func(k int) bool {
700 if state.convergeCount == 0 {
701 return !converged
702 }
703 return k < state.convergeCount
704 }
705 for k := 0; keepGoing(k); k++ {
706 if state.loggingLevel > 0 {
707 state.logf("Liveness pass %d\n", k)
708 }
709 converged = true
710 for i := len(po) - 1; i >= 0; i-- {
711 b := po[i]
712 locs := blockLocs[b.ID]
713 if locs == nil {
714 locs = state.allocBlock(b)
715 blockLocs[b.ID] = locs
716 }
717
718
719
720 startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false)
721 locs.lastCheckedTime = counterTime
722 counterTime++
723 if state.loggingLevel > 1 {
724 state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState))
725 }
726
727 if blockChanged {
728
729 converged = false
730 changed := false
731 state.changedSlots.clear()
732
733
734 for _, v := range b.Values {
735 slots := state.valueNames[v.ID]
736
737
738 var source *Value
739 switch v.Op {
740 case OpStoreReg:
741 source = v.Args[0]
742 case OpLoadReg:
743 switch a := v.Args[0]; a.Op {
744 case OpArg, OpPhi:
745 source = a
746 case OpStoreReg:
747 source = a.Args[0]
748 default:
749 if state.loggingLevel > 1 {
750 state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a)
751 }
752 }
753 }
754
755
756 if source != nil && k == 0 {
757
758 slots = append(slots, state.valueNames[source.ID]...)
759 state.valueNames[v.ID] = slots
760 }
761
762 reg, _ := state.f.getHome(v.ID).(*Register)
763 c := state.processValue(v, slots, reg)
764 changed = changed || c
765 }
766
767 if state.loggingLevel > 1 {
768 state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState))
769 }
770
771 locs.relevant = locs.relevant || changed
772 if !changed {
773 locs.endState = startState
774 } else {
775 for _, id := range state.changedSlots.contents() {
776 slotID := SlotID(id)
777 slotLoc := state.currentState.slots[slotID]
778 if slotLoc.absent() {
779 startState.Delete(int32(slotID))
780 continue
781 }
782 old := startState.Find(int32(slotID))
783 if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc {
784 startState.Insert(int32(slotID),
785 &liveSlot{VarLoc: slotLoc})
786 }
787 }
788 locs.endState = startState
789 }
790 locs.lastChangedTime = counterTime
791 }
792 counterTime++
793 }
794 }
795 return blockLocs
796 }
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832 func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) {
833
834 var predsBuf [10]*Block
835
836 preds := predsBuf[:0]
837 locs := blockLocs[b.ID]
838
839 blockChanged := !locs.everProcessed
840 updating := locs.everProcessed
841
842
843
844 for _, pred := range b.Preds {
845 if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed {
846
847 preds = append(preds, pred.b)
848 }
849 }
850
851 locs.everProcessed = true
852
853 if state.loggingLevel > 1 {
854
855
856 preds2 := make([]*Block, len(preds))
857 copy(preds2, preds)
858 state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime)
859 }
860
861 state.changedVars.clear()
862
863 markChangedVars := func(slots, merged abt.T) {
864 if !forLocationLists {
865 return
866 }
867
868
869
870 for it := slots.Iterator(); !it.Done(); {
871 k, v := it.Next()
872 m := merged.Find(k)
873 if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc {
874 state.changedVars.add(ID(state.slotVars[k]))
875 }
876 }
877 }
878
879 reset := func(ourStartState abt.T) {
880 if !(forLocationLists || blockChanged) {
881
882
883
884 return
885 }
886 state.currentState.reset(ourStartState)
887 }
888
889
890 if len(preds) == 0 {
891 if previousBlock != nil {
892 state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String())
893 }
894
895 reset(abt.T{})
896 return abt.T{}, blockChanged
897 }
898
899
900 l0 := blockLocs[preds[0].ID]
901 p0 := l0.endState
902 if len(preds) == 1 {
903 if previousBlock != nil && preds[0].ID != previousBlock.ID {
904
905 markChangedVars(blockLocs[previousBlock.ID].endState, p0)
906 }
907 locs.startState = p0
908 blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime
909 reset(p0)
910 return p0, blockChanged
911 }
912
913
914
915 if updating {
916
917
918
919
920
921
922
923 for i := len(preds) - 1; i >= 0; i-- {
924 pred := preds[i]
925 if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime {
926 continue
927 }
928 preds[i] = preds[len(preds)-1]
929 preds = preds[:len(preds)-1]
930 if state.loggingLevel > 2 {
931 state.logf("Pruned b%d, lastChanged was %d but b%d lastChecked is %d\n", pred.ID, blockLocs[pred.ID].lastChangedTime, b.ID, locs.lastCheckedTime)
932 }
933 }
934
935
936 if len(preds) == 0 {
937 blockChanged = false
938
939 reset(locs.startState)
940 if state.loggingLevel > 2 {
941 state.logf("Early out, no predecessors changed since last check\n")
942 }
943 if previousBlock != nil {
944 markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState)
945 }
946 return locs.startState, blockChanged
947 }
948 }
949
950 baseID := preds[0].ID
951 baseState := p0
952
953
954 for _, pred := range preds[1:] {
955 if blockLocs[pred.ID].endState.Size() < baseState.Size() {
956 baseState = blockLocs[pred.ID].endState
957 baseID = pred.ID
958 }
959 }
960
961 if state.loggingLevel > 2 {
962 state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID]))
963 for _, pred := range preds {
964 if pred.ID == baseID {
965 continue
966 }
967 state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID]))
968 }
969 }
970
971 state.currentState.reset(abt.T{})
972
973
974 slotLocs := state.currentState.slots
975
976
977
978
979
980 newState := baseState
981 if updating {
982 newState = blockLocs[b.ID].startState
983 }
984
985 for it := newState.Iterator(); !it.Done(); {
986 k, d := it.Next()
987 thisSlot := d.(*liveSlot)
988 x := thisSlot.VarLoc
989 x0 := x
990
991
992 for _, other := range preds {
993 if !updating && other.ID == baseID {
994 continue
995 }
996 otherSlot := blockLocs[other.ID].endState.Find(k)
997 if otherSlot == nil {
998 x = VarLoc{}
999 break
1000 }
1001 y := otherSlot.(*liveSlot).VarLoc
1002 x = x.intersect(y)
1003 if x.absent() {
1004 x = VarLoc{}
1005 break
1006 }
1007 }
1008
1009
1010 if x.absent() {
1011 if !x0.absent() {
1012 blockChanged = true
1013 newState.Delete(k)
1014 }
1015 slotLocs[k] = VarLoc{}
1016 continue
1017 }
1018 if x != x0 {
1019 blockChanged = true
1020 newState.Insert(k, &liveSlot{VarLoc: x})
1021 }
1022
1023 slotLocs[k] = x
1024 mask := uint64(x.Registers)
1025 for {
1026 if mask == 0 {
1027 break
1028 }
1029 reg := uint8(bits.TrailingZeros64(mask))
1030 mask &^= 1 << reg
1031 state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k))
1032 }
1033 }
1034
1035 if previousBlock != nil {
1036 markChangedVars(blockLocs[previousBlock.ID].endState, newState)
1037 }
1038 locs.startState = newState
1039 return newState, blockChanged
1040 }
1041
1042
1043
1044
1045
1046 func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool {
1047 locs := state.currentState
1048 changed := false
1049 setSlot := func(slot SlotID, loc VarLoc) {
1050 changed = true
1051 state.changedVars.add(ID(state.slotVars[slot]))
1052 state.changedSlots.add(ID(slot))
1053 state.currentState.slots[slot] = loc
1054 }
1055
1056
1057
1058
1059 clobbers := uint64(opcodeTable[v.Op].reg.clobbers)
1060 for {
1061 if clobbers == 0 {
1062 break
1063 }
1064 reg := uint8(bits.TrailingZeros64(clobbers))
1065 clobbers &^= 1 << reg
1066
1067 for _, slot := range locs.registers[reg] {
1068 if state.loggingLevel > 1 {
1069 state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg])
1070 }
1071
1072 last := locs.slots[slot]
1073 if last.absent() {
1074 state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg])
1075 continue
1076 }
1077 regs := last.Registers &^ (1 << reg)
1078 setSlot(slot, VarLoc{regs, last.StackOffset})
1079 }
1080
1081 locs.registers[reg] = locs.registers[reg][:0]
1082 }
1083
1084 switch {
1085 case v.Op == OpVarDef:
1086 n := v.Aux.(*ir.Name)
1087 if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
1088 break
1089 }
1090
1091 slotID := state.varParts[n][0]
1092 var stackOffset StackOffset
1093 if v.Op == OpVarDef {
1094 stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1)
1095 }
1096 setSlot(slotID, VarLoc{0, stackOffset})
1097 if state.loggingLevel > 1 {
1098 if v.Op == OpVarDef {
1099 state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID])
1100 } else {
1101 state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID])
1102 }
1103 }
1104
1105 case v.Op == OpArg:
1106 home := state.f.getHome(v.ID).(LocalSlot)
1107 stackOffset := state.stackOffset(home)<<1 | 1
1108 for _, slot := range vSlots {
1109 if state.loggingLevel > 1 {
1110 state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home)
1111 if last := locs.slots[slot]; !last.absent() {
1112 state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot])
1113 }
1114 }
1115
1116 setSlot(slot, VarLoc{0, StackOffset(stackOffset)})
1117 }
1118
1119 case v.Op == OpStoreReg:
1120 home := state.f.getHome(v.ID).(LocalSlot)
1121 stackOffset := state.stackOffset(home)<<1 | 1
1122 for _, slot := range vSlots {
1123 last := locs.slots[slot]
1124 if last.absent() {
1125 if state.loggingLevel > 1 {
1126 state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg)
1127 }
1128 break
1129 }
1130
1131 setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)})
1132 if state.loggingLevel > 1 {
1133 state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home))
1134 }
1135 }
1136
1137 case vReg != nil:
1138 if state.loggingLevel > 1 {
1139 newSlots := make([]bool, len(state.slots))
1140 for _, slot := range vSlots {
1141 newSlots[slot] = true
1142 }
1143
1144 for _, slot := range locs.registers[vReg.num] {
1145 if !newSlots[slot] {
1146 state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg)
1147 }
1148 }
1149 }
1150
1151 for _, slot := range locs.registers[vReg.num] {
1152 last := locs.slots[slot]
1153 setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset})
1154 }
1155 locs.registers[vReg.num] = locs.registers[vReg.num][:0]
1156 locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...)
1157 for _, slot := range vSlots {
1158 if state.loggingLevel > 1 {
1159 state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg)
1160 }
1161
1162 last := locs.slots[slot]
1163 setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset})
1164 }
1165 }
1166 return changed
1167 }
1168
1169
1170
1171 func varOffset(slot LocalSlot) int64 {
1172 offset := slot.Off
1173 s := &slot
1174 for ; s.SplitOf != nil; s = s.SplitOf {
1175 offset += s.SplitOffset
1176 }
1177 return offset
1178 }
1179
1180
1181
1182 type pendingEntry struct {
1183 present bool
1184 startBlock, startValue ID
1185
1186
1187 pieces []VarLoc
1188 }
1189
1190 func (e *pendingEntry) clear() {
1191 e.present = false
1192 e.startBlock = 0
1193 e.startValue = 0
1194 for i := range e.pieces {
1195 e.pieces[i] = VarLoc{}
1196 }
1197 }
1198
1199
1200
1201
1202
1203 func canMerge(pending, new VarLoc) bool {
1204 if pending.absent() && new.absent() {
1205 return true
1206 }
1207 if pending.absent() || new.absent() {
1208 return false
1209 }
1210
1211
1212 if pending.onStack() && pending.StackOffset != new.StackOffset {
1213
1214
1215 return false
1216 }
1217 if pending.Registers&new.Registers != pending.Registers {
1218
1219 return false
1220 }
1221 return true
1222 }
1223
1224
1225 func firstReg(set RegisterSet) uint8 {
1226 if set == 0 {
1227
1228
1229 return 0
1230 }
1231 return uint8(bits.TrailingZeros64(uint64(set)))
1232 }
1233
1234
1235
1236
1237
1238
1239 func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
1240
1241
1242
1243 var prevBlock *Block
1244 for _, b := range state.f.Blocks {
1245 state.mergePredecessors(b, blockLocs, prevBlock, true)
1246
1247
1248 for _, varID := range state.changedVars.contents() {
1249 state.updateVar(VarID(varID), b, BlockStart)
1250 }
1251 state.changedVars.clear()
1252
1253 if !blockLocs[b.ID].relevant {
1254 continue
1255 }
1256
1257 mustBeFirst := func(v *Value) bool {
1258 return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
1259 v.Op == OpArgIntReg || v.Op == OpArgFloatReg
1260 }
1261
1262 blockPrologComplete := func(v *Value) bool {
1263 if b.ID != state.f.Entry.ID {
1264 return !opcodeTable[v.Op].zeroWidth
1265 } else {
1266 return v.Op == OpInitMem
1267 }
1268 }
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290 for idx := 0; idx < len(b.Values); idx++ {
1291 v := b.Values[idx]
1292 if blockPrologComplete(v) {
1293 break
1294 }
1295
1296 if !mustBeFirst(v) && v.Op != OpArg {
1297 continue
1298 }
1299 slots := state.valueNames[v.ID]
1300 reg, _ := state.f.getHome(v.ID).(*Register)
1301 changed := state.processValue(v, slots, reg)
1302 if changed {
1303 for _, varID := range state.changedVars.contents() {
1304 state.updateVar(VarID(varID), v.Block, BlockStart)
1305 }
1306 state.changedVars.clear()
1307 }
1308 }
1309
1310
1311
1312 zeroWidthPending := false
1313 prologComplete := false
1314
1315 for _, v := range b.Values {
1316 if blockPrologComplete(v) {
1317 prologComplete = true
1318 }
1319 slots := state.valueNames[v.ID]
1320 reg, _ := state.f.getHome(v.ID).(*Register)
1321 changed := state.processValue(v, slots, reg)
1322
1323 if opcodeTable[v.Op].zeroWidth {
1324 if prologComplete && mustBeFirst(v) {
1325 panic(fmt.Errorf("Unexpected placement of op '%s' appearing after non-pseudo-op at beginning of block %s in %s\n%s", v.LongString(), b, b.Func.Name, b.Func))
1326 }
1327 if changed {
1328 if mustBeFirst(v) || v.Op == OpArg {
1329
1330 continue
1331 }
1332 zeroWidthPending = true
1333 }
1334 continue
1335 }
1336 if !changed && !zeroWidthPending {
1337 continue
1338 }
1339
1340
1341 zeroWidthPending = false
1342 for _, varID := range state.changedVars.contents() {
1343 state.updateVar(VarID(varID), v.Block, v)
1344 }
1345 state.changedVars.clear()
1346 }
1347 for _, varID := range state.changedVars.contents() {
1348 state.updateVar(VarID(varID), b, BlockEnd)
1349 }
1350
1351 prevBlock = b
1352 }
1353
1354 if state.loggingLevel > 0 {
1355 state.logf("location lists:\n")
1356 }
1357
1358
1359 for varID := range state.lists {
1360 state.writePendingEntry(VarID(varID), -1, FuncEnd.ID)
1361 list := state.lists[varID]
1362 if state.loggingLevel > 0 {
1363 if len(list) == 0 {
1364 state.logf("\t%v : empty list\n", state.vars[varID])
1365 } else {
1366 state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID]))
1367 }
1368 }
1369 }
1370 }
1371
1372
1373
1374
1375 func (state *debugState) updateVar(varID VarID, b *Block, v *Value) {
1376 curLoc := state.currentState.slots
1377
1378 empty := true
1379 for _, slotID := range state.varSlots[varID] {
1380 if !curLoc[slotID].absent() {
1381 empty = false
1382 break
1383 }
1384 }
1385 pending := &state.pendingEntries[varID]
1386 if empty {
1387 state.writePendingEntry(varID, b.ID, v.ID)
1388 pending.clear()
1389 return
1390 }
1391
1392
1393 if pending.present {
1394 merge := true
1395 for i, slotID := range state.varSlots[varID] {
1396 if !canMerge(pending.pieces[i], curLoc[slotID]) {
1397 merge = false
1398 break
1399 }
1400 }
1401 if merge {
1402 return
1403 }
1404 }
1405
1406 state.writePendingEntry(varID, b.ID, v.ID)
1407 pending.present = true
1408 pending.startBlock = b.ID
1409 pending.startValue = v.ID
1410 for i, slot := range state.varSlots[varID] {
1411 pending.pieces[i] = curLoc[slot]
1412 }
1413 }
1414
1415
1416
1417 func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
1418 pending := state.pendingEntries[varID]
1419 if !pending.present {
1420 return
1421 }
1422
1423
1424
1425 start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue)
1426 end, endOK := encodeValue(state.ctxt, endBlock, endValue)
1427 if !startOK || !endOK {
1428
1429
1430 return
1431 }
1432 if start == end {
1433 if state.loggingLevel > 1 {
1434
1435
1436 state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name)
1437 }
1438 return
1439 }
1440
1441 list := state.lists[varID]
1442 list = appendPtr(state.ctxt, list, start)
1443 list = appendPtr(state.ctxt, list, end)
1444
1445
1446 sizeIdx := len(list)
1447 list = list[:len(list)+2]
1448
1449 if state.loggingLevel > 1 {
1450 var partStrs []string
1451 for i, slot := range state.varSlots[varID] {
1452 partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i])))
1453 }
1454 state.logf("Add entry for %v: \tb%vv%v-b%vv%v = \t%v\n", state.vars[varID], pending.startBlock, pending.startValue, endBlock, endValue, strings.Join(partStrs, " "))
1455 }
1456
1457 for i, slotID := range state.varSlots[varID] {
1458 loc := pending.pieces[i]
1459 slot := state.slots[slotID]
1460
1461 if !loc.absent() {
1462 if loc.onStack() {
1463 if loc.stackOffsetValue() == 0 {
1464 list = append(list, dwarf.DW_OP_call_frame_cfa)
1465 } else {
1466 list = append(list, dwarf.DW_OP_fbreg)
1467 list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue()))
1468 }
1469 } else {
1470 regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()]
1471 if regnum < 32 {
1472 list = append(list, dwarf.DW_OP_reg0+byte(regnum))
1473 } else {
1474 list = append(list, dwarf.DW_OP_regx)
1475 list = dwarf.AppendUleb128(list, uint64(regnum))
1476 }
1477 }
1478 }
1479
1480 if len(state.varSlots[varID]) > 1 {
1481 list = append(list, dwarf.DW_OP_piece)
1482 list = dwarf.AppendUleb128(list, uint64(slot.Type.Size()))
1483 }
1484 }
1485 state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1486 state.lists[varID] = list
1487 }
1488
1489
1490
1491 func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
1492 if buildcfg.Experiment.Dwarf5 {
1493 debugInfo.PutLocationListDwarf5(list, ctxt, listSym, startPC)
1494 } else {
1495 debugInfo.PutLocationListDwarf4(list, ctxt, listSym, startPC)
1496 }
1497 }
1498
1499
1500
1501
1502
1503
1504
1505
1506 func (debugInfo *FuncDebug) PutLocationListDwarf5(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
1507 getPC := debugInfo.GetPC
1508
1509
1510 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx)
1511 listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
1512
1513 var stbuf, enbuf [10]byte
1514 stb, enb := stbuf[:], enbuf[:]
1515
1516 for i := 0; i < len(list); {
1517 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
1518 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
1519
1520
1521
1522 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
1523 stb, enb = stb[:0], enb[:0]
1524 stb = dwarf.AppendUleb128(stb, uint64(begin))
1525 enb = dwarf.AppendUleb128(enb, uint64(end))
1526 listSym.WriteBytes(ctxt, listSym.Size, stb)
1527 listSym.WriteBytes(ctxt, listSym.Size, enb)
1528
1529
1530
1531
1532 i += 2 * ctxt.Arch.PtrSize
1533 datalen := int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
1534 i += 2
1535 stb = stb[:0]
1536 stb = dwarf.AppendUleb128(stb, uint64(datalen))
1537 listSym.WriteBytes(ctxt, listSym.Size, stb)
1538 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen])
1539
1540 i += datalen
1541 }
1542
1543
1544 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
1545 }
1546
1547
1548
1549 func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
1550 getPC := debugInfo.GetPC
1551
1552 if ctxt.UseBASEntries {
1553 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
1554 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
1555 }
1556
1557
1558 for i := 0; i < len(list); {
1559 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
1560 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
1561
1562
1563
1564
1565
1566 if begin == 0 && end == 0 {
1567 end = 1
1568 }
1569
1570 if ctxt.UseBASEntries {
1571 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin))
1572 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end))
1573 } else {
1574 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin))
1575 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end))
1576 }
1577
1578 i += 2 * ctxt.Arch.PtrSize
1579 datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
1580 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen])
1581 i += datalen
1582 }
1583
1584
1585
1586 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1587 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1588 }
1589
1590
1591
1592
1593
1594
1595 func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) {
1596 if ctxt.Arch.PtrSize == 8 {
1597 result := uint64(b)<<32 | uint64(uint32(v))
1598
1599 return result, true
1600 }
1601 if ctxt.Arch.PtrSize != 4 {
1602 panic("unexpected pointer size")
1603 }
1604 if ID(int16(b)) != b || ID(int16(v)) != v {
1605 return 0, false
1606 }
1607 return uint64(b)<<16 | uint64(uint16(v)), true
1608 }
1609
1610
1611 func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) {
1612 if ctxt.Arch.PtrSize == 8 {
1613 b, v := ID(word>>32), ID(word)
1614
1615 return b, v
1616 }
1617 if ctxt.Arch.PtrSize != 4 {
1618 panic("unexpected pointer size")
1619 }
1620 return ID(word >> 16), ID(int16(word))
1621 }
1622
1623
1624 func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte {
1625 if cap(buf) < len(buf)+20 {
1626 b := make([]byte, len(buf), 20+cap(buf)*2)
1627 copy(b, buf)
1628 buf = b
1629 }
1630 writeAt := len(buf)
1631 buf = buf[0 : len(buf)+ctxt.Arch.PtrSize]
1632 writePtr(ctxt, buf[writeAt:], word)
1633 return buf
1634 }
1635
1636
1637 func writePtr(ctxt *obj.Link, buf []byte, word uint64) {
1638 switch ctxt.Arch.PtrSize {
1639 case 4:
1640 ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word))
1641 case 8:
1642 ctxt.Arch.ByteOrder.PutUint64(buf, word)
1643 default:
1644 panic("unexpected pointer size")
1645 }
1646
1647 }
1648
1649
1650 func readPtr(ctxt *obj.Link, buf []byte) uint64 {
1651 switch ctxt.Arch.PtrSize {
1652 case 4:
1653 return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
1654 case 8:
1655 return ctxt.Arch.ByteOrder.Uint64(buf)
1656 default:
1657 panic("unexpected pointer size")
1658 }
1659
1660 }
1661
1662
1663
1664
1665
1666 func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
1667 start, startOK := encodeValue(ctxt, f.Entry.ID, st)
1668 end, endOK := encodeValue(ctxt, f.Entry.ID, en)
1669 if !startOK || !endOK {
1670
1671
1672
1673 return nil, 0
1674 }
1675 list = appendPtr(ctxt, list, start)
1676 list = appendPtr(ctxt, list, end)
1677
1678
1679
1680 sizeIdx := len(list)
1681 list = list[:len(list)+2]
1682 return list, sizeIdx
1683 }
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705 func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) {
1706
1707
1708
1709
1710 isRegMoveLike := func(v *Value) (bool, ID) {
1711 n, ok := v.Aux.(*ir.Name)
1712 var r ID
1713 if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
1714 return false, r
1715 }
1716 regInputs, memInputs, spInputs := 0, 0, 0
1717 for _, a := range v.Args {
1718 if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
1719 (needCloCtx && a.Op.isLoweredGetClosurePtr()) {
1720 regInputs++
1721 r = a.ID
1722 } else if a.Type.IsMemory() {
1723 memInputs++
1724 } else if a.Op == OpSP {
1725 spInputs++
1726 } else {
1727 return false, r
1728 }
1729 }
1730 return v.Type.IsMemory() && memInputs == 1 &&
1731 regInputs == 1 && spInputs == 1, r
1732 }
1733
1734
1735
1736 regArgs := make([]ID, 0, 32)
1737
1738
1739
1740 removeReg := func(r ID) bool {
1741 for i := 0; i < len(regArgs); i++ {
1742 if regArgs[i] == r {
1743 regArgs = slices.Delete(regArgs, i, i+1)
1744 return true
1745 }
1746 }
1747 return false
1748 }
1749
1750
1751
1752
1753
1754 var cloRegStore *Value
1755 for k, v := range f.Entry.Values {
1756 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
1757 regArgs = append(regArgs, v.ID)
1758 continue
1759 }
1760 if needCloCtx && v.Op.isLoweredGetClosurePtr() {
1761 regArgs = append(regArgs, v.ID)
1762 cloRegStore = v
1763 continue
1764 }
1765 if ok, r := isRegMoveLike(v); ok {
1766 if removed := removeReg(r); removed {
1767 if len(regArgs) == 0 {
1768
1769
1770
1771
1772 if k < len(f.Entry.Values)-1 {
1773 return f.Entry.Values[k+1].ID, cloRegStore
1774 }
1775 return BlockEnd.ID, cloRegStore
1776 }
1777 }
1778 }
1779 if v.Op.IsCall() {
1780
1781 return v.ID, cloRegStore
1782 }
1783 }
1784
1785 return ID(-1), cloRegStore
1786 }
1787
1788
1789
1790
1791 func isNamedRegParam(p abi.ABIParamAssignment) bool {
1792 if p.Name == nil {
1793 return false
1794 }
1795 n := p.Name
1796 if n.Sym() == nil || n.Sym().IsBlank() {
1797 return false
1798 }
1799 if len(p.Registers) == 0 {
1800 return false
1801 }
1802 return true
1803 }
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816 func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
1817
1818 needCloCtx := f.CloSlot != nil
1819 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
1820
1821
1822
1823
1824
1825 numRegParams := 0
1826 for _, inp := range pri.InParams() {
1827 if isNamedRegParam(inp) {
1828 numRegParams++
1829 }
1830 }
1831 if numRegParams == 0 && !needCloCtx {
1832 return
1833 }
1834
1835 state := debugState{f: f}
1836
1837 if loggingEnabled {
1838 state.logf("generating -N reg param loc lists for func %q\n", f.Name)
1839 }
1840
1841
1842
1843 var cloReg int16
1844
1845 extraForCloCtx := 0
1846 if needCloCtx {
1847 extraForCloCtx = 1
1848 }
1849
1850
1851 rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
1852
1853
1854
1855 afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
1856
1857 if needCloCtx {
1858 reg, _ := state.f.getHome(cloRegStore.ID).(*Register)
1859 cloReg = reg.ObjNum()
1860 if loggingEnabled {
1861 state.logf("needCloCtx is true for func %q, cloreg=%v\n",
1862 f.Name, reg)
1863 }
1864 }
1865
1866 addVarSlot := func(name *ir.Name, typ *types.Type) {
1867 sl := LocalSlot{N: name, Type: typ, Off: 0}
1868 rval.Vars = append(rval.Vars, name)
1869 rval.Slots = append(rval.Slots, sl)
1870 slid := len(rval.VarSlots)
1871 rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
1872 }
1873
1874
1875
1876
1877 params := []abi.ABIParamAssignment{}
1878 for _, inp := range pri.InParams() {
1879 if !isNamedRegParam(inp) {
1880
1881 continue
1882 }
1883 if !IsVarWantedForDebug(inp.Name) {
1884 continue
1885 }
1886 addVarSlot(inp.Name, inp.Type)
1887 params = append(params, inp)
1888 }
1889 if needCloCtx {
1890 addVarSlot(f.CloSlot, f.CloSlot.Type())
1891 cloAssign := abi.ABIParamAssignment{
1892 Type: f.CloSlot.Type(),
1893 Name: f.CloSlot,
1894 Registers: []abi.RegIndex{0},
1895 }
1896 params = append(params, cloAssign)
1897 }
1898
1899
1900 pidx := 0
1901 for _, inp := range params {
1902 if !isNamedRegParam(inp) {
1903
1904 continue
1905 }
1906 if !IsVarWantedForDebug(inp.Name) {
1907 continue
1908 }
1909
1910 sl := rval.Slots[pidx]
1911 n := rval.Vars[pidx]
1912
1913 if afterPrologVal == ID(-1) {
1914
1915
1916
1917
1918 if loggingEnabled {
1919 state.logf("locatePrologEnd failed, skipping %v\n", n)
1920 }
1921 pidx++
1922 continue
1923 }
1924
1925
1926
1927
1928 list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
1929 BlockStart.ID, afterPrologVal)
1930 if list == nil {
1931 pidx++
1932 continue
1933 }
1934 if loggingEnabled {
1935 state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal)
1936 }
1937 rtypes, _ := inp.RegisterTypesAndOffsets()
1938 padding := make([]uint64, 0, 32)
1939 padding = inp.ComputePadding(padding)
1940 for k, r := range inp.Registers {
1941 var reg int16
1942 if n == f.CloSlot {
1943 reg = cloReg
1944 } else {
1945 reg = ObjRegForAbiReg(r, f.Config)
1946 }
1947 dwreg := ctxt.Arch.DWARFRegisters[reg]
1948 if dwreg < 32 {
1949 list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
1950 } else {
1951 list = append(list, dwarf.DW_OP_regx)
1952 list = dwarf.AppendUleb128(list, uint64(dwreg))
1953 }
1954 if loggingEnabled {
1955 state.logf(" piece %d -> dwreg %d", k, dwreg)
1956 }
1957 if len(inp.Registers) > 1 {
1958 list = append(list, dwarf.DW_OP_piece)
1959 ts := rtypes[k].Size()
1960 list = dwarf.AppendUleb128(list, uint64(ts))
1961 if padding[k] > 0 {
1962 if loggingEnabled {
1963 state.logf(" [pad %d bytes]", padding[k])
1964 }
1965 list = append(list, dwarf.DW_OP_piece)
1966 list = dwarf.AppendUleb128(list, padding[k])
1967 }
1968 }
1969 if loggingEnabled {
1970 state.logf("\n")
1971 }
1972 }
1973
1974 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1975
1976
1977
1978 list, sizeIdx = setupLocList(ctxt, f, list,
1979 afterPrologVal, FuncEnd.ID)
1980 if list == nil {
1981 pidx++
1982 continue
1983 }
1984 soff := stackOffset(sl)
1985 if soff == 0 {
1986 list = append(list, dwarf.DW_OP_call_frame_cfa)
1987 } else {
1988 list = append(list, dwarf.DW_OP_fbreg)
1989 list = dwarf.AppendSleb128(list, int64(soff))
1990 }
1991 if loggingEnabled {
1992 state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
1993 }
1994
1995
1996 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1997
1998 rval.LocationLists[pidx] = list
1999 pidx++
2000 }
2001 }
2002
2003
2004
2005
2006
2007 func IsVarWantedForDebug(n ir.Node) bool {
2008 name := n.Sym().Name
2009 if len(name) > 0 && name[0] == '&' {
2010 name = name[1:]
2011 }
2012 if len(name) > 0 && name[0] == '#' {
2013
2014 return strings.HasPrefix(name, "#yield")
2015 }
2016 return true
2017 }
2018
View as plain text