1
2
3
4
5 package ssa
6
7 import (
8 "fmt"
9 "strings"
10
11 "cmd/compile/internal/abt"
12 "cmd/compile/internal/ir"
13 "cmd/compile/internal/ssa/ssabase"
14 "cmd/compile/internal/ssa/ssaop"
15 "cmd/internal/dwarf"
16 "cmd/internal/obj"
17 )
18
19 type slotSet = abt.T[*liveSlot]
20
21 type BlockDebug struct {
22
23
24 startState, endState slotSet
25
26
27
28 lastCheckedTime, lastChangedTime int32
29
30 relevant bool
31
32
33
34 everProcessed bool
35 }
36
37 var BlockEnd = &Value{
38 ID: -20000,
39 Op: ssaop.OpInvalid,
40 Aux: StringToAux("BlockEnd"),
41 }
42
43 var BlockStart = &Value{
44 ID: -10000,
45 Op: ssaop.OpInvalid,
46 Aux: StringToAux("BlockStart"),
47 }
48
49 type DebugState struct {
50
51 Slots []LocalSlot
52 Vars []*ir.Name
53 VarSlots [][]SlotID
54 Lists [][]LocListEntry
55
56
57 SlotVars []VarID
58
59 F *Func
60 LoggingLevel int
61 ConvergeCount int
62 Registers []ssabase.Register
63 StackOffset func(LocalSlot) int32
64 Ctxt *obj.Link
65
66
67 ValueNames [][]SlotID
68
69
70 currentState StateAtPC
71 changedVars *SparseSet
72 changedSlots *SparseSet
73
74
75 pendingEntries []pendingEntry
76
77 VarParts map[*ir.Name][]SlotID
78 blockDebug []BlockDebug
79 pendingSlotLocs []VarLoc
80 }
81
82 var FuncEnd = &Value{
83 ID: -30000,
84 Op: ssaop.OpInvalid,
85 Aux: StringToAux("FuncEnd"),
86 }
87
88
89
90
91
92 func IsVarWantedForDebug(n ir.Node) bool {
93 name := n.Sym().Name
94 if len(name) > 0 && name[0] == '&' {
95 name = name[1:]
96 }
97 if len(name) > 0 && name[0] == '#' {
98
99 return strings.HasPrefix(name, "#yield")
100 }
101 return true
102 }
103
104
105
106
107 type LocListEntry struct {
108 StartBlock, StartValue ID
109 EndBlock, EndValue ID
110 Expr []byte
111 }
112
113
114 type RegisterSet = ssaop.RegMask
115
116 type SlotID int32
117
118
119
120
121 type StackOffset int32
122
123
124 type StateAtPC struct {
125
126 slots []VarLoc
127
128 registers [][]SlotID
129 }
130
131 type VarID int32
132
133
134 type VarLoc struct {
135
136
137 Registers RegisterSet
138
139 StackOffset
140 }
141
142
143
144
145
146 func canMerge(pending, new VarLoc) bool {
147 if pending.absent() && new.absent() {
148 return true
149 }
150 if pending.absent() || new.absent() {
151 return false
152 }
153
154
155 if pending.onStack() && pending.StackOffset != new.StackOffset {
156
157
158 return false
159 }
160 if pending.Registers.Intersect(new.Registers) != pending.Registers {
161
162 return false
163 }
164 return true
165 }
166
167
168 func firstReg(set RegisterSet) uint8 {
169 if set.Empty() {
170
171
172 return 0
173 }
174 return uint8(set.PickReg())
175 }
176
177
178 type liveSlot struct {
179 VarLoc
180 }
181
182
183
184 type pendingEntry struct {
185 present bool
186 startBlock, startValue ID
187
188
189 pieces []VarLoc
190 }
191
192 func (ls *liveSlot) String() string {
193 return fmt.Sprintf("[%v].%d.%d", ls.Registers, ls.stackOffsetValue(), int32(ls.StackOffset)&1)
194 }
195
196 func (s StackOffset) onStack() bool {
197 return s != 0
198 }
199
200 func (s StackOffset) stackOffsetValue() int32 {
201 return int32(s) >> 1
202 }
203
204
205 func (state *StateAtPC) reset(live slotSet) {
206 slots, registers := state.slots, state.registers
207 clear(slots)
208 for i := range registers {
209 registers[i] = registers[i][:0]
210 }
211 for it := live.Iterator(); !it.Done(); {
212 k, live := it.Next()
213 slots[k] = live.VarLoc
214 if live.VarLoc.Registers.Empty() {
215 continue
216 }
217
218 mask := live.VarLoc.Registers
219 for !mask.Empty() {
220 reg := uint8(mask.PickReg())
221 mask = mask.RemoveReg(ssaop.Register(reg))
222 registers[reg] = append(registers[reg], SlotID(k))
223 }
224 }
225 state.slots, state.registers = slots, registers
226 }
227
228 func (s *DebugState) LocString(loc VarLoc) string {
229 if loc.absent() {
230 return "<nil>"
231 }
232
233 var storage []string
234 if loc.onStack() {
235 storage = append(storage, fmt.Sprintf("@%+d", loc.stackOffsetValue()))
236 }
237
238 mask := loc.Registers
239 for !mask.Empty() {
240 reg := uint8(mask.PickReg())
241 mask = mask.RemoveReg(ssaop.Register(reg))
242 storage = append(storage, s.Registers[reg].String())
243 }
244 return strings.Join(storage, ",")
245 }
246
247 func (loc VarLoc) absent() bool {
248 return loc.Registers.Empty() && !loc.onStack()
249 }
250
251 func (loc VarLoc) intersect(other VarLoc) VarLoc {
252 if !loc.onStack() || !other.onStack() || loc.StackOffset != other.StackOffset {
253 loc.StackOffset = 0
254 }
255 loc.Registers = loc.Registers.Intersect(other.Registers)
256 return loc
257 }
258
259
260
261
262 func (s *DebugState) Logf(msg string, args ...any) {
263 if s.F.PrintOrHtmlSSA {
264 fmt.Printf(msg, args...)
265 }
266 }
267
268 func (state *DebugState) InitializeCache(f *Func, numVars, numSlots int) {
269
270 if cap(state.blockDebug) < f.NumBlocks() {
271 state.blockDebug = make([]BlockDebug, f.NumBlocks())
272 } else {
273 clear(state.blockDebug[:f.NumBlocks()])
274 }
275
276
277 if cap(state.ValueNames) < f.NumValues() {
278 old := state.ValueNames
279 state.ValueNames = make([][]SlotID, f.NumValues())
280 copy(state.ValueNames, old)
281 }
282 vn := state.ValueNames[:f.NumValues()]
283 for i := range vn {
284 vn[i] = vn[i][:0]
285 }
286
287
288 if cap(state.currentState.slots) < numSlots {
289 state.currentState.slots = make([]VarLoc, numSlots)
290 } else {
291 state.currentState.slots = state.currentState.slots[:numSlots]
292 }
293 if cap(state.currentState.registers) < len(state.Registers) {
294 state.currentState.registers = make([][]SlotID, len(state.Registers))
295 } else {
296 state.currentState.registers = state.currentState.registers[:len(state.Registers)]
297 }
298
299
300 state.changedVars = NewSparseSet(numVars)
301 state.changedSlots = NewSparseSet(numSlots)
302
303
304 numPieces := 0
305 for i := range state.VarSlots {
306 numPieces += len(state.VarSlots[i])
307 }
308 if cap(state.pendingSlotLocs) < numPieces {
309 state.pendingSlotLocs = make([]VarLoc, numPieces)
310 } else {
311 clear(state.pendingSlotLocs[:numPieces])
312 }
313 if cap(state.pendingEntries) < numVars {
314 state.pendingEntries = make([]pendingEntry, numVars)
315 }
316 pe := state.pendingEntries[:numVars]
317 freePieceIdx := 0
318 for varID, slots := range state.VarSlots {
319 pe[varID] = pendingEntry{
320 pieces: state.pendingSlotLocs[freePieceIdx : freePieceIdx+len(slots)],
321 }
322 freePieceIdx += len(slots)
323 }
324 state.pendingEntries = pe
325
326 if cap(state.Lists) < numVars {
327 state.Lists = make([][]LocListEntry, numVars)
328 } else {
329 state.Lists = state.Lists[:numVars]
330 clear(state.Lists)
331 }
332 }
333
334 func (state *DebugState) allocBlock(b *Block) *BlockDebug {
335 return &state.blockDebug[b.ID]
336 }
337
338 func (s *DebugState) blockEndStateString(b *BlockDebug) string {
339 endState := StateAtPC{slots: make([]VarLoc, len(s.Slots)), registers: make([][]SlotID, len(s.Registers))}
340 endState.reset(b.endState)
341 return s.stateString(endState)
342 }
343
344 func (s *DebugState) stateString(state StateAtPC) string {
345 var strs []string
346 for slotID, loc := range state.slots {
347 if !loc.absent() {
348 strs = append(strs, fmt.Sprintf("\t%v = %v\n", s.Slots[slotID], s.LocString(loc)))
349 }
350 }
351
352 strs = append(strs, "\n")
353 for reg, slots := range state.registers {
354 if len(slots) != 0 {
355 var slotStrs []string
356 for _, slot := range slots {
357 slotStrs = append(slotStrs, s.Slots[slot].String())
358 }
359 strs = append(strs, fmt.Sprintf("\t%v = %v\n", &s.Registers[reg], slotStrs))
360 }
361 }
362
363 if len(strs) == 1 {
364 return "(no vars)\n"
365 }
366 return strings.Join(strs, "")
367 }
368
369
370
371 func (state *DebugState) Liveness() []*BlockDebug {
372 blockLocs := make([]*BlockDebug, state.F.NumBlocks())
373 counterTime := int32(1)
374
375
376
377 po := state.F.Postorder()
378 converged := false
379
380
381
382
383
384
385 keepGoing := func(k int) bool {
386 if state.ConvergeCount == 0 {
387 return !converged
388 }
389 return k < state.ConvergeCount
390 }
391 for k := 0; keepGoing(k); k++ {
392 if state.LoggingLevel > 0 {
393 state.Logf("Liveness pass %d\n", k)
394 }
395 converged = true
396 for i := len(po) - 1; i >= 0; i-- {
397 b := po[i]
398 locs := blockLocs[b.ID]
399 if locs == nil {
400 locs = state.allocBlock(b)
401 blockLocs[b.ID] = locs
402 }
403
404
405
406 startState, blockChanged := state.mergePredecessors(b, blockLocs, nil, false)
407 locs.lastCheckedTime = counterTime
408 counterTime++
409 if state.LoggingLevel > 1 {
410 state.Logf("Processing %v, block changed %v, initial state:\n%v", b, blockChanged, state.stateString(state.currentState))
411 }
412
413 if blockChanged {
414
415 converged = false
416 changed := false
417 state.changedSlots.Clear()
418
419
420 for _, v := range b.Values {
421 slots := state.ValueNames[v.ID]
422
423
424 var source *Value
425 switch v.Op {
426 case ssaop.OpStoreReg:
427 source = v.Args[0]
428 case ssaop.OpLoadReg:
429 switch a := v.Args[0]; a.Op {
430 case ssaop.OpArg, ssaop.OpPhi:
431 source = a
432 case ssaop.OpStoreReg:
433 source = a.Args[0]
434 default:
435 if state.LoggingLevel > 1 {
436 state.Logf("at %v: load with unexpected source op: %v (%v)\n", v, a.Op, a)
437 }
438 }
439 }
440
441
442 if source != nil && k == 0 {
443
444 slots = append(slots, state.ValueNames[source.ID]...)
445 state.ValueNames[v.ID] = slots
446 }
447
448 reg, _ := state.F.GetHome(v.ID).(*ssabase.Register)
449 c := state.processValue(v, slots, reg)
450 changed = changed || c
451 }
452
453 if state.LoggingLevel > 1 {
454 state.Logf("Block %v done, locs:\n%v", b, state.stateString(state.currentState))
455 }
456
457 locs.relevant = locs.relevant || changed
458 if !changed {
459 locs.endState = startState
460 } else {
461 for _, id := range state.changedSlots.Contents() {
462 slotID := SlotID(id)
463 slotLoc := state.currentState.slots[slotID]
464 if slotLoc.absent() {
465 startState.Delete(int32(slotID))
466 continue
467 }
468 old := startState.Find(int32(slotID))
469 if old == nil || old.VarLoc != slotLoc {
470 startState.Insert(int32(slotID),
471 &liveSlot{VarLoc: slotLoc})
472 }
473 }
474 locs.endState = startState
475 }
476 locs.lastChangedTime = counterTime
477 }
478 counterTime++
479 }
480 }
481 return blockLocs
482 }
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518 func (state *DebugState) mergePredecessors(b *Block, blockLocs []*BlockDebug, previousBlock *Block, forLocationLists bool) (slotSet, bool) {
519
520 var predsBuf [10]*Block
521
522 preds := predsBuf[:0]
523 locs := blockLocs[b.ID]
524
525 blockChanged := !locs.everProcessed
526 updating := locs.everProcessed
527
528
529
530 for _, pred := range b.Preds {
531 if bl := blockLocs[pred.B.ID]; bl != nil && bl.everProcessed {
532
533 preds = append(preds, pred.B)
534 }
535 }
536
537 locs.everProcessed = true
538
539 if state.LoggingLevel > 1 {
540
541
542 preds2 := make([]*Block, len(preds))
543 copy(preds2, preds)
544 state.Logf("Merging %v into %v (changed=%d, checked=%d)\n", preds2, b, locs.lastChangedTime, locs.lastCheckedTime)
545 }
546
547 state.changedVars.Clear()
548
549 markChangedVars := func(slots, merged slotSet) {
550 if !forLocationLists {
551 return
552 }
553
554
555
556 for it := slots.Iterator(); !it.Done(); {
557 k, v := it.Next()
558 m := merged.Find(k)
559 if m == nil || v.VarLoc != m.VarLoc {
560 state.changedVars.Add(ID(state.SlotVars[k]))
561 }
562 }
563 }
564
565 reset := func(ourStartState slotSet) {
566 if !(forLocationLists || blockChanged) {
567
568
569
570 return
571 }
572 state.currentState.reset(ourStartState)
573 }
574
575
576 if len(preds) == 0 {
577 if previousBlock != nil {
578 state.F.Fatalf("Function %v, block %s with no predecessors is not first block, has previous %s", state.F, b.String(), previousBlock.String())
579 }
580
581 reset(slotSet{})
582 return slotSet{}, blockChanged
583 }
584
585
586 l0 := blockLocs[preds[0].ID]
587 p0 := l0.endState
588 if len(preds) == 1 {
589 if previousBlock != nil && preds[0].ID != previousBlock.ID {
590
591 markChangedVars(blockLocs[previousBlock.ID].endState, p0)
592 }
593 locs.startState = p0
594 blockChanged = blockChanged || l0.lastChangedTime > locs.lastCheckedTime
595 reset(p0)
596 return p0, blockChanged
597 }
598
599
600
601 if updating {
602
603
604
605
606
607
608
609 for i := len(preds) - 1; i >= 0; i-- {
610 pred := preds[i]
611 if blockLocs[pred.ID].lastChangedTime > locs.lastCheckedTime {
612 continue
613 }
614 preds[i] = preds[len(preds)-1]
615 preds = preds[:len(preds)-1]
616 if state.LoggingLevel > 2 {
617 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)
618 }
619 }
620
621
622 if len(preds) == 0 {
623 blockChanged = false
624
625 reset(locs.startState)
626 if state.LoggingLevel > 2 {
627 state.Logf("Early out, no predecessors changed since last check\n")
628 }
629 if previousBlock != nil {
630 markChangedVars(blockLocs[previousBlock.ID].endState, locs.startState)
631 }
632 return locs.startState, blockChanged
633 }
634 }
635
636 baseID := preds[0].ID
637 baseState := p0
638
639
640 for _, pred := range preds[1:] {
641 if blockLocs[pred.ID].endState.Size() < baseState.Size() {
642 baseState = blockLocs[pred.ID].endState
643 baseID = pred.ID
644 }
645 }
646
647 if state.LoggingLevel > 2 {
648 state.Logf("Starting %v with state from b%v:\n%v", b, baseID, state.blockEndStateString(blockLocs[baseID]))
649 for _, pred := range preds {
650 if pred.ID == baseID {
651 continue
652 }
653 state.Logf("Merging in state from %v:\n%v", pred, state.blockEndStateString(blockLocs[pred.ID]))
654 }
655 }
656
657 state.currentState.reset(slotSet{})
658
659
660 slotLocs := state.currentState.slots
661
662
663
664
665
666 newState := baseState
667 if updating {
668 newState = blockLocs[b.ID].startState
669 }
670
671 for it := newState.Iterator(); !it.Done(); {
672 k, thisSlot := it.Next()
673 x := thisSlot.VarLoc
674 x0 := x
675
676
677 for _, other := range preds {
678 if !updating && other.ID == baseID {
679 continue
680 }
681 otherSlot := blockLocs[other.ID].endState.Find(k)
682 if otherSlot == nil {
683 x = VarLoc{}
684 break
685 }
686 y := otherSlot.VarLoc
687 x = x.intersect(y)
688 if x.absent() {
689 x = VarLoc{}
690 break
691 }
692 }
693
694
695 if x.absent() {
696 if !x0.absent() {
697 blockChanged = true
698 newState.Delete(k)
699 }
700 slotLocs[k] = VarLoc{}
701 continue
702 }
703 if x != x0 {
704 blockChanged = true
705 newState.Insert(k, &liveSlot{VarLoc: x})
706 }
707
708 slotLocs[k] = x
709 mask := x.Registers
710 for !mask.Empty() {
711 reg := uint8(mask.PickReg())
712 mask = mask.RemoveReg(ssaop.Register(reg))
713 state.currentState.registers[reg] = append(state.currentState.registers[reg], SlotID(k))
714 }
715 }
716
717 if previousBlock != nil {
718 markChangedVars(blockLocs[previousBlock.ID].endState, newState)
719 }
720 locs.startState = newState
721 return newState, blockChanged
722 }
723
724
725
726
727
728 func (state *DebugState) processValue(v *Value, vSlots []SlotID, vReg *ssabase.Register) bool {
729 locs := state.currentState
730 changed := false
731 setSlot := func(slot SlotID, loc VarLoc) {
732 changed = true
733 state.changedVars.Add(ID(state.SlotVars[slot]))
734 state.changedSlots.Add(ID(slot))
735 state.currentState.slots[slot] = loc
736 }
737
738
739
740
741 clobbers := ssaop.OpcodeTable[v.Op].Reg.Clobbers
742 for {
743 if clobbers.Empty() {
744 break
745 }
746 reg := clobbers.PickReg()
747 clobbers = clobbers.RemoveReg(reg)
748
749 for _, slot := range locs.registers[reg] {
750 if state.LoggingLevel > 1 {
751 state.Logf("at %v: %v clobbered out of %v\n", v, state.Slots[slot], &state.Registers[reg])
752 }
753
754 last := locs.slots[slot]
755 if last.absent() {
756 state.F.Fatalf("at %v: slot %v in register %v with no location entry", v, state.Slots[slot], &state.Registers[reg])
757 continue
758 }
759 regs := last.Registers.RemoveReg(ssaop.Register(reg))
760 setSlot(slot, VarLoc{regs, last.StackOffset})
761 }
762
763 locs.registers[reg] = locs.registers[reg][:0]
764 }
765
766 switch {
767 case v.Op == ssaop.OpVarDef:
768 n := v.Aux.(*ir.Name)
769 if ir.IsSynthetic(n) || !IsVarWantedForDebug(n) {
770 break
771 }
772
773 slotID := state.VarParts[n][0]
774 var stackOffset StackOffset
775 if v.Op == ssaop.OpVarDef {
776 stackOffset = StackOffset(state.StackOffset(state.Slots[slotID])<<1 | 1)
777 }
778 setSlot(slotID, VarLoc{RegisterSet{}, stackOffset})
779 if state.LoggingLevel > 1 {
780 if v.Op == ssaop.OpVarDef {
781 state.Logf("at %v: stack-only var %v now live\n", v, state.Slots[slotID])
782 } else {
783 state.Logf("at %v: stack-only var %v now dead\n", v, state.Slots[slotID])
784 }
785 }
786
787 case v.Op == ssaop.OpArg:
788 home := state.F.GetHome(v.ID).(LocalSlot)
789 stackOffset := state.StackOffset(home)<<1 | 1
790 for _, slot := range vSlots {
791 if state.LoggingLevel > 1 {
792 state.Logf("at %v: arg %v now on stack in location %v\n", v, state.Slots[slot], home)
793 if last := locs.slots[slot]; !last.absent() {
794 state.Logf("at %v: unexpected arg op on already-live slot %v\n", v, state.Slots[slot])
795 }
796 }
797
798 setSlot(slot, VarLoc{RegisterSet{}, StackOffset(stackOffset)})
799 }
800
801 case v.Op == ssaop.OpStoreReg:
802 home := state.F.GetHome(v.ID).(LocalSlot)
803 stackOffset := state.StackOffset(home)<<1 | 1
804 for _, slot := range vSlots {
805 last := locs.slots[slot]
806 if last.absent() {
807 if state.LoggingLevel > 1 {
808 state.Logf("at %v: unexpected spill of unnamed register %s\n", v, vReg)
809 }
810 break
811 }
812
813 setSlot(slot, VarLoc{last.Registers, StackOffset(stackOffset)})
814 if state.LoggingLevel > 1 {
815 state.Logf("at %v: %v spilled to stack location %v@%d\n", v, state.Slots[slot], home, state.StackOffset(home))
816 }
817 }
818
819 case vReg != nil:
820 if state.LoggingLevel > 1 {
821 newSlots := make([]bool, len(state.Slots))
822 for _, slot := range vSlots {
823 newSlots[slot] = true
824 }
825
826 for _, slot := range locs.registers[vReg.Num] {
827 if !newSlots[slot] {
828 state.Logf("at %v: overwrote %v in register %v\n", v, state.Slots[slot], vReg)
829 }
830 }
831 }
832
833 for _, slot := range locs.registers[vReg.Num] {
834 last := locs.slots[slot]
835 setSlot(slot, VarLoc{last.Registers.RemoveReg(ssaop.Register(vReg.Num)), last.StackOffset})
836 }
837 locs.registers[vReg.Num] = locs.registers[vReg.Num][:0]
838 locs.registers[vReg.Num] = append(locs.registers[vReg.Num], vSlots...)
839 for _, slot := range vSlots {
840 if state.LoggingLevel > 1 {
841 state.Logf("at %v: %v now in %s\n", v, state.Slots[slot], vReg)
842 }
843
844 last := locs.slots[slot]
845 setSlot(slot, VarLoc{last.Registers.AddReg(ssaop.Register(vReg.Num)), last.StackOffset})
846 }
847 }
848 return changed
849 }
850
851 func (e *pendingEntry) clear() {
852 e.present = false
853 e.startBlock = 0
854 e.startValue = 0
855 clear(e.pieces)
856 }
857
858
859
860
861
862
863 func (state *DebugState) BuildLocationLists(blockLocs []*BlockDebug) {
864
865
866
867 var prevBlock *Block
868 for _, b := range state.F.Blocks {
869 state.mergePredecessors(b, blockLocs, prevBlock, true)
870
871
872 for _, varID := range state.changedVars.Contents() {
873 state.updateVar(VarID(varID), b, BlockStart)
874 }
875 state.changedVars.Clear()
876
877 if !blockLocs[b.ID].relevant {
878 continue
879 }
880
881 mustBeFirst := func(v *Value) bool {
882 return v.Op == ssaop.OpPhi || v.Op.IsLoweredGetClosurePtr() ||
883 v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg
884 }
885
886 blockPrologComplete := func(v *Value) bool {
887 if b.ID != state.F.Entry.ID {
888 return !ssaop.OpcodeTable[v.Op].ZeroWidth
889 } else {
890 return v.Op == ssaop.OpInitMem
891 }
892 }
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914 for idx := 0; idx < len(b.Values); idx++ {
915 v := b.Values[idx]
916 if blockPrologComplete(v) {
917 break
918 }
919
920 if !mustBeFirst(v) && v.Op != ssaop.OpArg {
921 continue
922 }
923 slots := state.ValueNames[v.ID]
924 reg, _ := state.F.GetHome(v.ID).(*ssabase.Register)
925 changed := state.processValue(v, slots, reg)
926 if changed {
927 for _, varID := range state.changedVars.Contents() {
928 state.updateVar(VarID(varID), v.Block, BlockStart)
929 }
930 state.changedVars.Clear()
931 }
932 }
933
934
935
936 zeroWidthPending := false
937 prologComplete := false
938
939 for _, v := range b.Values {
940 if blockPrologComplete(v) {
941 prologComplete = true
942 }
943 slots := state.ValueNames[v.ID]
944 reg, _ := state.F.GetHome(v.ID).(*ssabase.Register)
945 changed := state.processValue(v, slots, reg)
946
947 if ssaop.OpcodeTable[v.Op].ZeroWidth {
948 if prologComplete && mustBeFirst(v) {
949 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))
950 }
951 if changed {
952 if mustBeFirst(v) || v.Op == ssaop.OpArg {
953
954 continue
955 }
956 zeroWidthPending = true
957 }
958 continue
959 }
960 if !changed && !zeroWidthPending {
961 continue
962 }
963
964
965 zeroWidthPending = false
966 for _, varID := range state.changedVars.Contents() {
967 state.updateVar(VarID(varID), v.Block, v)
968 }
969 state.changedVars.Clear()
970 }
971 for _, varID := range state.changedVars.Contents() {
972 state.updateVar(VarID(varID), b, BlockEnd)
973 }
974
975 prevBlock = b
976 }
977
978 if state.LoggingLevel > 0 {
979 state.Logf("location lists:\n")
980 }
981
982
983 for varID := range state.Lists {
984 state.writePendingEntry(VarID(varID), -1, FuncEnd.ID)
985 list := state.Lists[varID]
986 if state.LoggingLevel > 0 {
987 if len(list) == 0 {
988 state.Logf("\t%v : empty list\n", state.Vars[varID])
989 } else {
990 state.Logf("\t%v : %d entries\n", state.Vars[varID], len(list))
991 }
992 }
993 }
994 }
995
996
997
998
999 func (state *DebugState) updateVar(varID VarID, b *Block, v *Value) {
1000 curLoc := state.currentState.slots
1001
1002 empty := true
1003 for _, slotID := range state.VarSlots[varID] {
1004 if !curLoc[slotID].absent() {
1005 empty = false
1006 break
1007 }
1008 }
1009 pending := &state.pendingEntries[varID]
1010 if empty {
1011 state.writePendingEntry(varID, b.ID, v.ID)
1012 pending.clear()
1013 return
1014 }
1015
1016
1017 if pending.present {
1018 merge := true
1019 for i, slotID := range state.VarSlots[varID] {
1020 if !canMerge(pending.pieces[i], curLoc[slotID]) {
1021 merge = false
1022 break
1023 }
1024 }
1025 if merge {
1026 return
1027 }
1028 }
1029
1030 state.writePendingEntry(varID, b.ID, v.ID)
1031 pending.present = true
1032 pending.startBlock = b.ID
1033 pending.startValue = v.ID
1034 for i, slot := range state.VarSlots[varID] {
1035 pending.pieces[i] = curLoc[slot]
1036 }
1037 }
1038
1039
1040
1041 func (state *DebugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
1042 pending := state.pendingEntries[varID]
1043 if !pending.present {
1044 return
1045 }
1046
1047
1048 if pending.startBlock == endBlock && pending.startValue == endValue {
1049 if state.LoggingLevel > 1 {
1050 state.Logf("Skipping empty location list for %v in %s\n", state.Vars[varID], state.F.Name)
1051 }
1052 return
1053 }
1054
1055 if state.LoggingLevel > 1 {
1056 var partStrs []string
1057 for i, slot := range state.VarSlots[varID] {
1058 partStrs = append(partStrs, fmt.Sprintf("%v@%v", state.Slots[slot], state.LocString(pending.pieces[i])))
1059 }
1060 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, " "))
1061 }
1062
1063
1064 var expr []byte
1065 for i, slotID := range state.VarSlots[varID] {
1066 loc := pending.pieces[i]
1067 slot := state.Slots[slotID]
1068
1069 if !loc.absent() {
1070 if loc.onStack() {
1071 if loc.stackOffsetValue() == 0 {
1072 expr = append(expr, dwarf.DW_OP_call_frame_cfa)
1073 } else {
1074 expr = append(expr, dwarf.DW_OP_fbreg)
1075 expr = dwarf.AppendSleb128(expr, int64(loc.stackOffsetValue()))
1076 }
1077 } else {
1078 regnum := state.Ctxt.Arch.DWARFRegisters[state.Registers[firstReg(loc.Registers)].ObjNum]
1079 if regnum < 32 {
1080 expr = append(expr, dwarf.DW_OP_reg0+byte(regnum))
1081 } else {
1082 expr = append(expr, dwarf.DW_OP_regx)
1083 expr = dwarf.AppendUleb128(expr, uint64(regnum))
1084 }
1085 }
1086 }
1087
1088 if len(state.VarSlots[varID]) > 1 {
1089 expr = append(expr, dwarf.DW_OP_piece)
1090 expr = dwarf.AppendUleb128(expr, uint64(slot.Type.Size()))
1091 }
1092 }
1093
1094 entry := LocListEntry{
1095 StartBlock: pending.startBlock,
1096 StartValue: pending.startValue,
1097 EndBlock: endBlock,
1098 EndValue: endValue,
1099 Expr: expr,
1100 }
1101 state.Lists[varID] = append(state.Lists[varID], entry)
1102 }
1103
View as plain text