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