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 "encoding/hex"
16 "fmt"
17 "internal/buildcfg"
18 "math/bits"
19 "sort"
20 "strings"
21 )
22
23 type SlotID int32
24 type VarID int32
25
26
27
28
29 type FuncDebug struct {
30
31 Slots []LocalSlot
32
33 Vars []*ir.Name
34
35 VarSlots [][]SlotID
36
37 LocationLists [][]byte
38
39
40 RegOutputParams []*ir.Name
41
42 OptDcl []*ir.Name
43
44
45
46
47
48 GetPC func(block, value ID) int64
49 }
50
51 type BlockDebug struct {
52
53
54 startState, endState abt.T
55
56
57
58 lastCheckedTime, lastChangedTime int32
59
60 relevant bool
61
62
63
64 everProcessed bool
65 }
66
67
68 type liveSlot struct {
69 VarLoc
70 }
71
72 func (ls *liveSlot) String() string {
73 return fmt.Sprintf("0x%x.%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1)
74 }
75
76 func (ls liveSlot) absent() bool {
77 return ls.Registers == 0 && !ls.onStack()
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 for i := range slots {
105 slots[i] = VarLoc{}
106 }
107 for i := range registers {
108 registers[i] = registers[i][:0]
109 }
110 for it := live.Iterator(); !it.Done(); {
111 k, d := it.Next()
112 live := d.(*liveSlot)
113 slots[k] = live.VarLoc
114 if live.VarLoc.Registers == 0 {
115 continue
116 }
117
118 mask := uint64(live.VarLoc.Registers)
119 for {
120 if mask == 0 {
121 break
122 }
123 reg := uint8(bits.TrailingZeros64(mask))
124 mask &^= 1 << reg
125
126 registers[reg] = append(registers[reg], SlotID(k))
127 }
128 }
129 state.slots, state.registers = slots, registers
130 }
131
132 func (s *debugState) LocString(loc VarLoc) string {
133 if loc.absent() {
134 return "<nil>"
135 }
136
137 var storage []string
138 if loc.onStack() {
139 storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue()))
140 }
141
142 mask := uint64(loc.Registers)
143 for {
144 if mask == 0 {
145 break
146 }
147 reg := uint8(bits.TrailingZeros64(mask))
148 mask &^= 1 << reg
149
150 storage = append(storage, s.registers[reg].String())
151 }
152 return strings.Join(storage, ",")
153 }
154
155
156 type VarLoc struct {
157
158
159 Registers RegisterSet
160
161 StackOffset
162 }
163
164 func (loc VarLoc) absent() bool {
165 return loc.Registers == 0 && !loc.onStack()
166 }
167
168 func (loc VarLoc) intersect(other VarLoc) VarLoc {
169 if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset {
170 loc.StackOffset = 0
171 }
172 loc.Registers &= other.Registers
173 return loc
174 }
175
176 var BlockStart = &Value{
177 ID: -10000,
178 Op: OpInvalid,
179 Aux: StringToAux("BlockStart"),
180 }
181
182 var BlockEnd = &Value{
183 ID: -20000,
184 Op: OpInvalid,
185 Aux: StringToAux("BlockEnd"),
186 }
187
188 var FuncEnd = &Value{
189 ID: -30000,
190 Op: OpInvalid,
191 Aux: StringToAux("FuncEnd"),
192 }
193
194
195 type RegisterSet uint64
196
197
198
199
200 func (s *debugState) logf(msg string, args ...interface{}) {
201 if s.f.PrintOrHtmlSSA {
202 fmt.Printf(msg, args...)
203 }
204 }
205
206 type debugState struct {
207
208 slots []LocalSlot
209 vars []*ir.Name
210 varSlots [][]SlotID
211 lists [][]byte
212
213
214 slotVars []VarID
215
216 f *Func
217 loggingLevel int
218 convergeCount int
219 registers []Register
220 stackOffset func(LocalSlot) int32
221 ctxt *obj.Link
222
223
224 valueNames [][]SlotID
225
226
227 currentState stateAtPC
228 changedVars *sparseSet
229 changedSlots *sparseSet
230
231
232 pendingEntries []pendingEntry
233
234 varParts map[*ir.Name][]SlotID
235 blockDebug []BlockDebug
236 pendingSlotLocs []VarLoc
237 partsByVarOffset sort.Interface
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 for n := range state.varParts {
592 delete(state.varParts, n)
593 }
594 }
595
596
597
598
599 state.slots = state.slots[:0]
600 state.vars = state.vars[:0]
601 for i, slot := range f.Names {
602 state.slots = append(state.slots, *slot)
603 if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
604 continue
605 }
606
607 topSlot := slot
608 for topSlot.SplitOf != nil {
609 topSlot = topSlot.SplitOf
610 }
611 if _, ok := state.varParts[topSlot.N]; !ok {
612 state.vars = append(state.vars, topSlot.N)
613 }
614 state.varParts[topSlot.N] = append(state.varParts[topSlot.N], SlotID(i))
615 }
616
617
618
619 for _, b := range f.Blocks {
620 for _, v := range b.Values {
621 if v.Op == OpVarDef {
622 n := v.Aux.(*ir.Name)
623 if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
624 continue
625 }
626
627 if _, ok := state.varParts[n]; !ok {
628 slot := LocalSlot{N: n, Type: v.Type, Off: 0}
629 state.slots = append(state.slots, slot)
630 state.varParts[n] = []SlotID{SlotID(len(state.slots) - 1)}
631 state.vars = append(state.vars, n)
632 }
633 }
634 }
635 }
636
637
638 if cap(state.varSlots) < len(state.vars) {
639 state.varSlots = make([][]SlotID, len(state.vars))
640 } else {
641 state.varSlots = state.varSlots[:len(state.vars)]
642 for i := range state.varSlots {
643 state.varSlots[i] = state.varSlots[i][:0]
644 }
645 }
646 if cap(state.slotVars) < len(state.slots) {
647 state.slotVars = make([]VarID, len(state.slots))
648 } else {
649 state.slotVars = state.slotVars[:len(state.slots)]
650 }
651
652 if state.partsByVarOffset == nil {
653 state.partsByVarOffset = &partsByVarOffset{}
654 }
655 for varID, n := range state.vars {
656 parts := state.varParts[n]
657 state.varSlots[varID] = parts
658 for _, slotID := range parts {
659 state.slotVars[slotID] = VarID(varID)
660 }
661 *state.partsByVarOffset.(*partsByVarOffset) = partsByVarOffset{parts, state.slots}
662 sort.Sort(state.partsByVarOffset)
663 }
664
665 state.initializeCache(f, len(state.varParts), len(state.slots))
666
667 for i, slot := range f.Names {
668 if ir.IsSynthetic(slot.N) || !IsVarWantedForDebug(slot.N) {
669 continue
670 }
671 for _, value := range f.NamedValues[*slot] {
672 state.valueNames[value.ID] = append(state.valueNames[value.ID], SlotID(i))
673 }
674 }
675
676 blockLocs := state.liveness()
677 state.buildLocationLists(blockLocs)
678
679
680 rval.Slots = state.slots
681 rval.VarSlots = state.varSlots
682 rval.Vars = state.vars
683 rval.LocationLists = state.lists
684 }
685
686
687
688 func (state *debugState) liveness() []*BlockDebug {
689 blockLocs := make([]*BlockDebug, state.f.NumBlocks())
690 counterTime := int32(1)
691
692
693
694 po := state.f.Postorder()
695 converged := false
696
697
698
699
700
701
702 keepGoing := func(k int) bool {
703 if state.convergeCount == 0 {
704 return !converged
705 }
706 return k < state.convergeCount
707 }
708 for k := 0; keepGoing(k); k++ {
709 if state.loggingLevel > 0 {
710 state.logf("Liveness pass %d\n", k)
711 }
712 converged = true
713 for i := len(po) - 1; i >= 0; i-- {
714 b := po[i]
715 locs := blockLocs[b.ID]
716 if locs == nil {
717 locs = state.allocBlock(b)
718 blockLocs[b.ID] = locs
719 }
720
721
722
723 startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false)
724 locs.lastCheckedTime = counterTime
725 counterTime++
726 if state.loggingLevel > 1 {
727 state.logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState))
728 }
729
730 if blockChanged {
731
732 converged = false
733 changed := false
734 state.changedSlots.clear()
735
736
737 for _, v := range b.Values {
738 slots := state.valueNames[v.ID]
739
740
741 var source *Value
742 switch v.Op {
743 case OpStoreReg:
744 source = v.Args[0]
745 case OpLoadReg:
746 switch a := v.Args[0]; a.Op {
747 case OpArg, OpPhi:
748 source = a
749 case OpStoreReg:
750 source = a.Args[0]
751 default:
752 if state.loggingLevel > 1 {
753 state.logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a)
754 }
755 }
756 }
757
758
759 if source != nil && k == 0 {
760
761 slots = append(slots, state.valueNames[source.ID]...)
762 state.valueNames[v.ID] = slots
763 }
764
765 reg, _ := state.f.getHome(v.ID).(*Register)
766 c := state.processValue(v, slots, reg)
767 changed = changed || c
768 }
769
770 if state.loggingLevel > 1 {
771 state.logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState))
772 }
773
774 locs.relevant = locs.relevant || changed
775 if !changed {
776 locs.endState = startState
777 } else {
778 for _, id := range state.changedSlots.contents() {
779 slotID := SlotID(id)
780 slotLoc := state.currentState.slots[slotID]
781 if slotLoc.absent() {
782 startState.Delete(int32(slotID))
783 continue
784 }
785 old := startState.Find(int32(slotID))
786 if oldLS, ok := old.(*liveSlot); !ok || oldLS.VarLoc != slotLoc {
787 startState.Insert(int32(slotID),
788 &liveSlot{VarLoc: slotLoc})
789 }
790 }
791 locs.endState = startState
792 }
793 locs.lastChangedTime = counterTime
794 }
795 counterTime++
796 }
797 }
798 return blockLocs
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
833
834
835 func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (abt.T, bool) {
836
837 var predsBuf [10]*Block
838
839 preds := predsBuf[:0]
840 locs := blockLocs[b.ID]
841
842 blockChanged := !locs.everProcessed
843 updating := locs.everProcessed
844
845
846
847 for _, pred := range b.Preds {
848 if bl := blockLocs[pred.b.ID]; bl != nil && bl.everProcessed {
849
850 preds = append(preds, pred.b)
851 }
852 }
853
854 locs.everProcessed = true
855
856 if state.loggingLevel > 1 {
857
858
859 preds2 := make([]*Block, len(preds))
860 copy(preds2, preds)
861 state.logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime)
862 }
863
864 state.changedVars.clear()
865
866 markChangedVars := func(slots, merged abt.T) {
867 if !forLocationLists {
868 return
869 }
870
871
872
873 for it := slots.Iterator(); !it.Done(); {
874 k, v := it.Next()
875 m := merged.Find(k)
876 if m == nil || v.(*liveSlot).VarLoc != m.(*liveSlot).VarLoc {
877 state.changedVars.add(ID(state.slotVars[k]))
878 }
879 }
880 }
881
882 reset := func(ourStartState abt.T) {
883 if !(forLocationLists || blockChanged) {
884
885
886
887 return
888 }
889 state.currentState.reset(ourStartState)
890 }
891
892
893 if len(preds) == 0 {
894 if previousBlock != nil {
895 state.f.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.f, b.String(), previousBlock.String())
896 }
897
898 reset(abt.T{})
899 return abt.T{}, blockChanged
900 }
901
902
903 l0 := blockLocs[preds[0].ID]
904 p0 := l0.endState
905 if len(preds) == 1 {
906 if previousBlock != nil && preds[0].ID != previousBlock.ID {
907
908 markChangedVars(blockLocs[previousBlock.ID].endState, p0)
909 }
910 locs.startState = p0
911 blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime
912 reset(p0)
913 return p0, blockChanged
914 }
915
916
917
918 if updating {
919
920
921
922
923
924
925
926 for i := len(preds) - 1; i >= 0; i-- {
927 pred := preds[i]
928 if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime {
929 continue
930 }
931 preds[i] = preds[len(preds)-1]
932 preds = preds[:len(preds)-1]
933 if state.loggingLevel > 2 {
934 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)
935 }
936 }
937
938
939 if len(preds) == 0 {
940 blockChanged = false
941
942 reset(locs.startState)
943 if state.loggingLevel > 2 {
944 state.logf("Early out, no predecessors changed since last check\n")
945 }
946 if previousBlock != nil {
947 markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState)
948 }
949 return locs.startState, blockChanged
950 }
951 }
952
953 baseID := preds[0].ID
954 baseState := p0
955
956
957 for _, pred := range preds[1:] {
958 if blockLocs[pred.ID].endState.Size() < baseState.Size() {
959 baseState = blockLocs[pred.ID].endState
960 baseID = pred.ID
961 }
962 }
963
964 if state.loggingLevel > 2 {
965 state.logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID]))
966 for _, pred := range preds {
967 if pred.ID == baseID {
968 continue
969 }
970 state.logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID]))
971 }
972 }
973
974 state.currentState.reset(abt.T{})
975
976
977 slotLocs := state.currentState.slots
978
979
980
981
982
983 newState := baseState
984 if updating {
985 newState = blockLocs[b.ID].startState
986 }
987
988 for it := newState.Iterator(); !it.Done(); {
989 k, d := it.Next()
990 thisSlot := d.(*liveSlot)
991 x := thisSlot.VarLoc
992 x0 := x
993
994
995 for _, other := range preds {
996 if !updating && other.ID == baseID {
997 continue
998 }
999 otherSlot := blockLocs[other.ID].endState.Find(k)
1000 if otherSlot == nil {
1001 x = VarLoc{}
1002 break
1003 }
1004 y := otherSlot.(*liveSlot).VarLoc
1005 x = x.intersect(y)
1006 if x.absent() {
1007 x = VarLoc{}
1008 break
1009 }
1010 }
1011
1012
1013 if x.absent() {
1014 if !x0.absent() {
1015 blockChanged = true
1016 newState.Delete(k)
1017 }
1018 slotLocs[k] = VarLoc{}
1019 continue
1020 }
1021 if x != x0 {
1022 blockChanged = true
1023 newState.Insert(k, &liveSlot{VarLoc: x})
1024 }
1025
1026 slotLocs[k] = x
1027 mask := uint64(x.Registers)
1028 for {
1029 if mask == 0 {
1030 break
1031 }
1032 reg := uint8(bits.TrailingZeros64(mask))
1033 mask &^= 1 << reg
1034 state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k))
1035 }
1036 }
1037
1038 if previousBlock != nil {
1039 markChangedVars(blockLocs[previousBlock.ID].endState, newState)
1040 }
1041 locs.startState = newState
1042 return newState, blockChanged
1043 }
1044
1045
1046
1047
1048
1049 func (state *debugState) processValue(v *Value, vSlots []SlotID, vReg *Register) bool {
1050 locs := state.currentState
1051 changed := false
1052 setSlot := func(slot SlotID, loc VarLoc) {
1053 changed = true
1054 state.changedVars.add(ID(state.slotVars[slot]))
1055 state.changedSlots.add(ID(slot))
1056 state.currentState.slots[slot] = loc
1057 }
1058
1059
1060
1061
1062 clobbers := uint64(opcodeTable[v.Op].reg.clobbers)
1063 for {
1064 if clobbers == 0 {
1065 break
1066 }
1067 reg := uint8(bits.TrailingZeros64(clobbers))
1068 clobbers &^= 1 << reg
1069
1070 for _, slot := range locs.registers[reg] {
1071 if state.loggingLevel > 1 {
1072 state.logf("at %v: %v clobbered out of %v\n", v, state.slots[slot], &state.registers[reg])
1073 }
1074
1075 last := locs.slots[slot]
1076 if last.absent() {
1077 state.f.Fatalf("at %v: slot %v in register %v with no location entry", v, state.slots[slot], &state.registers[reg])
1078 continue
1079 }
1080 regs := last.Registers &^ (1 << reg)
1081 setSlot(slot, VarLoc{regs, last.StackOffset})
1082 }
1083
1084 locs.registers[reg] = locs.registers[reg][:0]
1085 }
1086
1087 switch {
1088 case v.Op == OpVarDef:
1089 n := v.Aux.(*ir.Name)
1090 if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
1091 break
1092 }
1093
1094 slotID := state.varParts[n][0]
1095 var stackOffset StackOffset
1096 if v.Op == OpVarDef {
1097 stackOffset = StackOffset(state.stackOffset(state.slots[slotID])<<1 | 1)
1098 }
1099 setSlot(slotID, VarLoc{0, stackOffset})
1100 if state.loggingLevel > 1 {
1101 if v.Op == OpVarDef {
1102 state.logf("at %v: stack-only var %v now live\n", v, state.slots[slotID])
1103 } else {
1104 state.logf("at %v: stack-only var %v now dead\n", v, state.slots[slotID])
1105 }
1106 }
1107
1108 case v.Op == OpArg:
1109 home := state.f.getHome(v.ID).(LocalSlot)
1110 stackOffset := state.stackOffset(home)<<1 | 1
1111 for _, slot := range vSlots {
1112 if state.loggingLevel > 1 {
1113 state.logf("at %v: arg %v now on stack in location %v\n", v, state.slots[slot], home)
1114 if last := locs.slots[slot]; !last.absent() {
1115 state.logf("at %v: unexpected arg op on already-live slot %v\n", v, state.slots[slot])
1116 }
1117 }
1118
1119 setSlot(slot, VarLoc{0, StackOffset(stackOffset)})
1120 }
1121
1122 case v.Op == OpStoreReg:
1123 home := state.f.getHome(v.ID).(LocalSlot)
1124 stackOffset := state.stackOffset(home)<<1 | 1
1125 for _, slot := range vSlots {
1126 last := locs.slots[slot]
1127 if last.absent() {
1128 if state.loggingLevel > 1 {
1129 state.logf("at %v: unexpected spill of unnamed register %s\n", v, vReg)
1130 }
1131 break
1132 }
1133
1134 setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)})
1135 if state.loggingLevel > 1 {
1136 state.logf("at %v: %v spilled to stack location %v@%d\n", v, state.slots[slot], home, state.stackOffset(home))
1137 }
1138 }
1139
1140 case vReg != nil:
1141 if state.loggingLevel > 1 {
1142 newSlots := make([]bool, len(state.slots))
1143 for _, slot := range vSlots {
1144 newSlots[slot] = true
1145 }
1146
1147 for _, slot := range locs.registers[vReg.num] {
1148 if !newSlots[slot] {
1149 state.logf("at %v: overwrote %v in register %v\n", v, state.slots[slot], vReg)
1150 }
1151 }
1152 }
1153
1154 for _, slot := range locs.registers[vReg.num] {
1155 last := locs.slots[slot]
1156 setSlot(slot, VarLoc{last.Registers &^ (1 << uint8(vReg.num)), last.StackOffset})
1157 }
1158 locs.registers[vReg.num] = locs.registers[vReg.num][:0]
1159 locs.registers[vReg.num] = append(locs.registers[vReg.num], vSlots...)
1160 for _, slot := range vSlots {
1161 if state.loggingLevel > 1 {
1162 state.logf("at %v: %v now in %s\n", v, state.slots[slot], vReg)
1163 }
1164
1165 last := locs.slots[slot]
1166 setSlot(slot, VarLoc{1<<uint8(vReg.num) | last.Registers, last.StackOffset})
1167 }
1168 }
1169 return changed
1170 }
1171
1172
1173
1174 func varOffset(slot LocalSlot) int64 {
1175 offset := slot.Off
1176 s := &slot
1177 for ; s.SplitOf != nil; s = s.SplitOf {
1178 offset += s.SplitOffset
1179 }
1180 return offset
1181 }
1182
1183 type partsByVarOffset struct {
1184 slotIDs []SlotID
1185 slots []LocalSlot
1186 }
1187
1188 func (a partsByVarOffset) Len() int { return len(a.slotIDs) }
1189 func (a partsByVarOffset) Less(i, j int) bool {
1190 return varOffset(a.slots[a.slotIDs[i]]) < varOffset(a.slots[a.slotIDs[j]])
1191 }
1192 func (a partsByVarOffset) Swap(i, j int) { a.slotIDs[i], a.slotIDs[j] = a.slotIDs[j], a.slotIDs[i] }
1193
1194
1195
1196 type pendingEntry struct {
1197 present bool
1198 startBlock, startValue ID
1199
1200
1201 pieces []VarLoc
1202 }
1203
1204 func (e *pendingEntry) clear() {
1205 e.present = false
1206 e.startBlock = 0
1207 e.startValue = 0
1208 for i := range e.pieces {
1209 e.pieces[i] = VarLoc{}
1210 }
1211 }
1212
1213
1214
1215
1216
1217 func canMerge(pending, new VarLoc) bool {
1218 if pending.absent() && new.absent() {
1219 return true
1220 }
1221 if pending.absent() || new.absent() {
1222 return false
1223 }
1224
1225
1226 if pending.onStack() && pending.StackOffset != new.StackOffset {
1227
1228
1229 return false
1230 }
1231 if pending.Registers&new.Registers != pending.Registers {
1232
1233 return false
1234 }
1235 return true
1236 }
1237
1238
1239 func firstReg(set RegisterSet) uint8 {
1240 if set == 0 {
1241
1242
1243 return 0
1244 }
1245 return uint8(bits.TrailingZeros64(uint64(set)))
1246 }
1247
1248
1249
1250
1251
1252
1253 func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
1254
1255
1256
1257 var prevBlock *Block
1258 for _, b := range state.f.Blocks {
1259 state.mergePredecessors(b, blockLocs, prevBlock, true)
1260
1261
1262 for _, varID := range state.changedVars.contents() {
1263 state.updateVar(VarID(varID), b, BlockStart)
1264 }
1265 state.changedVars.clear()
1266
1267 if !blockLocs[b.ID].relevant {
1268 continue
1269 }
1270
1271 mustBeFirst := func(v *Value) bool {
1272 return v.Op == OpPhi || v.Op.isLoweredGetClosurePtr() ||
1273 v.Op == OpArgIntReg || v.Op == OpArgFloatReg
1274 }
1275
1276 blockPrologComplete := func(v *Value) bool {
1277 if b.ID != state.f.Entry.ID {
1278 return !opcodeTable[v.Op].zeroWidth
1279 } else {
1280 return v.Op == OpInitMem
1281 }
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304 for idx := 0; idx < len(b.Values); idx++ {
1305 v := b.Values[idx]
1306 if blockPrologComplete(v) {
1307 break
1308 }
1309
1310 if !mustBeFirst(v) && v.Op != OpArg {
1311 continue
1312 }
1313 slots := state.valueNames[v.ID]
1314 reg, _ := state.f.getHome(v.ID).(*Register)
1315 changed := state.processValue(v, slots, reg)
1316 if changed {
1317 for _, varID := range state.changedVars.contents() {
1318 state.updateVar(VarID(varID), v.Block, BlockStart)
1319 }
1320 state.changedVars.clear()
1321 }
1322 }
1323
1324
1325
1326 zeroWidthPending := false
1327 prologComplete := false
1328
1329 for _, v := range b.Values {
1330 if blockPrologComplete(v) {
1331 prologComplete = true
1332 }
1333 slots := state.valueNames[v.ID]
1334 reg, _ := state.f.getHome(v.ID).(*Register)
1335 changed := state.processValue(v, slots, reg)
1336
1337 if opcodeTable[v.Op].zeroWidth {
1338 if prologComplete && mustBeFirst(v) {
1339 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))
1340 }
1341 if changed {
1342 if mustBeFirst(v) || v.Op == OpArg {
1343
1344 continue
1345 }
1346 zeroWidthPending = true
1347 }
1348 continue
1349 }
1350 if !changed && !zeroWidthPending {
1351 continue
1352 }
1353
1354
1355 zeroWidthPending = false
1356 for _, varID := range state.changedVars.contents() {
1357 state.updateVar(VarID(varID), v.Block, v)
1358 }
1359 state.changedVars.clear()
1360 }
1361 for _, varID := range state.changedVars.contents() {
1362 state.updateVar(VarID(varID), b, BlockEnd)
1363 }
1364
1365 prevBlock = b
1366 }
1367
1368 if state.loggingLevel > 0 {
1369 state.logf("location lists:\n")
1370 }
1371
1372
1373 for varID := range state.lists {
1374 state.writePendingEntry(VarID(varID), -1, FuncEnd.ID)
1375 list := state.lists[varID]
1376 if state.loggingLevel > 0 {
1377 if len(list) == 0 {
1378 state.logf("\t%v : empty list\n", state.vars[varID])
1379 } else {
1380 state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID]))
1381 }
1382 }
1383 }
1384 }
1385
1386
1387
1388
1389 func (state *debugState) updateVar(varID VarID, b *Block, v *Value) {
1390 curLoc := state.currentState.slots
1391
1392 empty := true
1393 for _, slotID := range state.varSlots[varID] {
1394 if !curLoc[slotID].absent() {
1395 empty = false
1396 break
1397 }
1398 }
1399 pending := &state.pendingEntries[varID]
1400 if empty {
1401 state.writePendingEntry(varID, b.ID, v.ID)
1402 pending.clear()
1403 return
1404 }
1405
1406
1407 if pending.present {
1408 merge := true
1409 for i, slotID := range state.varSlots[varID] {
1410 if !canMerge(pending.pieces[i], curLoc[slotID]) {
1411 merge = false
1412 break
1413 }
1414 }
1415 if merge {
1416 return
1417 }
1418 }
1419
1420 state.writePendingEntry(varID, b.ID, v.ID)
1421 pending.present = true
1422 pending.startBlock = b.ID
1423 pending.startValue = v.ID
1424 for i, slot := range state.varSlots[varID] {
1425 pending.pieces[i] = curLoc[slot]
1426 }
1427 }
1428
1429
1430
1431 func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
1432 pending := state.pendingEntries[varID]
1433 if !pending.present {
1434 return
1435 }
1436
1437
1438
1439 start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue)
1440 end, endOK := encodeValue(state.ctxt, endBlock, endValue)
1441 if !startOK || !endOK {
1442
1443
1444 return
1445 }
1446 if start == end {
1447 if state.loggingLevel > 1 {
1448
1449
1450 state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name)
1451 }
1452 return
1453 }
1454
1455 list := state.lists[varID]
1456 list = appendPtr(state.ctxt, list, start)
1457 list = appendPtr(state.ctxt, list, end)
1458
1459
1460 sizeIdx := len(list)
1461 list = list[:len(list)+2]
1462
1463 if state.loggingLevel > 1 {
1464 var partStrs []string
1465 for i, slot := range state.varSlots[varID] {
1466 partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.slots[slot], state.LocString(pending.pieces[i])))
1467 }
1468 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, " "))
1469 }
1470
1471 for i, slotID := range state.varSlots[varID] {
1472 loc := pending.pieces[i]
1473 slot := state.slots[slotID]
1474
1475 if !loc.absent() {
1476 if loc.onStack() {
1477 if loc.stackOffsetValue() == 0 {
1478 list = append(list, dwarf.DW_OP_call_frame_cfa)
1479 } else {
1480 list = append(list, dwarf.DW_OP_fbreg)
1481 list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue()))
1482 }
1483 } else {
1484 regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()]
1485 if regnum < 32 {
1486 list = append(list, dwarf.DW_OP_reg0+byte(regnum))
1487 } else {
1488 list = append(list, dwarf.DW_OP_regx)
1489 list = dwarf.AppendUleb128(list, uint64(regnum))
1490 }
1491 }
1492 }
1493
1494 if len(state.varSlots[varID]) > 1 {
1495 list = append(list, dwarf.DW_OP_piece)
1496 list = dwarf.AppendUleb128(list, uint64(slot.Type.Size()))
1497 }
1498 }
1499 state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1500 state.lists[varID] = list
1501 }
1502
1503
1504 func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
1505 getPC := debugInfo.GetPC
1506
1507 if ctxt.UseBASEntries {
1508 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
1509 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
1510 }
1511
1512
1513 for i := 0; i < len(list); {
1514 begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
1515 end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
1516
1517
1518
1519
1520
1521 if begin == 0 && end == 0 {
1522 end = 1
1523 }
1524
1525 if ctxt.UseBASEntries {
1526 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(begin))
1527 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, int64(end))
1528 } else {
1529 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(begin))
1530 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, int64(end))
1531 }
1532
1533 i += 2 * ctxt.Arch.PtrSize
1534 datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
1535 listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen])
1536 i += datalen
1537 }
1538
1539
1540
1541 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1542 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
1543 }
1544
1545
1546
1547
1548
1549
1550 func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) {
1551 if ctxt.Arch.PtrSize == 8 {
1552 result := uint64(b)<<32 | uint64(uint32(v))
1553
1554 return result, true
1555 }
1556 if ctxt.Arch.PtrSize != 4 {
1557 panic("unexpected pointer size")
1558 }
1559 if ID(int16(b)) != b || ID(int16(v)) != v {
1560 return 0, false
1561 }
1562 return uint64(b)<<16 | uint64(uint16(v)), true
1563 }
1564
1565
1566 func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) {
1567 if ctxt.Arch.PtrSize == 8 {
1568 b, v := ID(word>>32), ID(word)
1569
1570 return b, v
1571 }
1572 if ctxt.Arch.PtrSize != 4 {
1573 panic("unexpected pointer size")
1574 }
1575 return ID(word >> 16), ID(int16(word))
1576 }
1577
1578
1579 func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte {
1580 if cap(buf) < len(buf)+20 {
1581 b := make([]byte, len(buf), 20+cap(buf)*2)
1582 copy(b, buf)
1583 buf = b
1584 }
1585 writeAt := len(buf)
1586 buf = buf[0 : len(buf)+ctxt.Arch.PtrSize]
1587 writePtr(ctxt, buf[writeAt:], word)
1588 return buf
1589 }
1590
1591
1592 func writePtr(ctxt *obj.Link, buf []byte, word uint64) {
1593 switch ctxt.Arch.PtrSize {
1594 case 4:
1595 ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word))
1596 case 8:
1597 ctxt.Arch.ByteOrder.PutUint64(buf, word)
1598 default:
1599 panic("unexpected pointer size")
1600 }
1601
1602 }
1603
1604
1605 func readPtr(ctxt *obj.Link, buf []byte) uint64 {
1606 switch ctxt.Arch.PtrSize {
1607 case 4:
1608 return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
1609 case 8:
1610 return ctxt.Arch.ByteOrder.Uint64(buf)
1611 default:
1612 panic("unexpected pointer size")
1613 }
1614
1615 }
1616
1617
1618
1619
1620
1621 func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
1622 start, startOK := encodeValue(ctxt, f.Entry.ID, st)
1623 end, endOK := encodeValue(ctxt, f.Entry.ID, en)
1624 if !startOK || !endOK {
1625
1626
1627
1628 return nil, 0
1629 }
1630 list = appendPtr(ctxt, list, start)
1631 list = appendPtr(ctxt, list, end)
1632
1633
1634
1635 sizeIdx := len(list)
1636 list = list[:len(list)+2]
1637 return list, sizeIdx
1638 }
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660 func locatePrologEnd(f *Func, needCloCtx bool) (ID, *Value) {
1661
1662
1663
1664
1665 isRegMoveLike := func(v *Value) (bool, ID) {
1666 n, ok := v.Aux.(*ir.Name)
1667 var r ID
1668 if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
1669 return false, r
1670 }
1671 regInputs, memInputs, spInputs := 0, 0, 0
1672 for _, a := range v.Args {
1673 if a.Op == OpArgIntReg || a.Op == OpArgFloatReg ||
1674 (needCloCtx && a.Op.isLoweredGetClosurePtr()) {
1675 regInputs++
1676 r = a.ID
1677 } else if a.Type.IsMemory() {
1678 memInputs++
1679 } else if a.Op == OpSP {
1680 spInputs++
1681 } else {
1682 return false, r
1683 }
1684 }
1685 return v.Type.IsMemory() && memInputs == 1 &&
1686 regInputs == 1 && spInputs == 1, r
1687 }
1688
1689
1690
1691 regArgs := make([]ID, 0, 32)
1692
1693
1694
1695 removeReg := func(r ID) bool {
1696 for i := 0; i < len(regArgs); i++ {
1697 if regArgs[i] == r {
1698 regArgs = append(regArgs[:i], regArgs[i+1:]...)
1699 return true
1700 }
1701 }
1702 return false
1703 }
1704
1705
1706
1707
1708
1709 var cloRegStore *Value
1710 for k, v := range f.Entry.Values {
1711 if v.Op == OpArgIntReg || v.Op == OpArgFloatReg {
1712 regArgs = append(regArgs, v.ID)
1713 continue
1714 }
1715 if needCloCtx && v.Op.isLoweredGetClosurePtr() {
1716 regArgs = append(regArgs, v.ID)
1717 cloRegStore = v
1718 continue
1719 }
1720 if ok, r := isRegMoveLike(v); ok {
1721 if removed := removeReg(r); removed {
1722 if len(regArgs) == 0 {
1723
1724
1725
1726
1727 if k < len(f.Entry.Values)-1 {
1728 return f.Entry.Values[k+1].ID, cloRegStore
1729 }
1730 return BlockEnd.ID, cloRegStore
1731 }
1732 }
1733 }
1734 if v.Op.IsCall() {
1735
1736 return v.ID, cloRegStore
1737 }
1738 }
1739
1740 return ID(-1), cloRegStore
1741 }
1742
1743
1744
1745
1746 func isNamedRegParam(p abi.ABIParamAssignment) bool {
1747 if p.Name == nil {
1748 return false
1749 }
1750 n := p.Name
1751 if n.Sym() == nil || n.Sym().IsBlank() {
1752 return false
1753 }
1754 if len(p.Registers) == 0 {
1755 return false
1756 }
1757 return true
1758 }
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771 func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
1772
1773 needCloCtx := f.CloSlot != nil
1774 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
1775
1776
1777
1778
1779
1780 numRegParams := 0
1781 for _, inp := range pri.InParams() {
1782 if isNamedRegParam(inp) {
1783 numRegParams++
1784 }
1785 }
1786 if numRegParams == 0 && !needCloCtx {
1787 return
1788 }
1789
1790 state := debugState{f: f}
1791
1792 if loggingEnabled {
1793 state.logf("generating -N reg param loc lists for func %q\n", f.Name)
1794 }
1795
1796
1797
1798 var cloReg int16
1799
1800 extraForCloCtx := 0
1801 if needCloCtx {
1802 extraForCloCtx = 1
1803 }
1804
1805
1806 rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
1807
1808
1809
1810 afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
1811
1812 if needCloCtx {
1813 reg, _ := state.f.getHome(cloRegStore.ID).(*Register)
1814 cloReg = reg.ObjNum()
1815 if loggingEnabled {
1816 state.logf("needCloCtx is true for func %q, cloreg=%v\n",
1817 f.Name, reg)
1818 }
1819 }
1820
1821 addVarSlot := func(name *ir.Name, typ *types.Type) {
1822 sl := LocalSlot{N: name, Type: typ, Off: 0}
1823 rval.Vars = append(rval.Vars, name)
1824 rval.Slots = append(rval.Slots, sl)
1825 slid := len(rval.VarSlots)
1826 rval.VarSlots = append(rval.VarSlots, []SlotID{SlotID(slid)})
1827 }
1828
1829
1830
1831
1832 params := []abi.ABIParamAssignment{}
1833 for _, inp := range pri.InParams() {
1834 if !isNamedRegParam(inp) {
1835
1836 continue
1837 }
1838 if !IsVarWantedForDebug(inp.Name) {
1839 continue
1840 }
1841 addVarSlot(inp.Name, inp.Type)
1842 params = append(params, inp)
1843 }
1844 if needCloCtx {
1845 addVarSlot(f.CloSlot, f.CloSlot.Type())
1846 cloAssign := abi.ABIParamAssignment{
1847 Type: f.CloSlot.Type(),
1848 Name: f.CloSlot,
1849 Registers: []abi.RegIndex{0},
1850 }
1851 params = append(params, cloAssign)
1852 }
1853
1854
1855 pidx := 0
1856 for _, inp := range params {
1857 if !isNamedRegParam(inp) {
1858
1859 continue
1860 }
1861 if !IsVarWantedForDebug(inp.Name) {
1862 continue
1863 }
1864
1865 sl := rval.Slots[pidx]
1866 n := rval.Vars[pidx]
1867
1868 if afterPrologVal == ID(-1) {
1869
1870
1871
1872
1873 if loggingEnabled {
1874 state.logf("locatePrologEnd failed, skipping %v\n", n)
1875 }
1876 pidx++
1877 continue
1878 }
1879
1880
1881
1882
1883 list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
1884 BlockStart.ID, afterPrologVal)
1885 if list == nil {
1886 pidx++
1887 continue
1888 }
1889 if loggingEnabled {
1890 state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal)
1891 }
1892 rtypes, _ := inp.RegisterTypesAndOffsets()
1893 padding := make([]uint64, 0, 32)
1894 padding = inp.ComputePadding(padding)
1895 for k, r := range inp.Registers {
1896 var reg int16
1897 if n == f.CloSlot {
1898 reg = cloReg
1899 } else {
1900 reg = ObjRegForAbiReg(r, f.Config)
1901 }
1902 dwreg := ctxt.Arch.DWARFRegisters[reg]
1903 if dwreg < 32 {
1904 list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
1905 } else {
1906 list = append(list, dwarf.DW_OP_regx)
1907 list = dwarf.AppendUleb128(list, uint64(dwreg))
1908 }
1909 if loggingEnabled {
1910 state.logf(" piece %d -> dwreg %d", k, dwreg)
1911 }
1912 if len(inp.Registers) > 1 {
1913 list = append(list, dwarf.DW_OP_piece)
1914 ts := rtypes[k].Size()
1915 list = dwarf.AppendUleb128(list, uint64(ts))
1916 if padding[k] > 0 {
1917 if loggingEnabled {
1918 state.logf(" [pad %d bytes]", padding[k])
1919 }
1920 list = append(list, dwarf.DW_OP_piece)
1921 list = dwarf.AppendUleb128(list, padding[k])
1922 }
1923 }
1924 if loggingEnabled {
1925 state.logf("\n")
1926 }
1927 }
1928
1929 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1930
1931
1932
1933 list, sizeIdx = setupLocList(ctxt, f, list,
1934 afterPrologVal, FuncEnd.ID)
1935 if list == nil {
1936 pidx++
1937 continue
1938 }
1939 soff := stackOffset(sl)
1940 if soff == 0 {
1941 list = append(list, dwarf.DW_OP_call_frame_cfa)
1942 } else {
1943 list = append(list, dwarf.DW_OP_fbreg)
1944 list = dwarf.AppendSleb128(list, int64(soff))
1945 }
1946 if loggingEnabled {
1947 state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
1948 }
1949
1950
1951 ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
1952
1953 rval.LocationLists[pidx] = list
1954 pidx++
1955 }
1956 }
1957
1958
1959
1960
1961
1962 func IsVarWantedForDebug(n ir.Node) bool {
1963 name := n.Sym().Name
1964 if len(name) > 0 && name[0] == '&' {
1965 name = name[1:]
1966 }
1967 if len(name) > 0 && name[0] == '#' {
1968
1969 return strings.HasPrefix(name, "#yield")
1970 }
1971 return true
1972 }
1973
View as plain text