From 42f4ccb6f9c2f64f3ebe684c6bc81a6f286dd164 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 21 Aug 2023 11:26:15 -0700 Subject: [PATCH] cmd/compile/internal/reflectdata: share hmap and hiter types There's no need for distinct hmap and hiter types for each map. Shaves 9kB off cmd/go binary size. Change-Id: I7bc3b2d8ec82e7fcd78c1cb17733ebd8b615990a Reviewed-on: https://go-review.googlesource.com/c/go/+/521615 Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Auto-Submit: Matthew Dempsky --- .../compile/internal/reflectdata/reflect.go | 94 +++++++++++-------- src/cmd/compile/internal/ssagen/ssa.go | 4 +- src/cmd/compile/internal/typecheck/subr.go | 10 +- src/cmd/compile/internal/types/fmt.go | 4 - src/cmd/compile/internal/types/sizeof_test.go | 2 +- src/cmd/compile/internal/types/type.go | 2 - src/cmd/compile/internal/walk/builtin.go | 4 +- src/cmd/compile/internal/walk/order.go | 2 +- src/cmd/compile/internal/walk/range.go | 4 +- test/live.go | 4 +- test/live2.go | 4 +- test/live_regabi.go | 4 +- 12 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index ba8c0b2af6..d593a312bd 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -195,14 +195,14 @@ func MapBucketType(t *types.Type) *types.Type { return bucket } -// MapType builds a type representing a Hmap structure for the given map type. -// Make sure this stays in sync with runtime/map.go. -func MapType(t *types.Type) *types.Type { - if t.MapType().Hmap != nil { - return t.MapType().Hmap - } +var hmapType *types.Type - bmap := MapBucketType(t) +// MapType returns a type interchangeable with runtime.hmap. +// Make sure this stays in sync with runtime/map.go. +func MapType() *types.Type { + if hmapType != nil { + return hmapType + } // build a struct: // type hmap struct { @@ -211,8 +211,8 @@ func MapType(t *types.Type) *types.Type { // B uint8 // noverflow uint16 // hash0 uint32 - // buckets *bmap - // oldbuckets *bmap + // buckets unsafe.Pointer + // oldbuckets unsafe.Pointer // nevacuate uintptr // extra unsafe.Pointer // *mapextra // } @@ -222,15 +222,19 @@ func MapType(t *types.Type) *types.Type { makefield("flags", types.Types[types.TUINT8]), makefield("B", types.Types[types.TUINT8]), makefield("noverflow", types.Types[types.TUINT16]), - makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP. - makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP. - makefield("oldbuckets", types.NewPtr(bmap)), + makefield("hash0", types.Types[types.TUINT32]), // Used in walk.go for OMAKEMAP. + makefield("buckets", types.Types[types.TUNSAFEPTR]), // Used in walk.go for OMAKEMAP. + makefield("oldbuckets", types.Types[types.TUNSAFEPTR]), makefield("nevacuate", types.Types[types.TUINTPTR]), makefield("extra", types.Types[types.TUNSAFEPTR]), } - hmap := types.NewStruct(fields) - hmap.SetNoalg(true) + n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hmap")) + hmap := types.NewNamed(n) + n.SetType(hmap) + n.SetTypecheck(1) + + hmap.SetUnderlying(types.NewStruct(fields)) types.CalcSize(hmap) // The size of hmap should be 48 bytes on 64 bit @@ -239,29 +243,29 @@ func MapType(t *types.Type) *types.Type { base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size) } - t.MapType().Hmap = hmap - hmap.StructType().Map = t + hmapType = hmap return hmap } -// MapIterType builds a type representing an Hiter structure for the given map type. +var hiterType *types.Type + +// MapIterType returns a type interchangeable with runtime.hiter. // Make sure this stays in sync with runtime/map.go. -func MapIterType(t *types.Type) *types.Type { - if t.MapType().Hiter != nil { - return t.MapType().Hiter +func MapIterType() *types.Type { + if hiterType != nil { + return hiterType } - hmap := MapType(t) - bmap := MapBucketType(t) + hmap := MapType() // build a struct: // type hiter struct { - // key *Key - // elem *Elem + // key unsafe.Pointer // *Key + // elem unsafe.Pointer // *Elem // t unsafe.Pointer // *MapType // h *hmap - // buckets *bmap - // bptr *bmap + // buckets unsafe.Pointer + // bptr unsafe.Pointer // *bmap // overflow unsafe.Pointer // *[]*bmap // oldoverflow unsafe.Pointer // *[]*bmap // startBucket uintptr @@ -274,12 +278,12 @@ func MapIterType(t *types.Type) *types.Type { // } // must match runtime/map.go:hiter. fields := []*types.Field{ - makefield("key", types.NewPtr(t.Key())), // Used in range.go for TMAP. - makefield("elem", types.NewPtr(t.Elem())), // Used in range.go for TMAP. + makefield("key", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP. + makefield("elem", types.Types[types.TUNSAFEPTR]), // Used in range.go for TMAP. makefield("t", types.Types[types.TUNSAFEPTR]), makefield("h", types.NewPtr(hmap)), - makefield("buckets", types.NewPtr(bmap)), - makefield("bptr", types.NewPtr(bmap)), + makefield("buckets", types.Types[types.TUNSAFEPTR]), + makefield("bptr", types.Types[types.TUNSAFEPTR]), makefield("overflow", types.Types[types.TUNSAFEPTR]), makefield("oldoverflow", types.Types[types.TUNSAFEPTR]), makefield("startBucket", types.Types[types.TUINTPTR]), @@ -292,14 +296,18 @@ func MapIterType(t *types.Type) *types.Type { } // build iterator struct holding the above fields - hiter := types.NewStruct(fields) - hiter.SetNoalg(true) + n := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.Pkgs.Runtime.Lookup("hiter")) + hiter := types.NewNamed(n) + n.SetType(hiter) + n.SetTypecheck(1) + + hiter.SetUnderlying(types.NewStruct(fields)) types.CalcSize(hiter) if hiter.Size() != int64(12*types.PtrSize) { base.Fatalf("hash_iter size not correct %d %d", hiter.Size(), 12*types.PtrSize) } - t.MapType().Hiter = hiter - hiter.StructType().Map = t + + hiterType = hiter return hiter } @@ -947,16 +955,11 @@ func writeType(t *types.Type) *obj.LSym { s := types.TypeSym(t) lsym := s.Linksym() - if s.Siggen() { - return lsym - } - s.SetSiggen(true) // special case (look for runtime below): // when compiling package runtime, // emit the type structures for int, float, etc. tbase := t - if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil { tbase = t.Elem() } @@ -964,6 +967,19 @@ func writeType(t *types.Type) *obj.LSym { base.Fatalf("unresolved defined type: %v", tbase) } + // This is a fake type we generated for our builtin pseudo-runtime + // package. We'll emit a description for the real type while + // compiling package runtime, so we don't need or want to emit one + // from this fake type. + if sym := tbase.Sym(); sym != nil && sym.Pkg == ir.Pkgs.Runtime { + return lsym + } + + if s.Siggen() { + return lsym + } + s.SetSiggen(true) + if !NeedEmit(tbase) { if i := typecheck.BaseTypeIndex(t); i >= 0 { lsym.Pkg = tbase.Sym().Pkg.Prefix diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 28f68e01bc..864bd4f9d0 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -86,6 +86,7 @@ func InitConfig() { _ = types.NewPtr(types.Types[types.TINT16]) // *int16 _ = types.NewPtr(types.Types[types.TINT64]) // *int64 _ = types.NewPtr(types.ErrorType) // *error + _ = types.NewPtr(reflectdata.MapType()) // *runtime.hmap types.NewPtrCacheEnabled = false ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat) ssaConfig.Race = base.Flag.Race @@ -2802,8 +2803,7 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { } // map <--> *hmap - if to.Kind() == types.TMAP && from.IsPtr() && - to.MapType().Hmap == from.Elem() { + if to.Kind() == types.TMAP && from == types.NewPtr(reflectdata.MapType()) { return v } diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 56ff7a7317..0583635013 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -537,15 +537,7 @@ func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) { return ir.OCONVNOP, "" } - // 10. src is map and dst is a pointer to corresponding hmap. - // This rule is needed for the implementation detail that - // go gc maps are implemented as a pointer to a hmap struct. - if src.Kind() == types.TMAP && dst.IsPtr() && - src.MapType().Hmap == dst.Elem() { - return ir.OCONVNOP, "" - } - - // 11. src is a slice and dst is an array or pointer-to-array. + // 10. src is a slice and dst is an array or pointer-to-array. // They must have same element type. if src.IsSlice() { if dst.IsArray() && types.Identical(src.Elem(), dst.Elem()) { diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 0016fb9606..0646f43754 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -511,10 +511,6 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type switch t { case mt.Bucket: b.WriteString("map.bucket[") - case mt.Hmap: - b.WriteString("map.hdr[") - case mt.Hiter: - b.WriteString("map.iter[") default: base.Fatalf("unknown internal map type") } diff --git a/src/cmd/compile/internal/types/sizeof_test.go b/src/cmd/compile/internal/types/sizeof_test.go index 76ccbd54a5..cec1283435 100644 --- a/src/cmd/compile/internal/types/sizeof_test.go +++ b/src/cmd/compile/internal/types/sizeof_test.go @@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) { }{ {Sym{}, 32, 64}, {Type{}, 56, 96}, - {Map{}, 20, 40}, + {Map{}, 12, 24}, {Forward{}, 20, 32}, {Func{}, 20, 32}, {Struct{}, 12, 24}, diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go index 7a69aaabc1..5f7e6e131d 100644 --- a/src/cmd/compile/internal/types/type.go +++ b/src/cmd/compile/internal/types/type.go @@ -276,8 +276,6 @@ type Map struct { Elem *Type // Val (elem) type Bucket *Type // internal struct type representing a hash bucket - Hmap *Type // internal struct type representing the Hmap (map header object) - Hiter *Type // internal struct type representing hash iterator state } // MapType returns t's extra map-specific fields. diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go index f936022433..9f3102cf30 100644 --- a/src/cmd/compile/internal/walk/builtin.go +++ b/src/cmd/compile/internal/walk/builtin.go @@ -302,7 +302,7 @@ func walkMakeChan(n *ir.MakeExpr, init *ir.Nodes) ir.Node { // walkMakeMap walks an OMAKEMAP node. func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { t := n.Type() - hmapType := reflectdata.MapType(t) + hmapType := reflectdata.MapType() hint := n.Len // var h *hmap @@ -340,7 +340,7 @@ func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node { // h.buckets = b bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap - na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), b) + na := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, h, bsym), typecheck.ConvNop(b, types.Types[types.TUNSAFEPTR])) nif.Body.Append(na) appendWalkStmt(init, nif) } diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 80dd120934..f8b755c946 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -872,7 +872,7 @@ func (o *orderState) stmt(n ir.Node) { // n.Prealloc is the temp for the iterator. // MapIterType contains pointers and needs to be zeroed. - n.Prealloc = o.newTemp(reflectdata.MapIterType(xt), true) + n.Prealloc = o.newTemp(reflectdata.MapIterType(), true) } n.Key = o.exprInPlace(n.Key) n.Value = o.exprInPlace(n.Value) diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index ed1a9c402f..dd2f346c5f 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -241,13 +241,13 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { fn = typecheck.SubstArgTypes(fn, th) nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit)) - key := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym)) + key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key()))) if v1 == nil { body = nil } else if v2 == nil { body = []ir.Node{rangeAssign(nrange, key)} } else { - elem := ir.NewStarExpr(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym)) + elem := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, elemsym), types.NewPtr(t.Elem()))) body = []ir.Node{rangeAssign2(nrange, key, elem)} } diff --git a/test/live.go b/test/live.go index 0e015db34c..1777a51f2a 100644 --- a/test/live.go +++ b/test/live.go @@ -458,7 +458,7 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$" + for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } @@ -667,7 +667,7 @@ func bad40() { func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" t := &ret printnl() // ERROR "live at call to printnl: ret$" // Note: ret is live at the printnl because the compiler moves &ret diff --git a/test/live2.go b/test/live2.go index 83a6cb7db6..2beac4f8d2 100644 --- a/test/live2.go +++ b/test/live2.go @@ -27,14 +27,14 @@ func newT40() *T40 { } func bad40() { - t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + t := newT40() // ERROR "stack object ret T40$" "stack object .autotmp_[0-9]+ runtime.hmap$" printnl() // ERROR "live at call to printnl: ret$" useT40(t) } func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + ret.m = make(map[int]int, 42) // ERROR "stack object .autotmp_[0-9]+ runtime.hmap$" t := &ret printnl() // ERROR "live at call to printnl: ret$" useT40(t) diff --git a/test/live_regabi.go b/test/live_regabi.go index bae319d2fd..7ae84891e4 100644 --- a/test/live_regabi.go +++ b/test/live_regabi.go @@ -455,7 +455,7 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.iter\[string\]int$" + for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hiter$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } @@ -664,7 +664,7 @@ func bad40() { func good40() { ret := T40{} // ERROR "stack object ret T40$" - ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ map.hdr\[int\]int$" + ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ runtime.hmap$" t := &ret printnl() // ERROR "live at call to printnl: ret$" // Note: ret is live at the printnl because the compiler moves &ret