cmd/compile: fixes for bad DWARF abstract origin references

Change the compiler's DWARF inline info generation to be more careful
about producing consistent instances of abstract function DIEs. The
new strategy is to insure that the only params/variables created in an
abstract subprogram DIE are those corresponding to declarations in the
original pre-inlining version of the code. If a concrete subprogram
winds up with other vars as part of the compilation process (return
temps, for example, or scalars generated by splitting a structure into
pieces) these are emitted as regular param/variable DIEs instead of
concrete DIEs.

The linker dwarf test now has a couple of new testpoints that include
checks to make sure that all abstract DIE references are
sane/resolvable; this will help catch similar problems in the future.

Fixes #23046.

Change-Id: I9b0030da8673fbb80b7ad50461fcf8c6ac823a37
Reviewed-on: https://go-review.googlesource.com/83675
Run-TryBot: Than McIntosh <thanm@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Than McIntosh 2017-12-11 15:53:31 -05:00
parent 44213336f0
commit fdecaa837c
5 changed files with 345 additions and 134 deletions

View file

@ -414,6 +414,9 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var, map[*Node]bool
if genDwarfInline > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM
}
}
}
declpos := Ctxt.InnermostPos(n.Pos)
@ -513,9 +516,8 @@ func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*No
}
var dcl []*Node
var chopVersion bool
if fnsym.WasInlined() {
dcl, chopVersion = preInliningDcls(fnsym)
dcl = preInliningDcls(fnsym)
} else {
dcl = automDecls
}
@ -534,7 +536,7 @@ func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*No
continue
}
c := n.Sym.Name[0]
if c == '~' || c == '.' || n.Type.IsUntyped() {
if c == '.' || n.Type.IsUntyped() {
continue
}
typename := dwarf.InfoPrefix + typesymname(n.Type)
@ -547,6 +549,9 @@ func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*No
if genDwarfInline > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := Ctxt.InnermostPos(n.Pos)
@ -575,49 +580,59 @@ func createDwarfVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug, automDecls []*No
}
// Parameter and local variable names are given middle dot
// version numbers as part of the writing them out to export
// data (see issue 4326). If DWARF inlined routine generation
// is turned on, undo this versioning, since DWARF variables
// in question will be parented by the inlined routine and
// not the top-level caller.
if genDwarfInline > 1 && chopVersion {
for _, v := range vars {
if v.InlIndex != -1 {
if i := strings.Index(v.Name, "·"); i > 0 {
v.Name = v.Name[:i] // cut off Vargen
}
}
}
}
return decls, vars
}
// Given a function that was inlined at some point during the compilation,
// return a list of nodes corresponding to the autos/locals in that
// function prior to inlining. Untyped and compiler-synthesized vars are
// stripped out along the way.
func preInliningDcls(fnsym *obj.LSym) ([]*Node, bool) {
// 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) []*Node {
fn := Ctxt.DwFixups.GetPrecursorFunc(fnsym).(*Node)
imported := false
var dcl, rdcl []*Node
if fn.Name.Defn != nil {
dcl = fn.Func.Inldcl.Slice() // local function
} else {
dcl = fn.Func.Dcl // imported function
imported = true
}
for _, n := range dcl {
c := n.Sym.Name[0]
if c == '~' || c == '.' || n.Type.IsUntyped() {
if c == '.' || n.Type.IsUntyped() {
continue
}
rdcl = append(rdcl, n)
}
return rdcl, imported
sort.Sort(byNodeName(rdcl))
return rdcl
}
func cmpNodeName(a, b *Node) bool {
aart := 0
if strings.HasPrefix(a.Sym.Name, "~") {
aart = 1
}
bart := 0
if strings.HasPrefix(b.Sym.Name, "~") {
bart = 1
}
if aart != bart {
return aart < bart
}
aname := unversion(a.Sym.Name)
bname := unversion(b.Sym.Name)
return aname < bname
}
// byNodeName implements sort.Interface for []*Node using cmpNodeName.
type byNodeName []*Node
func (s byNodeName) Len() int { return len(s) }
func (s byNodeName) Less(i, j int) bool { return cmpNodeName(s[i], s[j]) }
func (s byNodeName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// varOffset returns the offset of slot within the user variable it was
// decomposed from. This has nothing to do with its stack offset.
func varOffset(slot *ssa.LocalSlot) int64 {
@ -682,6 +697,9 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
if genDwarfInline > 1 {
if n.InlFormal() || n.InlLocal() {
inlIndex = posInlIndex(n.Pos) + 1
if n.InlFormal() {
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
}
}
}
declpos := Ctxt.InnermostPos(n.Pos)