// 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
}
// 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
}
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
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(),
}
}
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<<uint8(reg)) != 0 {
// function. Variables are identified by their LocalSlot, which may be the
// result of decomposing a larger variable.
type FuncDebug struct {
- // Slots are all the slots in the function, indexed by their SlotID as
- // used in various functions and parallel to BlockDebug.Variables.
+ // Slots is all the slots used in the debug info, indexed by their SlotID.
+ // Use this when getting a LocalSlot from a SlotID.
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.
Blocks []*BlockDebug
// The registers of the current architecture, indexed by Register.num.
func (f *FuncDebug) BlockString(b *BlockDebug) string {
var vars []string
- for slot, list := range b.Variables {
- if len(list.Locations) == 0 {
+ for slot := range f.VarSlots {
+ if len(b.Variables[slot].Locations) == 0 {
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, ", "))
}
// The registers this variable is available in. There can be more than
// one in various situations, e.g. it's being moved between registers.
Registers RegisterSet
- // Indicates whether the variable is on the stack. The stack position is
- // stored in the associated gc.Node.
- OnStack bool
+ // OnStack indicates that the variable is on the stack in the LocalSlot
+ // identified by StackLocation.
+ OnStack bool
+ StackLocation SlotID
}
var BlockStart = &Value{
type debugState struct {
loggingEnabled bool
slots []*LocalSlot
+ varSlots []*LocalSlot
f *Func
cache *Cache
numRegisters int
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 {
f := &FuncDebug{
Slots: s.slots,
+ VarSlots: s.varSlots,
Registers: s.f.Config.registers,
}
return f.BlockString(b)
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 {
var names []string
info := &FuncDebug{
Slots: state.slots,
+ VarSlots: state.varSlots,
Registers: f.Config.registers,
}
// Consumers want the information in textual order, not by block ID.
if state.loggingEnabled {
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)))
}
}
loc := state.cache.NewVarLoc()
loc.Start = BlockStart
loc.OnStack = last.OnStack
+ loc.StackLocation = last.StackLocation
loc.Registers = last.Registers
live[slot].append(loc)
}
}
// 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
}
}
loc := state.cache.NewVarLoc()
loc.Start = v
loc.OnStack = last.OnStack
+ loc.StackLocation = last.StackLocation
loc.Registers = regs
locs.append(slot, loc)
}
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)
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:
loc.Start = v
if last != nil {
loc.OnStack = last.OnStack
+ loc.StackLocation = last.StackLocation
loc.Registers = last.Registers
}
loc.Registers |= 1 << uint8(vReg.num)