diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index 8acb7046354..96d1fb12f04 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 417f1ba7167..4332305c7aa 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 1a960497abc..a0c77d112be 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 91b11716bb7..20cf70bd8b9 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 ea8bc3dbe9f..b3fa2f674f0 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)