mirror of
https://github.com/golang/go
synced 2024-10-03 16:31:27 -06:00
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 <alessandro.arzilli@gmail.com> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
ca8c361d86
commit
0b6b5641d7
@ -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.
|
// Group SSA variables by the user variable they were decomposed from.
|
||||||
varParts := map[*Node][]varPart{}
|
varParts := map[*Node][]varPart{}
|
||||||
for slotID, slot := range debugInfo.Slots {
|
for slotID, slot := range debugInfo.VarSlots {
|
||||||
for slot.SplitOf != nil {
|
for slot.SplitOf != nil {
|
||||||
slot = slot.SplitOf
|
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.
|
// createComplexVar has side effects. Instead, go by slot.
|
||||||
var decls []*Node
|
var decls []*Node
|
||||||
var vars []*dwarf.Var
|
var vars []*dwarf.Var
|
||||||
for _, slot := range debugInfo.Slots {
|
for _, slot := range debugInfo.VarSlots {
|
||||||
for slot.SplitOf != nil {
|
for slot.SplitOf != nil {
|
||||||
slot = slot.SplitOf
|
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) 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] }
|
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.
|
// createComplexVar builds a DWARF variable entry and location list representing n.
|
||||||
func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf.Var {
|
func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf.Var {
|
||||||
slots := debugInfo.Slots
|
slots := debugInfo.Slots
|
||||||
@ -514,14 +534,15 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
|
|||||||
|
|
||||||
gotype := ngotype(n).Linksym()
|
gotype := ngotype(n).Linksym()
|
||||||
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
|
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{
|
dvar := &dwarf.Var{
|
||||||
Name: n.Sym.Name,
|
Name: n.Sym.Name,
|
||||||
Abbrev: abbrev,
|
Abbrev: abbrev,
|
||||||
Type: Ctxt.Lookup(typename),
|
Type: Ctxt.Lookup(typename),
|
||||||
StackOffset: int32(stackOffset),
|
// 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(),
|
DeclLine: n.Pos.Line(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,7 +687,7 @@ func createComplexVar(debugInfo *ssa.FuncDebug, n *Node, parts []varPart) *dwarf
|
|||||||
}
|
}
|
||||||
if loc.OnStack {
|
if loc.OnStack {
|
||||||
dpiece.OnStack = true
|
dpiece.OnStack = true
|
||||||
dpiece.StackOffset = int32(offs + slots[part.slot].Off + slots[part.slot].N.(*Node).Xoffset)
|
dpiece.StackOffset = stackOffset(slots[loc.StackLocation])
|
||||||
} else {
|
} else {
|
||||||
for reg := 0; reg < len(debugInfo.Registers); reg++ {
|
for reg := 0; reg < len(debugInfo.Registers); reg++ {
|
||||||
if loc.Registers&(1<<uint8(reg)) != 0 {
|
if loc.Registers&(1<<uint8(reg)) != 0 {
|
||||||
|
@ -15,9 +15,12 @@ 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 are all the slots in the function, indexed by their SlotID as
|
// Slots is all the slots used in the debug info, indexed by their SlotID.
|
||||||
// used in various functions and parallel to BlockDebug.Variables.
|
// Use this when getting a LocalSlot from a SlotID.
|
||||||
Slots []*LocalSlot
|
Slots []*LocalSlot
|
||||||
|
// VarSlots is the slots that represent part of user variables.
|
||||||
|
// Use this when iterating over all the slots to generate debug information.
|
||||||
|
VarSlots []*LocalSlot
|
||||||
// The blocks in the function, in program text order.
|
// The blocks in the function, in program text order.
|
||||||
Blocks []*BlockDebug
|
Blocks []*BlockDebug
|
||||||
// The registers of the current architecture, indexed by Register.num.
|
// The registers of the current architecture, indexed by Register.num.
|
||||||
@ -27,11 +30,11 @@ type FuncDebug struct {
|
|||||||
func (f *FuncDebug) BlockString(b *BlockDebug) string {
|
func (f *FuncDebug) BlockString(b *BlockDebug) string {
|
||||||
var vars []string
|
var vars []string
|
||||||
|
|
||||||
for slot, list := range b.Variables {
|
for slot := range f.VarSlots {
|
||||||
if len(list.Locations) == 0 {
|
if len(b.Variables[slot].Locations) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], list))
|
vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], b.Variables[slot]))
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{%v}", strings.Join(vars, ", "))
|
return fmt.Sprintf("{%v}", strings.Join(vars, ", "))
|
||||||
}
|
}
|
||||||
@ -159,9 +162,10 @@ type VarLoc struct {
|
|||||||
// The registers this variable is available in. There can be more than
|
// The registers this variable is available in. There can be more than
|
||||||
// one in various situations, e.g. it's being moved between registers.
|
// one in various situations, e.g. it's being moved between registers.
|
||||||
Registers RegisterSet
|
Registers RegisterSet
|
||||||
// Indicates whether the variable is on the stack. The stack position is
|
// OnStack indicates that the variable is on the stack in the LocalSlot
|
||||||
// stored in the associated gc.Node.
|
// identified by StackLocation.
|
||||||
OnStack bool
|
OnStack bool
|
||||||
|
StackLocation SlotID
|
||||||
}
|
}
|
||||||
|
|
||||||
var BlockStart = &Value{
|
var BlockStart = &Value{
|
||||||
@ -194,6 +198,7 @@ func (s *debugState) logf(msg string, args ...interface{}) {
|
|||||||
type debugState struct {
|
type debugState struct {
|
||||||
loggingEnabled bool
|
loggingEnabled bool
|
||||||
slots []*LocalSlot
|
slots []*LocalSlot
|
||||||
|
varSlots []*LocalSlot
|
||||||
f *Func
|
f *Func
|
||||||
cache *Cache
|
cache *Cache
|
||||||
numRegisters int
|
numRegisters int
|
||||||
@ -202,9 +207,24 @@ type debugState struct {
|
|||||||
registerContents [][]SlotID
|
registerContents [][]SlotID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getHomeSlot returns the SlotID of the home slot for v, adding to s.slots
|
||||||
|
// if necessary.
|
||||||
|
func (s *debugState) getHomeSlot(v *Value) SlotID {
|
||||||
|
home := s.f.getHome(v.ID).(LocalSlot)
|
||||||
|
for id, slot := range s.slots {
|
||||||
|
if *slot == home {
|
||||||
|
return SlotID(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This slot wasn't in the NamedValue table so it needs to be added.
|
||||||
|
s.slots = append(s.slots, &home)
|
||||||
|
return SlotID(len(s.slots) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *debugState) BlockString(b *BlockDebug) string {
|
func (s *debugState) BlockString(b *BlockDebug) string {
|
||||||
f := &FuncDebug{
|
f := &FuncDebug{
|
||||||
Slots: s.slots,
|
Slots: s.slots,
|
||||||
|
VarSlots: s.varSlots,
|
||||||
Registers: s.f.Config.registers,
|
Registers: s.f.Config.registers,
|
||||||
}
|
}
|
||||||
return f.BlockString(b)
|
return f.BlockString(b)
|
||||||
@ -239,6 +259,9 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
|
|||||||
valueNames[value.ID] = append(valueNames[value.ID], SlotID(i))
|
valueNames[value.ID] = append(valueNames[value.ID], SlotID(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// state.varSlots is never changed, and state.slots is only appended to,
|
||||||
|
// so aliasing is safe.
|
||||||
|
state.varSlots = state.slots
|
||||||
|
|
||||||
if state.loggingEnabled {
|
if state.loggingEnabled {
|
||||||
var names []string
|
var names []string
|
||||||
@ -335,6 +358,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
|
|||||||
|
|
||||||
info := &FuncDebug{
|
info := &FuncDebug{
|
||||||
Slots: state.slots,
|
Slots: state.slots,
|
||||||
|
VarSlots: state.varSlots,
|
||||||
Registers: f.Config.registers,
|
Registers: f.Config.registers,
|
||||||
}
|
}
|
||||||
// Consumers want the information in textual order, not by block ID.
|
// Consumers want the information in textual order, not by block ID.
|
||||||
@ -344,7 +368,7 @@ func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
|
|||||||
|
|
||||||
if state.loggingEnabled {
|
if state.loggingEnabled {
|
||||||
f.Logf("Final result:\n")
|
f.Logf("Final result:\n")
|
||||||
for slot := range info.Slots {
|
for slot := range info.VarSlots {
|
||||||
f.Logf("\t%v => %v\n", info.Slots[slot], info.SlotLocsString(SlotID(slot)))
|
f.Logf("\t%v => %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 := state.cache.NewVarLoc()
|
||||||
loc.Start = BlockStart
|
loc.Start = BlockStart
|
||||||
loc.OnStack = last.OnStack
|
loc.OnStack = last.OnStack
|
||||||
|
loc.StackLocation = last.StackLocation
|
||||||
loc.Registers = last.Registers
|
loc.Registers = last.Registers
|
||||||
live[slot].append(loc)
|
live[slot].append(loc)
|
||||||
}
|
}
|
||||||
@ -433,7 +458,10 @@ func (state *debugState) mergePredecessors(b *Block, blockLocs []*BlockDebug) *B
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unify storage locations.
|
// 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
|
liveLoc.Registers &= predLoc.Registers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,6 +534,7 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI
|
|||||||
loc := state.cache.NewVarLoc()
|
loc := state.cache.NewVarLoc()
|
||||||
loc.Start = v
|
loc.Start = v
|
||||||
loc.OnStack = last.OnStack
|
loc.OnStack = last.OnStack
|
||||||
|
loc.StackLocation = last.StackLocation
|
||||||
loc.Registers = regs
|
loc.Registers = regs
|
||||||
locs.append(slot, loc)
|
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])
|
state.unexpected(v, "Arg op on already-live slot %v", state.slots[slot])
|
||||||
last.End = v
|
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 := state.cache.NewVarLoc()
|
||||||
loc.Start = v
|
loc.Start = v
|
||||||
loc.OnStack = true
|
loc.OnStack = true
|
||||||
|
loc.StackLocation = state.getHomeSlot(v)
|
||||||
locs.append(slot, loc)
|
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:
|
case v.Op == OpStoreReg:
|
||||||
for _, slot := range vSlots {
|
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)
|
last := locs.lastLoc(slot)
|
||||||
if last == nil {
|
if last == nil {
|
||||||
state.unexpected(v, "spill of unnamed register %s\n", vReg)
|
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 := state.cache.NewVarLoc()
|
||||||
loc.Start = v
|
loc.Start = v
|
||||||
loc.OnStack = true
|
loc.OnStack = true
|
||||||
|
loc.StackLocation = state.getHomeSlot(v)
|
||||||
loc.Registers = last.Registers
|
loc.Registers = last.Registers
|
||||||
locs.append(slot, loc)
|
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:
|
case vReg != nil:
|
||||||
@ -569,6 +601,7 @@ func (state *debugState) processValue(locs *BlockDebug, v *Value, vSlots []SlotI
|
|||||||
loc.Start = v
|
loc.Start = v
|
||||||
if last != nil {
|
if last != nil {
|
||||||
loc.OnStack = last.OnStack
|
loc.OnStack = last.OnStack
|
||||||
|
loc.StackLocation = last.StackLocation
|
||||||
loc.Registers = last.Registers
|
loc.Registers = last.Registers
|
||||||
}
|
}
|
||||||
loc.Registers |= 1 << uint8(vReg.num)
|
loc.Registers |= 1 << uint8(vReg.num)
|
||||||
|
Loading…
Reference in New Issue
Block a user