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