Commit 6bbe1bc9 authored by Heschi Kreinick's avatar Heschi Kreinick

cmd/compile: cover control flow insns in location lists

The information that's used to generate DWARF location lists is very
ssa.Value centric; it uses Values as start and end coordinates to define
ranges. That mostly works fine, but control flow instructions don't come
from Values, so the ranges couldn't cover them.

Control flow instructions are generated when the SSA representation is
converted to assembly, so that's the best place to extend the ranges
to cover them. (Before that, there's nothing to refer to, and afterward
the boundaries between blocks have been lost.) That requires block
information in the debugInfo type, which then flows down to make
everything else awkward. On the plus side, there's a little less copying
slices around than there used to be, so it should be a little faster.

Previously, the ranges for empty blocks were not very meaningful. That
was fine, because they had no Values to cover, so no debug information
was generated for them. But they do have control flow instructions
(that's why they exist) and so now it's important that the information
be correct. Introduce two sentinel values, BlockStart and BlockEnd, that
denote the boundary of a block, even if the block is empty. BlockEnd
replaces the previous SurvivedBlock flag.

There's one more problem: the last instruction in the function will be a
control flow instruction, so any live ranges need to be extended past
it. But there's no instruction after it to use as the end of the range.
Instead, leave the EndProg field of those ranges as nil and fix it up to
point to past the end of the assembled text at the very last moment.

Change-Id: I81f884020ff36fd6fe8d7888fc57c99412c4245b
Reviewed-on: https://go-review.googlesource.com/63010Reviewed-by: 's avatarAlessandro Arzilli <alessandro.arzilli@gmail.com>
Reviewed-by: 's avatarDavid Chase <drchase@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 6f1724ff
......@@ -572,14 +572,12 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.Block %v": "",
"*cmd/compile/internal/ssa.Func %s": "",
"*cmd/compile/internal/ssa.Func %v": "",
"*cmd/compile/internal/ssa.FuncDebug %v": "",
"*cmd/compile/internal/ssa.LocalSlot %+v": "",
"*cmd/compile/internal/ssa.LocalSlot %v": "",
"*cmd/compile/internal/ssa.Register %s": "",
"*cmd/compile/internal/ssa.SparseTreeNode %v": "",
"*cmd/compile/internal/ssa.Value %s": "",
"*cmd/compile/internal/ssa.Value %v": "",
"*cmd/compile/internal/ssa.VarLoc %+v": "",
"*cmd/compile/internal/ssa.VarLoc %v": "",
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
"*cmd/compile/internal/types.Field %p": "",
......@@ -639,6 +637,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/gc.Val %v": "",
"cmd/compile/internal/gc.fmtMode %d": "",
"cmd/compile/internal/gc.initKind %d": "",
"cmd/compile/internal/gc.locID %v": "",
"cmd/compile/internal/ssa.BranchPrediction %d": "",
"cmd/compile/internal/ssa.Edge %v": "",
"cmd/compile/internal/ssa.GCNode %v": "",
......
......@@ -338,7 +338,7 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
var dwarfVars []*dwarf.Var
var decls []*Node
if Ctxt.Flag_locationlists && Ctxt.Flag_optimize {
decls, dwarfVars = createComplexVars(fn, debugInfo)
decls, dwarfVars = createComplexVars(fnsym, debugInfo)
} else {
decls, dwarfVars = createSimpleVars(automDecls)
}
......@@ -413,17 +413,19 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var) {
type varPart struct {
varOffset int64
slot ssa.SlotID
locs ssa.VarLocList
}
func createComplexVars(fn *Node, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Var) {
for _, locList := range debugInfo.Variables {
func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Var) {
for _, blockDebug := range debugInfo.Blocks {
for _, locList := range blockDebug.Variables {
for _, loc := range locList.Locations {
if loc.StartProg != nil {
loc.StartPC = loc.StartProg.Pc
}
if loc.EndProg != nil {
loc.EndPC = loc.EndProg.Pc
} else {
loc.EndPC = fnsym.Size
}
if Debug_locationlist == 0 {
loc.EndProg = nil
......@@ -431,19 +433,16 @@ func createComplexVars(fn *Node, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Va
}
}
}
}
// Group SSA variables by the user variable they were decomposed from.
varParts := map[*Node][]varPart{}
for slotID, locList := range debugInfo.Variables {
if len(locList.Locations) == 0 {
continue
}
slot := debugInfo.Slots[slotID]
for slotID, slot := range debugInfo.Slots {
for slot.SplitOf != nil {
slot = slot.SplitOf
}
n := slot.N.(*Node)
varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID), locList})
varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID)})
}
// Produce a DWARF variable entry for each user variable.
......@@ -529,7 +528,7 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
if Debug_locationlist != 0 {
Ctxt.Logf("Building location list for %+v. Parts:\n", n)
for _, part := range parts {
Ctxt.Logf("\t%v => %v\n", debugInfo.Slots[part.slot], part.locs)
Ctxt.Logf("\t%v => %v\n", debugInfo.Slots[part.slot], debugInfo.SlotLocsString(part.slot))
}
}
......@@ -553,18 +552,52 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
// - build the piece for the range between that transition point and the next
// - repeat
curLoc := make([]int, len(slots))
type locID struct {
block int
loc int
}
findLoc := func(part varPart, id locID) *ssa.VarLoc {
if id.block >= len(debugInfo.Blocks) {
return nil
}
return debugInfo.Blocks[id.block].Variables[part.slot].Locations[id.loc]
}
nextLoc := func(part varPart, id locID) (locID, *ssa.VarLoc) {
// Check if there's another loc in this block
id.loc++
if b := debugInfo.Blocks[id.block]; b != nil && id.loc < len(b.Variables[part.slot].Locations) {
return id, findLoc(part, id)
}
// Find the next block that has a loc for this part.
id.loc = 0
id.block++
for ; id.block < len(debugInfo.Blocks); id.block++ {
if b := debugInfo.Blocks[id.block]; b != nil && len(b.Variables[part.slot].Locations) != 0 {
return id, findLoc(part, id)
}
}
return id, nil
}
curLoc := make([]locID, len(slots))
// Position each pointer at the first entry for its slot.
for _, part := range parts {
if b := debugInfo.Blocks[0]; b != nil && len(b.Variables[part.slot].Locations) != 0 {
// Block 0 has an entry; no need to advance.
continue
}
curLoc[part.slot], _ = nextLoc(part, curLoc[part.slot])
}
// findBoundaryAfter finds the next beginning or end of a piece after currentPC.
findBoundaryAfter := func(currentPC int64) int64 {
min := int64(math.MaxInt64)
for slot, part := range parts {
for _, part := range parts {
// For each part, find the first PC greater than current. Doesn't
// matter if it's a start or an end, since we're looking for any boundary.
// If it's the new winner, save it.
onePart:
for i := curLoc[slot]; i < len(part.locs.Locations); i++ {
for _, pc := range [2]int64{part.locs.Locations[i].StartPC, part.locs.Locations[i].EndPC} {
for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) {
for _, pc := range [2]int64{loc.StartPC, loc.EndPC} {
if pc > currentPC {
if pc < min {
min = pc
......@@ -595,14 +628,14 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
// After this loop, if there's a location that covers [start, end), it will be current.
// Otherwise the current piece will be too early.
for _, part := range parts {
choice := -1
for i := curLoc[part.slot]; i < len(part.locs.Locations); i++ {
if part.locs.Locations[i].StartPC > start {
choice := locID{-1, -1}
for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) {
if loc.StartPC > start {
break //overshot
}
choice = i // best yet
}
if choice != -1 {
if choice.block != -1 {
curLoc[part.slot] = choice
}
if Debug_locationlist != 0 {
......@@ -618,10 +651,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
dpiece := dwarf.Piece{
Length: slots[part.slot].Type.Size(),
}
locIdx := curLoc[part.slot]
if locIdx >= len(part.locs.Locations) ||
start >= part.locs.Locations[locIdx].EndPC ||
end <= part.locs.Locations[locIdx].StartPC {
loc := findLoc(part, curLoc[part.slot])
if loc == nil || start >= loc.EndPC || end <= loc.StartPC {
if Debug_locationlist != 0 {
Ctxt.Logf("\t%v: missing", slots[part.slot])
}
......@@ -630,9 +661,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
continue
}
present++
loc := part.locs.Locations[locIdx]
if Debug_locationlist != 0 {
Ctxt.Logf("\t%v: %v", slots[part.slot], loc)
Ctxt.Logf("\t%v: %v", slots[part.slot], debugInfo.Blocks[curLoc[part.slot].block].LocString(loc))
}
if loc.OnStack {
dpiece.OnStack = true
......
......@@ -4492,13 +4492,31 @@ func genssa(f *ssa.Func, pp *Progs) {
}
if Ctxt.Flag_locationlists {
for _, locList := range e.curfn.Func.DebugInfo.Variables {
for i := range f.Blocks {
blockDebug := e.curfn.Func.DebugInfo.Blocks[i]
for _, locList := range blockDebug.Variables {
for _, loc := range locList.Locations {
if loc.Start == ssa.BlockStart {
loc.StartProg = s.bstart[f.Blocks[i].ID]
} else {
loc.StartProg = valueToProg[loc.Start.ID]
}
if loc.End == nil {
Fatalf("empty loc %v compiling %v", loc, f.Name)
}
if loc.End == ssa.BlockEnd {
// If this variable was live at the end of the block, it should be
// live over the control flow instructions. Extend it up to the
// beginning of the next block.
// If this is the last block, then there's no Prog to use for it, and
// EndProg is unset.
if i < len(f.Blocks)-1 {
loc.EndProg = s.bstart[f.Blocks[i+1].ID]
}
} else {
loc.EndProg = valueToProg[loc.End.ID]
}
if !logLocationLists {
loc.Start = nil
loc.End = nil
......@@ -4506,6 +4524,7 @@ func genssa(f *ssa.Func, pp *Progs) {
}
}
}
}
// Resolve branches
for _, br := range s.Branches {
......
This diff is collapsed.
......@@ -821,6 +821,18 @@ func putscope(ctxt Context, info, loc, ranges, startPC Sym, curscope int32, scop
func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
n := v.Name
// If the variable was entirely optimized out, don't emit a location list;
// convert to an inline abbreviation and emit an empty location.
missing := false
switch {
case v.Abbrev == DW_ABRV_AUTO_LOCLIST && len(v.LocationList) == 0:
missing = true
v.Abbrev = DW_ABRV_AUTO
case v.Abbrev == DW_ABRV_PARAM_LOCLIST && len(v.LocationList) == 0:
missing = true
v.Abbrev = DW_ABRV_PARAM
}
Uleb128put(ctxt, info, int64(v.Abbrev))
putattr(ctxt, info, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
putattr(ctxt, info, v.Abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
......@@ -829,13 +841,15 @@ func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
addLocList(ctxt, loc, startPC, v, encbuf)
} else {
loc := encbuf[:0]
if v.StackOffset == 0 {
switch {
case missing:
break // no location
case v.StackOffset == 0:
loc = append(loc, DW_OP_call_frame_cfa)
} else {
default:
loc = append(loc, DW_OP_fbreg)
loc = AppendSleb128(loc, int64(v.StackOffset))
}
putattr(ctxt, info, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
}
putattr(ctxt, info, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment