diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/dwarf.go')
-rw-r--r-- | src/cmd/compile/internal/gc/dwarf.go | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/dwarf.go b/src/cmd/compile/internal/gc/dwarf.go new file mode 100644 index 0000000000..e853c51422 --- /dev/null +++ b/src/cmd/compile/internal/gc/dwarf.go @@ -0,0 +1,412 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gc + +import ( + "sort" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/ssa" + "cmd/compile/internal/ssagen" + "cmd/compile/internal/types" + "cmd/internal/dwarf" + "cmd/internal/obj" + "cmd/internal/objabi" + "cmd/internal/src" +) + +func debuginfo(fnsym *obj.LSym, infosym *obj.LSym, curfn interface{}) ([]dwarf.Scope, dwarf.InlCalls) { + fn := curfn.(*ir.Func) + + if fn.Nname != nil { + expect := fn.Sym().Linksym() + if fnsym.ABI() == obj.ABI0 { + expect = fn.Sym().LinksymABI0() + } + if fnsym != expect { + base.Fatalf("unexpected fnsym: %v != %v", fnsym, expect) + } + } + + // Back when there were two different *Funcs for a function, this code + // was not consistent about whether a particular *Node being processed + // was an ODCLFUNC or ONAME node. Partly this is because inlined function + // bodies have no ODCLFUNC node, which was it's own inconsistency. + // In any event, the handling of the two different nodes for DWARF purposes + // was subtly different, likely in unintended ways. CL 272253 merged the + // two nodes' Func fields, so that code sees the same *Func whether it is + // holding the ODCLFUNC or the ONAME. This resulted in changes in the + // DWARF output. To preserve the existing DWARF output and leave an + // intentional change for a future CL, this code does the following when + // fn.Op == ONAME: + // + // 1. Disallow use of createComplexVars in createDwarfVars. + // It was not possible to reach that code for an ONAME before, + // because the DebugInfo was set only on the ODCLFUNC Func. + // Calling into it in the ONAME case causes an index out of bounds panic. + // + // 2. Do not populate apdecls. fn.Func.Dcl was in the ODCLFUNC Func, + // not the ONAME Func. Populating apdecls for the ONAME case results + // in selected being populated after createSimpleVars is called in + // createDwarfVars, and then that causes the loop to skip all the entries + // in dcl, meaning that the RecordAutoType calls don't happen. + // + // These two adjustments keep toolstash -cmp working for now. + // Deciding the right answer is, as they say, future work. + // + // We can tell the difference between the old ODCLFUNC and ONAME + // cases by looking at the infosym.Name. If it's empty, DebugInfo is + // being called from (*obj.Link).populateDWARF, which used to use + // the ODCLFUNC. If it's non-empty (the name will end in $abstract), + // DebugInfo is being called from (*obj.Link).DwarfAbstractFunc, + // which used to use the ONAME form. + isODCLFUNC := infosym.Name == "" + + var apdecls []*ir.Name + // Populate decls for fn. + if isODCLFUNC { + for _, n := range fn.Dcl { + if n.Op() != ir.ONAME { // might be OTYPE or OLITERAL + continue + } + switch n.Class_ { + case ir.PAUTO: + if !n.Used() { + // Text == nil -> generating abstract function + if fnsym.Func().Text != nil { + base.Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)") + } + continue + } + case ir.PPARAM, ir.PPARAMOUT: + default: + continue + } + apdecls = append(apdecls, n) + fnsym.Func().RecordAutoType(ngotype(n).Linksym()) + } + } + + decls, dwarfVars := createDwarfVars(fnsym, isODCLFUNC, fn, apdecls) + + // For each type referenced by the functions auto vars but not + // already referenced by a dwarf var, attach an R_USETYPE relocation to + // the function symbol to insure that the type included in DWARF + // processing during linking. + typesyms := []*obj.LSym{} + for t, _ := range fnsym.Func().Autot { + typesyms = append(typesyms, t) + } + sort.Sort(obj.BySymName(typesyms)) + for _, sym := range typesyms { + r := obj.Addrel(infosym) + r.Sym = sym + r.Type = objabi.R_USETYPE + } + fnsym.Func().Autot = nil + + var varScopes []ir.ScopeID + for _, decl := range decls { + pos := declPos(decl) + varScopes = append(varScopes, findScope(fn.Marks, pos)) + } + + scopes := assembleScopes(fnsym, fn, dwarfVars, varScopes) + var inlcalls dwarf.InlCalls + if base.Flag.GenDwarfInl > 0 { + inlcalls = assembleInlines(fnsym, dwarfVars) + } + return scopes, inlcalls +} + +func declPos(decl *ir.Name) src.XPos { + if decl.Name().Defn != nil && (decl.Name().Captured() || decl.Name().Byval()) { + // It's not clear which position is correct for captured variables here: + // * decl.Pos is the wrong position for captured variables, in the inner + // function, but it is the right position in the outer function. + // * decl.Name.Defn is nil for captured variables that were arguments + // on the outer function, however the decl.Pos for those seems to be + // correct. + // * decl.Name.Defn is the "wrong" thing for variables declared in the + // header of a type switch, it's their position in the header, rather + // than the position of the case statement. In principle this is the + // right thing, but here we prefer the latter because it makes each + // instance of the header variable local to the lexical block of its + // case statement. + // This code is probably wrong for type switch variables that are also + // captured. + return decl.Name().Defn.Pos() + } + return decl.Pos() +} + +// createDwarfVars process fn, returning a list of DWARF variables and the +// Nodes they represent. +func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir.Name) ([]*ir.Name, []*dwarf.Var) { + // Collect a raw list of DWARF vars. + var vars []*dwarf.Var + var decls []*ir.Name + var selected map[*ir.Name]bool + if base.Ctxt.Flag_locationlists && base.Ctxt.Flag_optimize && fn.DebugInfo != nil && complexOK { + decls, vars, selected = createComplexVars(fnsym, fn) + } else { + decls, vars, selected = createSimpleVars(fnsym, apDecls) + } + + dcl := apDecls + if fnsym.WasInlined() { + dcl = preInliningDcls(fnsym) + } + + // 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. + for _, n := range dcl { + if _, found := selected[n]; found { + continue + } + c := n.Sym().Name[0] + if c == '.' || n.Type().IsUntyped() { + continue + } + if n.Class_ == ir.PPARAM && !ssagen.TypeOK(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)) + decls = append(decls, n) + continue + } + typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) + decls = append(decls, n) + abbrev := dwarf.DW_ABRV_AUTO_LOCLIST + isReturnValue := (n.Class_ == ir.PPARAMOUT) + if n.Class_ == ir.PPARAM || n.Class_ == ir.PPARAMOUT { + abbrev = dwarf.DW_ABRV_PARAM_LOCLIST + } else if n.Class_ == ir.PAUTOHEAP { + // If dcl in question has been promoted to heap, do a bit + // of extra work to recover original class (auto or param); + // see issue 30908. This insures that we get the proper + // signature in the abstract function DIE, but leaves a + // misleading location for the param (we want pointer-to-heap + // and not stack). + // TODO(thanm): generate a better location expression + stackcopy := n.Name().Stackcopy + if stackcopy != nil && (stackcopy.Class_ == ir.PPARAM || stackcopy.Class_ == ir.PPARAMOUT) { + abbrev = dwarf.DW_ABRV_PARAM_LOCLIST + isReturnValue = (stackcopy.Class_ == ir.PPARAMOUT) + } + } + inlIndex := 0 + if base.Flag.GenDwarfInl > 1 { + if n.Name().InlFormal() || n.Name().InlLocal() { + inlIndex = posInlIndex(n.Pos()) + 1 + if n.Name().InlFormal() { + abbrev = dwarf.DW_ABRV_PARAM_LOCLIST + } + } + } + declpos := base.Ctxt.InnermostPos(n.Pos()) + vars = append(vars, &dwarf.Var{ + Name: n.Sym().Name, + IsReturnValue: isReturnValue, + Abbrev: abbrev, + StackOffset: int32(n.FrameOffset()), + Type: base.Ctxt.Lookup(typename), + DeclFile: declpos.RelFilename(), + DeclLine: declpos.RelLine(), + DeclCol: declpos.Col(), + InlIndex: int32(inlIndex), + ChildIndex: -1, + }) + // Record go type of to insure that it gets emitted by the linker. + fnsym.Func().RecordAutoType(ngotype(n).Linksym()) + } + + return decls, vars +} + +// Given a function that was inlined at some point during the +// compilation, return a sorted list of nodes corresponding to the +// autos/locals in that function prior to inlining. If this is a +// function that is not local to the package being compiled, then the +// names of the variables may have been "versioned" to avoid conflicts +// with local vars; disregard this versioning when sorting. +func preInliningDcls(fnsym *obj.LSym) []*ir.Name { + fn := base.Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*ir.Func) + var rdcl []*ir.Name + for _, n := range fn.Inl.Dcl { + c := n.Sym().Name[0] + // Avoid reporting "_" parameters, since if there are more than + // one, it can result in a collision later on, as in #23179. + if unversion(n.Sym().Name) == "_" || c == '.' || n.Type().IsUntyped() { + continue + } + rdcl = append(rdcl, n) + } + 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) ([]*ir.Name, []*dwarf.Var, map[*ir.Name]bool) { + var vars []*dwarf.Var + var decls []*ir.Name + selected := make(map[*ir.Name]bool) + for _, n := range apDecls { + if ir.IsAutoTmp(n) { + continue + } + + decls = append(decls, n) + vars = append(vars, createSimpleVar(fnsym, n)) + selected[n] = true + } + return decls, vars, selected +} + +func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var { + var abbrev int + var offs int64 + + switch n.Class_ { + case ir.PAUTO: + offs = n.FrameOffset() + abbrev = dwarf.DW_ABRV_AUTO + if base.Ctxt.FixedFrameSize() == 0 { + offs -= int64(types.PtrSize) + } + if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" { + // There is a word space for FP on ARM64 even if the frame pointer is disabled + offs -= int64(types.PtrSize) + } + + case ir.PPARAM, ir.PPARAMOUT: + abbrev = dwarf.DW_ABRV_PARAM + offs = n.FrameOffset() + base.Ctxt.FixedFrameSize() + default: + base.Fatalf("createSimpleVar unexpected class %v for node %v", n.Class_, n) + } + + typename := dwarf.InfoPrefix + types.TypeSymName(n.Type()) + delete(fnsym.Func().Autot, ngotype(n).Linksym()) + inlIndex := 0 + if base.Flag.GenDwarfInl > 1 { + if n.Name().InlFormal() || n.Name().InlLocal() { + inlIndex = posInlIndex(n.Pos()) + 1 + if n.Name().InlFormal() { + abbrev = dwarf.DW_ABRV_PARAM + } + } + } + declpos := base.Ctxt.InnermostPos(declPos(n)) + return &dwarf.Var{ + Name: n.Sym().Name, + IsReturnValue: n.Class_ == ir.PPARAMOUT, + IsInlFormal: n.Name().InlFormal(), + Abbrev: abbrev, + StackOffset: int32(offs), + Type: base.Ctxt.Lookup(typename), + DeclFile: declpos.RelFilename(), + DeclLine: declpos.RelLine(), + DeclCol: declpos.Col(), + InlIndex: int32(inlIndex), + ChildIndex: -1, + } +} + +// createComplexVars creates recomposed DWARF vars with location lists, +// suitable for describing optimized code. +func createComplexVars(fnsym *obj.LSym, fn *ir.Func) ([]*ir.Name, []*dwarf.Var, map[*ir.Name]bool) { + debugInfo := fn.DebugInfo.(*ssa.FuncDebug) + + // Produce a DWARF variable entry for each user variable. + var decls []*ir.Name + var vars []*dwarf.Var + ssaVars := make(map[*ir.Name]bool) + + for varID, dvar := range debugInfo.Vars { + n := dvar + ssaVars[n] = true + for _, slot := range debugInfo.VarSlots[varID] { + ssaVars[debugInfo.Slots[slot].N] = true + } + + if dvar := createComplexVar(fnsym, fn, ssa.VarID(varID)); 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) *dwarf.Var { + debug := fn.DebugInfo.(*ssa.FuncDebug) + n := debug.Vars[varID] + + var abbrev int + switch n.Class_ { + case ir.PAUTO: + abbrev = dwarf.DW_ABRV_AUTO_LOCLIST + case ir.PPARAM, ir.PPARAMOUT: + abbrev = dwarf.DW_ABRV_PARAM_LOCLIST + default: + return nil + } + + gotype := ngotype(n).Linksym() + delete(fnsym.Func().Autot, gotype) + typename := dwarf.InfoPrefix + gotype.Name[len("type."):] + inlIndex := 0 + if base.Flag.GenDwarfInl > 1 { + if n.Name().InlFormal() || n.Name().InlLocal() { + inlIndex = posInlIndex(n.Pos()) + 1 + if n.Name().InlFormal() { + abbrev = dwarf.DW_ABRV_PARAM_LOCLIST + } + } + } + declpos := base.Ctxt.InnermostPos(n.Pos()) + dvar := &dwarf.Var{ + Name: n.Sym().Name, + IsReturnValue: n.Class_ == ir.PPARAMOUT, + IsInlFormal: n.Name().InlFormal(), + Abbrev: abbrev, + Type: base.Ctxt.Lookup(typename), + // The stack offset is used as a sorting key, so for decomposed + // variables just give it the first one. It's not used otherwise. + // This won't work well if the first slot hasn't been assigned a stack + // location, but it's not obvious how to do better. + StackOffset: ssagen.StackOffset(debug.Slots[debug.VarSlots[varID][0]]), + DeclFile: declpos.RelFilename(), + DeclLine: declpos.RelLine(), + DeclCol: declpos.Col(), + InlIndex: int32(inlIndex), + ChildIndex: -1, + } + list := debug.LocationLists[varID] + if len(list) != 0 { + dvar.PutLocationList = func(listSym, startPC dwarf.Sym) { + debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym)) + } + } + return dvar +} |