Source file src/cmd/compile/internal/dwarfgen/dwarf.go

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package dwarfgen
     6  
     7  import (
     8  	"bytes"
     9  	"flag"
    10  	"fmt"
    11  	"internal/buildcfg"
    12  	"slices"
    13  	"sort"
    14  	"strings"
    15  
    16  	"cmd/compile/internal/base"
    17  	"cmd/compile/internal/ir"
    18  	"cmd/compile/internal/reflectdata"
    19  	"cmd/compile/internal/ssa"
    20  	"cmd/compile/internal/ssa/ssadebug"
    21  	"cmd/compile/internal/ssagen"
    22  	"cmd/compile/internal/typecheck"
    23  	"cmd/compile/internal/types"
    24  	"cmd/internal/dwarf"
    25  	"cmd/internal/obj"
    26  	"cmd/internal/objabi"
    27  	"cmd/internal/src"
    28  )
    29  
    30  func Info(ctxt *obj.Link, fnsym *obj.LSym, infosym *obj.LSym, curfn obj.Func) (scopes []dwarf.Scope, inlcalls dwarf.InlCalls) {
    31  	fn := curfn.(*ir.Func)
    32  
    33  	if fn.Nname != nil {
    34  		expect := fn.Linksym()
    35  		if fnsym.ABI() == obj.ABI0 {
    36  			expect = fn.LinksymABI(obj.ABI0)
    37  		}
    38  		if fnsym != expect {
    39  			base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
    40  		}
    41  	}
    42  
    43  	// Back when there were two different *Funcs for a function, this code
    44  	// was not consistent about whether a particular *Node being processed
    45  	// was an ODCLFUNC or ONAME node. Partly this is because inlined function
    46  	// bodies have no ODCLFUNC node, which was it's own inconsistency.
    47  	// In any event, the handling of the two different nodes for DWARF purposes
    48  	// was subtly different, likely in unintended ways. CL 272253 merged the
    49  	// two nodes' Func fields, so that code sees the same *Func whether it is
    50  	// holding the ODCLFUNC or the ONAME. This resulted in changes in the
    51  	// DWARF output. To preserve the existing DWARF output and leave an
    52  	// intentional change for a future CL, this code does the following when
    53  	// fn.Op == ONAME:
    54  	//
    55  	// 1. Disallow use of createComplexVars in createDwarfVars.
    56  	//    It was not possible to reach that code for an ONAME before,
    57  	//    because the DebugInfo was set only on the ODCLFUNC Func.
    58  	//    Calling into it in the ONAME case causes an index out of bounds panic.
    59  	//
    60  	// 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func,
    61  	//    not the ONAME Func. Populating apdecls for the ONAME case results
    62  	//    in selected being populated after createSimpleVars is called in
    63  	//    createDwarfVars, and then that causes the loop to skip all the entries
    64  	//    in dcl, meaning that the RecordAutoType calls don't happen.
    65  	//
    66  	// These two adjustments keep toolstash -cmp working for now.
    67  	// Deciding the right answer is, as they say, future work.
    68  	//
    69  	// We can tell the difference between the old ODCLFUNC and ONAME
    70  	// cases by looking at the infosym.Name. If it's empty, DebugInfo is
    71  	// being called from (*obj.Link).populateDWARF, which used to use
    72  	// the ODCLFUNC. If it's non-empty (the name will end in $abstract),
    73  	// DebugInfo is being called from (*obj.Link).DwarfAbstractFunc,
    74  	// which used to use the ONAME form.
    75  	isODCLFUNC := infosym.Name == ""
    76  
    77  	var apdecls []*ir.Name
    78  	// Populate decls for fn.
    79  	if isODCLFUNC {
    80  		for _, n := range fn.Dcl {
    81  			if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL
    82  				continue
    83  			}
    84  			switch n.Class {
    85  			case ir.PAUTO:
    86  				if !n.Used() {
    87  					// Text == nil -> generating abstract function
    88  					if fnsym.Func().Text != nil {
    89  						base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
    90  					}
    91  					continue
    92  				}
    93  			case ir.PPARAM, ir.PPARAMOUT:
    94  			default:
    95  				continue
    96  			}
    97  			if !shouldEmitDwarfVar(n) {
    98  				continue
    99  			}
   100  			apdecls = append(apdecls, n)
   101  			if n.Type().Kind() == types.TSSA {
   102  				// Can happen for TypeInt128 types. This only happens for
   103  				// spill locations, so not a huge deal.
   104  				continue
   105  			}
   106  			fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
   107  		}
   108  	}
   109  
   110  	var closureVars map[*ir.Name]int64
   111  	if fn.Needctxt() {
   112  		closureVars = make(map[*ir.Name]int64)
   113  		csiter := typecheck.NewClosureStructIter(fn.ClosureVars)
   114  		for {
   115  			n, _, offset := csiter.Next()
   116  			if n == nil {
   117  				break
   118  			}
   119  			closureVars[n] = offset
   120  			if n.Heapaddr != nil {
   121  				closureVars[n.Heapaddr] = offset
   122  			}
   123  		}
   124  	}
   125  
   126  	decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls, closureVars)
   127  
   128  	// For each type referenced by the functions auto vars but not
   129  	// already referenced by a dwarf var, attach an R_USETYPE relocation to
   130  	// the function symbol to insure that the type included in DWARF
   131  	// processing during linking.
   132  	// Do the same with R_USEIFACE relocations from the function symbol for the
   133  	// same reason.
   134  	// All these R_USETYPE relocations are only looked at if the function
   135  	// survives deadcode elimination in the linker.
   136  	typesyms := []*obj.LSym{}
   137  	for t := range fnsym.Func().Autot {
   138  		typesyms = append(typesyms, t)
   139  	}
   140  	for i := range fnsym.R {
   141  		if fnsym.R[i].Type == objabi.R_USEIFACE && !strings.HasPrefix(fnsym.R[i].Sym.Name, "go:itab.") {
   142  			// Types referenced through itab will be referenced from somewhere else
   143  			typesyms = append(typesyms, fnsym.R[i].Sym)
   144  		}
   145  	}
   146  	slices.SortFunc(typesyms, func(a, b *obj.LSym) int {
   147  		return strings.Compare(a.Name, b.Name)
   148  	})
   149  	var lastsym *obj.LSym
   150  	for _, sym := range typesyms {
   151  		if sym == lastsym {
   152  			continue
   153  		}
   154  		lastsym = sym
   155  		infosym.AddRel(ctxt, obj.Reloc{Type: objabi.R_USETYPE, Sym: sym})
   156  	}
   157  	fnsym.Func().Autot = nil
   158  
   159  	var varScopes []ir.ScopeID
   160  	for _, decl := range decls {
   161  		pos := declPos(decl)
   162  		varScopes = append(varScopes, findScope(fn.Marks, pos))
   163  	}
   164  
   165  	scopes = assembleScopes(fnsym, fn, dwarfVars, varScopes)
   166  	if base.Flag.GenDwarfInl > 0 {
   167  		inlcalls = assembleInlines(fnsym, dwarfVars)
   168  	}
   169  	return scopes, inlcalls
   170  }
   171  
   172  func declPos(decl *ir.Name) src.XPos {
   173  	return decl.Canonical().Pos()
   174  }
   175  
   176  // createDwarfVars process fn, returning a list of DWARF variables and the
   177  // Nodes they represent.
   178  func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name, closureVars map[*ir.Name]int64) ([]*ir.Name, []*dwarf.Var) {
   179  	// Collect a raw list of DWARF vars.
   180  	var vars []*dwarf.Var
   181  	var decls []*ir.Name
   182  
   183  	// Build a VarID lookup map for SSA debug info if available.
   184  	var debug *ssadebug.FuncDebug
   185  	var varIDMap map[*ir.Name]ssa.VarID
   186  	if fn.DebugInfo != nil {
   187  		debug = fn.DebugInfo.(*ssadebug.FuncDebug)
   188  		varIDMap = make(map[*ir.Name]ssa.VarID, len(debug.Vars))
   189  		for i, n := range debug.Vars {
   190  			varIDMap[n] = ssa.VarID(i)
   191  		}
   192  	}
   193  	canUseComplex := complexOK && debug != nil
   194  
   195  	// markVarSeen marks a variable and all its associated slot names as seen.
   196  	// This is needed because decomposed variables may have slots whose ir.Name
   197  	// differs from the variable itself (e.g., PAUTO vs PPARAMOUT for the same
   198  	// logical variable). Without this, the dcl loop could create duplicate
   199  	// conservative entries for names that are already covered by a complex var.
   200  	seen := make(map[*ir.Name]bool)
   201  	markVarSeen := func(n *ir.Name, varID ssa.VarID) {
   202  		seen[n] = true
   203  		if debug != nil && int(varID) < len(debug.VarSlots) {
   204  			for _, slot := range debug.VarSlots[varID] {
   205  				seen[debug.Slots[slot].N] = true
   206  			}
   207  		}
   208  	}
   209  
   210  	// Unified loop: for each variable in apDecls, try createComplexVar
   211  	// (SSA debug info) first, then fall back to createSimpleVar.
   212  	for _, n := range apDecls {
   213  		if !shouldEmitDwarfVar(n) {
   214  			continue
   215  		}
   216  		if canUseComplex {
   217  			if vid, ok := varIDMap[n]; ok {
   218  				if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
   219  					decls = append(decls, n)
   220  					vars = append(vars, dvar)
   221  					markVarSeen(n, vid)
   222  					continue
   223  				}
   224  			}
   225  		}
   226  		seen[n] = true
   227  		decls = append(decls, n)
   228  		vars = append(vars, createSimpleVar(fnsym, n, closureVars))
   229  	}
   230  
   231  	// Add SSA-tracked vars not in apDecls.
   232  	if canUseComplex {
   233  		for i, n := range debug.Vars {
   234  			if seen[n] {
   235  				continue
   236  			}
   237  			if !shouldEmitDwarfVar(n) {
   238  				continue
   239  			}
   240  			if dvar := createComplexVar(fnsym, fn, ssa.VarID(i), closureVars); dvar != nil {
   241  				decls = append(decls, n)
   242  				vars = append(vars, dvar)
   243  				markVarSeen(n, ssa.VarID(i))
   244  			}
   245  		}
   246  	}
   247  
   248  	// Recover zero-sized variables eliminated by the stackframe pass.
   249  	if debug != nil {
   250  		for _, n := range debug.OptDcl {
   251  			if seen[n] {
   252  				continue
   253  			}
   254  			if n.Class != ir.PAUTO {
   255  				continue
   256  			}
   257  			types.CalcSize(n.Type())
   258  			if n.Type().Size() == 0 {
   259  				decls = append(decls, n)
   260  				vars = append(vars, createSimpleVar(fnsym, n, closureVars))
   261  				vars[len(vars)-1].StackOffset = 0
   262  				fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
   263  				seen[n] = true
   264  			}
   265  		}
   266  	}
   267  
   268  	// For inlined functions or functions with register output params,
   269  	// collect additional declarations that may not be in apDecls.
   270  	dcl := apDecls
   271  	if fnsym.WasInlined() {
   272  		dcl = preInliningDcls(fnsym)
   273  	} else if debug != nil {
   274  		// The backend's stackframe pass prunes away entries from the
   275  		// fn's Dcl list, including PARAMOUT nodes that correspond to
   276  		// output params passed in registers. Add back in these
   277  		// entries here so that we can process them properly during
   278  		// DWARF-gen. See issue 48573 for more details.
   279  		for _, n := range debug.RegOutputParams {
   280  			if !ssa.IsVarWantedForDebug(n) {
   281  				continue
   282  			}
   283  			if n.Class != ir.PPARAMOUT || !n.IsOutputParamInRegisters() {
   284  				base.Fatalf("invalid ir.Name on debugInfo.RegOutputParams list")
   285  			}
   286  			dcl = append(dcl, n)
   287  		}
   288  	}
   289  
   290  	// Process remaining variables not yet handled. For each variable,
   291  	// try createComplexVar first, then fall back to createSimpleVar
   292  	// for non-SSA-able params, or createConservativeVar for the rest.
   293  	for _, n := range dcl {
   294  		if seen[n] {
   295  			continue
   296  		}
   297  		if !shouldEmitDwarfVar(n) {
   298  			continue
   299  		}
   300  		seen[n] = true
   301  		if canUseComplex {
   302  			if vid, ok := varIDMap[n]; ok {
   303  				if dvar := createComplexVar(fnsym, fn, vid, closureVars); dvar != nil {
   304  					decls = append(decls, n)
   305  					vars = append(vars, dvar)
   306  					continue
   307  				}
   308  			}
   309  		}
   310  		if n.Class == ir.PPARAM && !ssa.CanSSA(n.Type()) {
   311  			decls = append(decls, n)
   312  			vars = append(vars, createSimpleVar(fnsym, n, closureVars))
   313  			continue
   314  		}
   315  		decls = append(decls, n)
   316  		vars = append(vars, createConservativeVar(fnsym, fn, n, closureVars))
   317  	}
   318  
   319  	// Sort decls and vars.
   320  	sortDeclsAndVars(fn, decls, vars)
   321  
   322  	return decls, vars
   323  }
   324  
   325  // createConservativeVar creates a DWARF variable with a conservative location
   326  // description. This is used for variables that were optimized away or otherwise
   327  // don't have precise location info. The intent is to communicate that "yes,
   328  // there is a variable named X in this function, but no, I don't have enough
   329  // information to reliably report its contents."
   330  // For heap-escaped variables, a location list is created that describes
   331  // dereferencing the pointer at the stack offset.
   332  func createConservativeVar(fnsym *obj.LSym, fn *ir.Func, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
   333  	typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
   334  	tag := dwarf.DW_TAG_variable
   335  	isReturnValue := (n.Class == ir.PPARAMOUT)
   336  	if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
   337  		tag = dwarf.DW_TAG_formal_parameter
   338  	}
   339  	inlIndex := 0
   340  	if base.Flag.GenDwarfInl > 1 {
   341  		if n.InlFormal() || n.InlLocal() {
   342  			inlIndex = posInlIndex(n.Pos()) + 1
   343  			if n.InlFormal() {
   344  				tag = dwarf.DW_TAG_formal_parameter
   345  			}
   346  		}
   347  	}
   348  	declpos := base.Ctxt.InnermostPos(n.Pos())
   349  	dvar := &dwarf.Var{
   350  		Name:          n.Sym().Name,
   351  		IsReturnValue: isReturnValue,
   352  		Tag:           tag,
   353  		WithLoclist:   true,
   354  		StackOffset:   int32(n.FrameOffset()),
   355  		Type:          base.Ctxt.Lookup(typename),
   356  		DeclFile:      declpos.RelFilename(),
   357  		DeclLine:      declpos.RelLine(),
   358  		DeclCol:       declpos.RelCol(),
   359  		InlIndex:      int32(inlIndex),
   360  		ChildIndex:    -1,
   361  		DictIndex:     n.DictIndex,
   362  		ClosureOffset: closureOffset(n, closureVars),
   363  	}
   364  	if n.Esc() == ir.EscHeap && n.Heapaddr != nil {
   365  		// The variable was promoted to the heap and has a known heap
   366  		// address, so describe its location by dereferencing the pointer
   367  		// stored at its stack offset. A heap-escaped variable may have no
   368  		// Heapaddr if it was declared in unreachable code: escape analysis
   369  		// marks it as heap-allocated, but SSA generation skips the dead
   370  		// declaration and never allocates the address. In that case fall
   371  		// through and emit a conservative variable with no location list.
   372  		debug := fn.DebugInfo.(*ssadebug.FuncDebug)
   373  		list := createHeapDerefLocationList(n, debug.EntryID)
   374  		dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
   375  			debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
   376  		}
   377  	}
   378  	// Record go type to ensure that it gets emitted by the linker.
   379  	fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
   380  	return dvar
   381  }
   382  
   383  // sortDeclsAndVars sorts the decl and dwarf var lists according to
   384  // parameter declaration order, so as to insure that when a subprogram
   385  // DIE is emitted, its parameter children appear in declaration order.
   386  // Prior to the advent of the register ABI, sorting by frame offset
   387  // would achieve this; with the register we now need to go back to the
   388  // original function signature.
   389  func sortDeclsAndVars(fn *ir.Func, decls []*ir.Name, vars []*dwarf.Var) {
   390  	paramOrder := make(map[*ir.Name]int)
   391  	idx := 1
   392  	for _, f := range fn.Type().RecvParamsResults() {
   393  		if n, ok := f.Nname.(*ir.Name); ok {
   394  			paramOrder[n] = idx
   395  			idx++
   396  		}
   397  	}
   398  	sort.Stable(varsAndDecls{decls, vars, paramOrder})
   399  }
   400  
   401  type varsAndDecls struct {
   402  	decls      []*ir.Name
   403  	vars       []*dwarf.Var
   404  	paramOrder map[*ir.Name]int
   405  }
   406  
   407  func (v varsAndDecls) Len() int {
   408  	return len(v.decls)
   409  }
   410  
   411  func (v varsAndDecls) Less(i, j int) bool {
   412  	nameLT := func(ni, nj *ir.Name) bool {
   413  		oi, foundi := v.paramOrder[ni]
   414  		oj, foundj := v.paramOrder[nj]
   415  		if foundi {
   416  			if foundj {
   417  				return oi < oj
   418  			} else {
   419  				return true
   420  			}
   421  		}
   422  		return false
   423  	}
   424  	return nameLT(v.decls[i], v.decls[j])
   425  }
   426  
   427  func (v varsAndDecls) Swap(i, j int) {
   428  	v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
   429  	v.decls[i], v.decls[j] = v.decls[j], v.decls[i]
   430  }
   431  
   432  // Given a function that was inlined at some point during the
   433  // compilation, return a sorted list of nodes corresponding to the
   434  // autos/locals in that function prior to inlining. If this is a
   435  // function that is not local to the package being compiled, then the
   436  // names of the variables may have been "versioned" to avoid conflicts
   437  // with local vars; disregard this versioning when sorting.
   438  func preInliningDcls(fnsym *obj.LSym) []*ir.Name {
   439  	fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func)
   440  	var rdcl []*ir.Name
   441  	for _, n := range fn.Inl.Dcl {
   442  		if n.Sym().Name[0] == '.' || !shouldEmitDwarfVarSafe(n) {
   443  			continue
   444  		}
   445  		rdcl = append(rdcl, n)
   446  	}
   447  	return rdcl
   448  }
   449  
   450  func createSimpleVar(fnsym *obj.LSym, n *ir.Name, closureVars map[*ir.Name]int64) *dwarf.Var {
   451  	var tag int
   452  	var offs int64
   453  
   454  	localAutoOffset := func() int64 {
   455  		offs = n.FrameOffset()
   456  		if base.Ctxt.Arch.FixedFrameSize == 0 {
   457  			offs -= int64(types.PtrSize)
   458  		}
   459  		if buildcfg.FramePointerEnabled {
   460  			offs -= int64(types.PtrSize)
   461  		}
   462  		return offs
   463  	}
   464  
   465  	switch n.Class {
   466  	case ir.PAUTO:
   467  		offs = localAutoOffset()
   468  		tag = dwarf.DW_TAG_variable
   469  	case ir.PPARAM, ir.PPARAMOUT:
   470  		tag = dwarf.DW_TAG_formal_parameter
   471  		if n.IsOutputParamInRegisters() {
   472  			offs = localAutoOffset()
   473  		} else {
   474  			offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
   475  		}
   476  
   477  	default:
   478  		base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class, n)
   479  	}
   480  
   481  	typename := dwarf.InfoPrefix + types.TypeSymName(n.Type())
   482  	delete(fnsym.Func().Autot, reflectdata.TypeLinksym(n.Type()))
   483  	inlIndex := 0
   484  	if base.Flag.GenDwarfInl > 1 {
   485  		if n.InlFormal() || n.InlLocal() {
   486  			inlIndex = posInlIndex(n.Pos()) + 1
   487  			if n.InlFormal() {
   488  				tag = dwarf.DW_TAG_formal_parameter
   489  			}
   490  		}
   491  	}
   492  	declpos := base.Ctxt.InnermostPos(declPos(n))
   493  	return &dwarf.Var{
   494  		Name:          n.Sym().Name,
   495  		IsReturnValue: n.Class == ir.PPARAMOUT,
   496  		IsInlFormal:   n.InlFormal(),
   497  		Tag:           tag,
   498  		StackOffset:   int32(offs),
   499  		Type:          base.Ctxt.Lookup(typename),
   500  		DeclFile:      declpos.RelFilename(),
   501  		DeclLine:      declpos.RelLine(),
   502  		DeclCol:       declpos.RelCol(),
   503  		InlIndex:      int32(inlIndex),
   504  		ChildIndex:    -1,
   505  		DictIndex:     n.DictIndex,
   506  		ClosureOffset: closureOffset(n, closureVars),
   507  	}
   508  }
   509  
   510  // createComplexVar builds a single DWARF variable entry and location list.
   511  func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars map[*ir.Name]int64) *dwarf.Var {
   512  	debug := fn.DebugInfo.(*ssadebug.FuncDebug)
   513  	n := debug.Vars[varID]
   514  
   515  	var tag int
   516  	switch n.Class {
   517  	case ir.PAUTO:
   518  		tag = dwarf.DW_TAG_variable
   519  	case ir.PPARAM, ir.PPARAMOUT:
   520  		tag = dwarf.DW_TAG_formal_parameter
   521  	default:
   522  		return nil
   523  	}
   524  
   525  	gotype := reflectdata.TypeLinksym(n.Type())
   526  	delete(fnsym.Func().Autot, gotype)
   527  	typename := dwarf.InfoPrefix + gotype.Name[len("type:"):]
   528  	inlIndex := 0
   529  	if base.Flag.GenDwarfInl > 1 {
   530  		if n.InlFormal() || n.InlLocal() {
   531  			inlIndex = posInlIndex(n.Pos()) + 1
   532  			if n.InlFormal() {
   533  				tag = dwarf.DW_TAG_formal_parameter
   534  			}
   535  		}
   536  	}
   537  	declpos := base.Ctxt.InnermostPos(n.Pos())
   538  	dvar := &dwarf.Var{
   539  		Name:          n.Sym().Name,
   540  		IsReturnValue: n.Class == ir.PPARAMOUT,
   541  		IsInlFormal:   n.InlFormal(),
   542  		Tag:           tag,
   543  		WithLoclist:   true,
   544  		Type:          base.Ctxt.Lookup(typename),
   545  		// The stack offset is used as a sorting key, so for decomposed
   546  		// variables just give it the first one. It's not used otherwise.
   547  		// This won't work well if the first slot hasn't been assigned a stack
   548  		// location, but it's not obvious how to do better.
   549  		StackOffset:   ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]),
   550  		DeclFile:      declpos.RelFilename(),
   551  		DeclLine:      declpos.RelLine(),
   552  		DeclCol:       declpos.RelCol(),
   553  		InlIndex:      int32(inlIndex),
   554  		ChildIndex:    -1,
   555  		DictIndex:     n.DictIndex,
   556  		ClosureOffset: closureOffset(n, closureVars),
   557  	}
   558  	list := debug.LocationLists[varID]
   559  	if len(list) != 0 {
   560  		dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
   561  			debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
   562  		}
   563  	}
   564  	return dvar
   565  }
   566  
   567  // createHeapDerefLocationList creates a location list for a heap-escaped variable
   568  // that describes "dereference pointer at stack offset"
   569  func createHeapDerefLocationList(n *ir.Name, entryID ssa.ID) []ssa.LocListEntry {
   570  	// Get the stack offset where the heap pointer is stored
   571  	heapPtrOffset := n.Heapaddr.FrameOffset()
   572  	if base.Ctxt.Arch.FixedFrameSize == 0 {
   573  		heapPtrOffset -= int64(types.PtrSize)
   574  	}
   575  	if buildcfg.FramePointerEnabled {
   576  		heapPtrOffset -= int64(types.PtrSize)
   577  	}
   578  
   579  	// Create a location expression: DW_OP_fbreg <offset> DW_OP_deref
   580  	var expr []byte
   581  	expr = append(expr, dwarf.DW_OP_fbreg)
   582  	expr = dwarf.AppendSleb128(expr, heapPtrOffset)
   583  	expr = append(expr, dwarf.DW_OP_deref)
   584  
   585  	return []ssa.LocListEntry{{
   586  		StartBlock: entryID,
   587  		StartValue: ssa.BlockStart.ID,
   588  		EndBlock:   entryID,
   589  		EndValue:   ssa.FuncEnd.ID,
   590  		Expr:       expr,
   591  	}}
   592  }
   593  
   594  // RecordFlags records the specified command-line flags to be placed
   595  // in the DWARF info.
   596  func RecordFlags(flags ...string) {
   597  	if base.Ctxt.Pkgpath == "" {
   598  		base.Fatalf("missing pkgpath")
   599  	}
   600  
   601  	type BoolFlag interface {
   602  		IsBoolFlag() bool
   603  	}
   604  	type CountFlag interface {
   605  		IsCountFlag() bool
   606  	}
   607  	var cmd bytes.Buffer
   608  	for _, name := range flags {
   609  		f := flag.Lookup(name)
   610  		if f == nil {
   611  			continue
   612  		}
   613  		getter := f.Value.(flag.Getter)
   614  		if getter.String() == f.DefValue {
   615  			// Flag has default value, so omit it.
   616  			continue
   617  		}
   618  		if bf, ok := f.Value.(BoolFlag); ok && bf.IsBoolFlag() {
   619  			val, ok := getter.Get().(bool)
   620  			if ok && val {
   621  				fmt.Fprintf(&cmd, " -%s", f.Name)
   622  				continue
   623  			}
   624  		}
   625  		if cf, ok := f.Value.(CountFlag); ok && cf.IsCountFlag() {
   626  			val, ok := getter.Get().(int)
   627  			if ok && val == 1 {
   628  				fmt.Fprintf(&cmd, " -%s", f.Name)
   629  				continue
   630  			}
   631  		}
   632  		fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
   633  	}
   634  
   635  	// Adds flag to producer string signaling whether regabi is turned on or
   636  	// off.
   637  	// Once regabi is turned on across the board and the relative GOEXPERIMENT
   638  	// knobs no longer exist this code should be removed.
   639  	if buildcfg.Experiment.RegabiArgs {
   640  		cmd.Write([]byte(" regabi"))
   641  	}
   642  
   643  	if cmd.Len() == 0 {
   644  		return
   645  	}
   646  	s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "producer." + base.Ctxt.Pkgpath)
   647  	s.Type = objabi.SDWARFCUINFO
   648  	// Sometimes (for example when building tests) we can link
   649  	// together two package main archives. So allow dups.
   650  	s.Set(obj.AttrDuplicateOK, true)
   651  	base.Ctxt.Data = append(base.Ctxt.Data, s)
   652  	s.P = cmd.Bytes()[1:]
   653  }
   654  
   655  // RecordPackageName records the name of the package being
   656  // compiled, so that the linker can save it in the compile unit's DIE.
   657  func RecordPackageName() {
   658  	s := base.Ctxt.Lookup(dwarf.CUInfoPrefix + "packagename." + base.Ctxt.Pkgpath)
   659  	s.Type = objabi.SDWARFCUINFO
   660  	// Sometimes (for example when building tests) we can link
   661  	// together two package main archives. So allow dups.
   662  	s.Set(obj.AttrDuplicateOK, true)
   663  	base.Ctxt.Data = append(base.Ctxt.Data, s)
   664  	s.P = []byte(types.LocalPkg.Name)
   665  }
   666  
   667  // shouldEmitDwarfVar reports whether n should have a DWARF variable entry.
   668  // This consolidates filtering that was previously spread across IR (AutoTemp),
   669  // SSA (IsVarWantedForDebug), and dwarfgen (symbol name checks).
   670  func shouldEmitDwarfVar(n *ir.Name) bool {
   671  	if ir.IsAutoTmp(n) {
   672  		return false
   673  	}
   674  	return shouldEmitDwarfVarSafe(n)
   675  }
   676  
   677  // shouldEmitDwarfVarSafe is like shouldEmitDwarfVar but omits the ir.IsAutoTmp
   678  // check, making it safe to call during parallel compilation on shared ir.Name
   679  // nodes (e.g., in preInliningDcls). ir.IsAutoTmp reads the mutable flags bitset,
   680  // which can race with other goroutines writing different flags during compilation.
   681  // Auto temps have names starting with "." so callers must filter those separately.
   682  func shouldEmitDwarfVarSafe(n *ir.Name) bool {
   683  	if !ssa.IsVarWantedForDebug(n) {
   684  		return false
   685  	}
   686  	if n.Sym().Name == "_" {
   687  		return false
   688  	}
   689  	if n.Type().IsUntyped() {
   690  		return false
   691  	}
   692  	return true
   693  }
   694  
   695  func closureOffset(n *ir.Name, closureVars map[*ir.Name]int64) int64 {
   696  	return closureVars[n]
   697  }
   698  

View as plain text