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 func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
1491 getPC := debugInfo.GetPC
1492
1493 if ctxt.UseBASEntries {
1494 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
1495 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
1496 }
1497
1498
1499 for i := 0; i < len(list); {
1500 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
1501 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
1502
1503
1504
1505
1506
1507 if begin == 0 && end == 0 {
1508 end = 1
1509 }
1510
1511 if ctxt.UseBASEntries {
1512 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin))
1513 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end))
1514 } else {
1515 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin))
1516 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end))
1517 }
1518
1519 i += 2 * ctxt.Arch.PtrSize
1520 datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
1521 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen])
1522 i += datalen
1523 }
1524
1525
1526
1527 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1528 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1529 }
1530
1531
1532
1533
1534
1535
1536 func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) {
1537 if ctxt.Arch.PtrSize == 8 {
1538 result := uint64(b)<<32 | uint64(uint32(v))
1539
1540 return result, true
1541 }
1542 if ctxt.Arch.PtrSize != 4 {
1543 panic("unexpected pointer size")
1544 }
1545 if ID(int16(b)) != b || ID(int16(v)) != v {
1546 return 0, false
1547 }
1548 return uint64(b)<<16 | uint64(uint16(v)), true
1549 }
1550
1551
1552 func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) {
1553 if ctxt.Arch.PtrSize == 8 {
1554 b, v := ID(word>>32), ID(word)
1555
1556 return b, v
1557 }
1558 if ctxt.Arch.PtrSize != 4 {
1559 panic("unexpected pointer size")
1560 }
1561 return ID(word >> 16), ID(int16(word))
1562 }
1563
1564
1565 func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte {
1566 if cap(buf) < len(buf)+20 {
1567 b := make([]byte, len(buf), 20+cap(buf)*2)
1568 copy(b, buf)
1569 buf = b
1570 }
1571 writeAt := len(buf)
1572 buf = buf[0 : len(buf)+ctxt.Arch.PtrSize]
1573 writePtr(ctxt, buf[writeAt:], word)
1574 return buf
1575 }
1576
1577
1578 func writePtr(ctxt *obj.Link, buf []byte, word uint64) {
1579 switch ctxt.Arch.PtrSize {
1580 case 4:
1581 ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word))
1582 case 8:
1583 ctxt.Arch.ByteOrder.PutUint64(buf, word)
1584 default:
1585 panic("unexpected pointer size")
1586 }
1587
1588 }
1589
1590
1591 func readPtr(ctxt *obj.Link, buf []byte) uint64 {
1592 switch ctxt.Arch.PtrSize {
1593 case 4:
1594 return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
1595 case 8:
1596 return ctxt.Arch.ByteOrder.Uint64(buf)
1597 default:
1598 panic("unexpected pointer size")
1599 }
1600
1601 }
1602
1603
1604
1605
1606
1607 func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
1608 start, startOK := encodeValue(ctxt, f.Entry.ID, st)
1609 end, endOK := encodeValue(ctxt, f.Entry.ID, en)
1610 if !startOK || !endOK {
1611
1612
1613
1614 return nil, 0
1615 }
1616 list = appendPtr(ctxt, list, start)
1617 list = appendPtr(ctxt, list, end)
1618
1619
1620
1621 sizeIdx := len(list)
1622 list = list[:len(list)+2]
1623 return list, sizeIdx
1624 }
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646 func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) {
1647
1648
1649
1650
1651 isRegMoveLike := func(v *Value) (bool, ID) {
1652 n, ok := v.Aux.(*ir.Name)
1653 var r ID
1654 if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
1655 return false, r
1656 }
1657 regInputs, memInputs, spInputs := 0, 0, 0
1658 for _, a := range v.Args {
1659 if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
1660 (needCloCtx && a.Op.isLoweredGetClosurePtr()) {
1661 regInputs++
1662 r = a.ID
1663 } else if a.Type.IsMemory() {
1664 memInputs++
1665 } else if a.Op == OpSP {
1666 spInputs++
1667 } else {
1668 return false, r
1669 }
1670 }
1671 return v.Type.IsMemory() && memInputs == 1 &&
1672 regInputs == 1 && spInputs == 1, r
1673 }
1674
1675
1676
1677 regArgs := make([]ID, 0, 32)
1678
1679
1680
1681 removeReg := func(r ID) bool {
1682 for i := 0; i < len(regArgs); i++ {
1683 if regArgs[i] == r {
1684 regArgs = slices.Delete(regArgs, i, i+1)
1685 return true
1686 }
1687 }
1688 return false
1689 }
1690
1691
1692
1693
1694
1695 var cloRegStore *Value
1696 for k, v := range f.Entry.Values {
1697 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
1698 regArgs = append(regArgs, v.ID)
1699 continue
1700 }
1701 if needCloCtx && v.Op.isLoweredGetClosurePtr() {
1702 regArgs = append(regArgs, v.ID)
1703 cloRegStore = v
1704 continue
1705 }
1706 if ok, r := isRegMoveLike(v); ok {
1707 if removed := removeReg(r); removed {
1708 if len(regArgs) == 0 {
1709
1710
1711
1712
1713 if k < len(f.Entry.Values)-1 {
1714 return f.Entry.Values[k+1].ID, cloRegStore
1715 }
1716 return BlockEnd.ID, cloRegStore
1717 }
1718 }
1719 }
1720 if v.Op.IsCall() {
1721
1722 return v.ID, cloRegStore
1723 }
1724 }
1725
1726 return ID(-1), cloRegStore
1727 }
1728
1729
1730
1731
1732 func isNamedRegParam(p abi.ABIParamAssignment) bool {
1733 if p.Name == nil {
1734 return false
1735 }
1736 n := p.Name
1737 if n.Sym() == nil || n.Sym().IsBlank() {
1738 return false
1739 }
1740 if len(p.Registers) == 0 {
1741 return false
1742 }
1743 return true
1744 }
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757 func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
1758
1759 needCloCtx := f.CloSlot != nil
1760 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
1761
1762
1763
1764
1765
1766 numRegParams := 0
1767 for _, inp := range pri.InParams() {
1768 if isNamedRegParam(inp) {
1769 numRegParams++
1770 }
1771 }
1772 if numRegParams == 0 && !needCloCtx {
1773 return
1774 }
1775
1776 state := debugState{f: f}
1777
1778 if loggingEnabled {
1779 state.logf("generating -N reg param loc lists for func %q\n", f.Name)
1780 }
1781
1782
1783
1784 var cloReg int16
1785
1786 extraForCloCtx := 0
1787 if needCloCtx {
1788 extraForCloCtx = 1
1789 }
1790
1791
1792 rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
1793
1794
1795
1796 afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
1797
1798 if needCloCtx {
1799 reg, _ := state.f.getHome(cloRegStore.ID).(*Register)
1800 cloReg = reg.ObjNum()
1801 if loggingEnabled {
1802 state.logf("needCloCtx is true for func %q, cloreg=%v\n",
1803 f.Name, reg)
1804 }
1805 }
1806
1807 addVarSlot := func(name *ir.Name, typ *types.Type) {
1808 sl := LocalSlot{N: name, Type: typ, Off: 0}
1809 rval.Vars = append(rval.Vars, name)
1810 rval.Slots = append(rval.Slots, sl)
1811 slid := len(rval.VarSlots)
1812 rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
1813 }
1814
1815
1816
1817
1818 params := []abi.ABIParamAssignment{}
1819 for _, inp := range pri.InParams() {
1820 if !isNamedRegParam(inp) {
1821
1822 continue
1823 }
1824 if !IsVarWantedForDebug(inp.Name) {
1825 continue
1826 }
1827 addVarSlot(inp.Name, inp.Type)
1828 params = append(params, inp)
1829 }
1830 if needCloCtx {
1831 addVarSlot(f.CloSlot, f.CloSlot.Type())
1832 cloAssign := abi.ABIParamAssignment{
1833 Type: f.CloSlot.Type(),
1834 Name: f.CloSlot,
1835 Registers: []abi.RegIndex{0},
1836 }
1837 params = append(params, cloAssign)
1838 }
1839
1840
1841 pidx := 0
1842 for _, inp := range params {
1843 if !isNamedRegParam(inp) {
1844
1845 continue
1846 }
1847 if !IsVarWantedForDebug(inp.Name) {
1848 continue
1849 }
1850
1851 sl := rval.Slots[pidx]
1852 n := rval.Vars[pidx]
1853
1854 if afterPrologVal == ID(-1) {
1855
1856
1857
1858
1859 if loggingEnabled {
1860 state.logf("locatePrologEnd failed, skipping %v\n", n)
1861 }
1862 pidx++
1863 continue
1864 }
1865
1866
1867
1868
1869 list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
1870 BlockStart.ID, afterPrologVal)
1871 if list == nil {
1872 pidx++
1873 continue
1874 }
1875 if loggingEnabled {
1876 state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal)
1877 }
1878 rtypes, _ := inp.RegisterTypesAndOffsets()
1879 padding := make([]uint64, 0, 32)
1880 padding = inp.ComputePadding(padding)
1881 for k, r := range inp.Registers {
1882 var reg int16
1883 if n == f.CloSlot {
1884 reg = cloReg
1885 } else {
1886 reg = ObjRegForAbiReg(r, f.Config)
1887 }
1888 dwreg := ctxt.Arch.DWARFRegisters[reg]
1889 if dwreg < 32 {
1890 list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
1891 } else {
1892 list = append(list, dwarf.DW_OP_regx)
1893 list = dwarf.AppendUleb128(list, uint64(dwreg))
1894 }
1895 if loggingEnabled {
1896 state.logf(" piece %d -> dwreg %d", k, dwreg)
1897 }
1898 if len(inp.Registers) > 1 {
1899 list = append(list, dwarf.DW_OP_piece)
1900 ts := rtypes[k].Size()
1901 list = dwarf.AppendUleb128(list, uint64(ts))
1902 if padding[k] > 0 {
1903 if loggingEnabled {
1904 state.logf(" [pad %d bytes]", padding[k])
1905 }
1906 list = append(list, dwarf.DW_OP_piece)
1907 list = dwarf.AppendUleb128(list, padding[k])
1908 }
1909 }
1910 if loggingEnabled {
1911 state.logf("\n")
1912 }
1913 }
1914
1915 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1916
1917
1918
1919 list, sizeIdx = setupLocList(ctxt, f, list,
1920 afterPrologVal, FuncEnd.ID)
1921 if list == nil {
1922 pidx++
1923 continue
1924 }
1925 soff := stackOffset(sl)
1926 if soff == 0 {
1927 list = append(list, dwarf.DW_OP_call_frame_cfa)
1928 } else {
1929 list = append(list, dwarf.DW_OP_fbreg)
1930 list = dwarf.AppendSleb128(list, int64(soff))
1931 }
1932 if loggingEnabled {
1933 state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
1934 }
1935
1936
1937 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1938
1939 rval.LocationLists[pidx] = list
1940 pidx++
1941 }
1942 }
1943
1944
1945
1946
1947
1948 func IsVarWantedForDebug(n ir.Node) bool {
1949 name := n.Sym().Name
1950 if len(name) > 0 && name[0] == '&' {
1951 name = name[1:]
1952 }
1953 if len(name) > 0 && name[0] == '#' {
1954
1955 return strings.HasPrefix(name, "#yield")
1956 }
1957 return true
1958 }
1959
View as plain text