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{ ...@@ -572,14 +572,12 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.Block %v": "", "*cmd/compile/internal/ssa.Block %v": "",
"*cmd/compile/internal/ssa.Func %s": "", "*cmd/compile/internal/ssa.Func %s": "",
"*cmd/compile/internal/ssa.Func %v": "", "*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.LocalSlot %v": "", "*cmd/compile/internal/ssa.LocalSlot %v": "",
"*cmd/compile/internal/ssa.Register %s": "", "*cmd/compile/internal/ssa.Register %s": "",
"*cmd/compile/internal/ssa.SparseTreeNode %v": "", "*cmd/compile/internal/ssa.SparseTreeNode %v": "",
"*cmd/compile/internal/ssa.Value %s": "", "*cmd/compile/internal/ssa.Value %s": "",
"*cmd/compile/internal/ssa.Value %v": "", "*cmd/compile/internal/ssa.Value %v": "",
"*cmd/compile/internal/ssa.VarLoc %+v": "",
"*cmd/compile/internal/ssa.VarLoc %v": "", "*cmd/compile/internal/ssa.VarLoc %v": "",
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "", "*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
"*cmd/compile/internal/types.Field %p": "", "*cmd/compile/internal/types.Field %p": "",
...@@ -639,6 +637,7 @@ var knownFormats = map[string]string{ ...@@ -639,6 +637,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/gc.Val %v": "", "cmd/compile/internal/gc.Val %v": "",
"cmd/compile/internal/gc.fmtMode %d": "", "cmd/compile/internal/gc.fmtMode %d": "",
"cmd/compile/internal/gc.initKind %d": "", "cmd/compile/internal/gc.initKind %d": "",
"cmd/compile/internal/gc.locID %v": "",
"cmd/compile/internal/ssa.BranchPrediction %d": "", "cmd/compile/internal/ssa.BranchPrediction %d": "",
"cmd/compile/internal/ssa.Edge %v": "", "cmd/compile/internal/ssa.Edge %v": "",
"cmd/compile/internal/ssa.GCNode %v": "", "cmd/compile/internal/ssa.GCNode %v": "",
......
...@@ -338,7 +338,7 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope { ...@@ -338,7 +338,7 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
var dwarfVars []*dwarf.Var var dwarfVars []*dwarf.Var
var decls []*Node var decls []*Node
if Ctxt.Flag_locationlists && Ctxt.Flag_optimize { if Ctxt.Flag_locationlists && Ctxt.Flag_optimize {
decls, dwarfVars = createComplexVars(fn, debugInfo) decls, dwarfVars = createComplexVars(fnsym, debugInfo)
} else { } else {
decls, dwarfVars = createSimpleVars(automDecls) decls, dwarfVars = createSimpleVars(automDecls)
} }
...@@ -413,37 +413,36 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var) { ...@@ -413,37 +413,36 @@ func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var) {
type varPart struct { type varPart struct {
varOffset int64 varOffset int64
slot ssa.SlotID slot ssa.SlotID
locs ssa.VarLocList
} }
func createComplexVars(fn *Node, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Var) { func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug) ([]*Node, []*dwarf.Var) {
for _, locList := range debugInfo.Variables { for _, blockDebug := range debugInfo.Blocks {
for _, loc := range locList.Locations { for _, locList := range blockDebug.Variables {
if loc.StartProg != nil { for _, loc := range locList.Locations {
loc.StartPC = loc.StartProg.Pc if loc.StartProg != nil {
} loc.StartPC = loc.StartProg.Pc
if loc.EndProg != nil { }
loc.EndPC = loc.EndProg.Pc if loc.EndProg != nil {
} loc.EndPC = loc.EndProg.Pc
if Debug_locationlist == 0 { } else {
loc.EndProg = nil loc.EndPC = fnsym.Size
loc.StartProg = nil }
if Debug_locationlist == 0 {
loc.EndProg = nil
loc.StartProg = nil
}
} }
} }
} }
// Group SSA variables by the user variable they were decomposed from. // Group SSA variables by the user variable they were decomposed from.
varParts := map[*Node][]varPart{} varParts := map[*Node][]varPart{}
for slotID, locList := range debugInfo.Variables { for slotID, slot := range debugInfo.Slots {
if len(locList.Locations) == 0 {
continue
}
slot := debugInfo.Slots[slotID]
for slot.SplitOf != nil { for slot.SplitOf != nil {
slot = slot.SplitOf slot = slot.SplitOf
} }
n := slot.N.(*Node) 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. // Produce a DWARF variable entry for each user variable.
...@@ -529,7 +528,7 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf ...@@ -529,7 +528,7 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
if Debug_locationlist != 0 { if Debug_locationlist != 0 {
Ctxt.Logf("Building location list for %+v. Parts:\n", n) Ctxt.Logf("Building location list for %+v. Parts:\n", n)
for _, part := range parts { 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 ...@@ -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 // - build the piece for the range between that transition point and the next
// - repeat // - 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 finds the next beginning or end of a piece after currentPC.
findBoundaryAfter := func(currentPC int64) int64 { findBoundaryAfter := func(currentPC int64) int64 {
min := int64(math.MaxInt64) 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 // 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. // matter if it's a start or an end, since we're looking for any boundary.
// If it's the new winner, save it. // If it's the new winner, save it.
onePart: onePart:
for i := curLoc[slot]; i < len(part.locs.Locations); i++ { for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) {
for _, pc := range [2]int64{part.locs.Locations[i].StartPC, part.locs.Locations[i].EndPC} { for _, pc := range [2]int64{loc.StartPC, loc.EndPC} {
if pc > currentPC { if pc > currentPC {
if pc < min { if pc < min {
min = pc min = pc
...@@ -595,14 +628,14 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf ...@@ -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. // After this loop, if there's a location that covers [start, end), it will be current.
// Otherwise the current piece will be too early. // Otherwise the current piece will be too early.
for _, part := range parts { for _, part := range parts {
choice := -1 choice := locID{-1, -1}
for i := curLoc[part.slot]; i < len(part.locs.Locations); i++ { for i, loc := curLoc[part.slot], findLoc(part, curLoc[part.slot]); loc != nil; i, loc = nextLoc(part, i) {
if part.locs.Locations[i].StartPC > start { if loc.StartPC > start {
break //overshot break //overshot
} }
choice = i // best yet choice = i // best yet
} }
if choice != -1 { if choice.block != -1 {
curLoc[part.slot] = choice curLoc[part.slot] = choice
} }
if Debug_locationlist != 0 { if Debug_locationlist != 0 {
...@@ -618,10 +651,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf ...@@ -618,10 +651,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
dpiece := dwarf.Piece{ dpiece := dwarf.Piece{
Length: slots[part.slot].Type.Size(), Length: slots[part.slot].Type.Size(),
} }
locIdx := curLoc[part.slot] loc := findLoc(part, curLoc[part.slot])
if locIdx >= len(part.locs.Locations) || if loc == nil || start >= loc.EndPC || end <= loc.StartPC {
start >= part.locs.Locations[locIdx].EndPC ||
end <= part.locs.Locations[locIdx].StartPC {
if Debug_locationlist != 0 { if Debug_locationlist != 0 {
Ctxt.Logf("\t%v: missing", slots[part.slot]) Ctxt.Logf("\t%v: missing", slots[part.slot])
} }
...@@ -630,9 +661,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf ...@@ -630,9 +661,8 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
continue continue
} }
present++ present++
loc := part.locs.Locations[locIdx]
if Debug_locationlist != 0 { 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 { if loc.OnStack {
dpiece.OnStack = true dpiece.OnStack = true
......
...@@ -4492,16 +4492,35 @@ func genssa(f *ssa.Func, pp *Progs) { ...@@ -4492,16 +4492,35 @@ func genssa(f *ssa.Func, pp *Progs) {
} }
if Ctxt.Flag_locationlists { if Ctxt.Flag_locationlists {
for _, locList := range e.curfn.Func.DebugInfo.Variables { for i := range f.Blocks {
for _, loc := range locList.Locations { blockDebug := e.curfn.Func.DebugInfo.Blocks[i]
loc.StartProg = valueToProg[loc.Start.ID] for _, locList := range blockDebug.Variables {
if loc.End == nil { for _, loc := range locList.Locations {
Fatalf("empty loc %v compiling %v", loc, f.Name) if loc.Start == ssa.BlockStart {
} loc.StartProg = s.bstart[f.Blocks[i].ID]
loc.EndProg = valueToProg[loc.End.ID] } else {
if !logLocationLists { loc.StartProg = valueToProg[loc.Start.ID]
loc.Start = nil }
loc.End = nil 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
}
} }
} }
} }
......
...@@ -15,24 +15,19 @@ type SlotID int32 ...@@ -15,24 +15,19 @@ type SlotID int32
// function. Variables are identified by their LocalSlot, which may be the // function. Variables are identified by their LocalSlot, which may be the
// result of decomposing a larger variable. // result of decomposing a larger variable.
type FuncDebug struct { type FuncDebug struct {
Slots []*LocalSlot // Slots are all the slots in the function, indexed by their SlotID as
Variables []VarLocList // used in various functions and parallel to BlockDebug.Variables.
Slots []*LocalSlot
// The blocks in the function, in program text order.
Blocks []*BlockDebug
// The registers of the current architecture, indexed by Register.num.
Registers []Register Registers []Register
} }
// append adds a location to the location list for slot. func (f *FuncDebug) BlockString(b *BlockDebug) string {
func (f *FuncDebug) append(slot SlotID, loc *VarLoc) {
f.Variables[slot].append(loc)
}
// lastLoc returns the last VarLoc for slot, or nil if it has none.
func (f *FuncDebug) lastLoc(slot SlotID) *VarLoc {
return f.Variables[slot].last()
}
func (f *FuncDebug) String() string {
var vars []string var vars []string
for slot, list := range f.Variables {
for slot, list := range b.Variables {
if len(list.Locations) == 0 { if len(list.Locations) == 0 {
continue continue
} }
...@@ -41,6 +36,76 @@ func (f *FuncDebug) String() string { ...@@ -41,6 +36,76 @@ func (f *FuncDebug) String() string {
return fmt.Sprintf("{%v}", strings.Join(vars, ", ")) return fmt.Sprintf("{%v}", strings.Join(vars, ", "))
} }
func (f *FuncDebug) SlotLocsString(id SlotID) string {
var locs []string
for _, block := range f.Blocks {
for _, loc := range block.Variables[id].Locations {
locs = append(locs, block.LocString(loc))
}
}
return strings.Join(locs, " ")
}
type BlockDebug struct {
// The SSA block that this tracks. For debug logging only.
Block *Block
// The variables in this block, indexed by their SlotID.
Variables []VarLocList
}
func (b *BlockDebug) LocString(loc *VarLoc) string {
registers := b.Block.Func.Config.registers
var storage []string
if loc.OnStack {
storage = append(storage, "stack")
}
for reg := 0; reg < 64; reg++ {
if loc.Registers&(1<<uint8(reg)) == 0 {
continue
}
if registers != nil {
storage = append(storage, registers[reg].String())
} else {
storage = append(storage, fmt.Sprintf("reg%d", reg))
}
}
if len(storage) == 0 {
storage = append(storage, "!!!no storage!!!")
}
pos := func(v *Value, p *obj.Prog, pc int64) string {
if v == nil {
return "?"
}
vStr := fmt.Sprintf("v%d", v.ID)
if v == BlockStart {
vStr = fmt.Sprintf("b%dStart", b.Block.ID)
}
if v == BlockEnd {
vStr = fmt.Sprintf("b%dEnd", b.Block.ID)
}
if p == nil {
return vStr
}
return fmt.Sprintf("%s/%x", vStr, pc)
}
start := pos(loc.Start, loc.StartProg, loc.StartPC)
end := pos(loc.End, loc.EndProg, loc.EndPC)
return fmt.Sprintf("%v-%v@%s", start, end, strings.Join(storage, ","))
}
// append adds a location to the location list for slot.
func (b *BlockDebug) append(slot SlotID, loc *VarLoc) {
b.Variables[slot].append(loc)
}
// lastLoc returns the last VarLoc for slot, or nil if it has none.
func (b *BlockDebug) lastLoc(slot SlotID) *VarLoc {
return b.Variables[slot].last()
}
// A VarLocList contains the locations for a variable, in program text order. // A VarLocList contains the locations for a variable, in program text order.
// It will often have gaps. // It will often have gaps.
type VarLocList struct { type VarLocList struct {
...@@ -70,13 +135,24 @@ type VarLoc struct { ...@@ -70,13 +135,24 @@ type VarLoc struct {
// Inclusive -- the first SSA value that the range covers. The value // Inclusive -- the first SSA value that the range covers. The value
// doesn't necessarily have anything to do with the variable; it just // doesn't necessarily have anything to do with the variable; it just
// identifies a point in the program text. // identifies a point in the program text.
// The special sentinel value BlockStart indicates that the range begins
// at the beginning of the containing block, even if the block doesn't
// actually have a Value to use to indicate that.
Start *Value Start *Value
// Exclusive -- the first SSA value after start that the range doesn't // Exclusive -- the first SSA value after start that the range doesn't
// cover. A location with start == end is empty. // cover. A location with start == end is empty.
// The special sentinel value BlockEnd indicates that the variable survives
// to the end of the of the containing block, after all its Values and any
// control flow instructions added later.
End *Value End *Value
// The prog/PCs corresponding to Start and End above. These are for the // The prog/PCs corresponding to Start and End above. These are for the
// convenience of later passes, since code generation isn't done when // convenience of later passes, since code generation isn't done when
// BuildFuncDebug runs. // BuildFuncDebug runs.
// Control flow instructions don't correspond to a Value, so EndProg
// may point to a Prog in the next block if SurvivedBlock is true. For
// the last block, where there's no later Prog, it will be nil to indicate
// the end of the function.
StartProg, EndProg *obj.Prog StartProg, EndProg *obj.Prog
StartPC, EndPC int64 StartPC, EndPC int64
...@@ -86,57 +162,23 @@ type VarLoc struct { ...@@ -86,57 +162,23 @@ type VarLoc struct {
// Indicates whether the variable is on the stack. The stack position is // Indicates whether the variable is on the stack. The stack position is
// stored in the associated gc.Node. // stored in the associated gc.Node.
OnStack bool OnStack bool
}
var BlockStart = &Value{
ID: -10000,
Op: OpInvalid,
Aux: "BlockStart",
}
// Used only during generation. Indicates whether this location lasts var BlockEnd = &Value{
// past the block's end. Without this, there would be no way to distinguish ID: -20000,
// between a range that ended on the last Value of a block and one that Op: OpInvalid,
// didn't end at all. Aux: "BlockEnd",
survivedBlock bool
} }
// RegisterSet is a bitmap of registers, indexed by Register.num. // RegisterSet is a bitmap of registers, indexed by Register.num.
type RegisterSet uint64 type RegisterSet uint64
func (v *VarLoc) String() string {
var registers []Register
if v.Start != nil {
registers = v.Start.Block.Func.Config.registers
}
loc := ""
if !v.OnStack && v.Registers == 0 {
loc = "!!!no location!!!"
}
if v.OnStack {
loc += "stack,"
}
var regnames []string
for reg := 0; reg < 64; reg++ {
if v.Registers&(1<<uint8(reg)) == 0 {
continue
}
if registers != nil {
regnames = append(regnames, registers[reg].String())
} else {
regnames = append(regnames, fmt.Sprintf("reg%d", reg))
}
}
loc += strings.Join(regnames, ",")
pos := func(v *Value, p *obj.Prog, pc int64) string {
if v == nil {
return "?"
}
if p == nil {
return fmt.Sprintf("v%v", v.ID)
}
return fmt.Sprintf("v%v/%x", v.ID, pc)
}
surv := ""
if v.survivedBlock {
surv = "+"
}
return fmt.Sprintf("%v-%v%s@%s", pos(v.Start, v.StartProg, v.StartPC), pos(v.End, v.EndProg, v.EndPC), surv, loc)
}
// unexpected is used to indicate an inconsistency or bug in the debug info // unexpected is used to indicate an inconsistency or bug in the debug info
// generation process. These are not fixable by users. At time of writing, // generation process. These are not fixable by users. At time of writing,
// changing this to a Fprintf(os.Stderr) and running make.bash generates // changing this to a Fprintf(os.Stderr) and running make.bash generates
...@@ -160,6 +202,14 @@ type debugState struct { ...@@ -160,6 +202,14 @@ type debugState struct {
registerContents [][]SlotID registerContents [][]SlotID
} }
func (s *debugState) BlockString(b *BlockDebug) string {
f := &FuncDebug{
Slots: s.slots,
Registers: s.f.Config.registers,
}
return f.BlockString(b)
}
// BuildFuncDebug returns debug information for f. // BuildFuncDebug returns debug information for f.
// f must be fully processed, so that each Value is where it will be when // f must be fully processed, so that each Value is where it will be when
// machine code is emitted. // machine code is emitted.
...@@ -204,7 +254,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { ...@@ -204,7 +254,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
// TODO: use a reverse post-order traversal instead of the work queue. // TODO: use a reverse post-order traversal instead of the work queue.
// Location list entries for each block. // Location list entries for each block.
blockLocs := make([]*FuncDebug, f.NumBlocks()) blockLocs := make([]*BlockDebug, f.NumBlocks())
// Work queue of blocks to visit. Some of them may already be processed. // Work queue of blocks to visit. Some of them may already be processed.
work := []*Block{f.Entry} work := []*Block{f.Entry}
...@@ -230,7 +280,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { ...@@ -230,7 +280,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
// state of its predecessors. // state of its predecessors.
locs := state.mergePredecessors(b, blockLocs) locs := state.mergePredecessors(b, blockLocs)
if state.loggingEnabled { if state.loggingEnabled {
state.logf("Processing %v, initial locs %v, regs %v\n", b, locs, state.registerContents) state.logf("Processing %v, initial locs %v, regs %v\n", b, state.BlockString(locs), state.registerContents)
} }
// Update locs/registers with the effects of each Value. // Update locs/registers with the effects of each Value.
for _, v := range b.Values { for _, v := range b.Values {
...@@ -269,62 +319,33 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug { ...@@ -269,62 +319,33 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
} }
// The block is done; end the locations for all its slots. // The block is done; mark any live locations as ending with the block.
for _, locList := range locs.Variables { for _, locList := range locs.Variables {
last := locList.last() last := locList.last()
if last == nil || last.End != nil { if last == nil || last.End != nil {
continue continue
} }
if len(b.Values) != 0 { last.End = BlockEnd
last.End = b.Values[len(b.Values)-1]
} else {
// This happens when a value survives into an empty block from its predecessor.
// Just carry it forward for liveness's sake.
last.End = last.Start
}
last.survivedBlock = true
} }
if state.loggingEnabled { if state.loggingEnabled {
f.Logf("Block done: locs %v, regs %v. work = %+v\n", locs, state.registerContents, work) f.Logf("Block done: locs %v, regs %v. work = %+v\n", state.BlockString(locs), state.registerContents, work)
} }
blockLocs[b.ID] = locs blockLocs[b.ID] = locs
} }
// Build the complete debug info by concatenating each of the blocks'
// locations together.
info := &FuncDebug{ info := &FuncDebug{
Variables: make([]VarLocList, len(state.slots)),
Slots: state.slots, Slots: state.slots,
Registers: f.Config.registers, Registers: f.Config.registers,
} }
// Consumers want the information in textual order, not by block ID.
for _, b := range f.Blocks { for _, b := range f.Blocks {
// Ignore empty blocks; there will be some records for liveness info.Blocks = append(info.Blocks, blockLocs[b.ID])
// but they're all useless.
if len(b.Values) == 0 {
continue
}
if blockLocs[b.ID] == nil {
state.unexpected(b.Values[0], "Never processed block %v\n", b)
continue
}
for slot, blockLocList := range blockLocs[b.ID].Variables {
for _, loc := range blockLocList.Locations {
if !loc.OnStack && loc.Registers == 0 {
state.unexpected(loc.Start, "Location for %v with no storage: %+v\n", state.slots[slot], loc)
continue // don't confuse downstream with our bugs
}
if loc.Start == nil || loc.End == nil {
state.unexpected(b.Values[0], "Location for %v missing start or end: %v\n", state.slots[slot], loc)
continue
}
info.append(SlotID(slot), loc)
}
}
} }
if state.loggingEnabled { if state.loggingEnabled {
f.Logf("Final result:\n") f.Logf("Final result:\n")
for slot, locList := range info.Variables { for slot := range info.Slots {
f.Logf("\t%v => %v\n", state.slots[slot], locList) f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot)))
} }
} }
return info return info
...@@ -338,7 +359,7 @@ func isSynthetic(slot *LocalSlot) bool { ...@@ -338,7 +359,7 @@ func isSynthetic(slot *LocalSlot) bool {
} }
// predecessorsDone reports whether block is ready to be processed. // predecessorsDone reports whether block is ready to be processed.
func (state *debugState) predecessorsDone(b *Block, blockLocs []*FuncDebug) bool { func (state *debugState) predecessorsDone(b *Block, blockLocs []*BlockDebug) bool {
f := b.Func f := b.Func
for _, edge := range b.Preds { for _, edge := range b.Preds {
// Ignore back branches, e.g. the continuation of a for loop. // Ignore back branches, e.g. the continuation of a for loop.
...@@ -364,7 +385,7 @@ func (state *debugState) predecessorsDone(b *Block, blockLocs []*FuncDebug) bool ...@@ -364,7 +385,7 @@ func (state *debugState) predecessorsDone(b *Block, blockLocs []*FuncDebug) bool
// mergePredecessors takes the end state of each of b's predecessors and // mergePredecessors takes the end state of each of b's predecessors and
// intersects them to form the starting state for b. // intersects them to form the starting state for b.
// The registers slice (the second return value) will be reused for each call to mergePredecessors. // The registers slice (the second return value) will be reused for each call to mergePredecessors.
func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *FuncDebug { func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *BlockDebug {
live := make([]VarLocList, len(state.slots)) live := make([]VarLocList, len(state.slots))
// Filter out back branches. // Filter out back branches.
...@@ -379,29 +400,23 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu ...@@ -379,29 +400,23 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu
p := preds[0] p := preds[0]
for slot, locList := range blockLocs[p.ID].Variables { for slot, locList := range blockLocs[p.ID].Variables {
last := locList.last() last := locList.last()
if last == nil || !last.survivedBlock { if last == nil || last.End != BlockEnd {
continue continue
} }
// If this block is empty, carry forward the end value for liveness.
// It'll be ignored later.
start := last.End
if len(b.Values) != 0 {
start = b.Values[0]
}
loc := state.cache.NewVarLoc() loc := state.cache.NewVarLoc()
loc.Start = start loc.Start = BlockStart
loc.OnStack = last.OnStack loc.OnStack = last.OnStack
loc.Registers = last.Registers loc.Registers = last.Registers
live[slot].append(loc) live[slot].append(loc)
} }
} }
if state.loggingEnabled && len(b.Preds) > 1 { if state.loggingEnabled && len(b.Preds) > 1 {
state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, blockLocs[b.Preds[0].b.ID]) state.logf("Starting merge with state from %v: %v\n", b.Preds[0].b, state.BlockString(blockLocs[b.Preds[0].b.ID]))
} }
for i := 1; i < len(preds); i++ { for i := 1; i < len(preds); i++ {
p := preds[i] p := preds[i]
if state.loggingEnabled { if state.loggingEnabled {
state.logf("Merging in state from %v: %v &= %v\n", p, live, blockLocs[p.ID]) state.logf("Merging in state from %v: %v &= %v\n", p, live, state.BlockString(blockLocs[p.ID]))
} }
for slot, liveVar := range live { for slot, liveVar := range live {
...@@ -410,9 +425,9 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu ...@@ -410,9 +425,9 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu
continue continue
} }
predLoc := blockLocs[p.ID].lastLoc(SlotID(slot)) predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last()
// Clear out slots missing/dead in p. // Clear out slots missing/dead in p.
if predLoc == nil || !predLoc.survivedBlock { if predLoc == nil || predLoc.End != BlockEnd {
live[slot].Locations = nil live[slot].Locations = nil
continue continue
} }
...@@ -424,7 +439,10 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu ...@@ -424,7 +439,10 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu
} }
// Create final result. // Create final result.
locs := &FuncDebug{Variables: live, Slots: state.slots} locs := &BlockDebug{Variables: live}
if state.loggingEnabled {
locs.Block = b
}
for reg := range state.registerContents { for reg := range state.registerContents {
state.registerContents[reg] = state.registerContents[reg][:0] state.registerContents[reg] = state.registerContents[reg][:0]
} }
...@@ -444,7 +462,7 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu ...@@ -444,7 +462,7 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu
// processValue updates locs and state.registerContents to reflect v, a value with // processValue updates locs and state.registerContents to reflect v, a value with
// the names in vSlots and homed in vReg. // the names in vSlots and homed in vReg.
func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID, vReg *Register) { func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotID, vReg *Register) {
switch { switch {
case v.Op == OpRegKill: case v.Op == OpRegKill:
if state.loggingEnabled { if state.loggingEnabled {
...@@ -493,6 +511,10 @@ func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID ...@@ -493,6 +511,10 @@ func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID
} }
case v.Op == OpArg: case v.Op == OpArg:
for _, slot := range vSlots { for _, slot := range vSlots {
if last := locs.lastLoc(slot); last != nil {
state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot])
last.End = v
}
if state.loggingEnabled { if state.loggingEnabled {
state.logf("at %v: %v now on stack from arg\n", v.ID, state.slots[slot]) state.logf("at %v: %v now on stack from arg\n", v.ID, state.slots[slot])
} }
...@@ -555,5 +577,4 @@ func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID ...@@ -555,5 +577,4 @@ func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID
default: default:
state.unexpected(v, "named value with no reg\n") state.unexpected(v, "named value with no reg\n")
} }
} }
...@@ -821,6 +821,18 @@ func putscope(ctxt Context, info, loc, ranges, startPC Sym, curscope int32, scop ...@@ -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) { func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
n := v.Name 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)) 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_string, DW_CLS_STRING, int64(len(n)), n)
putattr(ctxt, info, v.Abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil) 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) { ...@@ -829,13 +841,15 @@ func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
addLocList(ctxt, loc, startPC, v, encbuf) addLocList(ctxt, loc, startPC, v, encbuf)
} else { } else {
loc := encbuf[:0] 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) loc = append(loc, DW_OP_call_frame_cfa)
} else { default:
loc = append(loc, DW_OP_fbreg) loc = append(loc, DW_OP_fbreg)
loc = AppendSleb128(loc, int64(v.StackOffset)) 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_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
} }
putattr(ctxt, info, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type) 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