From: Heschi Kreinick Date: Mon, 11 Sep 2017 18:28:34 +0000 (-0400) Subject: cmd/compile: cover control flow insns in location lists X-Git-Tag: go1.10beta1~943 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=6bbe1bc94072533ec715cae32f7cda1ae0a2a5eb;p=gostls13.git 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/63010 Reviewed-by: Alessandro Arzilli Reviewed-by: David Chase Run-TryBot: Heschi Kreinick TryBot-Result: Gobot Gobot --- diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index 8acb704635..96d1fb12f0 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -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": "", diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 417f1ba716..4332305c7a 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -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,37 +413,36 @@ 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 { - for _, loc := range locList.Locations { - if loc.StartProg != nil { - loc.StartPC = loc.StartProg.Pc - } - if loc.EndProg != nil { - loc.EndPC = loc.EndProg.Pc - } - if Debug_locationlist == 0 { - loc.EndProg = nil - loc.StartProg = nil +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 + loc.StartProg = nil + } } } } // 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 diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1a960497ab..a0c77d112b 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4492,16 +4492,35 @@ func genssa(f *ssa.Func, pp *Progs) { } if Ctxt.Flag_locationlists { - for _, locList := range e.curfn.Func.DebugInfo.Variables { - for _, loc := range locList.Locations { - loc.StartProg = valueToProg[loc.Start.ID] - if loc.End == nil { - Fatalf("empty loc %v compiling %v", loc, f.Name) - } - loc.EndProg = valueToProg[loc.End.ID] - if !logLocationLists { - loc.Start = nil - loc.End = nil + 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 + } } } } diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go index 91b11716bb..20cf70bd8b 100644 --- a/src/cmd/compile/internal/ssa/debug.go +++ b/src/cmd/compile/internal/ssa/debug.go @@ -15,24 +15,19 @@ type SlotID int32 // function. Variables are identified by their LocalSlot, which may be the // result of decomposing a larger variable. type FuncDebug struct { - Slots []*LocalSlot - Variables []VarLocList + // Slots are all the slots in the function, indexed by their SlotID as + // 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 } -// append adds a location to the location list for slot. -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 { +func (f *FuncDebug) BlockString(b *BlockDebug) string { var vars []string - for slot, list := range f.Variables { + + for slot, list := range b.Variables { if len(list.Locations) == 0 { continue } @@ -41,6 +36,76 @@ func (f *FuncDebug) String() string { 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< %v\n", state.slots[slot], locList) + for slot := range info.Slots { + f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot))) } } return info @@ -338,7 +359,7 @@ func isSynthetic(slot *LocalSlot) bool { } // 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 for _, edge := range b.Preds { // Ignore back branches, e.g. the continuation of a for loop. @@ -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 // intersects them to form the starting state for b. // 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)) // Filter out back branches. @@ -379,29 +400,23 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu p := preds[0] for slot, locList := range blockLocs[p.ID].Variables { last := locList.last() - if last == nil || !last.survivedBlock { + if last == nil || last.End != BlockEnd { 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.Start = start + loc.Start = BlockStart loc.OnStack = last.OnStack loc.Registers = last.Registers live[slot].append(loc) } } 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++ { p := preds[i] 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 { @@ -410,9 +425,9 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu continue } - predLoc := blockLocs[p.ID].lastLoc(SlotID(slot)) + predLoc := blockLocs[p.ID].Variables[SlotID(slot)].last() // Clear out slots missing/dead in p. - if predLoc == nil || !predLoc.survivedBlock { + if predLoc == nil || predLoc.End != BlockEnd { live[slot].Locations = nil continue } @@ -424,7 +439,10 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*FuncDebug) *Fu } // 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 { state.registerContents[reg] = state.registerContents[reg][:0] } @@ -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 // 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 { case v.Op == OpRegKill: if state.loggingEnabled { @@ -493,6 +511,10 @@ func (state *debugState) processValue(locs *FuncDebug, v *Value, vSlots []SlotID } case v.Op == OpArg: 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 { 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 default: state.unexpected(v, "named value with no reg\n") } - } diff --git a/src/cmd/internal/dwarf/dwarf.go b/src/cmd/internal/dwarf/dwarf.go index ea8bc3dbe9..b3fa2f674f 100644 --- a/src/cmd/internal/dwarf/dwarf.go +++ b/src/cmd/internal/dwarf/dwarf.go @@ -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)