]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: emit DWARF info about dictionary entries
authorAlessandro Arzilli <alessandro.arzilli@gmail.com>
Wed, 25 Aug 2021 08:08:07 +0000 (10:08 +0200)
committerThan McIntosh <thanm@google.com>
Wed, 15 Sep 2021 16:32:27 +0000 (16:32 +0000)
When emitting the DIE of the instantiation of a generic function also
emit one DW_TAG_typedef_type entry for each dictionary entry in use,
referencing the shape type and having a custom attribute containing the
index inside the dictionary.

When emitting the DIE of variables that have an instantiated parametric
type, instead of referencing the shape type directly go through the
DW_TAG_typedef_type entry emitted for the dictionary entry describing
the real type of the variable.

Change-Id: Ia45d157ecd4c25e2b60300469e43bbb27a663582
Reviewed-on: https://go-review.googlesource.com/c/go/+/344929
Run-TryBot: Alessandro Arzilli <alessandro.arzilli@gmail.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dan Scales <danscales@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Trust: Dan Scales <danscales@google.com>
Trust: Jeremy Faller <jeremy@golang.org>

src/cmd/compile/internal/dwarfgen/dwarf.go
src/cmd/compile/internal/ir/name.go
src/cmd/compile/internal/noder/stencil.go
src/cmd/internal/dwarf/dwarf.go
src/cmd/link/internal/ld/dwarf_test.go

index 30472a9ebd750ead59dc5131737b7301ce378405..3007262db9d1b784f0c0238f6873a32d80e72203 100644 (file)
@@ -217,6 +217,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
                        DeclCol:       declpos.RelCol(),
                        InlIndex:      int32(inlIndex),
                        ChildIndex:    -1,
+                       DictIndex:     n.DictIndex,
                })
                // Record go type of to insure that it gets emitted by the linker.
                fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
@@ -374,6 +375,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
                DeclCol:       declpos.RelCol(),
                InlIndex:      int32(inlIndex),
                ChildIndex:    -1,
+               DictIndex:     n.DictIndex,
        }
 }
 
@@ -478,6 +480,7 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID) *dwarf.Var
                DeclCol:     declpos.RelCol(),
                InlIndex:    int32(inlIndex),
                ChildIndex:  -1,
+               DictIndex:   n.DictIndex,
        }
        list := debug.LocationLists[varID]
        if len(list) != 0 {
index 9fb22378cd98deebc43e6136beb8b2a93bd07e28..dcfff7debaeedf923c2efa947b4e3fabe292e1dc 100644 (file)
@@ -40,6 +40,7 @@ type Name struct {
        Class     Class      // uint8
        pragma    PragmaFlag // int16
        flags     bitset16
+       DictIndex uint16 // index of the dictionary entry describing the type of this variable declaration plus 1
        sym       *types.Sym
        Func      *Func // TODO(austin): nil for I.M, eqFor, hashfor, and hashmem
        Offset_   int64
index 5069db9fe1b888f6267d69919a9727b7609f8e8d..6c990c1828b356fc1231bf112ff5149496199c58 100644 (file)
@@ -779,6 +779,7 @@ func (subst *subster) localvar(name *ir.Name) *ir.Name {
        m.Func = name.Func
        subst.ts.Vars[name] = m
        m.SetTypecheck(1)
+       m.DictIndex = name.DictIndex
        if name.Defn != nil {
                if name.Defn.Op() == ir.ONAME {
                        // This is a closure variable, so its Defn is the outer
@@ -1268,14 +1269,18 @@ func (subst *subster) node(n ir.Node) ir.Node {
 // function info.gfInfo. This will indicate the dictionary entry with the
 // correct concrete type for the associated instantiated function.
 func findDictType(info *instInfo, t *types.Type) int {
-       for i, dt := range info.gfInfo.tparams {
+       return info.gfInfo.findDictType(t)
+}
+
+func (gfInfo *gfInfo) findDictType(t *types.Type) int {
+       for i, dt := range gfInfo.tparams {
                if dt == t {
                        return i
                }
        }
-       for i, dt := range info.gfInfo.derivedTypes {
+       for i, dt := range gfInfo.derivedTypes {
                if types.Identical(dt, t) {
-                       return i + len(info.gfInfo.tparams)
+                       return i + len(gfInfo.tparams)
                }
        }
        return -1
@@ -1736,6 +1741,7 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
 
        for _, n := range gf.Dcl {
                addType(&info, n, n.Type())
+               n.DictIndex = uint16(info.findDictType(n.Type()) + 1)
        }
 
        if infoPrintMode {
@@ -1805,9 +1811,13 @@ func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
                        // Visit the closure body and add all relevant entries to the
                        // dictionary of the outer function (closure will just use
                        // the dictionary of the outer function).
-                       for _, n1 := range n.(*ir.ClosureExpr).Func.Body {
+                       cfunc := n.(*ir.ClosureExpr).Func
+                       for _, n1 := range cfunc.Body {
                                ir.Visit(n1, visitFunc)
                        }
+                       for _, n := range cfunc.Dcl {
+                               n.DictIndex = uint16(info.findDictType(n.Type()) + 1)
+                       }
                }
                if n.Op() == ir.OSWITCH && n.(*ir.SwitchStmt).Tag != nil && n.(*ir.SwitchStmt).Tag.Op() == ir.OTYPESW && !n.(*ir.SwitchStmt).Tag.(*ir.TypeSwitchGuard).X.Type().IsEmptyInterface() {
                        for _, cc := range n.(*ir.SwitchStmt).Cases {
index 69aafaf986006adaf9e33a3508f54234e967bc70..be3764170694684e2b61f21fb2750c84289fc1c3 100644 (file)
@@ -50,6 +50,7 @@ type Var struct {
        Abbrev        int // Either DW_ABRV_AUTO[_LOCLIST] or DW_ABRV_PARAM[_LOCLIST]
        IsReturnValue bool
        IsInlFormal   bool
+       DictIndex     uint16 // index of the dictionary entry describing the type of this variable
        StackOffset   int32
        // This package can't use the ssa package, so it can't mention ssa.FuncDebug,
        // so indirect through a closure.
@@ -97,6 +98,8 @@ type FnState struct {
        Scopes        []Scope
        InlCalls      InlCalls
        UseBASEntries bool
+
+       dictIndexToOffset []int64
 }
 
 func EnableLogging(doit bool) {
@@ -315,6 +318,7 @@ const (
        DW_AT_go_runtime_type   = 0x2904
 
        DW_AT_go_package_name = 0x2905 // Attribute for DW_TAG_compile_unit
+       DW_AT_go_dict_index   = 0x2906 // Attribute for DW_TAG_typedef_type, index of the dictionary entry describing the real type of this type shape
 
        DW_AT_internal_location = 253 // params and locals; not emitted
 )
@@ -362,6 +366,7 @@ const (
        DW_ABRV_STRINGTYPE
        DW_ABRV_STRUCTTYPE
        DW_ABRV_TYPEDECL
+       DW_ABRV_DICT_INDEX
        DW_NABRV
 )
 
@@ -882,6 +887,17 @@ var abbrevs = [DW_NABRV]dwAbbrev{
                        {DW_AT_type, DW_FORM_ref_addr},
                },
        },
+
+       /* DICT_INDEX */
+       {
+               DW_TAG_typedef,
+               DW_CHILDREN_no,
+               []dwAttrForm{
+                       {DW_AT_name, DW_FORM_string},
+                       {DW_AT_type, DW_FORM_ref_addr},
+                       {DW_AT_go_dict_index, DW_FORM_udata},
+               },
+       },
 }
 
 // GetAbbrev returns the contents of the .debug_abbrev section.
@@ -1196,6 +1212,9 @@ func putPrunedScopes(ctxt Context, s *FnState, fnabbrev int) error {
                sort.Sort(byChildIndex(pruned.Vars))
                scopes[k] = pruned
        }
+
+       s.dictIndexToOffset = putparamtypes(ctxt, s, scopes, fnabbrev)
+
        var encbuf [20]byte
        if putscope(ctxt, s, scopes, 0, fnabbrev, encbuf[:0]) < int32(len(scopes)) {
                return errors.New("multiple toplevel scopes")
@@ -1451,6 +1470,47 @@ func PutDefaultFunc(ctxt Context, s *FnState, isWrapper bool) error {
        return nil
 }
 
+// putparamtypes writes typedef DIEs for any parametric types that are used by this function.
+func putparamtypes(ctxt Context, s *FnState, scopes []Scope, fnabbrev int) []int64 {
+       if fnabbrev == DW_ABRV_FUNCTION_CONCRETE {
+               return nil
+       }
+
+       maxDictIndex := uint16(0)
+
+       for i := range scopes {
+               for _, v := range scopes[i].Vars {
+                       if v.DictIndex > maxDictIndex {
+                               maxDictIndex = v.DictIndex
+                       }
+               }
+       }
+
+       if maxDictIndex == 0 {
+               return nil
+       }
+
+       dictIndexToOffset := make([]int64, maxDictIndex)
+
+       for i := range scopes {
+               for _, v := range scopes[i].Vars {
+                       if v.DictIndex == 0 || dictIndexToOffset[v.DictIndex-1] != 0 {
+                               continue
+                       }
+
+                       dictIndexToOffset[v.DictIndex-1] = ctxt.CurrentOffset(s.Info)
+
+                       Uleb128put(ctxt, s.Info, int64(DW_ABRV_DICT_INDEX))
+                       n := fmt.Sprintf(".param%d", v.DictIndex-1)
+                       putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
+                       putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+                       putattr(ctxt, s.Info, DW_ABRV_DICT_INDEX, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DictIndex-1), nil)
+               }
+       }
+
+       return dictIndexToOffset
+}
+
 func putscope(ctxt Context, s *FnState, scopes []Scope, curscope int32, fnabbrev int, encbuf []byte) int32 {
 
        if logDwarf {
@@ -1624,7 +1684,12 @@ func putvar(ctxt Context, s *FnState, v *Var, absfn Sym, fnabbrev, inlIndex int,
                        putattr(ctxt, s.Info, abbrev, DW_FORM_flag, DW_CLS_FLAG, isReturn, nil)
                }
                putattr(ctxt, s.Info, abbrev, DW_FORM_udata, DW_CLS_CONSTANT, int64(v.DeclLine), nil)
-               putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+               if v.DictIndex > 0 && s.dictIndexToOffset != nil && s.dictIndexToOffset[v.DictIndex-1] != 0 {
+                       // If the type of this variable is parametric use the entry emitted by putparamtypes
+                       putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, s.dictIndexToOffset[v.DictIndex-1], s.Info)
+               } else {
+                       putattr(ctxt, s.Info, abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
+               }
        }
 
        if abbrevUsesLoclist(abbrev) {
index 0aeaa7565c839f73ba678f3ebe4735178fad1283..3d112d97a4895d7659accbc618dd85c3fea9a72e 100644 (file)
@@ -11,6 +11,7 @@ import (
        "debug/pe"
        "errors"
        "fmt"
+       "internal/buildcfg"
        "internal/testenv"
        "io"
        "io/ioutil"
@@ -1746,3 +1747,86 @@ func main() {
                        expected, found)
        }
 }
+
+func TestDictIndex(t *testing.T) {
+       // Check that variables with a parametric type have a dictionary index attribute
+       testenv.MustHaveGoBuild(t)
+
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping on plan9; no DWARF symbol table in executables")
+       }
+       if buildcfg.Experiment.Unified {
+               t.Skip("GOEXPERIMENT=unified does not emit dictionaries yet")
+       }
+       t.Parallel()
+
+       const prog = `
+package main
+
+import "fmt"
+
+func testfn[T any](arg T) {
+       var mapvar = make(map[int]T)
+       mapvar[0] = arg
+       fmt.Println(arg, mapvar)
+}
+
+func main() {
+       testfn("test")
+}
+`
+
+       dir := t.TempDir()
+       f := gobuild(t, dir, prog, NoOpt)
+       defer f.Close()
+
+       d, err := f.DWARF()
+       if err != nil {
+               t.Fatalf("error reading DWARF: %v", err)
+       }
+
+       rdr := d.Reader()
+       found := false
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               name, _ := entry.Val(dwarf.AttrName).(string)
+               if strings.HasPrefix(name, "main.testfn") {
+                       found = true
+                       break
+               }
+       }
+
+       if !found {
+               t.Fatalf("could not find main.testfn")
+       }
+
+       offs := []dwarf.Offset{}
+       for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               if entry.Tag == 0 {
+                       break
+               }
+               name, _ := entry.Val(dwarf.AttrName).(string)
+               switch name {
+               case "arg", "mapvar":
+                       offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
+               }
+       }
+       if len(offs) != 2 {
+               t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
+       }
+       for _, off := range offs {
+               rdr.Seek(off)
+               entry, err := rdr.Next()
+               if err != nil {
+                       t.Fatalf("error reading DWARF: %v", err)
+               }
+               if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
+                       t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
+               }
+       }
+}