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