From 0b6b5641d7f30cc1cf6ec623793ec758861359dc Mon Sep 17 00:00:00 2001 From: Heschi Kreinick Date: Fri, 29 Sep 2017 14:14:03 -0400 Subject: [PATCH] cmd/compile: use correct stack slots in location lists When variables need to be spilled to the stack, they usually get their own stack slot. Local variables have a slot allocated if they need one, and arguments start out on the stack. Before this CL, the debug information made the assumption that this was always the case, and so didn't bother storing an actual stack offset during SSA analysis. There's at least one case where this isn't true: variables that alias arguments. Since the argument is the source of the variable, the variable will begin its life on the stack in the argument's stack slot, not its own. Therefore the debug info needs to track the actual stack slot for each location entry. No detectable performance change, despite the O(N) loop in getHomeSlot. Change-Id: I2701adb7eddee17d4524336cb7aa6786e8f32b46 Reviewed-on: https://go-review.googlesource.com/67231 Reviewed-by: Alessandro Arzilli Reviewed-by: David Chase --- src/cmd/compile/internal/gc/pgen.go | 41 ++++++++++++----- src/cmd/compile/internal/ssa/debug.go | 65 ++++++++++++++++++++------- 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 4332305c7a..0db5f369ad 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -437,7 +437,7 @@ func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug) ([]*Node, []*d // Group SSA variables by the user variable they were decomposed from. varParts := map[*Node][]varPart{} - for slotID, slot := range debugInfo.Slots { + for slotID, slot := range debugInfo.VarSlots { for slot.SplitOf != nil { slot = slot.SplitOf } @@ -450,7 +450,7 @@ func createComplexVars(fnsym *obj.LSym, debugInfo *ssa.FuncDebug) ([]*Node, []*d // createComplexVar has side effects. Instead, go by slot. var decls []*Node var vars []*dwarf.Var - for _, slot := range debugInfo.Slots { + for _, slot := range debugInfo.VarSlots { for slot.SplitOf != nil { slot = slot.SplitOf } @@ -490,6 +490,26 @@ func (a partsByVarOffset) Len() int { return len(a) } func (a partsByVarOffset) Less(i, j int) bool { return a[i].varOffset < a[j].varOffset } func (a partsByVarOffset) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +// stackOffset returns the stack location of a LocalSlot relative to the +// stack pointer, suitable for use in a DWARF location entry. This has nothing +// to do with its offset in the user variable. +func stackOffset(slot *ssa.LocalSlot) int32 { + n := slot.N.(*Node) + var base int64 + switch n.Class() { + case PAUTO: + if Ctxt.FixedFrameSize() == 0 { + base -= int64(Widthptr) + } + if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { + base -= int64(Widthptr) + } + case PPARAM, PPARAMOUT: + base += Ctxt.FixedFrameSize() + } + return int32(base + n.Xoffset + slot.Off) +} + // createComplexVar builds a DWARF variable entry and location list representing n. func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf.Var { slots := debugInfo.Slots @@ -514,14 +534,15 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf gotype := ngotype(n).Linksym() typename := dwarf.InfoPrefix + gotype.Name[len("type."):] - // The stack offset is used as a sorting key, so for decomposed - // variables just give it the lowest one. It's not used otherwise. - stackOffset := debugInfo.Slots[parts[0].slot].N.(*Node).Xoffset + offs dvar := &dwarf.Var{ - Name: n.Sym.Name, - Abbrev: abbrev, - Type: Ctxt.Lookup(typename), - StackOffset: int32(stackOffset), + Name: n.Sym.Name, + Abbrev: abbrev, + Type: Ctxt.Lookup(typename), + // The stack offset is used as a sorting key, so for decomposed + // variables just give it the lowest one. It's not used otherwise. + // This won't work well if the first slot hasn't been assigned a stack + // location, but it's not obvious how to do better. + StackOffset: int32(stackOffset(slots[parts[0].slot])), DeclLine: n.Pos.Line(), } @@ -666,7 +687,7 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf } if loc.OnStack { dpiece.OnStack = true - dpiece.StackOffset = int32(offs + slots[part.slot].Off + slots[part.slot].N.(*Node).Xoffset) + dpiece.StackOffset = stackOffset(slots[loc.StackLocation]) } else { for reg := 0; reg < len(debugInfo.Registers); reg++ { if loc.Registers&(1< %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot))) } } @@ -406,6 +430,7 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *B loc := state.cache.NewVarLoc() loc.Start = BlockStart loc.OnStack = last.OnStack + loc.StackLocation = last.StackLocation loc.Registers = last.Registers live[slot].append(loc) } @@ -433,7 +458,10 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *B } // Unify storage locations. - liveLoc.OnStack = liveLoc.OnStack && predLoc.OnStack + if !liveLoc.OnStack || !predLoc.OnStack || liveLoc.StackLocation != predLoc.StackLocation { + liveLoc.OnStack = false + liveLoc.StackLocation = 0 + } liveLoc.Registers &= predLoc.Registers } } @@ -506,6 +534,7 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI loc := state.cache.NewVarLoc() loc.Start = v loc.OnStack = last.OnStack + loc.StackLocation = last.StackLocation loc.Registers = regs locs.append(slot, loc) } @@ -515,20 +544,18 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI 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]) - } loc := state.cache.NewVarLoc() loc.Start = v loc.OnStack = true + loc.StackLocation = state.getHomeSlot(v) locs.append(slot, loc) + if state.loggingEnabled { + state.logf("at %v: arg %v now on stack in location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation]) + } } case v.Op == OpStoreReg: for _, slot := range vSlots { - if state.loggingEnabled { - state.logf("at %v: %v spilled to stack\n", v.ID, state.slots[slot]) - } last := locs.lastLoc(slot) if last == nil { state.unexpected(v, "spill of unnamed register %s\n", vReg) @@ -538,8 +565,13 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI loc := state.cache.NewVarLoc() loc.Start = v loc.OnStack = true + loc.StackLocation = state.getHomeSlot(v) loc.Registers = last.Registers locs.append(slot, loc) + if state.loggingEnabled { + state.logf("at %v: %v spilled to stack location %v\n", v.ID, state.slots[slot], state.slots[loc.StackLocation]) + } + } case vReg != nil: @@ -569,6 +601,7 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI loc.Start = v if last != nil { loc.OnStack = last.OnStack + loc.StackLocation = last.StackLocation loc.Registers = last.Registers } loc.Registers |= 1 << uint8(vReg.num)