]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.debug] cmd/compile: better DWARF with optimizations on
authorHeschi Kreinick <heschi@google.com>
Fri, 21 Jul 2017 22:30:19 +0000 (18:30 -0400)
committerHeschi Kreinick <heschi@google.com>
Thu, 27 Jul 2017 20:19:44 +0000 (20:19 +0000)
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.

After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.

There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:

  type myint int
  var a int
  b := myint(int)
and
  b := (*uint64)(unsafe.Pointer(a))

don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.

Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.

A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.

TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.

go build -toolexec 'toolstash -cmp' -a std succeeds.

With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after:  06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean  /tmp/220352263 /tmp/621364410
completed   15 of   15, estimated time remaining 0s (eta 3:52PM)
name        old time/op       new time/op       delta
Template          199ms ± 3%        198ms ± 2%     ~     (p=0.400 n=15+14)
Unicode          96.6ms ± 5%       96.4ms ± 5%     ~     (p=0.838 n=15+15)
GoTypes           653ms ± 2%        647ms ± 2%     ~     (p=0.102 n=15+14)
Flate             133ms ± 6%        129ms ± 3%   -2.62%  (p=0.041 n=15+15)
GoParser          164ms ± 5%        159ms ± 3%   -3.05%  (p=0.000 n=15+15)
Reflect           428ms ± 4%        422ms ± 3%     ~     (p=0.156 n=15+13)
Tar               123ms ±10%        124ms ± 8%     ~     (p=0.461 n=15+15)
XML               228ms ± 3%        224ms ± 3%   -1.57%  (p=0.045 n=15+15)
[Geo mean]        206ms             377ms       +82.86%

name        old user-time/op  new user-time/op  delta
Template          292ms ±10%        301ms ±12%     ~     (p=0.189 n=15+15)
Unicode           166ms ±37%        158ms ±14%     ~     (p=0.418 n=15+14)
GoTypes           962ms ± 6%        963ms ± 7%     ~     (p=0.976 n=15+15)
Flate             207ms ±19%        200ms ±14%     ~     (p=0.345 n=14+15)
GoParser          246ms ±22%        240ms ±15%     ~     (p=0.587 n=15+15)
Reflect           611ms ±13%        587ms ±14%     ~     (p=0.085 n=15+13)
Tar               211ms ±12%        217ms ±14%     ~     (p=0.355 n=14+15)
XML               335ms ±15%        320ms ±18%     ~     (p=0.169 n=15+15)
[Geo mean]        317ms             583ms       +83.72%

name        old alloc/op      new alloc/op      delta
Template         40.2MB ± 0%       40.2MB ± 0%   -0.15%  (p=0.000 n=14+15)
Unicode          29.2MB ± 0%       29.3MB ± 0%     ~     (p=0.624 n=15+15)
GoTypes           114MB ± 0%        114MB ± 0%   -0.15%  (p=0.000 n=15+14)
Flate            25.7MB ± 0%       25.6MB ± 0%   -0.18%  (p=0.000 n=13+15)
GoParser         32.2MB ± 0%       32.2MB ± 0%   -0.14%  (p=0.003 n=15+15)
Reflect          77.8MB ± 0%       77.9MB ± 0%     ~     (p=0.061 n=15+15)
Tar              27.1MB ± 0%       27.0MB ± 0%   -0.11%  (p=0.029 n=15+15)
XML              42.7MB ± 0%       42.5MB ± 0%   -0.29%  (p=0.000 n=15+15)
[Geo mean]       42.1MB            75.0MB       +78.05%

name        old allocs/op     new allocs/op     delta
Template           402k ± 1%         398k ± 0%   -0.91%  (p=0.000 n=15+15)
Unicode            344k ± 1%         344k ± 0%     ~     (p=0.715 n=15+14)
GoTypes           1.18M ± 0%        1.17M ± 0%   -0.91%  (p=0.000 n=15+14)
Flate              243k ± 0%         240k ± 1%   -1.05%  (p=0.000 n=13+15)
GoParser           327k ± 1%         324k ± 1%   -0.96%  (p=0.000 n=15+15)
Reflect            984k ± 1%         982k ± 0%     ~     (p=0.050 n=15+15)
Tar                261k ± 1%         259k ± 1%   -0.77%  (p=0.000 n=15+15)
XML                411k ± 0%         404k ± 1%   -1.55%  (p=0.000 n=15+15)
[Geo mean]         439k              755k       +72.01%

name        old text-bytes    new text-bytes    delta
HelloSize         694kB ± 0%        694kB ± 0%   -0.00%  (p=0.000 n=15+15)

name        old data-bytes    new data-bytes    delta
HelloSize        5.55kB ± 0%       5.55kB ± 0%     ~     (all equal)

name        old bss-bytes     new bss-bytes     delta
HelloSize         133kB ± 0%        133kB ± 0%     ~     (all equal)

name        old exe-bytes     new exe-bytes     delta
HelloSize        1.04MB ± 0%       1.04MB ± 0%     ~     (all equal)

Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
27 files changed:
src/cmd/compile/fmt_test.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/sizeof_test.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/syntax.go
src/cmd/compile/internal/ssa/cache.go
src/cmd/compile/internal/ssa/debug.go [new file with mode: 0644]
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/html.go
src/cmd/compile/internal/ssa/location.go
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/value.go
src/cmd/internal/dwarf/dwarf.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/plist.go
src/cmd/internal/obj/x86/a.out.go
src/cmd/internal/obj/x86/obj6.go
src/cmd/internal/objabi/symkind.go
src/cmd/internal/objabi/symkind_string.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/dwarf.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/symkind.go
src/cmd/link/internal/ld/symkind_string.go

index 59de326a91947bd0a3dd8156542cb8de5cae2554..2052a4200ef5e96e7c1db7acc523a5663e36bd93 100644 (file)
@@ -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":                                        "",
index 2b61564ad8d8cb3819c85f8e371bcd3963a07a64..a1f4767c8f0b9df96f6d47784f58b51a012dc0b5 100644 (file)
@@ -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
index d301ae19c8dd7e7426e36b733578f232615f03a7..542fd43b63117ccf0868bfe7b6fdbbeb57ba394f 100644 (file)
@@ -13,6 +13,7 @@ import (
        "cmd/internal/src"
        "cmd/internal/sys"
        "fmt"
+       "math"
        "math/rand"
        "sort"
        "sync"
@@ -303,47 +304,31 @@ 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
-
-                       abbrev = dwarf.DW_ABRV_AUTO
-                       if Ctxt.FixedFrameSize() == 0 {
-                               offs -= int64(Widthptr)
-                       }
-                       if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
-                               offs -= int64(Widthptr)
-                       }
-
                case PPARAM, PPARAMOUT:
                        name = obj.NAME_PARAM
-
-                       abbrev = dwarf.DW_ABRV_PARAM
-                       offs += Ctxt.FixedFrameSize()
-
                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),
@@ -351,32 +336,336 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
                        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)
+                       }
+                       if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
+                               offs -= int64(Widthptr)
+                       }
+
+               case PPARAM, PPARAMOUT:
+                       abbrev = dwarf.DW_ABRV_PARAM
+                       offs += Ctxt.FixedFrameSize()
+               default:
+                       Fatalf("createSimpleVars unexpected type %v for node %v", n.Class(), n)
+               }
 
-               typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
-               dwarfVars = append(dwarfVars, &dwarf.Var{
+               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
+}
 
-               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)
+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
+                       }
                }
+       }
 
-               varScopes = append(varScopes, scope)
+       // 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})
        }
 
-       return assembleScopes(fnsym, fn, dwarfVars, varScopes)
+       // 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
index 1ca0a615353afe1f3962bc7a573591f0806143e9..bd4453fa842120040f390bd5e6b99aaba5f61169 100644 (file)
@@ -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},
index f8aefaae5e76020744bd8375443c4632d868365e..c769efe8cd3144f589d340b1a82cbad79e4c18b7 100644 (file)
@@ -4384,6 +4384,7 @@ func genssa(f *ssa.Func, pp *Progs) {
        s.pp = pp
        var progToValue map[*obj.Prog]*ssa.Value
        var progToBlock map[*obj.Prog]*ssa.Block
+       var valueToProg []*obj.Prog
        var logProgs = e.log
        if logProgs {
                progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues())
@@ -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,12 +4444,16 @@ 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 {
                                        progToValue[x] = v
@@ -4469,6 +4479,22 @@ func genssa(f *ssa.Func, pp *Progs) {
                }
        }
 
+       if Ctxt.Flag_locationlists {
+               for _, locList := range e.curfn.Func.DebugInfo.Variables {
+                       for _, loc := range locList.Locations {
+                               loc.StartProg = valueToProg[loc.Start.ID]
+                               if loc.End == nil {
+                                       Fatalf("empty loc %v compiling %v", loc, f.Name)
+                               }
+                               loc.EndProg = valueToProg[loc.End.ID]
+                               if !logLocationLists {
+                                       loc.Start = nil
+                                       loc.End = nil
+                               }
+                       }
+               }
+       }
+
        // Resolve branches
        for _, br := range s.Branches {
                br.P.To.Val = s.bstart[br.B.ID]
index 0fd146bca26deb57258173aa37e0f9d9c15785ce..32ae6f2f28ee4f96b7b922c5427c1f7ec3d62eeb 100644 (file)
@@ -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
index f1018da497fc63a5e806f67f36d1ac7d887d7999..8434084bde28e4a2fa680c6137f2ba4e70d388fb 100644 (file)
@@ -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
 }
diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go
new file mode 100644 (file)
index 0000000..55db45b
--- /dev/null
@@ -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")
+       }
+
+}
index d962e4a193bf8d9663078d3b5843d4df77c32174..63bb9a8b61d1a2d096a74610630cd4929c4bf782 100644 (file)
@@ -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
index d554907bebe541b9a6dddbdc82ebea92869150a9..6efe93e74fd54a0f71e02858bf807f4401db6a43 100644 (file)
@@ -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
 }
index 70afa47e9d53aa97e4dbfcce0019b88b93bf37a8..dc01bd4235341f91734e6035b636efcfe5bdae00 100644 (file)
@@ -26,6 +26,12 @@ func (r *Register) Name() string {
        return r.name
 }
 
+// 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
index ae2dd5f5500c867731e05d65b61d19f044087956..87a2ea0656a15da6e748b20d34cc6cd5ccdfb4b6 100644 (file)
@@ -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,
index e297e6bce75719652e59d6dbaade74bf744ce46a..0abaeaeeb556b6e2a6cf3918e85d775cb09c654d 100644 (file)
@@ -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
                }
        }
index 7edc71be52a7d9d9293a6c26ce82cae8658e35e0..6df535153a7d573c9f486ba1bce1d65f7cc3f105 100644 (file)
@@ -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
 }
 
index b58052beb3d90a6cf191296db76d9441ab8716d9..2b034257a6afc5b92c2e2fb1bed7ca119e16f42e 100644 (file)
@@ -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
-       StackOffset 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
@@ -709,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++
@@ -744,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)
@@ -761,26 +781,66 @@ 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.StackOffset != 0 {
-               loc = append(loc, DW_OP_consts)
-               loc = AppendSleb128(loc, int64(v.StackOffset))
-               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, 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)
        }
-       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)
+       // End list
+       ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
+       ctxt.AddInt(listSym, ctxt.PtrSize(), 0)
 }
 
 // VarsByOffset attaches the methods of sort.Interface to []*Var,
index d49bc8c5644c78ad75c96e2bbd130effd73e81cd..68e1b70ac0dee9fd064cb1ad3dd6d003e3de16dd 100644 (file)
@@ -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
 }
index e309c5f7e785122a37edc74fad443ddc83872725..539d01303764a976fb22aa74b96127db9034eeb2 100644 (file)
@@ -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)
        }
index 861da88703c859f6e27d166776d7820b320e4456..1bb05aedfa95a0d862da897a5abbc52ed4862f12 100644 (file)
@@ -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.
index 04f9ef68a4e6f805bcc4c8a6bc46f7810508ea92..92d358ba4e7682f1377040c023b5a163b7d50a22 100644 (file)
@@ -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,
+}
index d34f0aeaa632af200a8717d1f13a7a344ce9ad33..27873e0824b6f636608f4d396bebebba87543d13 100644 (file)
@@ -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,
 }
index b037e9e4ed17c6ecfef1d136d3b226f484ff2618..ac91824d1726b79c7f8f5542c6472e272536de2a 100644 (file)
@@ -57,4 +57,5 @@ const (
        // Debugging data
        SDWARFINFO
        SDWARFRANGE
+       SDWARFLOC
 )
index 5123dc7097f49ad8aa917425af8f8d7f0d207ae0..3064c8ee0517fd584433caf4a18a96894d6d3ed3 100644 (file)
@@ -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) {
index bf219f7b62863a90c30b55afe3cbd59610e03785..1d053d23b79b3e5edcbdc381ced192f0f1f07470 100644 (file)
@@ -1862,6 +1862,8 @@ func (ctxt *Link) dodata() {
                        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)
                }
index 9b11fdcff604e2ec018d9e75156f561e911b0ca2..b6fb1bb5c18cb88351eb79ea0b308978afe9e557 100644 (file)
@@ -1579,10 +1579,35 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
        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.
  */
@@ -1595,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")
@@ -1602,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")
@@ -1628,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)
index 0fc947fec29eea724f5996ceed79a074f7b6af14..78f8d6e70ea8fd28867a2d1c9174ad4355d454be 100644 (file)
@@ -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")
index c057f6cd0c937a70c061d68ca757d4bd742acca1..5ac04cf45acad480d1af460340581716912ad932 100644 (file)
@@ -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
index 2178b50c36654f3bc82b3f6db4c39bd4cf28a3dc..87da3c40bb5af05e8d35267f240e6f194e94c176 100644 (file)
@@ -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) {