]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link,runtime: DWARF/gdb support for swiss maps
authorMichael Pratt <mpratt@google.com>
Thu, 10 Oct 2024 17:52:26 +0000 (13:52 -0400)
committerGopher Robot <gobot@golang.org>
Mon, 21 Oct 2024 21:59:06 +0000 (21:59 +0000)
For #54766.

Cq-Include-Trybots: luci.golang.try:gotip-linux-ppc64_power10,gotip-linux-amd64-longtest-swissmap
Change-Id: I6695c0b143560d974b710e1d78e7a7d09278f7cc
Reviewed-on: https://go-review.googlesource.com/c/go/+/620215
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/reflectdata/map_swiss.go
src/cmd/link/internal/ld/decodesym.go
src/cmd/link/internal/ld/dwarf.go
src/runtime/runtime-gdb.py
src/runtime/runtime-gdb_test.go

index 2cbe580f7f0ed0ace9596a14ae3a516aac07f0cc..50e123ddb0461dc6e55035f8e32c5abbfc745cf8 100644 (file)
@@ -36,7 +36,7 @@ func SwissMapGroupType(t *types.Type) *types.Type {
        // }
        slotFields := []*types.Field{
                makefield("key", t.Key()),
-               makefield("typ", t.Elem()),
+               makefield("elem", t.Elem()),
        }
        slot := types.NewStruct(slotFields)
        slot.SetNoalg(true)
index 32271b6f9180c1bca550e8c0ad12bd2a7fd1e0a7..9bce4a7a1274511a3a31997505c95a5244d5bbd9 100644 (file)
@@ -159,6 +159,11 @@ func decodetypeMapValue(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) l
        return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+int32(arch.PtrSize)) // 0x20 / 0x38
 }
 
+func decodetypeMapSwissGroup(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
+       relocs := ldr.Relocs(symIdx)
+       return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))+2*int32(arch.PtrSize)) // 0x24 / 0x40
+}
+
 func decodetypePtrElem(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) loader.Sym {
        relocs := ldr.Relocs(symIdx)
        return decodeRelocSym(ldr, symIdx, &relocs, int32(commonsize(arch))) // 0x1c / 0x30
index 45037030f595c42038bdbb87c159daf9977b7c3c..0b01946696df022d05b78a40ebd72081f8c80cef 100644 (file)
@@ -810,7 +810,7 @@ func (d *dwctxt) findprotodie(ctxt *Link, name string) *dwarf.DWDie {
                die = prototypedies[name]
        }
        if die == nil {
-               log.Fatalf("internal error: DIE generation failed for %s\nprototypedies: %+v", name, prototypedies)
+               log.Fatalf("internal error: DIE generation failed for %s\n", name)
        }
        return die
 }
@@ -873,101 +873,68 @@ func (d *dwctxt) synthesizemaptypes(ctxt *Link, die *dwarf.DWDie) {
 }
 
 func (d *dwctxt) synthesizemaptypesSwiss(ctxt *Link, die *dwarf.DWDie) {
-       hash := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.table"))
-       //bucket := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.Map"))
-
-       if hash == nil {
-               return
-       }
+       mapType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.Map"))
+       tableType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.table"))
+       tableSliceType := walktypedef(d.findprotodie(ctxt, "type:[]*internal/runtime/maps.table"))
+       groupsReferenceType := walktypedef(d.findprotodie(ctxt, "type:internal/runtime/maps.groupsReference"))
 
        for ; die != nil; die = die.Link {
                if die.Abbrev != dwarf.DW_ABRV_MAPTYPE {
                        continue
                }
                gotype := loader.Sym(getattr(die, dwarf.DW_AT_type).Data.(dwSym))
-               keytype := decodetypeMapKey(d.ldr, d.arch, gotype)
-               valtype := decodetypeMapValue(d.ldr, d.arch, gotype)
-               //keydata := d.ldr.Data(keytype)
-               //valdata := d.ldr.Data(valtype)
-               //keysize, valsize := decodetypeSize(d.arch, keydata), decodetypeSize(d.arch, valdata)
-               keytype, valtype = d.walksymtypedef(d.defgotype(keytype)), d.walksymtypedef(d.defgotype(valtype))
-
-               // compute size info like hashmap.c does.
-               //indirectKey, indirectVal := false, false
-               //if keysize > abi.SwissMapMaxKeyBytes {
-               //      keysize = int64(d.arch.PtrSize)
-               //      indirectKey = true
-               //}
-               //if valsize > abi.SwissMapMaxElemBytes {
-               //      valsize = int64(d.arch.PtrSize)
-               //      indirectVal = true
-               //}
 
-               // Construct type to represent an array of BucketSize keys
-               // TODO
-               keyname := d.nameFromDIESym(keytype)
-               //dwhks := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]key", keyname, "", func(dwhk *dwarf.DWDie) {
-               //      newattr(dwhk, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount*keysize, 0)
-               //      t := keytype
-               //      if indirectKey {
-               //              t = d.defptrto(keytype)
-               //      }
-               //      d.newrefattr(dwhk, dwarf.DW_AT_type, t)
-               //      fld := d.newdie(dwhk, dwarf.DW_ABRV_ARRAYRANGE, "size")
-               //      newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount, 0)
-               //      d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
-               //})
+               keyType := decodetypeMapKey(d.ldr, d.arch, gotype)
+               valType := decodetypeMapValue(d.ldr, d.arch, gotype)
+               groupType := decodetypeMapSwissGroup(d.ldr, d.arch, gotype)
+
+               keyType = d.walksymtypedef(d.defgotype(keyType))
+               valType = d.walksymtypedef(d.defgotype(valType))
+               groupType = d.walksymtypedef(d.defgotype(groupType))
+
+               keyName := d.nameFromDIESym(keyType)
+               valName := d.nameFromDIESym(valType)
+
+               // Construct groupsReference[K,V]
+               dwGroupsReference := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "groupReference", keyName, valName, func(dwh *dwarf.DWDie) {
+                       d.copychildren(ctxt, dwh, groupsReferenceType)
+                       // data *group[K,V]
+                       //
+                       // This is actually a pointer to an array
+                       // *[lengthMask+1]group[K,V], but the length is
+                       // variable, so we can't statically record the length.
+                       d.substitutetype(dwh, "data", d.defptrto(groupType))
+                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(groupsReferenceType, dwarf.DW_AT_byte_size).Value, nil)
+                       newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
+               })
 
-               // Construct type to represent an array of BucketSize values
-               // TODO
-               valname := d.nameFromDIESym(valtype)
-               //dwhvs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_ARRAYTYPE, "[]val", valname, "", func(dwhv *dwarf.DWDie) {
-               //      newattr(dwhv, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount*valsize, 0)
-               //      t := valtype
-               //      if indirectVal {
-               //              t = d.defptrto(valtype)
-               //      }
-               //      d.newrefattr(dwhv, dwarf.DW_AT_type, t)
-               //      fld := d.newdie(dwhv, dwarf.DW_ABRV_ARRAYRANGE, "size")
-               //      newattr(fld, dwarf.DW_AT_count, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount, 0)
-               //      d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
-               //})
+               // Construct table[K,V]
+               dwTable := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "table", keyName, valName, func(dwh *dwarf.DWDie) {
+                       d.copychildren(ctxt, dwh, tableType)
+                       d.substitutetype(dwh, "groups", dwGroupsReference)
+                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(tableType, dwarf.DW_AT_byte_size).Value, nil)
+                       newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
+               })
 
-               // Construct bucket<K,V>
-               // TODO
-               //dwhbs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "bucket", keyname, valname, func(dwhb *dwarf.DWDie) {
-               //      // Copy over all fields except the field "data" from the generic
-               //      // bucket. "data" will be replaced with keys/values below.
-               //      d.copychildrenexcept(ctxt, dwhb, bucket, findchild(bucket, "data"))
-
-               //      fld := d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "keys")
-               //      d.newrefattr(fld, dwarf.DW_AT_type, dwhks)
-               //      newmemberoffsetattr(fld, abi.SwissMapBucketCount)
-               //      fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "values")
-               //      d.newrefattr(fld, dwarf.DW_AT_type, dwhvs)
-               //      newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*int32(keysize))
-               //      fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "overflow")
-               //      d.newrefattr(fld, dwarf.DW_AT_type, d.defptrto(d.dtolsym(dwhb.Sym)))
-               //      newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*(int32(keysize)+int32(valsize)))
-               //      if d.arch.RegSize > d.arch.PtrSize {
-               //              fld = d.newdie(dwhb, dwarf.DW_ABRV_STRUCTFIELD, "pad")
-               //              d.newrefattr(fld, dwarf.DW_AT_type, d.uintptrInfoSym)
-               //              newmemberoffsetattr(fld, abi.SwissMapBucketCount+abi.SwissMapBucketCount*(int32(keysize)+int32(valsize))+int32(d.arch.PtrSize))
-               //      }
-
-               //      newattr(dwhb, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, abi.SwissMapBucketCount+abi.SwissMapBucketCount*keysize+abi.SwissMapBucketCount*valsize+int64(d.arch.RegSize), 0)
-               //})
+               // Construct type to represent []*table[K,V].
+               dwTableSlice := d.mkinternaltype(ctxt, dwarf.DW_ABRV_SLICETYPE, "[]*table", keyName, valName, func(dwh *dwarf.DWDie) {
+                       d.copychildren(ctxt, dwh, tableSliceType)
+                       d.substitutetype(dwh, "array", d.defptrto(d.defptrto(dwTable)))
+                       d.newrefattr(dwh, dwarf.DW_AT_go_elem, d.defptrto(dwTable))
+                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(tableSliceType, dwarf.DW_AT_byte_size).Value, nil)
+                       newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Slice), 0)
+               })
 
-               // Construct hash<K,V>
-               dwhs := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "hash", keyname, valname, func(dwh *dwarf.DWDie) {
-                       d.copychildren(ctxt, dwh, hash)
-                       //d.substitutetype(dwh, "buckets", d.defptrto(dwhbs))
-                       //d.substitutetype(dwh, "oldbuckets", d.defptrto(dwhbs))
-                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(hash, dwarf.DW_AT_byte_size).Value, nil)
+               // Construct map[K,V]
+               dwMap := d.mkinternaltype(ctxt, dwarf.DW_ABRV_STRUCTTYPE, "map", keyName, valName, func(dwh *dwarf.DWDie) {
+                       d.copychildren(ctxt, dwh, mapType)
+                       d.substitutetype(dwh, "directory", dwTableSlice)
+                       newattr(dwh, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, getattr(mapType, dwarf.DW_AT_byte_size).Value, nil)
+                       newattr(dwh, dwarf.DW_AT_go_kind, dwarf.DW_CLS_CONSTANT, int64(abi.Struct), 0)
                })
 
-               // make map type a pointer to hash<K,V>
-               d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwhs))
+               // make map type a pointer to map[K,V]
+               d.newrefattr(die, dwarf.DW_AT_type, d.defptrto(dwMap))
        }
 }
 
@@ -1882,7 +1849,10 @@ func dwarfGenerateDebugInfo(ctxt *Link) {
                "type:runtime.hchan":             nil,
        }
        if buildcfg.Experiment.SwissMap {
+               prototypedies["type:internal/runtime/maps.Map"] = nil
                prototypedies["type:internal/runtime/maps.table"] = nil
+               prototypedies["type:[]*internal/runtime/maps.table"] = nil
+               prototypedies["type:internal/runtime/maps.groupsReference"] = nil
        } else {
                prototypedies["type:runtime.hmap"] = nil
                prototypedies["type:runtime.bmap"] = nil
index 8618ebbc3bda506d720c885701993084adba400c..b0c96e594f96252f9e0d39c512df10aa94895dd8 100644 (file)
@@ -141,7 +141,6 @@ class SliceTypePrinter:
                        yield ('[{0}]'.format(idx), item)
 
 
-# TODO(go.dev/issue/54766): Support swisstable maps.
 class MapTypePrinter:
        """Pretty print map[K]V types.
 
@@ -161,7 +160,56 @@ class MapTypePrinter:
                return str(self.val.type)
 
        def children(self):
-               MapBucketCount = 8 # see internal/abi.go:MapBucketCount
+               fields = [f.name for f in self.val.type.strip_typedefs().target().fields()]
+               if 'buckets' in fields:
+                       yield from self.old_map_children()
+               else:
+                       yield from self.swiss_map_children()
+
+       def swiss_map_children(self):
+               SwissMapGroupSlots = 8 # see internal/abi:SwissMapGroupSlots
+
+               cnt = 0
+               directory = SliceValue(self.val['directory'])
+               for table in directory:
+                       table = table.dereference()
+                       groups = table['groups']['data']
+                       length = table['groups']['lengthMask'] + 1
+
+                       # The linker DWARF generation
+                       # (cmd/link/internal/ld.(*dwctxt).synthesizemaptypesSwiss) records
+                       # groups.data as a *group[K,V], but it is actually a pointer to
+                       # variable length array *[length]group[K,V].
+                       #
+                       # N.B. array() takes an _inclusive_ upper bound.
+
+                       # group[K,V]
+                       group_type = groups.type.target()
+                       # [length]group[K,V]
+                       array_group_type = group_type.array(length-1)
+                       # *[length]group[K,V]
+                       ptr_array_group_type = array_group_type.pointer()
+                       # groups = (*[length]group[K,V])(groups.data)
+                       groups = groups.cast(ptr_array_group_type)
+                       groups = groups.dereference()
+
+                       for i in xrange(length):
+                               group = groups[i]
+                               ctrl = group['ctrl']
+
+                               for i in xrange(SwissMapGroupSlots):
+                                       c = (ctrl >> (8*i)) & 0xff
+                                       if (c & 0x80) != 0:
+                                               # Empty or deleted
+                                               continue
+
+                                       # Full
+                                       yield str(cnt), group['slots'][i]['key']
+                                       yield str(cnt+1), group['slots'][i]['elem']
+
+
+       def old_map_children(self):
+               MapBucketCount = 8 # see internal/abi:OldMapBucketCount
                B = self.val['B']
                buckets = self.val['buckets']
                oldbuckets = self.val['oldbuckets']
@@ -386,7 +434,7 @@ goobjfile.pretty_printers.append(ifacematcher)
 class GoLenFunc(gdb.Function):
        "Length of strings, slices, maps or channels"
 
-       how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'count'), (ChanTypePrinter, 'qcount'))
+       how = ((StringTypePrinter, 'len'), (SliceTypePrinter, 'len'), (MapTypePrinter, 'used'), (ChanTypePrinter, 'qcount'))
 
        def __init__(self):
                gdb.Function.__init__(self, "len")
@@ -395,6 +443,12 @@ class GoLenFunc(gdb.Function):
                typename = str(obj.type)
                for klass, fld in self.how:
                        if klass.pattern.match(typename) or paramtypematch(obj.type, klass.pattern):
+                               if klass == MapTypePrinter:
+                                       fields = [f.name for f in self.val.type.strip_typedefs().target().fields()]
+                                       if 'buckets' in fields:
+                                               # Old maps.
+                                               fld = 'count'
+
                                return obj[fld]
 
 
index ef01d6a1946d898a71d1a0d752a5529e07c3a709..d31db52234cc5d6060447f165cdf973b1a5c0a9b 100644 (file)
@@ -186,9 +186,6 @@ func TestGdbPythonCgo(t *testing.T) {
 }
 
 func testGdbPython(t *testing.T, cgo bool) {
-       if goexperiment.SwissMap {
-               t.Skip("TODO(prattmic): swissmap DWARF")
-       }
        if cgo {
                testenv.MustHaveCGO(t)
        }
@@ -531,10 +528,6 @@ func main() {
 // TestGdbAutotmpTypes ensures that types of autotmp variables appear in .debug_info
 // See bug #17830.
 func TestGdbAutotmpTypes(t *testing.T) {
-       if goexperiment.SwissMap {
-               t.Skip("TODO(prattmic): swissmap DWARF")
-       }
-
        checkGdbEnvironment(t)
        t.Parallel()
        checkGdbVersion(t)
@@ -584,10 +577,21 @@ func TestGdbAutotmpTypes(t *testing.T) {
        // Check that the backtrace matches the source code.
        types := []string{
                "[]main.astruct",
-               "bucket<string,main.astruct>",
-               "hash<string,main.astruct>",
                "main.astruct",
-               "hash<string,main.astruct> * map[string]main.astruct",
+       }
+       if goexperiment.SwissMap {
+               types = append(types, []string{
+                       "groupReference<string,main.astruct>",
+                       "table<string,main.astruct>",
+                       "map<string,main.astruct>",
+                       "map<string,main.astruct> * map[string]main.astruct",
+               }...)
+       } else {
+               types = append(types, []string{
+                       "bucket<string,main.astruct>",
+                       "hash<string,main.astruct>",
+                       "hash<string,main.astruct> * map[string]main.astruct",
+               }...)
        }
        for _, name := range types {
                if !strings.Contains(sgot, name) {