1
2
3
4
5 package ssadebug
6
7 import (
8 "cmp"
9 "internal/buildcfg"
10 "slices"
11
12 "cmd/compile/internal/abi"
13 "cmd/compile/internal/ir"
14 "cmd/compile/internal/ssa"
15 "cmd/compile/internal/ssa/ssabase"
16 "cmd/compile/internal/ssa/ssaop"
17 "cmd/compile/internal/types"
18 "cmd/internal/dwarf"
19 "cmd/internal/obj"
20 "cmd/internal/src"
21 )
22
23
24
25
26 type FuncDebug struct {
27
28 Slots []ssa.LocalSlot
29
30 Vars []*ir.Name
31
32 VarSlots [][]ssa.SlotID
33
34 LocationLists [][]ssa.LocListEntry
35
36
37 RegOutputParams []*ir.Name
38
39 OptDcl []*ir.Name
40
41
42 EntryID ssa.ID
43
44
45
46
47
48 GetPC func(block, value ssa.ID) int64
49 }
50
51
52
53
54
55 type slotCanonicalizer struct {
56 slmap map[slotKey]SlKeyIdx
57 slkeys []ssa.LocalSlot
58 }
59
60 func newSlotCanonicalizer() *slotCanonicalizer {
61 return &slotCanonicalizer{
62 slmap: make(map[slotKey]SlKeyIdx),
63 slkeys: []ssa.LocalSlot{ssa.LocalSlot{N: nil}},
64 }
65 }
66
67 type SlKeyIdx uint32
68
69 const noSlot = SlKeyIdx(0)
70
71
72
73 type slotKey struct {
74 name *ir.Name
75 offset int64
76 width int64
77 splitOf SlKeyIdx
78 splitOffset int64
79 }
80
81
82
83
84
85 func (sc *slotCanonicalizer) lookup(ls ssa.LocalSlot) (SlKeyIdx, bool) {
86 split := noSlot
87 if ls.SplitOf != nil {
88 split, _ = sc.lookup(*ls.SplitOf)
89 }
90 k := slotKey{
91 name: ls.N, offset: ls.Off, width: ls.Type.Size(),
92 splitOf: split, splitOffset: ls.SplitOffset,
93 }
94 if idx, ok := sc.slmap[k]; ok {
95 return idx, true
96 }
97 rv := SlKeyIdx(len(sc.slkeys))
98 sc.slkeys = append(sc.slkeys, ls)
99 sc.slmap[k] = rv
100 return rv, false
101 }
102
103 func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) ssa.LocalSlot {
104 return sc.slkeys[idx]
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 func PopulateABIInRegArgOps(f *ssa.Func) {
139 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
140
141
142
143
144
145
146
147 sc := newSlotCanonicalizer()
148 for _, sl := range f.Names {
149 sc.lookup(sl)
150 }
151
152
153 addToNV := func(v *ssa.Value, sl ssa.LocalSlot) {
154 values, ok := f.NamedValues[sl]
155 if !ok {
156
157 f.Names = append(f.Names, sl)
158 } else {
159 for _, ev := range values {
160 if v == ev {
161 return
162 }
163 }
164 }
165 values = append(values, v)
166 f.NamedValues[sl] = values
167 }
168
169 newValues := []*ssa.Value{}
170
171 abiRegIndexToRegister := func(reg abi.RegIndex) int8 {
172 i := f.ABISelf.FloatIndexFor(reg)
173 if i >= 0 {
174 return f.Config.FloatParamRegs[i]
175 } else {
176 return f.Config.IntParamRegs[reg]
177 }
178 }
179
180
181 var pos src.XPos
182 if len(f.Entry.Values) != 0 {
183 pos = f.Entry.Values[0].Pos
184 }
185 synthesizeOpIntFloatArg := func(n *ir.Name, t *types.Type, reg abi.RegIndex, sl ssa.LocalSlot) *ssa.Value {
186 aux := &ssa.AuxNameOffset{Name: n, Offset: sl.Off}
187 op, auxInt := ssa.ArgOpAndRegisterFor(reg, f.ABISelf)
188 v := f.NewValueNoBlock(op, t, pos)
189 v.AuxInt = auxInt
190 v.Aux = aux
191 v.Args = nil
192 v.Block = f.Entry
193 newValues = append(newValues, v)
194 addToNV(v, sl)
195 f.SetHome(v, &f.Config.Registers[abiRegIndexToRegister(reg)])
196 return v
197 }
198
199
200
201
202
203
204 for _, v := range f.Entry.Values {
205 if v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg {
206 aux := v.Aux.(*ssa.AuxNameOffset)
207 sl := ssa.LocalSlot{N: aux.Name, Type: v.Type, Off: aux.Offset}
208
209 idx, _ := sc.lookup(sl)
210
211 addToNV(v, sc.canonSlot(idx))
212 } else if v.Op.IsCall() {
213
214 break
215 }
216 }
217
218
219
220 for _, inp := range pri.InParams() {
221 if !isNamedRegParam(inp) {
222 continue
223 }
224 n := inp.Name
225
226
227
228 types, offsets := inp.RegisterTypesAndOffsets()
229 for k, t := range types {
230
231
232
233
234
235
236
237
238
239 pieceSlot := ssa.LocalSlot{N: n, Type: t, Off: offsets[k]}
240
241
242
243 _, found := sc.lookup(pieceSlot)
244 if !found {
245
246
247
248
249 synthesizeOpIntFloatArg(n, t, inp.Registers[k],
250 pieceSlot)
251 }
252 }
253 }
254
255
256 f.Entry.Values = append(newValues, f.Entry.Values...)
257 }
258
259
260
261
262 func BuildFuncDebug(ctxt *obj.Link, f *ssa.Func, loggingLevel int, stackOffset func(ssa.LocalSlot) int32, rval *FuncDebug) {
263 if f.RegAlloc == nil {
264 f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
265 }
266 state := &f.Cache.DebugState
267 state.LoggingLevel = loggingLevel % 1000
268
269
270
271
272
273 state.ConvergeCount = loggingLevel / 1000
274 state.F = f
275 state.Registers = f.Config.Registers
276 state.StackOffset = stackOffset
277 state.Ctxt = ctxt
278
279 if buildcfg.Experiment.RegabiArgs {
280 PopulateABIInRegArgOps(f)
281 }
282
283 if state.LoggingLevel > 0 {
284 state.Logf("Generating location lists for function %q\n", f.Name)
285 }
286
287 if state.VarParts == nil {
288 state.VarParts = make(map[*ir.Name][]ssa.SlotID)
289 } else {
290 clear(state.VarParts)
291 }
292
293
294
295
296 state.Slots = state.Slots[:0]
297 state.Vars = state.Vars[:0]
298 for i, slot := range f.Names {
299 state.Slots = append(state.Slots, slot)
300 if ir.IsSynthetic(slot.N) || !ssa.IsVarWantedForDebug(slot.N) {
301 continue
302 }
303
304 topSlot := slot
305 for topSlot.SplitOf != nil {
306 topSlot = *topSlot.SplitOf
307 }
308 if _, ok := state.VarParts[topSlot.N]; !ok {
309 state.Vars = append(state.Vars, topSlot.N)
310 }
311 state.VarParts[topSlot.N] = append(state.VarParts[topSlot.N], ssa.SlotID(i))
312 }
313
314
315
316 for _, b := range f.Blocks {
317 for _, v := range b.Values {
318 if v.Op == ssaop.OpVarDef {
319 n := v.Aux.(*ir.Name)
320 if ir.IsSynthetic(n) || !ssa.IsVarWantedForDebug(n) {
321 continue
322 }
323
324 if _, ok := state.VarParts[n]; !ok {
325 slot := ssa.LocalSlot{N: n, Type: v.Type, Off: 0}
326 state.Slots = append(state.Slots, slot)
327 state.VarParts[n] = []ssa.SlotID{ssa.SlotID(len(state.Slots) - 1)}
328 state.Vars = append(state.Vars, n)
329 }
330 }
331 }
332 }
333
334
335 if cap(state.VarSlots) < len(state.Vars) {
336 state.VarSlots = make([][]ssa.SlotID, len(state.Vars))
337 } else {
338 state.VarSlots = state.VarSlots[:len(state.Vars)]
339 for i := range state.VarSlots {
340 state.VarSlots[i] = state.VarSlots[i][:0]
341 }
342 }
343 if cap(state.SlotVars) < len(state.Slots) {
344 state.SlotVars = make([]ssa.VarID, len(state.Slots))
345 } else {
346 state.SlotVars = state.SlotVars[:len(state.Slots)]
347 }
348
349 for varID, n := range state.Vars {
350 parts := state.VarParts[n]
351 slices.SortFunc(parts, func(a, b ssa.SlotID) int {
352 return cmp.Compare(varOffset(state.Slots[a]), varOffset(state.Slots[b]))
353 })
354
355 state.VarSlots[varID] = parts
356 for _, slotID := range parts {
357 state.SlotVars[slotID] = ssa.VarID(varID)
358 }
359 }
360
361 state.InitializeCache(f, len(state.VarParts), len(state.Slots))
362
363 for i, slot := range f.Names {
364 if ir.IsSynthetic(slot.N) || !ssa.IsVarWantedForDebug(slot.N) {
365 continue
366 }
367 for _, value := range f.NamedValues[slot] {
368 state.ValueNames[value.ID] = append(state.ValueNames[value.ID], ssa.SlotID(i))
369 }
370 }
371
372 blockLocs := state.Liveness()
373 state.BuildLocationLists(blockLocs)
374
375
376 rval.Slots = state.Slots
377 rval.VarSlots = state.VarSlots
378 rval.Vars = state.Vars
379 rval.LocationLists = state.Lists
380 }
381
382
383
384 func varOffset(slot ssa.LocalSlot) int64 {
385 offset := slot.Off
386 s := &slot
387 for ; s.SplitOf != nil; s = s.SplitOf {
388 offset += s.SplitOffset
389 }
390 return offset
391 }
392
393
394
395 func (debugInfo *FuncDebug) PutLocationList(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
396 if buildcfg.Experiment.Dwarf5 {
397 debugInfo.PutLocationListDwarf5(entries, ctxt, listSym, startPC)
398 } else {
399 debugInfo.PutLocationListDwarf4(entries, ctxt, listSym, startPC)
400 }
401 }
402
403
404
405 func (debugInfo *FuncDebug) PutLocationListDwarf5(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
406 getPC := debugInfo.GetPC
407
408
409 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_base_addressx)
410 listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
411
412 var stbuf, enbuf [10]byte
413 for _, entry := range entries {
414 begin := getPC(entry.StartBlock, entry.StartValue)
415 end := getPC(entry.EndBlock, entry.EndValue)
416
417
418
419 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
420 stb := stbuf[:0]
421 enb := enbuf[:0]
422 stb = dwarf.AppendUleb128(stb, uint64(begin))
423 enb = dwarf.AppendUleb128(enb, uint64(end))
424 listSym.WriteBytes(ctxt, listSym.Size, stb)
425 listSym.WriteBytes(ctxt, listSym.Size, enb)
426
427
428 stb = stbuf[:0]
429 stb = dwarf.AppendUleb128(stb, uint64(len(entry.Expr)))
430 listSym.WriteBytes(ctxt, listSym.Size, stb)
431 listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
432 }
433
434
435 listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
436 }
437
438
439
440 func (debugInfo *FuncDebug) PutLocationListDwarf4(entries []ssa.LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
441 getPC := debugInfo.GetPC
442
443 if ctxt.UseBASEntries {
444 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, ^0)
445 listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
446 }
447
448 for _, entry := range entries {
449 begin := getPC(entry.StartBlock, entry.StartValue)
450 end := getPC(entry.EndBlock, entry.EndValue)
451
452
453
454
455
456 if begin == 0 && end == 0 {
457 end = 1
458 }
459
460 if ctxt.UseBASEntries {
461 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, begin)
462 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, end)
463 } else {
464 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, begin)
465 listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, end)
466 }
467
468
469 listSym.WriteInt(ctxt, listSym.Size, 2, int64(len(entry.Expr)))
470 listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
471 }
472
473
474 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
475 listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
476 }
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498 func locatePrologEnd(f *ssa.Func, needCloCtx bool) (ssa.ID, *ssa.Value) {
499
500
501
502
503 isRegMoveLike := func(v *ssa.Value) (bool, ssa.ID) {
504 n, ok := v.Aux.(*ir.Name)
505 var r ssa.ID
506 if (!ok || n.Class != ir.PPARAM) && !needCloCtx {
507 return false, r
508 }
509 regInputs, memInputs, spInputs := 0, 0, 0
510 for _, a := range v.Args {
511 if a.Op == ssaop.OpArgIntReg || a.Op == ssaop.OpArgFloatReg ||
512 (needCloCtx && a.Op.IsLoweredGetClosurePtr()) {
513 regInputs++
514 r = a.ID
515 } else if a.Type.IsMemory() {
516 memInputs++
517 } else if a.Op == ssaop.OpSP {
518 spInputs++
519 } else {
520 return false, r
521 }
522 }
523 return v.Type.IsMemory() && memInputs == 1 &&
524 regInputs == 1 && spInputs == 1, r
525 }
526
527
528
529 regArgs := make([]ssa.ID, 0, 32)
530
531
532
533 removeReg := func(r ssa.ID) bool {
534 for i := 0; i < len(regArgs); i++ {
535 if regArgs[i] == r {
536 regArgs = slices.Delete(regArgs, i, i+1)
537 return true
538 }
539 }
540 return false
541 }
542
543
544
545
546
547 var cloRegStore *ssa.Value
548 for k, v := range f.Entry.Values {
549 if v.Op == ssaop.OpArgIntReg || v.Op == ssaop.OpArgFloatReg {
550 regArgs = append(regArgs, v.ID)
551 continue
552 }
553 if needCloCtx && v.Op.IsLoweredGetClosurePtr() {
554 regArgs = append(regArgs, v.ID)
555 cloRegStore = v
556 continue
557 }
558 if ok, r := isRegMoveLike(v); ok {
559 if removed := removeReg(r); removed {
560 if len(regArgs) == 0 {
561
562
563
564
565 if k < len(f.Entry.Values)-1 {
566 return f.Entry.Values[k+1].ID, cloRegStore
567 }
568 return ssa.BlockEnd.ID, cloRegStore
569 }
570 }
571 }
572 if v.Op.IsCall() {
573
574 return v.ID, cloRegStore
575 }
576 }
577
578 return ssa.ID(-1), cloRegStore
579 }
580
581
582
583
584 func isNamedRegParam(p abi.ABIParamAssignment) bool {
585 if p.Name == nil {
586 return false
587 }
588 n := p.Name
589 if n.Sym() == nil || n.Sym().IsBlank() {
590 return false
591 }
592 if len(p.Registers) == 0 {
593 return false
594 }
595 return true
596 }
597
598
599
600
601
602
603
604
605
606
607
608
609 func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *ssa.Func, loggingEnabled bool, stackOffset func(ssa.LocalSlot) int32, rval *FuncDebug) {
610 needCloCtx := f.CloSlot != nil
611 pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
612
613
614
615
616
617 numRegParams := 0
618 for _, inp := range pri.InParams() {
619 if isNamedRegParam(inp) {
620 numRegParams++
621 }
622 }
623 if numRegParams == 0 && !needCloCtx {
624 return
625 }
626
627 state := ssa.DebugState{F: f}
628
629 if loggingEnabled {
630 state.Logf("generating -N reg param loc lists for func %q\n", f.Name)
631 }
632
633
634
635 var cloReg int16
636
637 extraForCloCtx := 0
638 if needCloCtx {
639 extraForCloCtx = 1
640 }
641
642
643 rval.LocationLists = make([][]ssa.LocListEntry, numRegParams+extraForCloCtx)
644
645
646
647 afterPrologVal, cloRegStore := locatePrologEnd(f, needCloCtx)
648
649 if needCloCtx {
650 reg, _ := state.F.GetHome(cloRegStore.ID).(*ssabase.Register)
651 cloReg = reg.ObjNum
652 if loggingEnabled {
653 state.Logf("needCloCtx is true for func %q, cloreg=%v\n",
654 f.Name, reg)
655 }
656 }
657
658 addVarSlot := func(name *ir.Name, typ *types.Type) {
659 sl := ssa.LocalSlot{N: name, Type: typ, Off: 0}
660 rval.Vars = append(rval.Vars, name)
661 rval.Slots = append(rval.Slots, sl)
662 slid := len(rval.VarSlots)
663 rval.VarSlots = append(rval.VarSlots, []ssa.SlotID{ssa.SlotID(slid)})
664 }
665
666
667
668
669 params := []abi.ABIParamAssignment{}
670 for _, inp := range pri.InParams() {
671 if !isNamedRegParam(inp) {
672
673 continue
674 }
675 if !ssa.IsVarWantedForDebug(inp.Name) {
676 continue
677 }
678 addVarSlot(inp.Name, inp.Type)
679 params = append(params, inp)
680 }
681 if needCloCtx {
682 addVarSlot(f.CloSlot, f.CloSlot.Type())
683 cloAssign := abi.ABIParamAssignment{
684 Type: f.CloSlot.Type(),
685 Name: f.CloSlot,
686 Registers: []abi.RegIndex{0},
687 }
688 params = append(params, cloAssign)
689 }
690
691
692 pidx := 0
693 for _, inp := range params {
694 if !isNamedRegParam(inp) {
695
696 continue
697 }
698 if !ssa.IsVarWantedForDebug(inp.Name) {
699 continue
700 }
701
702 sl := rval.Slots[pidx]
703 n := rval.Vars[pidx]
704
705 if afterPrologVal == ssa.ID(-1) {
706
707
708
709
710 if loggingEnabled {
711 state.Logf("locatePrologEnd failed, skipping %v\n", n)
712 }
713 pidx++
714 continue
715 }
716
717
718
719
720 if loggingEnabled {
721 state.Logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal)
722 }
723 var regExpr []byte
724 rtypes, _ := inp.RegisterTypesAndOffsets()
725 padding := make([]uint64, 0, 32)
726 padding = inp.ComputePadding(padding)
727 for k, r := range inp.Registers {
728 var reg int16
729 if n == f.CloSlot {
730 reg = cloReg
731 } else {
732 reg = ssa.ObjRegForAbiReg(r, f.Config)
733 }
734 dwreg := ctxt.Arch.DWARFRegisters[reg]
735 if dwreg < 32 {
736 regExpr = append(regExpr, dwarf.DW_OP_reg0+byte(dwreg))
737 } else {
738 regExpr = append(regExpr, dwarf.DW_OP_regx)
739 regExpr = dwarf.AppendUleb128(regExpr, uint64(dwreg))
740 }
741 if loggingEnabled {
742 state.Logf(" piece %d -> dwreg %d", k, dwreg)
743 }
744 if len(inp.Registers) > 1 {
745 regExpr = append(regExpr, dwarf.DW_OP_piece)
746 ts := rtypes[k].Size()
747 regExpr = dwarf.AppendUleb128(regExpr, uint64(ts))
748 if padding[k] > 0 {
749 if loggingEnabled {
750 state.Logf(" [pad %d bytes]", padding[k])
751 }
752 regExpr = append(regExpr, dwarf.DW_OP_piece)
753 regExpr = dwarf.AppendUleb128(regExpr, padding[k])
754 }
755 }
756 if loggingEnabled {
757 state.Logf("\n")
758 }
759 }
760 rval.LocationLists[pidx] = append(rval.LocationLists[pidx], ssa.LocListEntry{
761 StartBlock: f.Entry.ID,
762 StartValue: ssa.BlockStart.ID,
763 EndBlock: f.Entry.ID,
764 EndValue: afterPrologVal,
765 Expr: regExpr,
766 })
767
768
769
770 var stackExpr []byte
771 soff := stackOffset(sl)
772 if soff == 0 {
773 stackExpr = append(stackExpr, dwarf.DW_OP_call_frame_cfa)
774 } else {
775 stackExpr = append(stackExpr, dwarf.DW_OP_fbreg)
776 stackExpr = dwarf.AppendSleb128(stackExpr, int64(soff))
777 }
778 if loggingEnabled {
779 state.Logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
780 }
781
782 rval.LocationLists[pidx] = append(rval.LocationLists[pidx], ssa.LocListEntry{
783 StartBlock: f.Entry.ID,
784 StartValue: afterPrologVal,
785 EndBlock: f.Entry.ID,
786 EndValue: ssa.FuncEnd.ID,
787 Expr: stackExpr,
788 })
789
790 pidx++
791 }
792 }
793
View as plain text