aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek Parker <parkerderek86@gmail.com>2026-03-02 16:25:23 -0800
committerGopher Robot <gobot@golang.org>2026-04-06 08:44:45 -0700
commit70dc75b79b7454caf0ed256b51ff716f880b317d (patch)
tree36e1e6a185320157dd5cc05427a0a0a22ae8e2cf
parentd74de3ce79d4ab3495650bfcc4682cab09514b89 (diff)
downloadgo-70dc75b79b7454caf0ed256b51ff716f880b317d.tar.xz
cmd/compile: unify DWARF variable generation and remove encoding
Refactor the DWARF variable generation in the compiler: 1. Replace the intermediate []byte location list encoding with a structured LocListEntry type. The old code packed SSA block/value IDs into pointer-sized integers, wrote them alongside DWARF4-format length-prefixed expressions, then re-read and decoded everything during final encoding. The new approach stores entries as {StartBlock, StartValue, EndBlock, EndValue, Expr} structs that PutLocationListDwarf4/5 directly encode into the appropriate format. This eliminates encodeValue, decodeValue, appendPtr, writePtr, readPtr, and SetupLocList, and removes the DWARF4 re-encoding in PutLocationListDwarf5. 2. Unify createDwarfVars into a single processing loop. The old code had three mutually exclusive paths (createSimpleVars, createABIVars, createComplexVars) selected by build mode, followed by a separate conservative-var loop. The new code uses one loop that tries createComplexVar first (when SSA debug info is available), then falls back to createSimpleVar. This removes createSimpleVars, createABIVars, and createComplexVars. 3. Extract createConservativeVar and shouldEmitDwarfVar as named functions, consolidating inline code and scattered filtering logic. 4. Fix createHeapDerefLocationList to return []LocListEntry instead of raw bytes, consistent with the new representation. Change-Id: If6fb755c22e398d7615dccaf33b1367828e6c47e Reviewed-on: https://go-review.googlesource.com/c/go/+/750920 Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com>
-rw-r--r--src/cmd/compile/internal/dwarfgen/dwarf.go345
-rw-r--r--src/cmd/compile/internal/ssa/debug.go289
2 files changed, 268 insertions, 366 deletions
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go
index 9d975e0bc1..8cc269a1c5 100644
--- a/src/cmd/compile/internal/dwarfgen/dwarf.go
+++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -93,7 +93,7 @@ func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (s
default:
continue
}
- if !ssa.IsVarWantedForDebug(n) {
+ if !shouldEmitDwarfVar(n) {
continue
}
apdecls = append(apdecls, n)
@@ -178,18 +178,78 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
// Collect a raw list of DWARF vars.
var vars []*dwarf.Var
var decls []*ir.Name
- var selected ir.NameSet
- if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK {
- decls, vars, selected = createComplexVars(fnsym, fn, closureVars)
- } else if fn.ABI == obj.ABIInternal && base.Flag.N != 0 && complexOK {
- decls, vars, selected = createABIVars(fnsym, fn, apDecls, closureVars)
- } else {
- decls, vars, selected = createSimpleVars(fnsym, apDecls, closureVars)
- }
+ // Build a VarID lookup map for SSA debug info if available.
+ var debug *ssa.FuncDebug
+ var varIDMap map[*ir.Name]ssa.VarID
if fn.DebugInfo != nil {
- // Recover zero sized variables eliminated by the stackframe pass
- for _, n := range fn.DebugInfo.(*ssa.FuncDebug).OptDcl {
+ debug = fn.DebugInfo.(*ssa.FuncDebug)
+ varIDMap = make(map[*ir.Name]ssa.VarID, len(debug.Vars))
+ for i, n := range debug.Vars {
+ varIDMap[n] = ssa.VarID(i)
+ }
+ }
+ canUseComplex := complexOK && debug != nil
+
+ // markVarSeen marks a variable and all its associated slot names as seen.
+ // This is needed because decomposed variables may have slots whose ir.Name
+ // differs from the variable itself (e.g., PAUTO vs PPARAMOUT for the same
+ // logical variable). Without this, the dcl loop could create duplicate
+ // conservative entries for names that are already covered by a complex var.
+ seen := make(map[*ir.Name]bool)
+ markVarSeen := func(n *ir.Name, varID ssa.VarID) {
+ seen[n] = true
+ if debug != nil && int(varID) < len(debug.VarSlots) {
+ for _, slot := range debug.VarSlots[varID] {
+ seen[debug.Slots[slot].N] = true
+ }
+ }
+ }
+
+ // Unified loop: for each variable in apDecls, try createComplexVar
+ // (SSA debug info) first, then fall back to createSimpleVar.
+ for _, n := range apDecls {
+ if !shouldEmitDwarfVar(n) {
+ continue
+ }
+ if canUseComplex {
+ if vid, ok := varIDMap[n]; ok {
+ if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
+ decls = append(decls, n)
+ vars = append(vars, dvar)
+ markVarSeen(n, vid)
+ continue
+ }
+ }
+ }
+ seen[n] = true
+ decls = append(decls, n)
+ vars = append(vars, createSimpleVar(fnsym, n, closureVars))
+ }
+
+ // Add SSA-tracked vars not in apDecls.
+ if canUseComplex {
+ for i, n := range debug.Vars {
+ if seen[n] {
+ continue
+ }
+ if !shouldEmitDwarfVar(n) {
+ continue
+ }
+ if dvar := createComplexVar(fnsym, fn, ssa.VarID(i), closureVars); dvar != nil {
+ decls = append(decls, n)
+ vars = append(vars, dvar)
+ markVarSeen(n, ssa.VarID(i))
+ }
+ }
+ }
+
+ // Recover zero-sized variables eliminated by the stackframe pass.
+ if debug != nil {
+ for _, n := range debug.OptDcl {
+ if seen[n] {
+ continue
+ }
if n.Class != ir.PAUTO {
continue
}
@@ -199,21 +259,23 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
vars = append(vars, createSimpleVar(fnsym, n, closureVars))
vars[len(vars)-1].StackOffset = 0
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
+ seen[n] = true
}
}
}
+ // For inlined functions or functions with register output params,
+ // collect additional declarations that may not be in apDecls.
dcl := apDecls
if fnsym.WasInlined() {
dcl = preInliningDcls(fnsym)
- } else {
+ } else if debug != nil {
// The backend's stackframe pass prunes away entries from the
// fn's Dcl list, including PARAMOUT nodes that correspond to
// output params passed in registers. Add back in these
// entries here so that we can process them properly during
// DWARF-gen. See issue 48573 for more details.
- debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
- for _, n := range debugInfo.RegOutputParams {
+ for _, n := range debug.RegOutputParams {
if !ssa.IsVarWantedForDebug(n) {
continue
}
@@ -224,83 +286,33 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
}
}
- // If optimization is enabled, the list above will typically be
- // missing some of the original pre-optimization variables in the
- // function (they may have been promoted to registers, folded into
- // constants, dead-coded away, etc). Input arguments not eligible
- // for SSA optimization are also missing. Here we add back in entries
- // for selected missing vars. Note that the recipe below creates a
- // conservative location. The idea here is that we want to
- // communicate to the user that "yes, there is a variable named X
- // in this function, but no, I don't have enough information to
- // reliably report its contents."
- // For non-SSA-able arguments, however, the correct information
- // is known -- they have a single home on the stack.
+ // Process remaining variables not yet handled. For each variable,
+ // try createComplexVar first, then fall back to createSimpleVar
+ // for non-SSA-able params, or createConservativeVar for the rest.
for _, n := range dcl {
- if selected.Has(n) {
+ if seen[n] {
continue
}
- c := n.Sym().Name[0]
- if c == '.' || n.Type().IsUntyped() {
+ if !shouldEmitDwarfVar(n) {
continue
}
+ seen[n] = true
+ if canUseComplex {
+ if vid, ok := varIDMap[n]; ok {
+ if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
+ decls = append(decls, n)
+ vars = append(vars, dvar)
+ continue
+ }
+ }
+ }
if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) {
- // SSA-able args get location lists, and may move in and
- // out of registers, so those are handled elsewhere.
- // Autos and named output params seem to get handled
- // with VARDEF, which creates location lists.
- // Args not of SSA-able type are treated here; they
- // are homed on the stack in a single place for the
- // entire call.
- vars = append(vars, createSimpleVar(fnsym, n, closureVars))
decls = append(decls, n)
+ vars = append(vars, createSimpleVar(fnsym, n, closureVars))
continue
}
- typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
decls = append(decls, n)
- tag := dwarf.DW_TAG_variable
- isReturnValue := (n.Class == ir.PPARAMOUT)
- if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
- tag = dwarf.DW_TAG_formal_parameter
- }
- inlIndex := 0
- if base.Flag.GenDwarfInl > 1 {
- if n.InlFormal() || n.InlLocal() {
- inlIndex = posInlIndex(n.Pos()) + 1
- if n.InlFormal() {
- tag = dwarf.DW_TAG_formal_parameter
- }
- }
- }
- declpos := base.Ctxt.InnermostPos(n.Pos())
- dvar := &dwarf.Var{
- Name: n.Sym().Name,
- IsReturnValue: isReturnValue,
- Tag: tag,
- WithLoclist: true,
- StackOffset: int32(n.FrameOffset()),
- Type: base.Ctxt.Lookup(typename),
- DeclFile: declpos.RelFilename(),
- DeclLine: declpos.RelLine(),
- DeclCol: declpos.RelCol(),
- InlIndex: int32(inlIndex),
- ChildIndex: -1,
- DictIndex: n.DictIndex,
- ClosureOffset: closureOffset(n, closureVars),
- }
- if n.Esc() == ir.EscHeap {
- if n.Heapaddr == nil {
- base.Fatalf("invalid heap allocated var without Heapaddr")
- }
- debug := fn.DebugInfo.(*ssa.FuncDebug)
- list := createHeapDerefLocationList(n, debug.EntryID)
- dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
- debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
- }
- }
- vars = append(vars, dvar)
- // Record go type to ensure that it gets emitted by the linker.
- fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
+ vars = append(vars, createConservativeVar(fnsym, fn, n, closureVars))
}
// Sort decls and vars.
@@ -309,6 +321,60 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
return decls, vars
}
+// createConservativeVar creates a DWARF variable with a conservative location
+// description. This is used for variables that were optimized away or otherwise
+// don't have precise location info. The intent is to communicate that "yes,
+// there is a variable named X in this function, but no, I don't have enough
+// information to reliably report its contents."
+// For heap-escaped variables, a location list is created that describes
+// dereferencing the pointer at the stack offset.
+func createConservativeVar(fnsym *obj.LSym, fn *ir.Func, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
+ typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
+ tag := dwarf.DW_TAG_variable
+ isReturnValue := (n.Class == ir.PPARAMOUT)
+ if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
+ tag = dwarf.DW_TAG_formal_parameter
+ }
+ inlIndex := 0
+ if base.Flag.GenDwarfInl > 1 {
+ if n.InlFormal() || n.InlLocal() {
+ inlIndex = posInlIndex(n.Pos()) + 1
+ if n.InlFormal() {
+ tag = dwarf.DW_TAG_formal_parameter
+ }
+ }
+ }
+ declpos := base.Ctxt.InnermostPos(n.Pos())
+ dvar := &dwarf.Var{
+ Name: n.Sym().Name,
+ IsReturnValue: isReturnValue,
+ Tag: tag,
+ WithLoclist: true,
+ StackOffset: int32(n.FrameOffset()),
+ Type: base.Ctxt.Lookup(typename),
+ DeclFile: declpos.RelFilename(),
+ DeclLine: declpos.RelLine(),
+ DeclCol: declpos.RelCol(),
+ InlIndex: int32(inlIndex),
+ ChildIndex: -1,
+ DictIndex: n.DictIndex,
+ ClosureOffset: closureOffset(n, closureVars),
+ }
+ if n.Esc() == ir.EscHeap {
+ if n.Heapaddr == nil {
+ base.Fatalf("invalid heap allocated var without Heapaddr")
+ }
+ debug := fn.DebugInfo.(*ssa.FuncDebug)
+ list := createHeapDerefLocationList(n, debug.EntryID)
+ dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
+ debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
+ }
+ }
+ // Record go type to ensure that it gets emitted by the linker.
+ fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
+ return dvar
+}
+
// sortDeclsAndVars sorts the decl and dwarf var lists according to
// parameter declaration order, so as to insure that when a subprogram
// DIE is emitted, its parameter children appear in declaration order.
@@ -379,24 +445,6 @@ func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
return rdcl
}
-// createSimpleVars creates a DWARF entry for every variable declared in the
-// function, claiming that they are permanently on the stack.
-func createSimpleVars(fnsym *obj.LSym, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
- var vars []*dwarf.Var
- var decls []*ir.Name
- var selected ir.NameSet
- for _, n := range apDecls {
- if ir.IsAutoTmp(n) {
- continue
- }
-
- decls = append(decls, n)
- vars = append(vars, createSimpleVar(fnsym, n, closureVars))
- selected.Add(n)
- }
- return decls, vars, selected
-}
-
func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
var tag int
var offs int64
@@ -457,63 +505,6 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64
}
}
-// createABIVars creates DWARF variables for functions in which the
-// register ABI is enabled but optimization is turned off. It uses a
-// hybrid approach in which register-resident input params are
-// captured with location lists, and all other vars use the "simple"
-// strategy.
-func createABIVars(fnsym *obj.LSym, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
-
- // Invoke createComplexVars to generate dwarf vars for input parameters
- // that are register-allocated according to the ABI rules.
- decls, vars, selected := createComplexVars(fnsym, fn, closureVars)
-
- // Now fill in the remainder of the variables: input parameters
- // that are not register-resident, output parameters, and local
- // variables.
- for _, n := range apDecls {
- if ir.IsAutoTmp(n) {
- continue
- }
- if _, ok := selected[n]; ok {
- // already handled
- continue
- }
-
- decls = append(decls, n)
- vars = append(vars, createSimpleVar(fnsym, n, closureVars))
- selected.Add(n)
- }
-
- return decls, vars, selected
-}
-
-// createComplexVars creates recomposed DWARF vars with location lists,
-// suitable for describing optimized code.
-func createComplexVars(fnsym *obj.LSym, fn *ir.Func, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var, ir.NameSet) {
- debugInfo := fn.DebugInfo.(*ssa.FuncDebug)
-
- // Produce a DWARF variable entry for each user variable.
- var decls []*ir.Name
- var vars []*dwarf.Var
- var ssaVars ir.NameSet
-
- for varID, dvar := range debugInfo.Vars {
- n := dvar
- ssaVars.Add(n)
- for _, slot := range debugInfo.VarSlots[varID] {
- ssaVars.Add(debugInfo.Slots[slot].N)
- }
-
- if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID), closureVars); dvar != nil {
- decls = append(decls, n)
- vars = append(vars, dvar)
- }
- }
-
- return decls, vars, ssaVars
-}
-
// createComplexVar builds a single DWARF variable entry and location list.
func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
debug := fn.DebugInfo.(*ssa.FuncDebug)
@@ -573,7 +564,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars
// createHeapDerefLocationList creates a location list for a heap-escaped variable
// that describes "dereference pointer at stack offset"
-func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []byte {
+func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []ssa.LocListEntry {
// Get the stack offset where the heap pointer is stored
heapPtrOffset := n.Heapaddr.FrameOffset()
if base.Ctxt.Arch.FixedFrameSize == 0 {
@@ -584,14 +575,18 @@ func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []byte {
}
// Create a location expression: DW_OP_fbreg <offset> DW_OP_deref
- var locExpr []byte
- var sizeIdx int
- locExpr, sizeIdx = ssa.SetupLocList(base.Ctxt, entryID, locExpr, ssa.BlockStart.ID, ssa.FuncEnd.ID)
- locExpr = append(locExpr, dwarf.DW_OP_fbreg)
- locExpr = dwarf.AppendSleb128(locExpr, heapPtrOffset)
- locExpr = append(locExpr, dwarf.DW_OP_deref)
- base.Ctxt.Arch.ByteOrder.PutUint16(locExpr[sizeIdx:], uint16(len(locExpr)-sizeIdx-2))
- return locExpr
+ var expr []byte
+ expr = append(expr, dwarf.DW_OP_fbreg)
+ expr = dwarf.AppendSleb128(expr, heapPtrOffset)
+ expr = append(expr, dwarf.DW_OP_deref)
+
+ return []ssa.LocListEntry{{
+ StartBlock: entryID,
+ StartValue: ssa.BlockStart.ID,
+ EndBlock: entryID,
+ EndValue: ssa.FuncEnd.ID,
+ Expr: expr,
+ }}
}
// RecordFlags records the specified command-line flags to be placed
@@ -667,6 +662,26 @@ func RecordPackageName() {
s.P = []byte(types.LocalPkg.Name)
}
+// shouldEmitDwarfVar reports whether n should have a DWARF variable entry.
+// This consolidates filtering that was previously spread across IR (AutoTemp),
+// SSA (IsVarWantedForDebug), and dwarfgen (symbol name checks).
+func shouldEmitDwarfVar(n *ir.Name) bool {
+ if ir.IsAutoTmp(n) {
+ return false
+ }
+ if !ssa.IsVarWantedForDebug(n) {
+ return false
+ }
+ if n.Sym().Name == "_" {
+ return false
+ }
+ c := n.Sym().Name[0]
+ if c == '.' || n.Type().IsUntyped() {
+ return false
+ }
+ return true
+}
+
func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
return closureVars[n]
}
diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go
index 687abc42cc..84ee128260 100644
--- a/src/cmd/compile/internal/ssa/debug.go
+++ b/src/cmd/compile/internal/ssa/debug.go
@@ -13,7 +13,6 @@ import (
"cmd/internal/obj"
"cmd/internal/src"
"cmp"
- "encoding/hex"
"fmt"
"internal/buildcfg"
"math/bits"
@@ -35,7 +34,7 @@ type FuncDebug struct {
// The slots that make up each variable, indexed by VarID.
VarSlots [][]SlotID
// The location list data, indexed by VarID. Must be processed by PutLocationList.
- LocationLists [][]byte
+ LocationLists [][]LocListEntry
// Register-resident output parameters for the function. This is filled in at
// SSA generation time.
RegOutputParams []*ir.Name
@@ -52,6 +51,15 @@ type FuncDebug struct {
GetPC func(block, value ID) int64
}
+// LocListEntry represents a single entry in a location list.
+// StartBlock/StartValue and EndBlock/EndValue are SSA coordinates
+// that get resolved to PCs during final encoding.
+type LocListEntry struct {
+ StartBlock, StartValue ID
+ EndBlock, EndValue ID
+ Expr []byte // DWARF location expression (DW_OP_*)
+}
+
type BlockDebug struct {
// State at the start and end of the block. These are initialized,
// and updated from new information that flows on back edges.
@@ -206,7 +214,7 @@ type debugState struct {
slots []LocalSlot
vars []*ir.Name
varSlots [][]SlotID
- lists [][]byte
+ lists [][]LocListEntry
// The user variable that each slot rolls up to, indexed by SlotID.
slotVars []VarID
@@ -293,7 +301,7 @@ func (state *debugState) initializeCache(f *Func, numVars, numSlots int) {
state.pendingEntries = pe
if cap(state.lists) < numVars {
- state.lists = make([][]byte, numVars)
+ state.lists = make([][]LocListEntry, numVars)
} else {
state.lists = state.lists[:numVars]
clear(state.lists)
@@ -1348,7 +1356,7 @@ func (state *debugState) buildLocationLists(blockLocs []*BlockDebug) {
if len(list) == 0 {
state.logf("\t%v : empty list\n", state.vars[varID])
} else {
- state.logf("\t%v : %q\n", state.vars[varID], hex.EncodeToString(state.lists[varID]))
+ state.logf("\t%v : %d entries\n", state.vars[varID], len(list))
}
}
}
@@ -1405,32 +1413,14 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
return
}
- // Pack the start/end coordinates into the start/end addresses
- // of the entry, for decoding by PutLocationList.
- start, startOK := encodeValue(state.ctxt, pending.startBlock, pending.startValue)
- end, endOK := encodeValue(state.ctxt, endBlock, endValue)
- if !startOK || !endOK {
- // If someone writes a function that uses >65K values,
- // they get incomplete debug info on 32-bit platforms.
- return
- }
- if start == end {
+ // Skip zero-width entries where start and end coordinates are identical.
+ if pending.startBlock == endBlock && pending.startValue == endValue {
if state.loggingLevel > 1 {
- // Printf not logf so not gated by GOSSAFUNC; this should fire very rarely.
- // TODO this fires a lot, need to figure out why.
state.logf("Skipping empty location list for %v in %s\n", state.vars[varID], state.f.Name)
}
return
}
- list := state.lists[varID]
- list = appendPtr(state.ctxt, list, start)
- list = appendPtr(state.ctxt, list, end)
- // Where to write the length of the location description once
- // we know how big it is.
- sizeIdx := len(list)
- list = list[:len(list)+2]
-
if state.loggingLevel > 1 {
var partStrs []string
for i, slot := range state.varSlots[varID] {
@@ -1439,6 +1429,8 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
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, " "))
}
+ // Build the DWARF location expression.
+ var expr []byte
for i, slotID := range state.varSlots[varID] {
loc := pending.pieces[i]
slot := state.slots[slotID]
@@ -1446,49 +1438,51 @@ func (state *debugState) writePendingEntry(varID VarID, endBlock, endValue ID) {
if !loc.absent() {
if loc.onStack() {
if loc.stackOffsetValue() == 0 {
- list = append(list, dwarf.DW_OP_call_frame_cfa)
+ expr = append(expr, dwarf.DW_OP_call_frame_cfa)
} else {
- list = append(list, dwarf.DW_OP_fbreg)
- list = dwarf.AppendSleb128(list, int64(loc.stackOffsetValue()))
+ expr = append(expr, dwarf.DW_OP_fbreg)
+ expr = dwarf.AppendSleb128(expr, int64(loc.stackOffsetValue()))
}
} else {
regnum := state.ctxt.Arch.DWARFRegisters[state.registers[firstReg(loc.Registers)].ObjNum()]
if regnum < 32 {
- list = append(list, dwarf.DW_OP_reg0+byte(regnum))
+ expr = append(expr, dwarf.DW_OP_reg0+byte(regnum))
} else {
- list = append(list, dwarf.DW_OP_regx)
- list = dwarf.AppendUleb128(list, uint64(regnum))
+ expr = append(expr, dwarf.DW_OP_regx)
+ expr = dwarf.AppendUleb128(expr, uint64(regnum))
}
}
}
if len(state.varSlots[varID]) > 1 {
- list = append(list, dwarf.DW_OP_piece)
- list = dwarf.AppendUleb128(list, uint64(slot.Type.Size()))
+ expr = append(expr, dwarf.DW_OP_piece)
+ expr = dwarf.AppendUleb128(expr, uint64(slot.Type.Size()))
}
}
- state.ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
- state.lists[varID] = list
+
+ entry := LocListEntry{
+ StartBlock: pending.startBlock,
+ StartValue: pending.startValue,
+ EndBlock: endBlock,
+ EndValue: endValue,
+ Expr: expr,
+ }
+ state.lists[varID] = append(state.lists[varID], entry)
}
-// PutLocationList adds list (a location list in its intermediate
-// representation) to listSym.
-func (debugInfo *FuncDebug) PutLocationList(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
+// PutLocationList adds entries (a location list in structured form)
+// to listSym, encoding it in the appropriate DWARF format.
+func (debugInfo *FuncDebug) PutLocationList(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
if buildcfg.Experiment.Dwarf5 {
- debugInfo.PutLocationListDwarf5(list, ctxt, listSym, startPC)
+ debugInfo.PutLocationListDwarf5(entries, ctxt, listSym, startPC)
} else {
- debugInfo.PutLocationListDwarf4(list, ctxt, listSym, startPC)
+ debugInfo.PutLocationListDwarf4(entries, ctxt, listSym, startPC)
}
}
-// PutLocationListDwarf5 adds list (a location list in its intermediate
-// representation) to listSym in DWARF 5 format. NB: this is a somewhat
-// hacky implementation in that it actually reads a DWARF4 encoded
-// info from list (with all its DWARF4-specific quirks) then re-encodes
-// it in DWARF5. It would probably be better at some point to have
-// ssa/debug encode the list in a version-independent form and then
-// have this func (and PutLocationListDwarf4) intoduce the quirks.
-func (debugInfo *FuncDebug) PutLocationListDwarf5(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
+// PutLocationListDwarf5 adds entries (a location list in structured form)
+// to listSym in DWARF 5 format.
+func (debugInfo *FuncDebug) PutLocationListDwarf5(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
getPC := debugInfo.GetPC
// base address entry
@@ -1496,42 +1490,34 @@ func (debugInfo *FuncDebug) PutLocationListDwarf5(list []byte, ctxt *obj.Link, l
listSym.WriteDwTxtAddrx(ctxt, listSym.Size, startPC, ctxt.DwTextCount*2)
var stbuf, enbuf [10]byte
- stb, enb := stbuf[:], enbuf[:]
- // Re-read list, translating its address from block/value ID to PC.
- for i := 0; i < len(list); {
- begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
- end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
+ for _, entry := range entries {
+ begin := getPC(entry.StartBlock, entry.StartValue)
+ end := getPC(entry.EndBlock, entry.EndValue)
// Write LLE_offset_pair tag followed by payload (ULEB for start
// and then end).
listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_offset_pair)
- stb, enb = stb[:0], enb[:0]
+ stb := stbuf[:0]
+ enb := enbuf[:0]
stb = dwarf.AppendUleb128(stb, uint64(begin))
enb = dwarf.AppendUleb128(enb, uint64(end))
listSym.WriteBytes(ctxt, listSym.Size, stb)
listSym.WriteBytes(ctxt, listSym.Size, enb)
- // The encoded data in "list" is in DWARF4 format, which uses
- // a 2-byte length; DWARF5 uses an LEB-encoded value for this
- // length. Read the length and then re-encode it.
- i += 2 * ctxt.Arch.PtrSize
- datalen := int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
- i += 2
- stb = stb[:0]
- stb = dwarf.AppendUleb128(stb, uint64(datalen))
- listSym.WriteBytes(ctxt, listSym.Size, stb) // copy length
- listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // loc desc
-
- i += datalen
+ // DWARF5 uses ULEB128-encoded length for the location expression.
+ stb = stbuf[:0]
+ stb = dwarf.AppendUleb128(stb, uint64(len(entry.Expr)))
+ listSym.WriteBytes(ctxt, listSym.Size, stb)
+ listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
}
// Terminator
listSym.WriteInt(ctxt, listSym.Size, 1, dwarf.DW_LLE_end_of_list)
}
-// PutLocationListDwarf4 adds list (a location list in its intermediate
-// representation) to listSym in DWARF 4 format.
-func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, listSym, startPC *obj.LSym) {
+// PutLocationListDwarf4 adds entries (a location list in structured form)
+// to listSym in DWARF 4 format.
+func (debugInfo *FuncDebug) PutLocationListDwarf4(entries []LocListEntry, ctxt *obj.Link, listSym, startPC *obj.LSym) {
getPC := debugInfo.GetPC
if ctxt.UseBASEntries {
@@ -1539,10 +1525,9 @@ func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, l
listSym.WriteAddr(ctxt, listSym.Size, ctxt.Arch.PtrSize, startPC, 0)
}
- // Re-read list, translating its address from block/value ID to PC.
- for i := 0; i < len(list); {
- begin := getPC(decodeValue(ctxt, readPtr(ctxt, list[i:])))
- end := getPC(decodeValue(ctxt, readPtr(ctxt, list[i+ctxt.Arch.PtrSize:])))
+ for _, entry := range entries {
+ begin := getPC(entry.StartBlock, entry.StartValue)
+ end := getPC(entry.EndBlock, entry.EndValue)
// Horrible hack. If a range contains only zero-width
// instructions, e.g. an Arg, and it's at the beginning of the
@@ -1560,113 +1545,16 @@ func (debugInfo *FuncDebug) PutLocationListDwarf4(list []byte, ctxt *obj.Link, l
listSym.WriteCURelativeAddr(ctxt, listSym.Size, startPC, end)
}
- i += 2 * ctxt.Arch.PtrSize
- datalen := 2 + int(ctxt.Arch.ByteOrder.Uint16(list[i:]))
- listSym.WriteBytes(ctxt, listSym.Size, list[i:i+datalen]) // copy datalen and location encoding
- i += datalen
+ // Write 2-byte length prefix followed by the location expression.
+ listSym.WriteInt(ctxt, listSym.Size, 2, int64(len(entry.Expr)))
+ listSym.WriteBytes(ctxt, listSym.Size, entry.Expr)
}
- // Location list contents, now with real PCs.
// End entry.
listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
listSym.WriteInt(ctxt, listSym.Size, ctxt.Arch.PtrSize, 0)
}
-// Pack a value and block ID into an address-sized uint, returning
-// encoded value and boolean indicating whether the encoding succeeded.
-// For 32-bit architectures the process may fail for very large
-// procedures(the theory being that it's ok to have degraded debug
-// quality in this case).
-func encodeValue(ctxt *obj.Link, b, v ID) (uint64, bool) {
- if ctxt.Arch.PtrSize == 8 {
- result := uint64(b)<<32 | uint64(uint32(v))
- //ctxt.Logf("b %#x (%d) v %#x (%d) -> %#x\n", b, b, v, v, result)
- return result, true
- }
- if ctxt.Arch.PtrSize != 4 {
- panic("unexpected pointer size")
- }
- if ID(int16(b)) != b || ID(int16(v)) != v {
- return 0, false
- }
- return uint64(b)<<16 | uint64(uint16(v)), true
-}
-
-// Unpack a value and block ID encoded by encodeValue.
-func decodeValue(ctxt *obj.Link, word uint64) (ID, ID) {
- if ctxt.Arch.PtrSize == 8 {
- b, v := ID(word>>32), ID(word)
- //ctxt.Logf("%#x -> b %#x (%d) v %#x (%d)\n", word, b, b, v, v)
- return b, v
- }
- if ctxt.Arch.PtrSize != 4 {
- panic("unexpected pointer size")
- }
- return ID(word >> 16), ID(int16(word))
-}
-
-// Append a pointer-sized uint to buf.
-func appendPtr(ctxt *obj.Link, buf []byte, word uint64) []byte {
- if cap(buf) < len(buf)+20 {
- b := make([]byte, len(buf), 20+cap(buf)*2)
- copy(b, buf)
- buf = b
- }
- writeAt := len(buf)
- buf = buf[0 : len(buf)+ctxt.Arch.PtrSize]
- writePtr(ctxt, buf[writeAt:], word)
- return buf
-}
-
-// Write a pointer-sized uint to the beginning of buf.
-func writePtr(ctxt *obj.Link, buf []byte, word uint64) {
- switch ctxt.Arch.PtrSize {
- case 4:
- ctxt.Arch.ByteOrder.PutUint32(buf, uint32(word))
- case 8:
- ctxt.Arch.ByteOrder.PutUint64(buf, word)
- default:
- panic("unexpected pointer size")
- }
-
-}
-
-// Read a pointer-sized uint from the beginning of buf.
-func readPtr(ctxt *obj.Link, buf []byte) uint64 {
- switch ctxt.Arch.PtrSize {
- case 4:
- return uint64(ctxt.Arch.ByteOrder.Uint32(buf))
- case 8:
- return ctxt.Arch.ByteOrder.Uint64(buf)
- default:
- panic("unexpected pointer size")
- }
-
-}
-
-// SetupLocList creates the initial portion of a location list for a
-// user variable. It emits the encoded start/end of the range and a
-// placeholder for the size. Return value is the new list plus the
-// slot in the list holding the size (to be updated later).
-func SetupLocList(ctxt *obj.Link, entryID ID, list []byte, st, en ID) ([]byte, int) {
- start, startOK := encodeValue(ctxt, entryID, st)
- end, endOK := encodeValue(ctxt, entryID, en)
- if !startOK || !endOK {
- // This could happen if someone writes a function that uses
- // >65K values on a 32-bit platform. Hopefully a degraded debugging
- // experience is ok in that case.
- return nil, 0
- }
- list = appendPtr(ctxt, list, start)
- list = appendPtr(ctxt, list, end)
-
- // Where to write the length of the location description once
- // we know how big it is.
- sizeIdx := len(list)
- list = list[:len(list)+2]
- return list, sizeIdx
-}
-
// locatePrologEnd walks the entry block of a function with incoming
// register arguments and locates the last instruction in the prolog
// that spills a register arg. It returns the ID of that instruction,
@@ -1832,7 +1720,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
// Allocate location lists.
- rval.LocationLists = make([][]byte, numRegParams+extraForCloCtx)
+ rval.LocationLists = make([][]LocListEntry, numRegParams+extraForCloCtx)
// Locate the value corresponding to the last spill of
// an input register.
@@ -1909,15 +1797,10 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
// Param is arriving in one or more registers. We need a 2-element
// location expression for it. First entry in location list
// will correspond to lifetime in input registers.
- list, sizeIdx := SetupLocList(ctxt, f.Entry.ID, rval.LocationLists[pidx],
- BlockStart.ID, afterPrologVal)
- if list == nil {
- pidx++
- continue
- }
if loggingEnabled {
state.logf("param %v:\n [<entry>, %d]:\n", n, afterPrologVal)
}
+ var regExpr []byte
rtypes, _ := inp.RegisterTypesAndOffsets()
padding := make([]uint64, 0, 32)
padding = inp.ComputePadding(padding)
@@ -1930,56 +1813,60 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
}
dwreg := ctxt.Arch.DWARFRegisters[reg]
if dwreg < 32 {
- list = append(list, dwarf.DW_OP_reg0+byte(dwreg))
+ regExpr = append(regExpr, dwarf.DW_OP_reg0+byte(dwreg))
} else {
- list = append(list, dwarf.DW_OP_regx)
- list = dwarf.AppendUleb128(list, uint64(dwreg))
+ regExpr = append(regExpr, dwarf.DW_OP_regx)
+ regExpr = dwarf.AppendUleb128(regExpr, uint64(dwreg))
}
if loggingEnabled {
state.logf(" piece %d -> dwreg %d", k, dwreg)
}
if len(inp.Registers) > 1 {
- list = append(list, dwarf.DW_OP_piece)
+ regExpr = append(regExpr, dwarf.DW_OP_piece)
ts := rtypes[k].Size()
- list = dwarf.AppendUleb128(list, uint64(ts))
+ regExpr = dwarf.AppendUleb128(regExpr, uint64(ts))
if padding[k] > 0 {
if loggingEnabled {
state.logf(" [pad %d bytes]", padding[k])
}
- list = append(list, dwarf.DW_OP_piece)
- list = dwarf.AppendUleb128(list, padding[k])
+ regExpr = append(regExpr, dwarf.DW_OP_piece)
+ regExpr = dwarf.AppendUleb128(regExpr, padding[k])
}
}
if loggingEnabled {
state.logf("\n")
}
}
- // fill in length of location expression element
- ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
+ rval.LocationLists[pidx] = append(rval.LocationLists[pidx], LocListEntry{
+ StartBlock: f.Entry.ID,
+ StartValue: BlockStart.ID,
+ EndBlock: f.Entry.ID,
+ EndValue: afterPrologVal,
+ Expr: regExpr,
+ })
// Second entry in the location list will be the stack home
// of the param, once it has been spilled. Emit that now.
- list, sizeIdx = SetupLocList(ctxt, f.Entry.ID, list,
- afterPrologVal, FuncEnd.ID)
- if list == nil {
- pidx++
- continue
- }
+ var stackExpr []byte
soff := stackOffset(sl)
if soff == 0 {
- list = append(list, dwarf.DW_OP_call_frame_cfa)
+ stackExpr = append(stackExpr, dwarf.DW_OP_call_frame_cfa)
} else {
- list = append(list, dwarf.DW_OP_fbreg)
- list = dwarf.AppendSleb128(list, int64(soff))
+ stackExpr = append(stackExpr, dwarf.DW_OP_fbreg)
+ stackExpr = dwarf.AppendSleb128(stackExpr, int64(soff))
}
if loggingEnabled {
state.logf(" [%d, <end>): stackOffset=%d\n", afterPrologVal, soff)
}
- // fill in size
- ctxt.Arch.ByteOrder.PutUint16(list[sizeIdx:], uint16(len(list)-sizeIdx-2))
+ rval.LocationLists[pidx] = append(rval.LocationLists[pidx], LocListEntry{
+ StartBlock: f.Entry.ID,
+ StartValue: afterPrologVal,
+ EndBlock: f.Entry.ID,
+ EndValue: FuncEnd.ID,
+ Expr: stackExpr,
+ })
- rval.LocationLists[pidx] = list
pidx++
}
}