mirror of
https://github.com/golang/go.git
synced 2026-01-29 23:22:06 +03:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c54a047c6 | ||
|
|
cd702b171c | ||
|
|
59fe2fbfe5 | ||
|
|
2d57d94ac3 | ||
|
|
788aa88cd0 | ||
|
|
c1c08a13e7 | ||
|
|
045f605ea1 |
@@ -571,9 +571,16 @@ var knownFormats = map[string]string{
|
||||
"*cmd/compile/internal/ssa.Block %s": "",
|
||||
"*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 %v": "",
|
||||
"*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": "",
|
||||
"*cmd/compile/internal/types.Field %v": "",
|
||||
@@ -592,6 +599,7 @@ var knownFormats = map[string]string{
|
||||
"*cmd/compile/internal/types.Type %p": "",
|
||||
"*cmd/compile/internal/types.Type %s": "",
|
||||
"*cmd/compile/internal/types.Type %v": "",
|
||||
"*cmd/internal/dwarf.Location %#v": "",
|
||||
"*cmd/internal/obj.Addr %v": "",
|
||||
"*cmd/internal/obj.LSym %v": "",
|
||||
"*cmd/internal/obj.Prog %s": "",
|
||||
@@ -600,17 +608,21 @@ var knownFormats = map[string]string{
|
||||
"[16]byte %x": "",
|
||||
"[]*cmd/compile/internal/gc.Node %v": "",
|
||||
"[]*cmd/compile/internal/gc.Sig %#v": "",
|
||||
"[]*cmd/compile/internal/ssa.Block %+v": "",
|
||||
"[]*cmd/compile/internal/ssa.Value %v": "",
|
||||
"[][]cmd/compile/internal/ssa.SlotID %v": "",
|
||||
"[]byte %s": "",
|
||||
"[]byte %x": "",
|
||||
"[]cmd/compile/internal/ssa.Edge %v": "",
|
||||
"[]cmd/compile/internal/ssa.ID %v": "",
|
||||
"[]cmd/compile/internal/ssa.VarLocList %v": "",
|
||||
"[]string %v": "",
|
||||
"bool %v": "",
|
||||
"byte %08b": "",
|
||||
"byte %c": "",
|
||||
"cmd/compile/internal/arm.shift %d": "",
|
||||
"cmd/compile/internal/gc.Class %d": "",
|
||||
"cmd/compile/internal/gc.Class %v": "",
|
||||
"cmd/compile/internal/gc.Ctype %d": "",
|
||||
"cmd/compile/internal/gc.Ctype %v": "",
|
||||
"cmd/compile/internal/gc.Level %d": "",
|
||||
@@ -630,11 +642,13 @@ var knownFormats = map[string]string{
|
||||
"cmd/compile/internal/ssa.Edge %v": "",
|
||||
"cmd/compile/internal/ssa.GCNode %v": "",
|
||||
"cmd/compile/internal/ssa.ID %d": "",
|
||||
"cmd/compile/internal/ssa.ID %v": "",
|
||||
"cmd/compile/internal/ssa.LocalSlot %v": "",
|
||||
"cmd/compile/internal/ssa.Location %v": "",
|
||||
"cmd/compile/internal/ssa.Op %s": "",
|
||||
"cmd/compile/internal/ssa.Op %v": "",
|
||||
"cmd/compile/internal/ssa.ValAndOff %s": "",
|
||||
"cmd/compile/internal/ssa.VarLocList %v": "",
|
||||
"cmd/compile/internal/ssa.rbrank %d": "",
|
||||
"cmd/compile/internal/ssa.regMask %d": "",
|
||||
"cmd/compile/internal/ssa.register %d": "",
|
||||
@@ -648,6 +662,7 @@ var knownFormats = map[string]string{
|
||||
"cmd/compile/internal/types.EType %d": "",
|
||||
"cmd/compile/internal/types.EType %s": "",
|
||||
"cmd/compile/internal/types.EType %v": "",
|
||||
"cmd/internal/dwarf.Location %#v": "",
|
||||
"cmd/internal/src.Pos %s": "",
|
||||
"cmd/internal/src.Pos %v": "",
|
||||
"error %v": "",
|
||||
|
||||
@@ -44,6 +44,7 @@ var (
|
||||
Debug_vlog bool
|
||||
Debug_wb int
|
||||
Debug_pctab string
|
||||
Debug_locationlist int
|
||||
)
|
||||
|
||||
// Debug arguments.
|
||||
@@ -69,6 +70,7 @@ var debugtab = []struct {
|
||||
{"wb", "print information about write barriers", &Debug_wb},
|
||||
{"export", "print export data", &Debug_export},
|
||||
{"pctab", "print named pc-value table", &Debug_pctab},
|
||||
{"locationlists", "print information about DWARF location list creation", &Debug_locationlist},
|
||||
}
|
||||
|
||||
const debugHelpHeader = `usage: -d arg[,arg]* and arg is <key>[=<value>]
|
||||
@@ -192,6 +194,7 @@ func Main(archInit func(*Arch)) {
|
||||
flag.BoolVar(&pure_go, "complete", false, "compiling complete package (no C or assembly)")
|
||||
flag.StringVar(&debugstr, "d", "", "print debug information about items in `list`; try -d help")
|
||||
flag.BoolVar(&flagDWARF, "dwarf", true, "generate DWARF symbols")
|
||||
flag.BoolVar(&Ctxt.Flag_locationlists, "dwarflocationlists", false, "add location lists to DWARF in optimized mode")
|
||||
objabi.Flagcount("e", "no limit on number of errors reported", &Debug['e'])
|
||||
objabi.Flagcount("f", "debug stack frames", &Debug['f'])
|
||||
objabi.Flagcount("h", "halt on error", &Debug['h'])
|
||||
@@ -298,6 +301,9 @@ func Main(archInit func(*Arch)) {
|
||||
if nBackendWorkers > 1 && !concurrentBackendAllowed() {
|
||||
log.Fatalf("cannot use concurrent backend compilation with provided flags; invoked as %v", os.Args)
|
||||
}
|
||||
if Ctxt.Flag_locationlists && len(Ctxt.Arch.DWARFRegisters) == 0 {
|
||||
log.Fatalf("location lists requested but register mapping not available on %v", Ctxt.Arch.Name)
|
||||
}
|
||||
|
||||
// parse -d argument
|
||||
if debugstr != "" {
|
||||
@@ -383,7 +389,7 @@ func Main(archInit func(*Arch)) {
|
||||
Debug['l'] = 1 - Debug['l']
|
||||
}
|
||||
|
||||
trackScopes = flagDWARF && Debug['l'] == 0 && Debug['N'] != 0
|
||||
trackScopes = flagDWARF && ((Debug['l'] == 0 && Debug['N'] != 0) || Ctxt.Flag_locationlists)
|
||||
|
||||
Widthptr = thearch.LinkArch.PtrSize
|
||||
Widthreg = thearch.LinkArch.RegSize
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"cmd/internal/src"
|
||||
"cmd/internal/sys"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"sync"
|
||||
@@ -303,29 +304,77 @@ func compileFunctions() {
|
||||
|
||||
func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
|
||||
fn := curfn.(*Node)
|
||||
debugInfo := fn.Func.DebugInfo
|
||||
fn.Func.DebugInfo = nil
|
||||
if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
|
||||
Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
|
||||
}
|
||||
|
||||
var dwarfVars []*dwarf.Var
|
||||
var varScopes []ScopeID
|
||||
|
||||
var automDecls []*Node
|
||||
// Populate Automs for fn.
|
||||
for _, n := range fn.Func.Dcl {
|
||||
if n.Op != ONAME { // might be OTYPE or OLITERAL
|
||||
continue
|
||||
}
|
||||
|
||||
var name obj.AddrName
|
||||
var abbrev int
|
||||
offs := n.Xoffset
|
||||
|
||||
switch n.Class() {
|
||||
case PAUTO:
|
||||
if !n.Name.Used() {
|
||||
Fatalf("debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)")
|
||||
}
|
||||
name = obj.NAME_AUTO
|
||||
case PPARAM, PPARAMOUT:
|
||||
name = obj.NAME_PARAM
|
||||
default:
|
||||
continue
|
||||
}
|
||||
automDecls = append(automDecls, n)
|
||||
gotype := ngotype(n).Linksym()
|
||||
fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{
|
||||
Asym: Ctxt.Lookup(n.Sym.Name),
|
||||
Aoffset: int32(n.Xoffset),
|
||||
Name: name,
|
||||
Gotype: gotype,
|
||||
})
|
||||
}
|
||||
|
||||
var dwarfVars []*dwarf.Var
|
||||
var decls []*Node
|
||||
if Ctxt.Flag_locationlists && Ctxt.Flag_optimize {
|
||||
decls, dwarfVars = createComplexVars(fn, debugInfo)
|
||||
} else {
|
||||
decls, dwarfVars = createSimpleVars(automDecls)
|
||||
}
|
||||
|
||||
var varScopes []ScopeID
|
||||
for _, decl := range decls {
|
||||
var scope ScopeID
|
||||
if !decl.Name.Captured() && !decl.Name.Byval() {
|
||||
// n.Pos of captured variables is their first
|
||||
// use in the closure but they should always
|
||||
// be assigned to scope 0 instead.
|
||||
// TODO(mdempsky): Verify this.
|
||||
scope = findScope(fn.Func.Marks, decl.Pos)
|
||||
}
|
||||
varScopes = append(varScopes, scope)
|
||||
}
|
||||
return assembleScopes(fnsym, fn, dwarfVars, varScopes)
|
||||
}
|
||||
|
||||
// createSimpleVars creates a DWARF entry for every variable declared in the
|
||||
// function, claiming that they are permanently on the stack.
|
||||
func createSimpleVars(automDecls []*Node) ([]*Node, []*dwarf.Var) {
|
||||
var vars []*dwarf.Var
|
||||
var decls []*Node
|
||||
for _, n := range automDecls {
|
||||
if n.IsAutoTmp() {
|
||||
continue
|
||||
}
|
||||
var abbrev int
|
||||
offs := n.Xoffset
|
||||
|
||||
switch n.Class() {
|
||||
case PAUTO:
|
||||
abbrev = dwarf.DW_ABRV_AUTO
|
||||
if Ctxt.FixedFrameSize() == 0 {
|
||||
offs -= int64(Widthptr)
|
||||
@@ -335,48 +384,288 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
|
||||
}
|
||||
|
||||
case PPARAM, PPARAMOUT:
|
||||
name = obj.NAME_PARAM
|
||||
|
||||
abbrev = dwarf.DW_ABRV_PARAM
|
||||
offs += Ctxt.FixedFrameSize()
|
||||
|
||||
default:
|
||||
continue
|
||||
Fatalf("createSimpleVars unexpected type %v for node %v", n.Class(), n)
|
||||
}
|
||||
|
||||
gotype := ngotype(n).Linksym()
|
||||
fnsym.Func.Autom = append(fnsym.Func.Autom, &obj.Auto{
|
||||
Asym: Ctxt.Lookup(n.Sym.Name),
|
||||
Aoffset: int32(n.Xoffset),
|
||||
Name: name,
|
||||
Gotype: gotype,
|
||||
typename := dwarf.InfoPrefix + typesymname(n.Type)
|
||||
decls = append(decls, n)
|
||||
vars = append(vars, &dwarf.Var{
|
||||
Name: n.Sym.Name,
|
||||
Abbrev: abbrev,
|
||||
StackOffset: int32(offs),
|
||||
Type: Ctxt.Lookup(typename),
|
||||
})
|
||||
}
|
||||
return decls, vars
|
||||
}
|
||||
|
||||
if n.IsAutoTmp() {
|
||||
continue
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
|
||||
dwarfVars = append(dwarfVars, &dwarf.Var{
|
||||
Name: n.Sym.Name,
|
||||
Abbrev: abbrev,
|
||||
Offset: int32(offs),
|
||||
Type: Ctxt.Lookup(typename),
|
||||
})
|
||||
|
||||
var scope ScopeID
|
||||
if !n.Name.Captured() && !n.Name.Byval() {
|
||||
// n.Pos of captured variables is their first
|
||||
// use in the closure but they should always
|
||||
// be assigned to scope 0 instead.
|
||||
// TODO(mdempsky): Verify this.
|
||||
scope = findScope(fn.Func.Marks, n.Pos)
|
||||
}
|
||||
|
||||
varScopes = append(varScopes, scope)
|
||||
}
|
||||
|
||||
return assembleScopes(fnsym, fn, dwarfVars, varScopes)
|
||||
// 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 slot.SplitOf != nil {
|
||||
slot = slot.SplitOf
|
||||
}
|
||||
n := slot.N.(*Node)
|
||||
varParts[n] = append(varParts[n], varPart{varOffset(slot), ssa.SlotID(slotID), locList})
|
||||
}
|
||||
|
||||
// Produce a DWARF variable entry for each user variable.
|
||||
// Don't iterate over the map -- that's nondeterministic, and
|
||||
// createComplexVar has side effects. Instead, go by slot.
|
||||
var decls []*Node
|
||||
var vars []*dwarf.Var
|
||||
for _, slot := range debugInfo.Slots {
|
||||
for slot.SplitOf != nil {
|
||||
slot = slot.SplitOf
|
||||
}
|
||||
n := slot.N.(*Node)
|
||||
parts := varParts[n]
|
||||
if parts == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the order the parts need to be in to represent the memory
|
||||
// of the decomposed user variable.
|
||||
sort.Sort(partsByVarOffset(parts))
|
||||
|
||||
if dvar := createComplexVar(debugInfo, n, parts); dvar != nil {
|
||||
decls = append(decls, n)
|
||||
vars = append(vars, dvar)
|
||||
}
|
||||
}
|
||||
return decls, vars
|
||||
}
|
||||
|
||||
// varOffset returns the offset of slot within the user variable it was
|
||||
// decomposed from. This has nothing to do with its stack offset.
|
||||
func varOffset(slot *ssa.LocalSlot) int64 {
|
||||
offset := slot.Off
|
||||
for ; slot.SplitOf != nil; slot = slot.SplitOf {
|
||||
offset += slot.SplitOffset
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
type partsByVarOffset []varPart
|
||||
|
||||
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] }
|
||||
|
||||
// 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
|
||||
var offs int64 // base stack offset for this kind of variable
|
||||
var abbrev int
|
||||
switch n.Class() {
|
||||
case PAUTO:
|
||||
abbrev = dwarf.DW_ABRV_AUTO_LOCLIST
|
||||
if Ctxt.FixedFrameSize() == 0 {
|
||||
offs -= int64(Widthptr)
|
||||
}
|
||||
if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
|
||||
offs -= int64(Widthptr)
|
||||
}
|
||||
|
||||
case PPARAM, PPARAMOUT:
|
||||
abbrev = dwarf.DW_ABRV_PARAM_LOCLIST
|
||||
offs += Ctxt.FixedFrameSize()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Given a variable that's been decomposed into multiple parts,
|
||||
// its location list may need a new entry after the beginning or
|
||||
// end of every location entry for each of its parts. For example:
|
||||
//
|
||||
// [variable] [pc range]
|
||||
// string.ptr |----|-----| |----|
|
||||
// string.len |------------| |--|
|
||||
// ... needs a location list like:
|
||||
// string |----|-----|-| |--|-|
|
||||
//
|
||||
// Note that location entries may or may not line up with each other,
|
||||
// and some of the result will only have one or the other part.
|
||||
//
|
||||
// To build the resulting list:
|
||||
// - keep a "current" pointer for each part
|
||||
// - find the next transition point
|
||||
// - advance the current pointer for each part up to that transition point
|
||||
// - build the piece for the range between that transition point and the next
|
||||
// - repeat
|
||||
|
||||
curLoc := make([]int, len(slots))
|
||||
|
||||
// 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 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} {
|
||||
if pc > currentPC {
|
||||
if pc < min {
|
||||
min = pc
|
||||
}
|
||||
break onePart
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
var start int64
|
||||
end := findBoundaryAfter(0)
|
||||
for {
|
||||
// Advance to the next chunk.
|
||||
start = end
|
||||
end = findBoundaryAfter(start)
|
||||
if end == math.MaxInt64 {
|
||||
break
|
||||
}
|
||||
|
||||
dloc := dwarf.Location{StartPC: start, EndPC: end}
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("Processing range %x -> %x\n", start, end)
|
||||
}
|
||||
|
||||
// Advance curLoc to the last location that starts before/at start.
|
||||
// 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 {
|
||||
break //overshot
|
||||
}
|
||||
choice = i // best yet
|
||||
}
|
||||
if choice != -1 {
|
||||
curLoc[part.slot] = choice
|
||||
}
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("\t %v => %v", slots[part.slot], curLoc[part.slot])
|
||||
}
|
||||
}
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("\n")
|
||||
}
|
||||
// Assemble the location list entry for this chunk.
|
||||
present := 0
|
||||
for _, part := range parts {
|
||||
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 {
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("\t%v: missing", slots[part.slot])
|
||||
}
|
||||
dpiece.Missing = true
|
||||
dloc.Pieces = append(dloc.Pieces, dpiece)
|
||||
continue
|
||||
}
|
||||
present++
|
||||
loc := part.locs.Locations[locIdx]
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("\t%v: %v", slots[part.slot], loc)
|
||||
}
|
||||
if loc.OnStack {
|
||||
dpiece.OnStack = true
|
||||
dpiece.StackOffset = int32(offs + slots[part.slot].Off + slots[part.slot].N.(*Node).Xoffset)
|
||||
} else {
|
||||
for reg := 0; reg < len(debugInfo.Registers); reg++ {
|
||||
if loc.Registers&(1<<uint8(reg)) != 0 {
|
||||
dpiece.RegNum = Ctxt.Arch.DWARFRegisters[debugInfo.Registers[reg].ObjNum()]
|
||||
}
|
||||
}
|
||||
}
|
||||
dloc.Pieces = append(dloc.Pieces, dpiece)
|
||||
}
|
||||
if present == 0 {
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf(" -> totally missing\n")
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Extend the previous entry if possible.
|
||||
if len(dvar.LocationList) > 0 {
|
||||
prev := &dvar.LocationList[len(dvar.LocationList)-1]
|
||||
if prev.EndPC == dloc.StartPC && len(prev.Pieces) == len(dloc.Pieces) {
|
||||
equal := true
|
||||
for i := range prev.Pieces {
|
||||
if prev.Pieces[i] != dloc.Pieces[i] {
|
||||
equal = false
|
||||
}
|
||||
}
|
||||
if equal {
|
||||
prev.EndPC = end
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("-> merged with previous, now %#v\n", prev)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
dvar.LocationList = append(dvar.LocationList, dloc)
|
||||
if Debug_locationlist != 0 {
|
||||
Ctxt.Logf("-> added: %#v\n", dloc)
|
||||
}
|
||||
}
|
||||
return dvar
|
||||
}
|
||||
|
||||
// fieldtrack adds R_USEFIELD relocations to fnsym to record any
|
||||
|
||||
@@ -168,7 +168,7 @@ func (v varsByScopeAndOffset) Less(i, j int) bool {
|
||||
if v.scopes[i] != v.scopes[j] {
|
||||
return v.scopes[i] < v.scopes[j]
|
||||
}
|
||||
return v.vars[i].Offset < v.vars[j].Offset
|
||||
return v.vars[i].StackOffset < v.vars[j].StackOffset
|
||||
}
|
||||
|
||||
func (v varsByScopeAndOffset) Swap(i, j int) {
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
|
||||
_32bit uintptr // size on 32bit platforms
|
||||
_64bit uintptr // size on 64bit platforms
|
||||
}{
|
||||
{Func{}, 124, 216},
|
||||
{Func{}, 128, 224},
|
||||
{Name{}, 36, 56},
|
||||
{Param{}, 28, 56},
|
||||
{Node{}, 76, 128},
|
||||
|
||||
@@ -4382,14 +4382,15 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
// Remember where each block starts.
|
||||
s.bstart = make([]*obj.Prog, f.NumBlocks())
|
||||
s.pp = pp
|
||||
var valueProgs map[*obj.Prog]*ssa.Value
|
||||
var blockProgs map[*obj.Prog]*ssa.Block
|
||||
var progToValue map[*obj.Prog]*ssa.Value
|
||||
var progToBlock map[*obj.Prog]*ssa.Block
|
||||
var valueToProg []*obj.Prog
|
||||
var logProgs = e.log
|
||||
if logProgs {
|
||||
valueProgs = make(map[*obj.Prog]*ssa.Value, f.NumValues())
|
||||
blockProgs = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
|
||||
progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
|
||||
progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks())
|
||||
f.Logf("genssa %s\n", f.Name)
|
||||
blockProgs[s.pp.next] = f.Blocks[0]
|
||||
progToBlock[s.pp.next] = f.Blocks[0]
|
||||
}
|
||||
|
||||
if thearch.Use387 {
|
||||
@@ -4398,6 +4399,11 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
|
||||
s.ScratchFpMem = e.scratchFpMem
|
||||
|
||||
logLocationLists := Debug_locationlist != 0
|
||||
if Ctxt.Flag_locationlists {
|
||||
e.curfn.Func.DebugInfo = ssa.BuildFuncDebug(f, logLocationLists)
|
||||
valueToProg = make([]*obj.Prog, f.NumValues())
|
||||
}
|
||||
// Emit basic blocks
|
||||
for i, b := range f.Blocks {
|
||||
s.bstart[b.ID] = s.pp.next
|
||||
@@ -4438,15 +4444,19 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
}
|
||||
case ssa.OpPhi:
|
||||
CheckLoweredPhi(v)
|
||||
|
||||
case ssa.OpRegKill:
|
||||
// nothing to do
|
||||
default:
|
||||
// let the backend handle it
|
||||
thearch.SSAGenValue(&s, v)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_locationlists {
|
||||
valueToProg[v.ID] = x
|
||||
}
|
||||
if logProgs {
|
||||
for ; x != s.pp.next; x = x.Link {
|
||||
valueProgs[x] = v
|
||||
progToValue[x] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4464,7 +4474,23 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
thearch.SSAGenBlock(&s, b, next)
|
||||
if logProgs {
|
||||
for ; x != s.pp.next; x = x.Link {
|
||||
blockProgs[x] = b
|
||||
progToBlock[x] = b
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4477,9 +4503,9 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
if logProgs {
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
var s string
|
||||
if v, ok := valueProgs[p]; ok {
|
||||
if v, ok := progToValue[p]; ok {
|
||||
s = v.String()
|
||||
} else if b, ok := blockProgs[p]; ok {
|
||||
} else if b, ok := progToBlock[p]; ok {
|
||||
s = b.String()
|
||||
} else {
|
||||
s = " " // most value and branch strings are 2-3 characters long
|
||||
@@ -4497,9 +4523,9 @@ func genssa(f *ssa.Func, pp *Progs) {
|
||||
buf.WriteString("<dl class=\"ssa-gen\">")
|
||||
for p := pp.Text; p != nil; p = p.Link {
|
||||
buf.WriteString("<dt class=\"ssa-prog-src\">")
|
||||
if v, ok := valueProgs[p]; ok {
|
||||
if v, ok := progToValue[p]; ok {
|
||||
buf.WriteString(v.HTML())
|
||||
} else if b, ok := blockProgs[p]; ok {
|
||||
} else if b, ok := progToBlock[p]; ok {
|
||||
buf.WriteString(b.HTML())
|
||||
}
|
||||
buf.WriteString("</dt>")
|
||||
@@ -4876,9 +4902,9 @@ func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
lenType := types.Types[TINT]
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
// Split this string up into two separate variables.
|
||||
p := e.namedAuto(n.Sym.Name+".ptr", ptrType, n.Pos)
|
||||
l := e.namedAuto(n.Sym.Name+".len", lenType, n.Pos)
|
||||
return ssa.LocalSlot{N: p, Type: ptrType, Off: 0}, ssa.LocalSlot{N: l, Type: lenType, Off: 0}
|
||||
p := e.splitSlot(&name, ".ptr", 0, ptrType)
|
||||
l := e.splitSlot(&name, ".len", ptrType.Size(), lenType)
|
||||
return p, l
|
||||
}
|
||||
// Return the two parts of the larger variable.
|
||||
return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off}, ssa.LocalSlot{N: n, Type: lenType, Off: name.Off + int64(Widthptr)}
|
||||
@@ -4893,9 +4919,9 @@ func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot
|
||||
if n.Type.IsEmptyInterface() {
|
||||
f = ".type"
|
||||
}
|
||||
c := e.namedAuto(n.Sym.Name+f, t, n.Pos)
|
||||
d := e.namedAuto(n.Sym.Name+".data", t, n.Pos)
|
||||
return ssa.LocalSlot{N: c, Type: t, Off: 0}, ssa.LocalSlot{N: d, Type: t, Off: 0}
|
||||
c := e.splitSlot(&name, f, 0, t)
|
||||
d := e.splitSlot(&name, ".data", t.Size(), t)
|
||||
return c, d
|
||||
}
|
||||
// Return the two parts of the larger variable.
|
||||
return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + int64(Widthptr)}
|
||||
@@ -4907,10 +4933,10 @@ func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ss
|
||||
lenType := types.Types[TINT]
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
// Split this slice up into three separate variables.
|
||||
p := e.namedAuto(n.Sym.Name+".ptr", ptrType, n.Pos)
|
||||
l := e.namedAuto(n.Sym.Name+".len", lenType, n.Pos)
|
||||
c := e.namedAuto(n.Sym.Name+".cap", lenType, n.Pos)
|
||||
return ssa.LocalSlot{N: p, Type: ptrType, Off: 0}, ssa.LocalSlot{N: l, Type: lenType, Off: 0}, ssa.LocalSlot{N: c, Type: lenType, Off: 0}
|
||||
p := e.splitSlot(&name, ".ptr", 0, ptrType)
|
||||
l := e.splitSlot(&name, ".len", ptrType.Size(), lenType)
|
||||
c := e.splitSlot(&name, ".cap", ptrType.Size()+lenType.Size(), lenType)
|
||||
return p, l, c
|
||||
}
|
||||
// Return the three parts of the larger variable.
|
||||
return ssa.LocalSlot{N: n, Type: ptrType, Off: name.Off},
|
||||
@@ -4929,9 +4955,9 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot)
|
||||
}
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
// Split this complex up into two separate variables.
|
||||
c := e.namedAuto(n.Sym.Name+".real", t, n.Pos)
|
||||
d := e.namedAuto(n.Sym.Name+".imag", t, n.Pos)
|
||||
return ssa.LocalSlot{N: c, Type: t, Off: 0}, ssa.LocalSlot{N: d, Type: t, Off: 0}
|
||||
r := e.splitSlot(&name, ".real", 0, t)
|
||||
i := e.splitSlot(&name, ".imag", t.Size(), t)
|
||||
return r, i
|
||||
}
|
||||
// Return the two parts of the larger variable.
|
||||
return ssa.LocalSlot{N: n, Type: t, Off: name.Off}, ssa.LocalSlot{N: n, Type: t, Off: name.Off + s}
|
||||
@@ -4947,9 +4973,10 @@ func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
|
||||
}
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
// Split this int64 up into two separate variables.
|
||||
h := e.namedAuto(n.Sym.Name+".hi", t, n.Pos)
|
||||
l := e.namedAuto(n.Sym.Name+".lo", types.Types[TUINT32], n.Pos)
|
||||
return ssa.LocalSlot{N: h, Type: t, Off: 0}, ssa.LocalSlot{N: l, Type: types.Types[TUINT32], Off: 0}
|
||||
if thearch.LinkArch.ByteOrder == binary.BigEndian {
|
||||
return e.splitSlot(&name, ".hi", 0, t), e.splitSlot(&name, ".lo", t.Size(), types.Types[TUINT32])
|
||||
}
|
||||
return e.splitSlot(&name, ".hi", t.Size(), t), e.splitSlot(&name, ".lo", 0, types.Types[TUINT32])
|
||||
}
|
||||
// Return the two parts of the larger variable.
|
||||
if thearch.LinkArch.ByteOrder == binary.BigEndian {
|
||||
@@ -4962,12 +4989,15 @@ func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
|
||||
n := name.N.(*Node)
|
||||
st := name.Type
|
||||
ft := st.FieldType(i)
|
||||
var offset int64
|
||||
for f := 0; f < i; f++ {
|
||||
offset += st.FieldType(f).Size()
|
||||
}
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
// Note: the _ field may appear several times. But
|
||||
// have no fear, identically-named but distinct Autos are
|
||||
// ok, albeit maybe confusing for a debugger.
|
||||
x := e.namedAuto(n.Sym.Name+"."+st.FieldName(i), ft, n.Pos)
|
||||
return ssa.LocalSlot{N: x, Type: ft, Off: 0}
|
||||
return e.splitSlot(&name, "."+st.FieldName(i), offset, ft)
|
||||
}
|
||||
return ssa.LocalSlot{N: n, Type: ft, Off: name.Off + st.FieldOff(i)}
|
||||
}
|
||||
@@ -4980,8 +5010,7 @@ func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
|
||||
}
|
||||
et := at.ElemType()
|
||||
if n.Class() == PAUTO && !n.Addrtaken() {
|
||||
x := e.namedAuto(n.Sym.Name+"[0]", et, n.Pos)
|
||||
return ssa.LocalSlot{N: x, Type: et, Off: 0}
|
||||
return e.splitSlot(&name, "[0]", 0, et)
|
||||
}
|
||||
return ssa.LocalSlot{N: n, Type: et, Off: name.Off}
|
||||
}
|
||||
@@ -4990,16 +5019,14 @@ func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
|
||||
return itabsym(it, offset)
|
||||
}
|
||||
|
||||
// namedAuto returns a new AUTO variable with the given name and type.
|
||||
// These are exposed to the debugger.
|
||||
func (e *ssafn) namedAuto(name string, typ *types.Type, pos src.XPos) ssa.GCNode {
|
||||
t := typ
|
||||
s := &types.Sym{Name: name, Pkg: localpkg}
|
||||
// splitSlot returns a slot representing the data of parent starting at offset.
|
||||
func (e *ssafn) splitSlot(parent *ssa.LocalSlot, suffix string, offset int64, t *types.Type) ssa.LocalSlot {
|
||||
s := &types.Sym{Name: parent.N.(*Node).Sym.Name + suffix, Pkg: localpkg}
|
||||
|
||||
n := new(Node)
|
||||
n.Name = new(Name)
|
||||
n.Op = ONAME
|
||||
n.Pos = pos
|
||||
n.Pos = parent.N.(*Node).Pos
|
||||
n.Orig = n
|
||||
|
||||
s.Def = asTypesNode(n)
|
||||
@@ -5012,7 +5039,7 @@ func (e *ssafn) namedAuto(name string, typ *types.Type, pos src.XPos) ssa.GCNode
|
||||
n.Name.Curfn = e.curfn
|
||||
e.curfn.Func.Dcl = append(e.curfn.Func.Dcl, n)
|
||||
dowidth(t)
|
||||
return n
|
||||
return ssa.LocalSlot{N: n, Type: t, Off: 0, SplitOf: parent, SplitOffset: offset}
|
||||
}
|
||||
|
||||
func (e *ssafn) CanSSA(t *types.Type) bool {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package gc
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/ssa"
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/obj"
|
||||
@@ -369,6 +370,7 @@ type Func struct {
|
||||
Closgen int
|
||||
Outerfunc *Node // outer function (for closure)
|
||||
FieldTrack map[*types.Sym]struct{}
|
||||
DebugInfo *ssa.FuncDebug
|
||||
Ntype *Node // signature
|
||||
Top int // top context (Ecall, Eproc, etc)
|
||||
Closure *Node // OCLOSURE <-> ODCLFUNC
|
||||
|
||||
@@ -14,6 +14,11 @@ type Cache struct {
|
||||
blocks [200]Block
|
||||
locs [2000]Location
|
||||
|
||||
// Storage for DWARF variable locations. Lazily allocated
|
||||
// since location lists are off by default.
|
||||
varLocs []VarLoc
|
||||
curVarLoc int
|
||||
|
||||
// Reusable stackAllocState.
|
||||
// See stackalloc.go's {new,put}StackAllocState.
|
||||
stackAllocState *stackAllocState
|
||||
@@ -38,4 +43,21 @@ func (c *Cache) Reset() {
|
||||
for i := range xl {
|
||||
xl[i] = nil
|
||||
}
|
||||
xvl := c.varLocs[:c.curVarLoc]
|
||||
for i := range xvl {
|
||||
xvl[i] = VarLoc{}
|
||||
}
|
||||
c.curVarLoc = 0
|
||||
}
|
||||
|
||||
func (c *Cache) NewVarLoc() *VarLoc {
|
||||
if c.varLocs == nil {
|
||||
c.varLocs = make([]VarLoc, 4000)
|
||||
}
|
||||
if c.curVarLoc == len(c.varLocs) {
|
||||
return &VarLoc{}
|
||||
}
|
||||
vl := &c.varLocs[c.curVarLoc]
|
||||
c.curVarLoc++
|
||||
return vl
|
||||
}
|
||||
|
||||
559
src/cmd/compile/internal/ssa/debug.go
Normal file
559
src/cmd/compile/internal/ssa/debug.go
Normal file
@@ -0,0 +1,559 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package ssa
|
||||
|
||||
import (
|
||||
"cmd/internal/obj"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SlotID int32
|
||||
|
||||
// A FuncDebug contains all the debug information for the variables in a
|
||||
// function. Variables are identified by their LocalSlot, which may be the
|
||||
// result of decomposing a larger variable.
|
||||
type FuncDebug struct {
|
||||
Slots []*LocalSlot
|
||||
Variables []VarLocList
|
||||
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 {
|
||||
var vars []string
|
||||
for slot, list := range f.Variables {
|
||||
if len(list.Locations) == 0 {
|
||||
continue
|
||||
}
|
||||
vars = append(vars, fmt.Sprintf("%v = %v", f.Slots[slot], list))
|
||||
}
|
||||
return fmt.Sprintf("{%v}", strings.Join(vars, ", "))
|
||||
}
|
||||
|
||||
// A VarLocList contains the locations for a variable, in program text order.
|
||||
// It will often have gaps.
|
||||
type VarLocList struct {
|
||||
Locations []*VarLoc
|
||||
}
|
||||
|
||||
func (l *VarLocList) append(loc *VarLoc) {
|
||||
l.Locations = append(l.Locations, loc)
|
||||
}
|
||||
|
||||
// last returns the last location in the list.
|
||||
func (l *VarLocList) last() *VarLoc {
|
||||
if l == nil || len(l.Locations) == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.Locations[len(l.Locations)-1]
|
||||
}
|
||||
|
||||
// A VarLoc describes a variable's location in a single contiguous range
|
||||
// of program text. It is generated from the SSA representation, but it
|
||||
// refers to the generated machine code, so the Values referenced are better
|
||||
// understood as PCs than actual Values, and the ranges can cross blocks.
|
||||
// The range is defined first by Values, which are then mapped to Progs
|
||||
// during genssa and finally to function PCs after assembly.
|
||||
// A variable can be on the stack and in any number of registers.
|
||||
type VarLoc struct {
|
||||
// Inclusive -- the first SSA value that the range covers. The value
|
||||
// doesn't necessarily have anything to do with the variable; it just
|
||||
// identifies a point in the program text.
|
||||
Start *Value
|
||||
// Exclusive -- the first SSA value after start that the range doesn't
|
||||
// cover. A location with start == end is empty.
|
||||
End *Value
|
||||
// The prog/PCs corresponding to Start and End above. These are for the
|
||||
// convenience of later passes, since code generation isn't done when
|
||||
// BuildFuncDebug runs.
|
||||
StartProg, EndProg *obj.Prog
|
||||
StartPC, EndPC int64
|
||||
|
||||
// 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
|
||||
|
||||
// Used only during generation. Indicates whether this location lasts
|
||||
// past the block's end. Without this, there would be no way to distinguish
|
||||
// between a range that ended on the last Value of a block and one that
|
||||
// didn't end at all.
|
||||
survivedBlock bool
|
||||
}
|
||||
|
||||
// RegisterSet is a bitmap of registers, indexed by Register.num.
|
||||
type RegisterSet uint64
|
||||
|
||||
func (v *VarLoc) String() string {
|
||||
var registers []Register
|
||||
if v.Start != nil {
|
||||
registers = v.Start.Block.Func.Config.registers
|
||||
}
|
||||
loc := ""
|
||||
if !v.OnStack && v.Registers == 0 {
|
||||
loc = "!!!no location!!!"
|
||||
}
|
||||
if v.OnStack {
|
||||
loc += "stack,"
|
||||
}
|
||||
var regnames []string
|
||||
for reg := 0; reg < 64; reg++ {
|
||||
if v.Registers&(1<<uint8(reg)) == 0 {
|
||||
continue
|
||||
}
|
||||
if registers != nil {
|
||||
regnames = append(regnames, registers[reg].Name())
|
||||
} else {
|
||||
regnames = append(regnames, fmt.Sprintf("reg%v", reg))
|
||||
}
|
||||
}
|
||||
loc += strings.Join(regnames, ",")
|
||||
pos := func(v *Value, p *obj.Prog, pc int64) string {
|
||||
if v == nil {
|
||||
return "?"
|
||||
}
|
||||
if p == nil {
|
||||
return fmt.Sprintf("v%v", v.ID)
|
||||
}
|
||||
return fmt.Sprintf("v%v/%x", v.ID, pc)
|
||||
}
|
||||
surv := ""
|
||||
if v.survivedBlock {
|
||||
surv = "+"
|
||||
}
|
||||
return fmt.Sprintf("%v-%v%s@%s", pos(v.Start, v.StartProg, v.StartPC), pos(v.End, v.EndProg, v.EndPC), surv, loc)
|
||||
}
|
||||
|
||||
// unexpected is used to indicate an inconsistency or bug in the debug info
|
||||
// generation process. These are not fixable by users. At time of writing,
|
||||
// changing this to a Fprintf(os.Stderr) and running make.bash generates
|
||||
// thousands of warnings.
|
||||
func (s *debugState) unexpected(v *Value, msg string, args ...interface{}) {
|
||||
s.f.Logf("unexpected at "+fmt.Sprint(v.ID)+":"+msg, args...)
|
||||
}
|
||||
|
||||
func (s *debugState) logf(msg string, args ...interface{}) {
|
||||
s.f.Logf(msg, args...)
|
||||
}
|
||||
|
||||
type debugState struct {
|
||||
loggingEnabled bool
|
||||
slots []*LocalSlot
|
||||
f *Func
|
||||
cache *Cache
|
||||
numRegisters int
|
||||
|
||||
// working storage for BuildFuncDebug, reused between blocks.
|
||||
registerContents [][]SlotID
|
||||
}
|
||||
|
||||
// BuildFuncDebug returns debug information for f.
|
||||
// f must be fully processed, so that each Value is where it will be when
|
||||
// machine code is emitted.
|
||||
func BuildFuncDebug(f *Func, loggingEnabled bool) *FuncDebug {
|
||||
if f.RegAlloc == nil {
|
||||
f.Fatalf("BuildFuncDebug on func %v that has not been fully processed", f)
|
||||
}
|
||||
state := &debugState{
|
||||
loggingEnabled: loggingEnabled,
|
||||
slots: make([]*LocalSlot, len(f.Names)),
|
||||
cache: f.Cache,
|
||||
f: f,
|
||||
numRegisters: len(f.Config.registers),
|
||||
registerContents: make([][]SlotID, len(f.Config.registers)),
|
||||
}
|
||||
// TODO: consider storing this in Cache and reusing across functions.
|
||||
valueNames := make([][]SlotID, f.NumValues())
|
||||
|
||||
for i, slot := range f.Names {
|
||||
slot := slot
|
||||
state.slots[i] = &slot
|
||||
|
||||
if isSynthetic(&slot) {
|
||||
continue
|
||||
}
|
||||
for _, value := range f.NamedValues[slot] {
|
||||
valueNames[value.ID] = append(valueNames[value.ID], SlotID(i))
|
||||
}
|
||||
}
|
||||
|
||||
if state.loggingEnabled {
|
||||
var names []string
|
||||
for i, name := range f.Names {
|
||||
names = append(names, fmt.Sprintf("%v = %v", i, name))
|
||||
}
|
||||
state.logf("Name table: %v\n", strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
// Build up block states, starting with the first block, then
|
||||
// processing blocks once their predecessors have been processed.
|
||||
|
||||
// TODO: use a reverse post-order traversal instead of the work queue.
|
||||
|
||||
// Location list entries for each block.
|
||||
blockLocs := make([]*FuncDebug, f.NumBlocks())
|
||||
|
||||
// Work queue of blocks to visit. Some of them may already be processed.
|
||||
work := []*Block{f.Entry}
|
||||
|
||||
for len(work) > 0 {
|
||||
b := work[0]
|
||||
work = work[1:]
|
||||
if blockLocs[b.ID] != nil {
|
||||
continue // already processed
|
||||
}
|
||||
if !state.predecessorsDone(b, blockLocs) {
|
||||
continue // not ready yet
|
||||
}
|
||||
|
||||
for _, edge := range b.Succs {
|
||||
if blockLocs[edge.Block().ID] != nil {
|
||||
continue
|
||||
}
|
||||
work = append(work, edge.Block())
|
||||
}
|
||||
|
||||
// Build the starting state for the block from the final
|
||||
// state of its predecessors.
|
||||
locs := state.mergePredecessors(b, blockLocs)
|
||||
if state.loggingEnabled {
|
||||
state.logf("Processing %v, initial locs %v, regs %v\n", b, locs, state.registerContents)
|
||||
}
|
||||
// Update locs/registers with the effects of each Value.
|
||||
for _, v := range b.Values {
|
||||
slots := valueNames[v.ID]
|
||||
|
||||
// Loads and stores inherit the names of their sources.
|
||||
var source *Value
|
||||
switch v.Op {
|
||||
case OpStoreReg:
|
||||
source = v.Args[0]
|
||||
case OpLoadReg:
|
||||
switch a := v.Args[0]; a.Op {
|
||||
case OpArg:
|
||||
source = a
|
||||
case OpStoreReg:
|
||||
source = a.Args[0]
|
||||
default:
|
||||
state.unexpected(v, "load with unexpected source op %v", a)
|
||||
}
|
||||
}
|
||||
if source != nil {
|
||||
slots = append(slots, valueNames[source.ID]...)
|
||||
// As of writing, the compiler never uses a load/store as a
|
||||
// source of another load/store, so there's no reason this should
|
||||
// ever be consulted. Update just in case, and so that when
|
||||
// valueNames is cached, we can reuse the memory.
|
||||
valueNames[v.ID] = slots
|
||||
}
|
||||
|
||||
if len(slots) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
reg, _ := f.getHome(v.ID).(*Register)
|
||||
state.processValue(locs, v, slots, reg)
|
||||
|
||||
}
|
||||
|
||||
// The block is done; end the locations for all its slots.
|
||||
for _, locList := range locs.Variables {
|
||||
last := locList.last()
|
||||
if last == nil || last.End != nil {
|
||||
continue
|
||||
}
|
||||
if len(b.Values) != 0 {
|
||||
last.End = b.Values[len(b.Values)-1]
|
||||
} else {
|
||||
// This happens when a value survives into an empty block from its predecessor.
|
||||
// Just carry it forward for liveness's sake.
|
||||
last.End = last.Start
|
||||
}
|
||||
last.survivedBlock = true
|
||||
}
|
||||
if state.loggingEnabled {
|
||||
f.Logf("Block done: locs %v, regs %v. work = %+v\n", locs, state.registerContents, work)
|
||||
}
|
||||
blockLocs[b.ID] = locs
|
||||
}
|
||||
|
||||
// Build the complete debug info by concatenating each of the blocks'
|
||||
// locations together.
|
||||
info := &FuncDebug{
|
||||
Variables: make([]VarLocList, len(state.slots)),
|
||||
Slots: state.slots,
|
||||
Registers: f.Config.registers,
|
||||
}
|
||||
for _, b := range f.Blocks {
|
||||
// Ignore empty blocks; there will be some records for liveness
|
||||
// but they're all useless.
|
||||
if len(b.Values) == 0 {
|
||||
continue
|
||||
}
|
||||
if blockLocs[b.ID] == nil {
|
||||
state.unexpected(b.Values[0], "Never processed block %v\n", b)
|
||||
continue
|
||||
}
|
||||
for slot, blockLocList := range blockLocs[b.ID].Variables {
|
||||
for _, loc := range blockLocList.Locations {
|
||||
if !loc.OnStack && loc.Registers == 0 {
|
||||
state.unexpected(loc.Start, "Location for %v with no storage: %+v\n", state.slots[slot], loc)
|
||||
continue // don't confuse downstream with our bugs
|
||||
}
|
||||
if loc.Start == nil || loc.End == nil {
|
||||
state.unexpected(b.Values[0], "Location for %v missing start or end: %v\n", state.slots[slot], loc)
|
||||
continue
|
||||
}
|
||||
info.append(SlotID(slot), loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
if state.loggingEnabled {
|
||||
f.Logf("Final result:\n")
|
||||
for slot, locList := range info.Variables {
|
||||
f.Logf("\t%v => %v\n", state.slots[slot], locList)
|
||||
}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
// isSynthetic reports whether if slot represents a compiler-inserted variable,
|
||||
// e.g. an autotmp or an anonymous return value that needed a stack slot.
|
||||
func isSynthetic(slot *LocalSlot) bool {
|
||||
c := slot.Name()[0]
|
||||
return c == '.' || c == '~'
|
||||
}
|
||||
|
||||
// predecessorsDone reports whether block is ready to be processed.
|
||||
func (state *debugState) predecessorsDone(b *Block, blockLocs []*FuncDebug) bool {
|
||||
f := b.Func
|
||||
for _, edge := range b.Preds {
|
||||
// Ignore back branches, e.g. the continuation of a for loop.
|
||||
// This may not work for functions with mutual gotos, which are not
|
||||
// reducible, in which case debug information will be missing for any
|
||||
// code after that point in the control flow.
|
||||
if f.sdom().isAncestorEq(b, edge.b) {
|
||||
if state.loggingEnabled {
|
||||
f.Logf("ignoring back branch from %v to %v\n", edge.b, b)
|
||||
}
|
||||
continue // back branch
|
||||
}
|
||||
if blockLocs[edge.b.ID] == nil {
|
||||
if state.loggingEnabled {
|
||||
f.Logf("%v is not ready because %v isn't done\n", b, edge.b)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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 {
|
||||
live := make([]VarLocList, len(state.slots))
|
||||
|
||||
// Filter out back branches.
|
||||
var preds []*Block
|
||||
for _, pred := range b.Preds {
|
||||
if blockLocs[pred.b.ID] != nil {
|
||||
preds = append(preds, pred.b)
|
||||
}
|
||||
}
|
||||
|
||||
if len(preds) > 0 {
|
||||
p := preds[0]
|
||||
for slot, locList := range blockLocs[p.ID].Variables {
|
||||
last := locList.last()
|
||||
if last == nil || !last.survivedBlock {
|
||||
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.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])
|
||||
}
|
||||
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])
|
||||
}
|
||||
|
||||
for slot, liveVar := range live {
|
||||
liveLoc := liveVar.last()
|
||||
if liveLoc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
predLoc := blockLocs[p.ID].lastLoc(SlotID(slot))
|
||||
// Clear out slots missing/dead in p.
|
||||
if predLoc == nil || !predLoc.survivedBlock {
|
||||
live[slot].Locations = nil
|
||||
continue
|
||||
}
|
||||
|
||||
// Unify storage locations.
|
||||
liveLoc.OnStack = liveLoc.OnStack && predLoc.OnStack
|
||||
liveLoc.Registers &= predLoc.Registers
|
||||
}
|
||||
}
|
||||
|
||||
// Create final result.
|
||||
locs := &FuncDebug{Variables: live, Slots: state.slots}
|
||||
for reg := range state.registerContents {
|
||||
state.registerContents[reg] = state.registerContents[reg][:0]
|
||||
}
|
||||
for slot, locList := range live {
|
||||
loc := locList.last()
|
||||
if loc == nil {
|
||||
continue
|
||||
}
|
||||
for reg := 0; reg < state.numRegisters; reg++ {
|
||||
if loc.Registers&(1<<uint8(reg)) != 0 {
|
||||
state.registerContents[reg] = append(state.registerContents[reg], SlotID(slot))
|
||||
}
|
||||
}
|
||||
}
|
||||
return locs
|
||||
}
|
||||
|
||||
// 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) {
|
||||
switch {
|
||||
case v.Op == OpRegKill:
|
||||
if state.loggingEnabled {
|
||||
existingSlots := make([]bool, len(state.slots))
|
||||
for _, slot := range state.registerContents[vReg.num] {
|
||||
existingSlots[slot] = true
|
||||
}
|
||||
for _, slot := range vSlots {
|
||||
if existingSlots[slot] {
|
||||
existingSlots[slot] = false
|
||||
} else {
|
||||
state.unexpected(v, "regkill of unassociated name %v\n", state.slots[slot])
|
||||
}
|
||||
}
|
||||
for slot, live := range existingSlots {
|
||||
if live {
|
||||
state.unexpected(v, "leftover register name: %v\n", state.slots[slot])
|
||||
}
|
||||
}
|
||||
}
|
||||
state.registerContents[vReg.num] = nil
|
||||
|
||||
for _, slot := range vSlots {
|
||||
last := locs.lastLoc(slot)
|
||||
if last == nil {
|
||||
state.unexpected(v, "regkill of already dead %v, %+v\n", vReg, state.slots[slot])
|
||||
continue
|
||||
}
|
||||
if state.loggingEnabled {
|
||||
state.logf("at %v: %v regkilled out of %v\n", v.ID, state.slots[slot], vReg.Name())
|
||||
}
|
||||
if last.End != nil {
|
||||
state.unexpected(v, "regkill of dead slot, died at %v\n", last.End)
|
||||
}
|
||||
last.End = v
|
||||
|
||||
regs := last.Registers &^ (1 << uint8(vReg.num))
|
||||
if !last.OnStack && regs == 0 {
|
||||
continue
|
||||
}
|
||||
loc := state.cache.NewVarLoc()
|
||||
loc.Start = v
|
||||
loc.OnStack = last.OnStack
|
||||
loc.Registers = regs
|
||||
locs.append(slot, loc)
|
||||
}
|
||||
case v.Op == OpArg:
|
||||
for _, slot := range vSlots {
|
||||
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
|
||||
locs.append(slot, loc)
|
||||
}
|
||||
|
||||
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 %v\n", vReg)
|
||||
break
|
||||
}
|
||||
last.End = v
|
||||
loc := state.cache.NewVarLoc()
|
||||
loc.Start = v
|
||||
loc.OnStack = true
|
||||
loc.Registers = last.Registers
|
||||
locs.append(slot, loc)
|
||||
}
|
||||
|
||||
case vReg != nil:
|
||||
if state.loggingEnabled {
|
||||
newSlots := make([]bool, len(state.slots))
|
||||
for _, slot := range vSlots {
|
||||
newSlots[slot] = true
|
||||
}
|
||||
|
||||
for _, slot := range state.registerContents[vReg.num] {
|
||||
if !newSlots[slot] {
|
||||
state.unexpected(v, "%v clobbered\n", state.slots[slot])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, slot := range vSlots {
|
||||
if state.loggingEnabled {
|
||||
state.logf("at %v: %v now in %v\n", v.ID, state.slots[slot], vReg.Name())
|
||||
}
|
||||
last := locs.lastLoc(slot)
|
||||
if last != nil && last.End == nil {
|
||||
last.End = v
|
||||
}
|
||||
state.registerContents[vReg.num] = append(state.registerContents[vReg.num], slot)
|
||||
loc := state.cache.NewVarLoc()
|
||||
loc.Start = v
|
||||
if last != nil {
|
||||
loc.OnStack = last.OnStack
|
||||
loc.Registers = last.Registers
|
||||
}
|
||||
loc.Registers |= 1 << uint8(vReg.num)
|
||||
locs.append(slot, loc)
|
||||
}
|
||||
default:
|
||||
state.unexpected(v, "named value with no reg\n")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -98,6 +98,7 @@ func decomposeBuiltIn(f *Func) {
|
||||
delete(f.NamedValues, name)
|
||||
case t.IsFloat():
|
||||
// floats are never decomposed, even ones bigger than RegSize
|
||||
newNames = append(newNames, name)
|
||||
case t.Size() > f.Config.RegSize:
|
||||
f.Fatalf("undecomposed named type %v %v", name, t)
|
||||
default:
|
||||
|
||||
@@ -82,33 +82,33 @@ func (DummyFrontend) Auto(pos src.XPos, t *types.Type) GCNode {
|
||||
return &DummyAuto{t: t, s: "aDummyAuto"}
|
||||
}
|
||||
func (d DummyFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{s.N, dummyTypes.BytePtr, s.Off}, LocalSlot{s.N, dummyTypes.Int, s.Off + 8}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 8}
|
||||
}
|
||||
func (d DummyFrontend) SplitInterface(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{s.N, dummyTypes.BytePtr, s.Off}, LocalSlot{s.N, dummyTypes.BytePtr, s.Off + 8}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.BytePtr, Off: s.Off + 8}
|
||||
}
|
||||
func (d DummyFrontend) SplitSlice(s LocalSlot) (LocalSlot, LocalSlot, LocalSlot) {
|
||||
return LocalSlot{s.N, s.Type.ElemType().PtrTo(), s.Off},
|
||||
LocalSlot{s.N, dummyTypes.Int, s.Off + 8},
|
||||
LocalSlot{s.N, dummyTypes.Int, s.Off + 16}
|
||||
return LocalSlot{N: s.N, Type: s.Type.ElemType().PtrTo(), Off: s.Off},
|
||||
LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 8},
|
||||
LocalSlot{N: s.N, Type: dummyTypes.Int, Off: s.Off + 16}
|
||||
}
|
||||
func (d DummyFrontend) SplitComplex(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
if s.Type.Size() == 16 {
|
||||
return LocalSlot{s.N, dummyTypes.Float64, s.Off}, LocalSlot{s.N, dummyTypes.Float64, s.Off + 8}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.Float64, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Float64, Off: s.Off + 8}
|
||||
}
|
||||
return LocalSlot{s.N, dummyTypes.Float32, s.Off}, LocalSlot{s.N, dummyTypes.Float32, s.Off + 4}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.Float32, Off: s.Off}, LocalSlot{N: s.N, Type: dummyTypes.Float32, Off: s.Off + 4}
|
||||
}
|
||||
func (d DummyFrontend) SplitInt64(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
if s.Type.IsSigned() {
|
||||
return LocalSlot{s.N, dummyTypes.Int32, s.Off + 4}, LocalSlot{s.N, dummyTypes.UInt32, s.Off}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.Int32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off}
|
||||
}
|
||||
return LocalSlot{s.N, dummyTypes.UInt32, s.Off + 4}, LocalSlot{s.N, dummyTypes.UInt32, s.Off}
|
||||
return LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off + 4}, LocalSlot{N: s.N, Type: dummyTypes.UInt32, Off: s.Off}
|
||||
}
|
||||
func (d DummyFrontend) SplitStruct(s LocalSlot, i int) LocalSlot {
|
||||
return LocalSlot{s.N, s.Type.FieldType(i), s.Off + s.Type.FieldOff(i)}
|
||||
return LocalSlot{N: s.N, Type: s.Type.FieldType(i), Off: s.Off + s.Type.FieldOff(i)}
|
||||
}
|
||||
func (d DummyFrontend) SplitArray(s LocalSlot) LocalSlot {
|
||||
return LocalSlot{s.N, s.Type.ElemType(), s.Off}
|
||||
return LocalSlot{N: s.N, Type: s.Type.ElemType(), Off: s.Off}
|
||||
}
|
||||
func (DummyFrontend) Line(_ src.XPos) string {
|
||||
return "unknown.go:0"
|
||||
|
||||
@@ -417,6 +417,7 @@ var genericOps = []opData{
|
||||
{name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
|
||||
{name: "VarLive", argLength: 1, aux: "Sym", symEffect: "None"}, // aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
|
||||
{name: "KeepAlive", argLength: 2, typ: "Mem"}, // arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
|
||||
{name: "RegKill"}, // regalloc has determined that the value in this register is dead
|
||||
|
||||
// Ops for breaking 64-bit operations on 32-bit architectures
|
||||
{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi, arg1=lo
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"html"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type HTMLWriter struct {
|
||||
@@ -362,6 +363,18 @@ func (v *Value) LongHTML() string {
|
||||
if int(v.ID) < len(r) && r[v.ID] != nil {
|
||||
s += " : " + html.EscapeString(r[v.ID].Name())
|
||||
}
|
||||
var names []string
|
||||
for name, values := range v.Block.Func.NamedValues {
|
||||
for _, value := range values {
|
||||
if value == v {
|
||||
names = append(names, name.Name())
|
||||
break // drop duplicates.
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(names) != 0 {
|
||||
s += " (" + strings.Join(names, ", ") + ")"
|
||||
}
|
||||
s += "</span>"
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -26,12 +26,38 @@ func (r *Register) Name() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
// A LocalSlot is a location in the stack frame.
|
||||
// It is (possibly a subpiece of) a PPARAM, PPARAMOUT, or PAUTO ONAME node.
|
||||
// ObjNum returns the register number from cmd/internal/obj/$ARCH that
|
||||
// corresponds to this register.
|
||||
func (r *Register) ObjNum() int16 {
|
||||
return r.objNum
|
||||
}
|
||||
|
||||
// A LocalSlot is a location in the stack frame, which identifies and stores
|
||||
// part or all of a PPARAM, PPARAMOUT, or PAUTO ONAME node.
|
||||
// It can represent a whole variable, part of a larger stack slot, or part of a
|
||||
// variable that has been decomposed into multiple stack slots.
|
||||
// As an example, a string could have the following configurations:
|
||||
//
|
||||
// stack layout LocalSlots
|
||||
//
|
||||
// Optimizations are disabled. s is on the stack and represented in its entirety.
|
||||
// [ ------- s string ---- ] { N: s, Type: string, Off: 0 }
|
||||
//
|
||||
// s was not decomposed, but the SSA operates on its parts individually, so
|
||||
// there is a LocalSlot for each of its fields that points into the single stack slot.
|
||||
// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8}
|
||||
//
|
||||
// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot.
|
||||
// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0},
|
||||
// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8}
|
||||
// parent = &{N: s, Type: string}
|
||||
type LocalSlot struct {
|
||||
N GCNode // an ONAME *gc.Node representing a variable on the stack
|
||||
N GCNode // an ONAME *gc.Node representing a stack location.
|
||||
Type *types.Type // type of slot
|
||||
Off int64 // offset of slot in N
|
||||
|
||||
SplitOf *LocalSlot // slot is a decomposition of SplitOf
|
||||
SplitOffset int64 // .. at this offset.
|
||||
}
|
||||
|
||||
func (s LocalSlot) Name() string {
|
||||
|
||||
@@ -1897,6 +1897,7 @@ const (
|
||||
OpVarKill
|
||||
OpVarLive
|
||||
OpKeepAlive
|
||||
OpRegKill
|
||||
OpInt64Make
|
||||
OpInt64Hi
|
||||
OpInt64Lo
|
||||
@@ -22497,6 +22498,11 @@ var opcodeTable = [...]opInfo{
|
||||
argLen: 2,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "RegKill",
|
||||
argLen: 0,
|
||||
generic: true,
|
||||
},
|
||||
{
|
||||
name: "Int64Make",
|
||||
argLen: 2,
|
||||
|
||||
@@ -242,6 +242,9 @@ type regAllocState struct {
|
||||
// current state of each (preregalloc) Value
|
||||
values []valState
|
||||
|
||||
// names associated with each Value
|
||||
valueNames [][]LocalSlot
|
||||
|
||||
// ID of SP, SB values
|
||||
sp, sb ID
|
||||
|
||||
@@ -300,6 +303,13 @@ type startReg struct {
|
||||
|
||||
// freeReg frees up register r. Any current user of r is kicked out.
|
||||
func (s *regAllocState) freeReg(r register) {
|
||||
s.freeOrResetReg(r, false)
|
||||
}
|
||||
|
||||
// freeOrResetReg frees up register r. Any current user of r is kicked out.
|
||||
// resetting indicates that the operation is only for bookkeeping,
|
||||
// e.g. when clearing out state upon entry to a new block.
|
||||
func (s *regAllocState) freeOrResetReg(r register, resetting bool) {
|
||||
v := s.regs[r].v
|
||||
if v == nil {
|
||||
s.f.Fatalf("tried to free an already free register %d\n", r)
|
||||
@@ -309,6 +319,16 @@ func (s *regAllocState) freeReg(r register) {
|
||||
if s.f.pass.debug > regDebug {
|
||||
fmt.Printf("freeReg %s (dump %s/%s)\n", s.registers[r].Name(), v, s.regs[r].c)
|
||||
}
|
||||
if !resetting && s.f.Config.ctxt.Flag_locationlists && len(s.valueNames[v.ID]) != 0 {
|
||||
kill := s.curBlock.NewValue0(src.NoXPos, OpRegKill, types.TypeVoid)
|
||||
for int(kill.ID) >= len(s.orig) {
|
||||
s.orig = append(s.orig, nil)
|
||||
}
|
||||
for _, name := range s.valueNames[v.ID] {
|
||||
s.f.NamedValues[name] = append(s.f.NamedValues[name], kill)
|
||||
}
|
||||
s.f.setHome(kill, &s.registers[r])
|
||||
}
|
||||
s.regs[r] = regState{}
|
||||
s.values[v.ID].regs &^= regMask(1) << r
|
||||
s.used &^= regMask(1) << r
|
||||
@@ -599,6 +619,17 @@ func (s *regAllocState) init(f *Func) {
|
||||
s.values = make([]valState, f.NumValues())
|
||||
s.orig = make([]*Value, f.NumValues())
|
||||
s.copies = make(map[*Value]bool)
|
||||
if s.f.Config.ctxt.Flag_locationlists {
|
||||
s.valueNames = make([][]LocalSlot, f.NumValues())
|
||||
for slot, values := range f.NamedValues {
|
||||
if isSynthetic(&slot) {
|
||||
continue
|
||||
}
|
||||
for _, value := range values {
|
||||
s.valueNames[value.ID] = append(s.valueNames[value.ID], slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, b := range f.Blocks {
|
||||
for _, v := range b.Values {
|
||||
if !v.Type.IsMemory() && !v.Type.IsVoid() && !v.Type.IsFlags() && !v.Type.IsTuple() {
|
||||
@@ -692,7 +723,9 @@ func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
|
||||
|
||||
// Sets the state of the registers to that encoded in regs.
|
||||
func (s *regAllocState) setState(regs []endReg) {
|
||||
s.freeRegs(s.used)
|
||||
for s.used != 0 {
|
||||
s.freeOrResetReg(pickReg(s.used), true)
|
||||
}
|
||||
for _, x := range regs {
|
||||
s.assignReg(x.r, x.v, x.c)
|
||||
}
|
||||
@@ -735,6 +768,9 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
}
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
if s.f.pass.debug > regDebug {
|
||||
fmt.Printf("Begin processing block %v\n", b)
|
||||
}
|
||||
s.curBlock = b
|
||||
|
||||
// Initialize regValLiveSet and uses fields for this block.
|
||||
@@ -830,9 +866,6 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
// This is the complicated case. We have more than one predecessor,
|
||||
// which means we may have Phi ops.
|
||||
|
||||
// Copy phi ops into new schedule.
|
||||
b.Values = append(b.Values, phis...)
|
||||
|
||||
// Start with the final register state of the primary predecessor
|
||||
idx := s.primary[b.ID]
|
||||
if idx < 0 {
|
||||
@@ -910,6 +943,9 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy phi ops into new schedule.
|
||||
b.Values = append(b.Values, phis...)
|
||||
|
||||
// Third pass - pick registers for phis whose inputs
|
||||
// were not in a register.
|
||||
for i, v := range phis {
|
||||
@@ -1005,7 +1041,7 @@ func (s *regAllocState) regalloc(f *Func) {
|
||||
pidx := e.i
|
||||
for _, v := range succ.Values {
|
||||
if v.Op != OpPhi {
|
||||
break
|
||||
continue
|
||||
}
|
||||
if !s.values[v.ID].needReg {
|
||||
continue
|
||||
@@ -1565,6 +1601,9 @@ func (s *regAllocState) placeSpills() {
|
||||
for _, b := range f.Blocks {
|
||||
var m regMask
|
||||
for _, v := range b.Values {
|
||||
if v.Op == OpRegKill {
|
||||
continue
|
||||
}
|
||||
if v.Op != OpPhi {
|
||||
break
|
||||
}
|
||||
@@ -1675,7 +1714,7 @@ func (s *regAllocState) placeSpills() {
|
||||
for _, b := range f.Blocks {
|
||||
nphi := 0
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi {
|
||||
if v.Op != OpRegKill && v.Op != OpPhi {
|
||||
break
|
||||
}
|
||||
nphi++
|
||||
@@ -1800,6 +1839,9 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
|
||||
}
|
||||
// Phis need their args to end up in a specific location.
|
||||
for _, v := range e.b.Values {
|
||||
if v.Op == OpRegKill {
|
||||
continue
|
||||
}
|
||||
if v.Op != OpPhi {
|
||||
break
|
||||
}
|
||||
@@ -1878,6 +1920,7 @@ func (e *edgeState) process() {
|
||||
if e.s.f.pass.debug > regDebug {
|
||||
fmt.Printf("breaking cycle with v%d in %s:%s\n", vid, loc.Name(), c)
|
||||
}
|
||||
e.erase(r)
|
||||
if _, isReg := loc.(*Register); isReg {
|
||||
c = e.p.NewValue1(d.pos, OpCopy, c.Type, c)
|
||||
} else {
|
||||
@@ -1943,6 +1986,18 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
|
||||
}
|
||||
}
|
||||
_, dstReg := loc.(*Register)
|
||||
|
||||
// Pre-clobber destination. This avoids the
|
||||
// following situation:
|
||||
// - v is currently held in R0 and stacktmp0.
|
||||
// - We want to copy stacktmp1 to stacktmp0.
|
||||
// - We choose R0 as the temporary register.
|
||||
// During the copy, both R0 and stacktmp0 are
|
||||
// clobbered, losing both copies of v. Oops!
|
||||
// Erasing the destination early means R0 will not
|
||||
// be chosen as the temp register, as it will then
|
||||
// be the last copy of v.
|
||||
e.erase(loc)
|
||||
var x *Value
|
||||
if c == nil {
|
||||
if !e.s.values[vid].rematerializeable {
|
||||
@@ -1953,8 +2008,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
|
||||
} else {
|
||||
// Rematerialize into stack slot. Need a free
|
||||
// register to accomplish this.
|
||||
e.erase(loc) // see pre-clobber comment below
|
||||
r := e.findRegFor(v.Type)
|
||||
e.erase(r)
|
||||
x = v.copyIntoNoXPos(e.p)
|
||||
e.set(r, vid, x, false, pos)
|
||||
// Make sure we spill with the size of the slot, not the
|
||||
@@ -1976,20 +2031,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
|
||||
x = e.p.NewValue1(pos, OpLoadReg, c.Type, c)
|
||||
} else {
|
||||
// mem->mem. Use temp register.
|
||||
|
||||
// Pre-clobber destination. This avoids the
|
||||
// following situation:
|
||||
// - v is currently held in R0 and stacktmp0.
|
||||
// - We want to copy stacktmp1 to stacktmp0.
|
||||
// - We choose R0 as the temporary register.
|
||||
// During the copy, both R0 and stacktmp0 are
|
||||
// clobbered, losing both copies of v. Oops!
|
||||
// Erasing the destination early means R0 will not
|
||||
// be chosen as the temp register, as it will then
|
||||
// be the last copy of v.
|
||||
e.erase(loc)
|
||||
|
||||
r := e.findRegFor(c.Type)
|
||||
e.erase(r)
|
||||
t := e.p.NewValue1(pos, OpLoadReg, c.Type, c)
|
||||
e.set(r, vid, t, false, pos)
|
||||
x = e.p.NewValue1(pos, OpStoreReg, loc.(LocalSlot).Type, t)
|
||||
@@ -2008,7 +2051,6 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
|
||||
// set changes the contents of location loc to hold the given value and its cached representative.
|
||||
func (e *edgeState) set(loc Location, vid ID, c *Value, final bool, pos src.XPos) {
|
||||
e.s.f.setHome(c, loc)
|
||||
e.erase(loc)
|
||||
e.contents[loc] = contentRecord{vid, c, final, pos}
|
||||
a := e.cache[vid]
|
||||
if len(a) == 0 {
|
||||
@@ -2059,6 +2101,16 @@ func (e *edgeState) erase(loc Location) {
|
||||
fmt.Printf("v%d no longer available in %s:%s\n", vid, loc.Name(), c)
|
||||
}
|
||||
a[i], a = a[len(a)-1], a[:len(a)-1]
|
||||
if e.s.f.Config.ctxt.Flag_locationlists {
|
||||
if _, isReg := loc.(*Register); isReg && int(c.ID) < len(e.s.valueNames) && len(e.s.valueNames[c.ID]) != 0 {
|
||||
kill := e.p.NewValue0(src.NoXPos, OpRegKill, types.TypeVoid)
|
||||
e.s.f.setHome(kill, loc)
|
||||
for _, name := range e.s.valueNames[c.ID] {
|
||||
e.s.f.NamedValues[name] = append(e.s.f.NamedValues[name], kill)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2118,8 +2170,8 @@ func (e *edgeState) findRegFor(typ *types.Type) Location {
|
||||
// Allocate a temp location to spill a register to.
|
||||
// The type of the slot is immaterial - it will not be live across
|
||||
// any safepoint. Just use a type big enough to hold any register.
|
||||
t := LocalSlot{e.s.f.fe.Auto(c.Pos, types.Int64), types.Int64, 0}
|
||||
// TODO: reuse these slots.
|
||||
t := LocalSlot{N: e.s.f.fe.Auto(c.Pos, types.Int64), Type: types.Int64}
|
||||
// TODO: reuse these slots. They'll need to be erased first.
|
||||
e.set(t, vid, x, false, c.Pos)
|
||||
if e.s.f.pass.debug > regDebug {
|
||||
fmt.Printf(" SPILL %s->%s %s\n", r.Name(), t.Name(), x.LongString())
|
||||
|
||||
@@ -24,6 +24,7 @@ func TestSizeof(t *testing.T) {
|
||||
}{
|
||||
{Value{}, 68, 112},
|
||||
{Block{}, 152, 288},
|
||||
{LocalSlot{}, 32, 48},
|
||||
{valState{}, 28, 40},
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func (s *stackAllocState) stackalloc() {
|
||||
if v.Op != OpArg {
|
||||
continue
|
||||
}
|
||||
loc := LocalSlot{v.Aux.(GCNode), v.Type, v.AuxInt}
|
||||
loc := LocalSlot{N: v.Aux.(GCNode), Type: v.Type, Off: v.AuxInt}
|
||||
if f.pass.debug > stackDebug {
|
||||
fmt.Printf("stackalloc %s to %s\n", v, loc.Name())
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"cmd/internal/src"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Value represents a value in the SSA representation of the program.
|
||||
@@ -98,7 +99,7 @@ func (v *Value) AuxValAndOff() ValAndOff {
|
||||
return ValAndOff(v.AuxInt)
|
||||
}
|
||||
|
||||
// long form print. v# = opcode <type> [aux] args [: reg]
|
||||
// long form print. v# = opcode <type> [aux] args [: reg] (names)
|
||||
func (v *Value) LongString() string {
|
||||
s := fmt.Sprintf("v%d = %s", v.ID, v.Op)
|
||||
s += " <" + v.Type.String() + ">"
|
||||
@@ -110,6 +111,18 @@ func (v *Value) LongString() string {
|
||||
if int(v.ID) < len(r) && r[v.ID] != nil {
|
||||
s += " : " + r[v.ID].Name()
|
||||
}
|
||||
var names []string
|
||||
for name, values := range v.Block.Func.NamedValues {
|
||||
for _, value := range values {
|
||||
if value == v {
|
||||
names = append(names, name.Name())
|
||||
break // drop duplicates.
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(names) != 0 {
|
||||
s += " (" + strings.Join(names, ", ") + ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
|
||||
const InfoPrefix = "go.info."
|
||||
|
||||
// RangePrefix is the prefix for all the symbols containing DWARF location lists.
|
||||
const LocPrefix = "go.loc."
|
||||
|
||||
// RangePrefix is the prefix for all the symbols containing DWARF range lists.
|
||||
const RangePrefix = "go.range."
|
||||
|
||||
@@ -23,13 +26,31 @@ type Sym interface {
|
||||
Len() int64
|
||||
}
|
||||
|
||||
// A Location represents a variable's location at a particular PC range.
|
||||
// It becomes a location list entry in the DWARF.
|
||||
type Location struct {
|
||||
StartPC, EndPC int64
|
||||
Pieces []Piece
|
||||
}
|
||||
|
||||
// A Piece represents the location of a particular part of a variable.
|
||||
// It becomes part of a location list entry (a DW_OP_piece) in the DWARF.
|
||||
type Piece struct {
|
||||
Length int64
|
||||
StackOffset int32
|
||||
RegNum int16
|
||||
Missing bool
|
||||
OnStack bool // if true, RegNum is unset.
|
||||
}
|
||||
|
||||
// A Var represents a local variable or a function parameter.
|
||||
type Var struct {
|
||||
Name string
|
||||
Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
|
||||
Offset int32
|
||||
Scope int32
|
||||
Type Sym
|
||||
Name string
|
||||
Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
|
||||
StackOffset int32
|
||||
LocationList []Location
|
||||
Scope int32
|
||||
Type Sym
|
||||
}
|
||||
|
||||
// A Scope represents a lexical scope. All variables declared within a
|
||||
@@ -205,7 +226,7 @@ const (
|
||||
)
|
||||
|
||||
// Index into the abbrevs table below.
|
||||
// Keep in sync with ispubname() and ispubtype() below.
|
||||
// Keep in sync with ispubname() and ispubtype() in ld/dwarf.go.
|
||||
// ispubtype considers >= NULLTYPE public
|
||||
const (
|
||||
DW_ABRV_NULL = iota
|
||||
@@ -213,7 +234,9 @@ const (
|
||||
DW_ABRV_FUNCTION
|
||||
DW_ABRV_VARIABLE
|
||||
DW_ABRV_AUTO
|
||||
DW_ABRV_AUTO_LOCLIST
|
||||
DW_ABRV_PARAM
|
||||
DW_ABRV_PARAM_LOCLIST
|
||||
DW_ABRV_LEXICAL_BLOCK_RANGES
|
||||
DW_ABRV_LEXICAL_BLOCK_SIMPLE
|
||||
DW_ABRV_STRUCTFIELD
|
||||
@@ -297,6 +320,17 @@ var abbrevs = [DW_NABRV]dwAbbrev{
|
||||
},
|
||||
},
|
||||
|
||||
/* AUTO_LOCLIST */
|
||||
{
|
||||
DW_TAG_variable,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_location, DW_FORM_sec_offset},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* PARAM */
|
||||
{
|
||||
DW_TAG_formal_parameter,
|
||||
@@ -307,6 +341,18 @@ var abbrevs = [DW_NABRV]dwAbbrev{
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* PARAM_LOCLIST */
|
||||
{
|
||||
DW_TAG_formal_parameter,
|
||||
DW_CHILDREN_no,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_name, DW_FORM_string},
|
||||
{DW_AT_location, DW_FORM_sec_offset},
|
||||
{DW_AT_type, DW_FORM_ref_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* LEXICAL_BLOCK_RANGES */
|
||||
{
|
||||
DW_TAG_lexical_block,
|
||||
@@ -684,31 +730,30 @@ func HasChildren(die *DWDie) bool {
|
||||
|
||||
// PutFunc writes a DIE for a function to s.
|
||||
// It also writes child DIEs for each variable in vars.
|
||||
func PutFunc(ctxt Context, s, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
|
||||
Uleb128put(ctxt, s, DW_ABRV_FUNCTION)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
|
||||
func PutFunc(ctxt Context, info, loc, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
|
||||
Uleb128put(ctxt, info, DW_ABRV_FUNCTION)
|
||||
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
||||
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
|
||||
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC)
|
||||
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
|
||||
var ev int64
|
||||
if external {
|
||||
ev = 1
|
||||
}
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
||||
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
||||
if len(scopes) > 0 {
|
||||
var encbuf [20]byte
|
||||
if putscope(ctxt, s, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
|
||||
if putscope(ctxt, info, loc, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
|
||||
return errors.New("multiple toplevel scopes")
|
||||
}
|
||||
}
|
||||
|
||||
Uleb128put(ctxt, s, 0)
|
||||
Uleb128put(ctxt, info, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
|
||||
func putscope(ctxt Context, info, loc, ranges, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
|
||||
for _, v := range scopes[curscope].Vars {
|
||||
putvar(ctxt, s, v, encbuf)
|
||||
putvar(ctxt, info, loc, v, startPC, encbuf)
|
||||
}
|
||||
this := curscope
|
||||
curscope++
|
||||
@@ -719,12 +764,12 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
|
||||
}
|
||||
|
||||
if len(scope.Ranges) == 1 {
|
||||
Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, startPC)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, startPC)
|
||||
Uleb128put(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
|
||||
putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, startPC)
|
||||
putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, startPC)
|
||||
} else {
|
||||
Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, ranges.Len(), ranges)
|
||||
Uleb128put(ctxt, info, DW_ABRV_LEXICAL_BLOCK_RANGES)
|
||||
putattr(ctxt, info, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_sec_offset, DW_CLS_PTR, ranges.Len(), ranges)
|
||||
|
||||
ctxt.AddAddress(ranges, nil, -1)
|
||||
ctxt.AddAddress(ranges, startPC, 0)
|
||||
@@ -736,32 +781,72 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
|
||||
ctxt.AddAddress(ranges, nil, 0)
|
||||
}
|
||||
|
||||
curscope = putscope(ctxt, s, ranges, startPC, curscope, scopes, encbuf)
|
||||
curscope = putscope(ctxt, info, loc, ranges, startPC, curscope, scopes, encbuf)
|
||||
|
||||
Uleb128put(ctxt, s, 0)
|
||||
Uleb128put(ctxt, info, 0)
|
||||
}
|
||||
return curscope
|
||||
}
|
||||
|
||||
func putvar(ctxt Context, s Sym, v *Var, encbuf []byte) {
|
||||
func putvar(ctxt Context, info, loc Sym, v *Var, startPC Sym, encbuf []byte) {
|
||||
n := v.Name
|
||||
|
||||
Uleb128put(ctxt, s, int64(v.Abbrev))
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
||||
loc := append(encbuf[:0], DW_OP_call_frame_cfa)
|
||||
if v.Offset != 0 {
|
||||
loc = append(loc, DW_OP_consts)
|
||||
loc = AppendSleb128(loc, int64(v.Offset))
|
||||
loc = append(loc, DW_OP_plus)
|
||||
Uleb128put(ctxt, info, int64(v.Abbrev))
|
||||
putattr(ctxt, info, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
||||
if v.Abbrev == DW_ABRV_AUTO_LOCLIST || v.Abbrev == DW_ABRV_PARAM_LOCLIST {
|
||||
putattr(ctxt, info, v.Abbrev, DW_FORM_sec_offset, DW_CLS_PTR, int64(loc.Len()), loc)
|
||||
addLocList(ctxt, loc, startPC, v, encbuf)
|
||||
} else {
|
||||
loc := append(encbuf[:0], DW_OP_call_frame_cfa)
|
||||
if v.StackOffset != 0 {
|
||||
loc = append(loc, DW_OP_consts)
|
||||
loc = AppendSleb128(loc, int64(v.StackOffset))
|
||||
loc = append(loc, DW_OP_plus)
|
||||
}
|
||||
putattr(ctxt, info, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
|
||||
}
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
||||
putattr(ctxt, info, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
||||
}
|
||||
|
||||
func addLocList(ctxt Context, listSym, startPC Sym, v *Var, encbuf []byte) {
|
||||
// Base address entry: max ptr followed by the base address.
|
||||
ctxt.AddInt(listSym, ctxt.PtrSize(), ^0)
|
||||
ctxt.AddAddress(listSym, startPC, 0)
|
||||
for _, entry := range v.LocationList {
|
||||
ctxt.AddInt(listSym, ctxt.PtrSize(), entry.StartPC)
|
||||
ctxt.AddInt(listSym, ctxt.PtrSize(), entry.EndPC)
|
||||
locBuf := encbuf[:0]
|
||||
for _, piece := range entry.Pieces {
|
||||
if !piece.Missing {
|
||||
if piece.OnStack {
|
||||
locBuf = append(locBuf, DW_OP_fbreg)
|
||||
locBuf = AppendSleb128(locBuf, int64(piece.StackOffset))
|
||||
} else {
|
||||
if piece.RegNum < 32 {
|
||||
locBuf = append(locBuf, DW_OP_reg0+byte(piece.RegNum))
|
||||
} else {
|
||||
locBuf = append(locBuf, DW_OP_regx)
|
||||
locBuf = AppendUleb128(locBuf, uint64(piece.RegNum))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(entry.Pieces) > 1 {
|
||||
locBuf = append(locBuf, DW_OP_piece)
|
||||
locBuf = AppendUleb128(locBuf, uint64(piece.Length))
|
||||
}
|
||||
}
|
||||
ctxt.AddInt(listSym, 2, int64(len(locBuf)))
|
||||
ctxt.AddBytes(listSym, locBuf)
|
||||
}
|
||||
// End list
|
||||
ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
|
||||
ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
|
||||
}
|
||||
|
||||
// VarsByOffset attaches the methods of sort.Interface to []*Var,
|
||||
// sorting in increasing Offset.
|
||||
// sorting in increasing StackOffset.
|
||||
type VarsByOffset []*Var
|
||||
|
||||
func (s VarsByOffset) Len() int { return len(s) }
|
||||
func (s VarsByOffset) Less(i, j int) bool { return s[i].Offset < s[j].Offset }
|
||||
func (s VarsByOffset) Less(i, j int) bool { return s[i].StackOffset < s[j].StackOffset }
|
||||
func (s VarsByOffset) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
@@ -330,7 +330,8 @@ type FuncInfo struct {
|
||||
Autom []*Auto
|
||||
Pcln Pcln
|
||||
|
||||
dwarfSym *LSym
|
||||
dwarfInfoSym *LSym
|
||||
dwarfLocSym *LSym
|
||||
dwarfRangesSym *LSym
|
||||
|
||||
GCArgs LSym
|
||||
@@ -476,25 +477,26 @@ type Pcdata struct {
|
||||
// Link holds the context for writing object code from a compiler
|
||||
// to be linker input or for reading that input into the linker.
|
||||
type Link struct {
|
||||
Headtype objabi.HeadType
|
||||
Arch *LinkArch
|
||||
Debugasm bool
|
||||
Debugvlog bool
|
||||
Debugpcln string
|
||||
Flag_shared bool
|
||||
Flag_dynlink bool
|
||||
Flag_optimize bool
|
||||
Bso *bufio.Writer
|
||||
Pathname string
|
||||
hashmu sync.Mutex // protects hash
|
||||
hash map[string]*LSym // name -> sym mapping
|
||||
statichash map[string]*LSym // name -> sym mapping for static syms
|
||||
PosTable src.PosTable
|
||||
InlTree InlTree // global inlining tree used by gc/inl.go
|
||||
Imports []string
|
||||
DiagFunc func(string, ...interface{})
|
||||
DebugInfo func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
|
||||
Errors int
|
||||
Headtype objabi.HeadType
|
||||
Arch *LinkArch
|
||||
Debugasm bool
|
||||
Debugvlog bool
|
||||
Debugpcln string
|
||||
Flag_shared bool
|
||||
Flag_dynlink bool
|
||||
Flag_optimize bool
|
||||
Flag_locationlists bool
|
||||
Bso *bufio.Writer
|
||||
Pathname string
|
||||
hashmu sync.Mutex // protects hash
|
||||
hash map[string]*LSym // name -> sym mapping
|
||||
statichash map[string]*LSym // name -> sym mapping for static syms
|
||||
PosTable src.PosTable
|
||||
InlTree InlTree // global inlining tree used by gc/inl.go
|
||||
Imports []string
|
||||
DiagFunc func(string, ...interface{})
|
||||
DebugInfo func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
|
||||
Errors int
|
||||
|
||||
Framepointer_enabled bool
|
||||
|
||||
@@ -533,9 +535,10 @@ func (ctxt *Link) FixedFrameSize() int64 {
|
||||
// LinkArch is the definition of a single architecture.
|
||||
type LinkArch struct {
|
||||
*sys.Arch
|
||||
Init func(*Link)
|
||||
Preprocess func(*Link, *LSym, ProgAlloc)
|
||||
Assemble func(*Link, *LSym, ProgAlloc)
|
||||
Progedit func(*Link, *Prog, ProgAlloc)
|
||||
UnaryDst map[As]bool // Instruction takes one operand, a destination.
|
||||
Init func(*Link)
|
||||
Preprocess func(*Link, *LSym, ProgAlloc)
|
||||
Assemble func(*Link, *LSym, ProgAlloc)
|
||||
Progedit func(*Link, *Prog, ProgAlloc)
|
||||
UnaryDst map[As]bool // Instruction takes one operand, a destination.
|
||||
DWARFRegisters map[int16]int16
|
||||
}
|
||||
|
||||
@@ -465,15 +465,18 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
|
||||
}
|
||||
|
||||
// dwarfSym returns the DWARF symbols for TEXT symbol.
|
||||
func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfRangesSym *LSym) {
|
||||
func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym *LSym) {
|
||||
if s.Type != objabi.STEXT {
|
||||
ctxt.Diag("dwarfSym of non-TEXT %v", s)
|
||||
}
|
||||
if s.Func.dwarfSym == nil {
|
||||
s.Func.dwarfSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
|
||||
if s.Func.dwarfInfoSym == nil {
|
||||
s.Func.dwarfInfoSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
|
||||
if ctxt.Flag_locationlists {
|
||||
s.Func.dwarfLocSym = ctxt.LookupDerived(s, dwarf.LocPrefix+s.Name)
|
||||
}
|
||||
s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
|
||||
}
|
||||
return s.Func.dwarfSym, s.Func.dwarfRangesSym
|
||||
return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym
|
||||
}
|
||||
|
||||
func (s *LSym) Len() int64 {
|
||||
@@ -483,15 +486,15 @@ func (s *LSym) Len() int64 {
|
||||
// populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s.
|
||||
// The DWARFs symbol must already have been initialized in InitTextSym.
|
||||
func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) {
|
||||
dsym, drsym := ctxt.dwarfSym(s)
|
||||
if dsym.Size != 0 {
|
||||
info, loc, ranges := ctxt.dwarfSym(s)
|
||||
if info.Size != 0 {
|
||||
ctxt.Diag("makeFuncDebugEntry double process %v", s)
|
||||
}
|
||||
var scopes []dwarf.Scope
|
||||
if ctxt.DebugInfo != nil {
|
||||
scopes = ctxt.DebugInfo(s, curfn)
|
||||
}
|
||||
err := dwarf.PutFunc(dwCtxt{ctxt}, dsym, drsym, s.Name, !s.Static(), s, s.Size, scopes)
|
||||
err := dwarf.PutFunc(dwCtxt{ctxt}, info, loc, ranges, s.Name, !s.Static(), s, s.Size, scopes)
|
||||
if err != nil {
|
||||
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
|
||||
}
|
||||
|
||||
@@ -136,13 +136,17 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
|
||||
ctxt.Text = append(ctxt.Text, s)
|
||||
|
||||
// Set up DWARF entries for s.
|
||||
dsym, drsym := ctxt.dwarfSym(s)
|
||||
dsym.Type = objabi.SDWARFINFO
|
||||
dsym.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
drsym.Type = objabi.SDWARFRANGE
|
||||
drsym.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
ctxt.Data = append(ctxt.Data, dsym)
|
||||
ctxt.Data = append(ctxt.Data, drsym)
|
||||
info, loc, ranges := ctxt.dwarfSym(s)
|
||||
info.Type = objabi.SDWARFINFO
|
||||
info.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
if loc != nil {
|
||||
loc.Type = objabi.SDWARFLOC
|
||||
loc.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
ctxt.Data = append(ctxt.Data, loc)
|
||||
}
|
||||
ranges.Type = objabi.SDWARFRANGE
|
||||
ranges.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
ctxt.Data = append(ctxt.Data, info, ranges)
|
||||
|
||||
// Set up the function's gcargs and gclocals.
|
||||
// They will be filled in later if needed.
|
||||
|
||||
@@ -1006,3 +1006,120 @@ const (
|
||||
T_64 = 1 << 6
|
||||
T_GOTYPE = 1 << 7
|
||||
)
|
||||
|
||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf, figure 3.36
|
||||
var AMD64DWARFRegisters = map[int16]int16{
|
||||
REG_AX: 0,
|
||||
REG_DX: 1,
|
||||
REG_CX: 2,
|
||||
REG_BX: 3,
|
||||
REG_SI: 4,
|
||||
REG_DI: 5,
|
||||
REG_BP: 6,
|
||||
REG_SP: 7,
|
||||
REG_R8: 8,
|
||||
REG_R9: 9,
|
||||
REG_R10: 10,
|
||||
REG_R11: 11,
|
||||
REG_R12: 12,
|
||||
REG_R13: 13,
|
||||
REG_R14: 14,
|
||||
REG_R15: 15,
|
||||
// 16 is "Return Address RA", whatever that is.
|
||||
// XMM registers. %xmmN => XN.
|
||||
REG_X0: 17,
|
||||
REG_X1: 18,
|
||||
REG_X2: 19,
|
||||
REG_X3: 20,
|
||||
REG_X4: 21,
|
||||
REG_X5: 22,
|
||||
REG_X6: 23,
|
||||
REG_X7: 24,
|
||||
REG_X8: 25,
|
||||
REG_X9: 26,
|
||||
REG_X10: 27,
|
||||
REG_X11: 28,
|
||||
REG_X12: 29,
|
||||
REG_X13: 30,
|
||||
REG_X14: 31,
|
||||
REG_X15: 32,
|
||||
// ST registers. %stN => FN.
|
||||
REG_F0: 33,
|
||||
REG_F1: 34,
|
||||
REG_F2: 35,
|
||||
REG_F3: 36,
|
||||
REG_F4: 37,
|
||||
REG_F5: 38,
|
||||
REG_F6: 39,
|
||||
REG_F7: 40,
|
||||
// MMX registers. %mmN => MN.
|
||||
REG_M0: 41,
|
||||
REG_M1: 42,
|
||||
REG_M2: 43,
|
||||
REG_M3: 44,
|
||||
REG_M4: 45,
|
||||
REG_M5: 46,
|
||||
REG_M6: 47,
|
||||
REG_M7: 48,
|
||||
// 48 is flags, which doesn't have a name.
|
||||
REG_ES: 50,
|
||||
REG_CS: 51,
|
||||
REG_SS: 52,
|
||||
REG_DS: 53,
|
||||
REG_FS: 54,
|
||||
REG_GS: 55,
|
||||
// 58 and 59 are {fs,gs}base, which don't have names.
|
||||
REG_TR: 62,
|
||||
REG_LDTR: 63,
|
||||
// 64-66 are mxcsr, fcw, fsw, which don't have names.
|
||||
}
|
||||
|
||||
// https://www.uclibc.org/docs/psABI-i386.pdf, table 2.14
|
||||
var X86DWARFRegisters = map[int16]int16{
|
||||
REG_AX: 0,
|
||||
REG_CX: 1,
|
||||
REG_DX: 2,
|
||||
REG_BX: 3,
|
||||
REG_SP: 4,
|
||||
REG_BP: 5,
|
||||
REG_SI: 6,
|
||||
REG_DI: 7,
|
||||
// 8 is "Return Address RA", whatever that is.
|
||||
// 9 is flags, which doesn't have a name.
|
||||
// ST registers. %stN => FN.
|
||||
REG_F0: 11,
|
||||
REG_F1: 12,
|
||||
REG_F2: 13,
|
||||
REG_F3: 14,
|
||||
REG_F4: 15,
|
||||
REG_F5: 16,
|
||||
REG_F6: 17,
|
||||
REG_F7: 18,
|
||||
// XMM registers. %xmmN => XN.
|
||||
REG_X0: 21,
|
||||
REG_X1: 22,
|
||||
REG_X2: 23,
|
||||
REG_X3: 24,
|
||||
REG_X4: 25,
|
||||
REG_X5: 26,
|
||||
REG_X6: 27,
|
||||
REG_X7: 28,
|
||||
// MMX registers. %mmN => MN.
|
||||
REG_M0: 29,
|
||||
REG_M1: 30,
|
||||
REG_M2: 31,
|
||||
REG_M3: 32,
|
||||
REG_M4: 33,
|
||||
REG_M5: 34,
|
||||
REG_M6: 35,
|
||||
REG_M7: 36,
|
||||
// 39 is mxcsr, which doesn't have a name.
|
||||
REG_ES: 40,
|
||||
REG_CS: 41,
|
||||
REG_SS: 42,
|
||||
REG_DS: 43,
|
||||
REG_FS: 44,
|
||||
REG_GS: 45,
|
||||
REG_TR: 48,
|
||||
REG_LDTR: 49,
|
||||
}
|
||||
|
||||
@@ -1231,28 +1231,31 @@ var unaryDst = map[obj.As]bool{
|
||||
}
|
||||
|
||||
var Linkamd64 = obj.LinkArch{
|
||||
Arch: sys.ArchAMD64,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
Arch: sys.ArchAMD64,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
DWARFRegisters: AMD64DWARFRegisters,
|
||||
}
|
||||
|
||||
var Linkamd64p32 = obj.LinkArch{
|
||||
Arch: sys.ArchAMD64P32,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
Arch: sys.ArchAMD64P32,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
DWARFRegisters: AMD64DWARFRegisters,
|
||||
}
|
||||
|
||||
var Link386 = obj.LinkArch{
|
||||
Arch: sys.Arch386,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
Arch: sys.Arch386,
|
||||
Init: instinit,
|
||||
Preprocess: preprocess,
|
||||
Assemble: span6,
|
||||
Progedit: progedit,
|
||||
UnaryDst: unaryDst,
|
||||
DWARFRegisters: AMD64DWARFRegisters,
|
||||
}
|
||||
|
||||
@@ -57,4 +57,5 @@ const (
|
||||
// Debugging data
|
||||
SDWARFINFO
|
||||
SDWARFRANGE
|
||||
SDWARFLOC
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@ package objabi
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE"
|
||||
const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGESDWARFLOC"
|
||||
|
||||
var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72}
|
||||
var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72, 81}
|
||||
|
||||
func (i SymKind) String() string {
|
||||
if i >= SymKind(len(_SymKind_index)-1) {
|
||||
|
||||
@@ -592,15 +592,7 @@ func relocsym(ctxt *Link, s *Symbol) {
|
||||
}
|
||||
|
||||
case objabi.R_DWARFREF:
|
||||
var sectName string
|
||||
var vaddr int64
|
||||
switch {
|
||||
case r.Sym.Sect != nil:
|
||||
sectName = r.Sym.Sect.Name
|
||||
vaddr = int64(r.Sym.Sect.Vaddr)
|
||||
case r.Sym.Type == SDWARFRANGE:
|
||||
sectName = ".debug_ranges"
|
||||
default:
|
||||
if r.Sym.Sect == nil {
|
||||
Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
|
||||
}
|
||||
|
||||
@@ -615,8 +607,8 @@ func relocsym(ctxt *Link, s *Symbol) {
|
||||
r.Type = objabi.R_ADDR
|
||||
}
|
||||
|
||||
r.Xsym = ctxt.Syms.ROLookup(sectName, 0)
|
||||
r.Xadd = r.Add + Symaddr(r.Sym) - vaddr
|
||||
r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
|
||||
r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
|
||||
|
||||
o = r.Xadd
|
||||
rs = r.Xsym
|
||||
@@ -625,7 +617,7 @@ func relocsym(ctxt *Link, s *Symbol) {
|
||||
}
|
||||
break
|
||||
}
|
||||
o = Symaddr(r.Sym) + r.Add - vaddr
|
||||
o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
|
||||
|
||||
case objabi.R_WEAKADDROFF:
|
||||
if !r.Sym.Attr.Reachable() {
|
||||
@@ -1843,9 +1835,9 @@ func (ctxt *Link) dodata() {
|
||||
|
||||
dwarfgeneratedebugsyms(ctxt)
|
||||
|
||||
var s *Symbol
|
||||
var i int
|
||||
for i, s = range dwarfp {
|
||||
for ; i < len(dwarfp); i++ {
|
||||
s := dwarfp[i]
|
||||
if s.Type != SDWARFSECT {
|
||||
break
|
||||
}
|
||||
@@ -1862,13 +1854,26 @@ func (ctxt *Link) dodata() {
|
||||
}
|
||||
checkdatsize(ctxt, datsize, SDWARFSECT)
|
||||
|
||||
if i < len(dwarfp) {
|
||||
sect = addsection(&Segdwarf, ".debug_info", 04)
|
||||
for i < len(dwarfp) {
|
||||
curType := dwarfp[i].Type
|
||||
var sect *Section
|
||||
switch curType {
|
||||
case SDWARFINFO:
|
||||
sect = addsection(&Segdwarf, ".debug_info", 04)
|
||||
case SDWARFRANGE:
|
||||
sect = addsection(&Segdwarf, ".debug_ranges", 04)
|
||||
case SDWARFLOC:
|
||||
sect = addsection(&Segdwarf, ".debug_loc", 04)
|
||||
default:
|
||||
Errorf(dwarfp[i], "unknown DWARF section %v", curType)
|
||||
}
|
||||
|
||||
sect.Align = 1
|
||||
datsize = Rnd(datsize, int64(sect.Align))
|
||||
sect.Vaddr = uint64(datsize)
|
||||
for _, s := range dwarfp[i:] {
|
||||
if s.Type != SDWARFINFO {
|
||||
for ; i < len(dwarfp); i++ {
|
||||
s := dwarfp[i]
|
||||
if s.Type != curType {
|
||||
break
|
||||
}
|
||||
s.Sect = sect
|
||||
@@ -1878,7 +1883,7 @@ func (ctxt *Link) dodata() {
|
||||
datsize += s.Size
|
||||
}
|
||||
sect.Length = uint64(datsize) - sect.Vaddr
|
||||
checkdatsize(ctxt, datsize, SDWARFINFO)
|
||||
checkdatsize(ctxt, datsize, curType)
|
||||
}
|
||||
|
||||
/* number the sections */
|
||||
|
||||
@@ -67,26 +67,15 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
|
||||
r.Add = ofs
|
||||
}
|
||||
|
||||
/*
|
||||
* Offsets and sizes of the debug_* sections in the cout file.
|
||||
*/
|
||||
var abbrevsym *Symbol
|
||||
var arangessec *Symbol
|
||||
var framesec *Symbol
|
||||
var infosec *Symbol
|
||||
var linesec *Symbol
|
||||
var rangesec *Symbol
|
||||
|
||||
var gdbscript string
|
||||
|
||||
var dwarfp []*Symbol
|
||||
|
||||
func writeabbrev(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
func writeabbrev(ctxt *Link) *Symbol {
|
||||
s := ctxt.Syms.Lookup(".debug_abbrev", 0)
|
||||
s.Type = SDWARFSECT
|
||||
abbrevsym = s
|
||||
Addbytes(s, dwarf.GetAbbrev())
|
||||
return append(syms, s)
|
||||
return s
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -993,13 +982,10 @@ func getCompilationDir() string {
|
||||
|
||||
func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
|
||||
var dwarfctxt dwarf.Context = dwctxt{ctxt}
|
||||
if linesec == nil {
|
||||
linesec = ctxt.Syms.Lookup(".debug_line", 0)
|
||||
}
|
||||
linesec.Type = SDWARFSECT
|
||||
linesec.R = linesec.R[:0]
|
||||
ls := ctxt.Syms.Lookup(".debug_line", 0)
|
||||
ls.Type = SDWARFSECT
|
||||
ls.R = ls.R[:0]
|
||||
|
||||
ls := linesec
|
||||
syms = append(syms, ls)
|
||||
var funcs []*Symbol
|
||||
|
||||
@@ -1019,7 +1005,7 @@ func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
|
||||
|
||||
dwinfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, "go", 0)
|
||||
newattr(dwinfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(lang), 0)
|
||||
newattr(dwinfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, linesec)
|
||||
newattr(dwinfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, ls)
|
||||
newattr(dwinfo, dwarf.DW_AT_low_pc, dwarf.DW_CLS_ADDRESS, s.Value, s)
|
||||
// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
|
||||
compDir := getCompilationDir()
|
||||
@@ -1178,12 +1164,9 @@ func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte {
|
||||
|
||||
func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
var dwarfctxt dwarf.Context = dwctxt{ctxt}
|
||||
if framesec == nil {
|
||||
framesec = ctxt.Syms.Lookup(".debug_frame", 0)
|
||||
}
|
||||
framesec.Type = SDWARFSECT
|
||||
framesec.R = framesec.R[:0]
|
||||
fs := framesec
|
||||
fs := ctxt.Syms.Lookup(".debug_frame", 0)
|
||||
fs.Type = SDWARFSECT
|
||||
fs.R = fs.R[:0]
|
||||
syms = append(syms, fs)
|
||||
|
||||
// Emit the CIE, Section 6.4.1
|
||||
@@ -1280,7 +1263,7 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
// ptrsize: address range
|
||||
Adduint32(ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself)
|
||||
if Linkmode == LinkExternal {
|
||||
adddwarfref(ctxt, fs, framesec, 4)
|
||||
adddwarfref(ctxt, fs, fs, 4)
|
||||
} else {
|
||||
Adduint32(ctxt, fs, 0) // CIE offset
|
||||
}
|
||||
@@ -1292,27 +1275,24 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
}
|
||||
|
||||
func writeranges(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
if rangesec == nil {
|
||||
rangesec = ctxt.Syms.Lookup(".debug_ranges", 0)
|
||||
}
|
||||
rangesec.Type = SDWARFSECT
|
||||
rangesec.Attr |= AttrReachable
|
||||
rangesec.R = rangesec.R[:0]
|
||||
|
||||
empty := true
|
||||
for _, s := range ctxt.Textp {
|
||||
rangeSym := ctxt.Syms.Lookup(dwarf.RangePrefix+s.Name, int(s.Version))
|
||||
rangeSym.Attr |= AttrReachable
|
||||
rangeSym.Type = SDWARFRANGE
|
||||
rangeSym.Value = rangesec.Size
|
||||
rangesec.P = append(rangesec.P, rangeSym.P...)
|
||||
for _, r := range rangeSym.R {
|
||||
r.Off += int32(rangesec.Size)
|
||||
rangesec.R = append(rangesec.R, r)
|
||||
if rangeSym.Size == 0 {
|
||||
continue
|
||||
}
|
||||
rangesec.Size += rangeSym.Size
|
||||
rangeSym.Attr |= AttrReachable | AttrNotInSymbolTable
|
||||
rangeSym.Type = SDWARFRANGE
|
||||
syms = append(syms, rangeSym)
|
||||
empty = false
|
||||
}
|
||||
if rangesec.Size > 0 {
|
||||
if !empty {
|
||||
// PE does not like empty sections
|
||||
rangesec := ctxt.Syms.Lookup(".debug_ranges", 0)
|
||||
rangesec.Type = SDWARFRANGE
|
||||
rangesec.Attr |= AttrReachable
|
||||
rangesec.R = rangesec.R[:0]
|
||||
|
||||
syms = append(syms, rangesec)
|
||||
}
|
||||
return syms
|
||||
@@ -1325,18 +1305,14 @@ const (
|
||||
COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
|
||||
)
|
||||
|
||||
func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
|
||||
if infosec == nil {
|
||||
infosec = ctxt.Syms.Lookup(".debug_info", 0)
|
||||
}
|
||||
func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol, abbrevsym *Symbol) []*Symbol {
|
||||
infosec := ctxt.Syms.Lookup(".debug_info", 0)
|
||||
infosec.R = infosec.R[:0]
|
||||
infosec.Type = SDWARFINFO
|
||||
infosec.Attr |= AttrReachable
|
||||
syms = append(syms, infosec)
|
||||
|
||||
if arangessec == nil {
|
||||
arangessec = ctxt.Syms.Lookup(".dwarfaranges", 0)
|
||||
}
|
||||
arangessec := ctxt.Syms.Lookup(".dwarfaranges", 0)
|
||||
arangessec.R = arangessec.R[:0]
|
||||
|
||||
var dwarfctxt dwarf.Context = dwctxt{ctxt}
|
||||
@@ -1577,10 +1553,10 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
|
||||
|
||||
genasmsym(ctxt, defdwsymb)
|
||||
|
||||
syms := writeabbrev(ctxt, nil)
|
||||
abbrev := writeabbrev(ctxt)
|
||||
syms := []*Symbol{abbrev}
|
||||
syms, funcs := writelines(ctxt, syms)
|
||||
syms = writeframes(ctxt, syms)
|
||||
syms = writeranges(ctxt, syms)
|
||||
|
||||
synthesizestringtypes(ctxt, dwtypes.Child)
|
||||
synthesizeslicetypes(ctxt, dwtypes.Child)
|
||||
@@ -1596,16 +1572,42 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
|
||||
|
||||
// Need to reorder symbols so SDWARFINFO is after all SDWARFSECT
|
||||
// (but we need to generate dies before writepub)
|
||||
infosyms := writeinfo(ctxt, nil, funcs)
|
||||
infosyms := writeinfo(ctxt, nil, funcs, abbrev)
|
||||
|
||||
syms = writepub(ctxt, ".debug_pubnames", ispubname, syms)
|
||||
syms = writepub(ctxt, ".debug_pubtypes", ispubtype, syms)
|
||||
syms = writearanges(ctxt, syms)
|
||||
syms = writegdbscript(ctxt, syms)
|
||||
syms = append(syms, infosyms...)
|
||||
syms = collectlocs(ctxt, syms, funcs)
|
||||
syms = writeranges(ctxt, syms)
|
||||
dwarfp = syms
|
||||
}
|
||||
|
||||
func collectlocs(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
|
||||
empty := true
|
||||
for _, fn := range funcs {
|
||||
for _, reloc := range fn.R {
|
||||
if reloc.Type == objabi.R_DWARFREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) {
|
||||
reloc.Sym.Attr |= AttrReachable | AttrNotInSymbolTable
|
||||
syms = append(syms, reloc.Sym)
|
||||
empty = false
|
||||
// One location list entry per function, but many relocations to it. Don't duplicate.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
|
||||
if !empty {
|
||||
locsym := ctxt.Syms.Lookup(".debug_loc", 0)
|
||||
locsym.R = locsym.R[:0]
|
||||
locsym.Type = SDWARFLOC
|
||||
locsym.Attr |= AttrReachable
|
||||
syms = append(syms, locsym)
|
||||
}
|
||||
return syms
|
||||
}
|
||||
|
||||
/*
|
||||
* Elf.
|
||||
*/
|
||||
@@ -1618,6 +1620,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
|
||||
Addstring(shstrtab, ".debug_aranges")
|
||||
Addstring(shstrtab, ".debug_frame")
|
||||
Addstring(shstrtab, ".debug_info")
|
||||
Addstring(shstrtab, ".debug_loc")
|
||||
Addstring(shstrtab, ".debug_line")
|
||||
Addstring(shstrtab, ".debug_pubnames")
|
||||
Addstring(shstrtab, ".debug_pubtypes")
|
||||
@@ -1625,6 +1628,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
|
||||
Addstring(shstrtab, ".debug_ranges")
|
||||
if Linkmode == LinkExternal {
|
||||
Addstring(shstrtab, elfRelType+".debug_info")
|
||||
Addstring(shstrtab, elfRelType+".debug_loc")
|
||||
Addstring(shstrtab, elfRelType+".debug_aranges")
|
||||
Addstring(shstrtab, elfRelType+".debug_line")
|
||||
Addstring(shstrtab, elfRelType+".debug_frame")
|
||||
@@ -1651,6 +1655,10 @@ func dwarfaddelfsectionsyms(ctxt *Link) {
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
sym = ctxt.Syms.Lookup(".debug_frame", 0)
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
sym = ctxt.Syms.Lookup(".debug_loc", 0)
|
||||
if sym.Sect != nil {
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
}
|
||||
sym = ctxt.Syms.Lookup(".debug_ranges", 0)
|
||||
if sym.Sect != nil {
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
|
||||
@@ -1808,7 +1808,7 @@ func elfrelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
|
||||
continue
|
||||
}
|
||||
if r.Xsym == nil {
|
||||
Errorf(sym, "missing xsym in relocation")
|
||||
Errorf(sym, "missing xsym in relocation %#v %#v", r.Sym.Name, sym)
|
||||
continue
|
||||
}
|
||||
if r.Xsym.ElfsymForReloc() == 0 {
|
||||
@@ -2596,12 +2596,9 @@ elfobj:
|
||||
elfshreloc(sect)
|
||||
}
|
||||
for _, s := range dwarfp {
|
||||
if len(s.R) > 0 || s.Type == SDWARFINFO {
|
||||
if len(s.R) > 0 || s.Type == SDWARFINFO || s.Type == SDWARFLOC {
|
||||
elfshreloc(s.Sect)
|
||||
}
|
||||
if s.Type == SDWARFINFO {
|
||||
break
|
||||
}
|
||||
}
|
||||
// add a .note.GNU-stack section to mark the stack as non-executable
|
||||
sh := elfshname(".note.GNU-stack")
|
||||
|
||||
@@ -105,6 +105,7 @@ const (
|
||||
SDWARFSECT
|
||||
SDWARFINFO
|
||||
SDWARFRANGE
|
||||
SDWARFLOC
|
||||
SSUB = SymKind(1 << 8)
|
||||
SMASK = SymKind(SSUB - 1)
|
||||
SHIDDEN = SymKind(1 << 9)
|
||||
@@ -124,6 +125,7 @@ var abiSymKindToSymKind = [...]SymKind{
|
||||
STLSBSS,
|
||||
SDWARFINFO,
|
||||
SDWARFRANGE,
|
||||
SDWARFLOC,
|
||||
}
|
||||
|
||||
// readOnly are the symbol kinds that form read-only sections. In some
|
||||
|
||||
@@ -4,9 +4,9 @@ package ld
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE"
|
||||
const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOC"
|
||||
|
||||
var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408, 419}
|
||||
var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408, 419, 428}
|
||||
|
||||
func (i SymKind) String() string {
|
||||
if i < 0 || i >= SymKind(len(_SymKind_index)-1) {
|
||||
|
||||
Reference in New Issue
Block a user