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 <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
12 files changed:
-// MapType builds a type representing a Hmap structure for the given map type.
+var hmapType *types.Type
+
+// MapType returns a type interchangeable with runtime.hmap.
// Make sure this stays in sync with runtime/map.go.
// 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
+func MapType() *types.Type {
+ if hmapType != nil {
+ return hmapType
- bmap := MapBucketType(t)
-
// build a struct:
// type hmap struct {
// count int
// build a struct:
// type hmap struct {
// count int
// B uint8
// noverflow uint16
// hash0 uint32
// B uint8
// noverflow uint16
// hash0 uint32
- // buckets *bmap
- // oldbuckets *bmap
+ // buckets unsafe.Pointer
+ // oldbuckets unsafe.Pointer
// nevacuate uintptr
// extra unsafe.Pointer // *mapextra
// }
// nevacuate uintptr
// extra unsafe.Pointer // *mapextra
// }
makefield("flags", types.Types[types.TUINT8]),
makefield("B", types.Types[types.TUINT8]),
makefield("noverflow", types.Types[types.TUINT16]),
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]),
}
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
types.CalcSize(hmap)
// The size of hmap should be 48 bytes on 64 bit
base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
}
base.Fatalf("hmap size not correct: got %d, want %d", hmap.Size(), size)
}
- t.MapType().Hmap = hmap
- hmap.StructType().Map = t
-// 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.
// 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)
// build a struct:
// type hiter struct {
// build a struct:
// type hiter struct {
- // key *Key
- // elem *Elem
+ // key unsafe.Pointer // *Key
+ // elem unsafe.Pointer // *Elem
// t unsafe.Pointer // *MapType
// h *hmap
// 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
// overflow unsafe.Pointer // *[]*bmap
// oldoverflow unsafe.Pointer // *[]*bmap
// startBucket uintptr
// }
// must match runtime/map.go:hiter.
fields := []*types.Field{
// }
// 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("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]),
makefield("overflow", types.Types[types.TUNSAFEPTR]),
makefield("oldoverflow", types.Types[types.TUNSAFEPTR]),
makefield("startBucket", types.Types[types.TUINTPTR]),
}
// build iterator struct holding the above fields
}
// 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)
}
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
s := types.TypeSym(t)
lsym := s.Linksym()
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
// 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()
}
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
tbase = t.Elem()
}
base.Fatalf("unresolved defined type: %v", tbase)
}
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
if !NeedEmit(tbase) {
if i := typecheck.BaseTypeIndex(t); i >= 0 {
lsym.Pkg = tbase.Sym().Pkg.Prefix
_ = types.NewPtr(types.Types[types.TINT16]) // *int16
_ = types.NewPtr(types.Types[types.TINT64]) // *int64
_ = types.NewPtr(types.ErrorType) // *error
_ = 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
types.NewPtrCacheEnabled = false
ssaConfig = ssa.NewConfig(base.Ctxt.Arch.Name, *types_, base.Ctxt, base.Flag.N == 0, Arch.SoftFloat)
ssaConfig.Race = base.Flag.Race
- if to.Kind() == types.TMAP && from.IsPtr() &&
- to.MapType().Hmap == from.Elem() {
+ if to.Kind() == types.TMAP && from == types.NewPtr(reflectdata.MapType()) {
- // 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()) {
// They must have same element type.
if src.IsSlice() {
if dst.IsArray() && types.Identical(src.Elem(), dst.Elem()) {
switch t {
case mt.Bucket:
b.WriteString("map.bucket[")
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")
}
default:
base.Fatalf("unknown internal map type")
}
}{
{Sym{}, 32, 64},
{Type{}, 56, 96},
}{
{Sym{}, 32, 64},
{Type{}, 56, 96},
{Forward{}, 20, 32},
{Func{}, 20, 32},
{Struct{}, 12, 24},
{Forward{}, 20, 32},
{Func{}, 20, 32},
{Struct{}, 12, 24},
Elem *Type // Val (elem) type
Bucket *Type // internal struct type representing a hash bucket
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.
}
// MapType returns t's extra map-specific fields.
// walkMakeMap walks an OMAKEMAP node.
func walkMakeMap(n *ir.MakeExpr, init *ir.Nodes) ir.Node {
t := n.Type()
// 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
hint := n.Len
// var h *hmap
// h.buckets = b
bsym := hmapType.Field(5).Sym // hmap.buckets see reflect.go:hmap
// 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)
}
nif.Body.Append(na)
appendWalkStmt(init, nif)
}
// n.Prealloc is the temp for the iterator.
// MapIterType contains pointers and needs to be zeroed.
// 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)
}
n.Key = o.exprInPlace(n.Key)
n.Value = o.exprInPlace(n.Value)
fn = typecheck.SubstArgTypes(fn, th)
nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit))
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 {
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)}
}
body = []ir.Node{rangeAssign2(nrange, key, elem)}
}
func f29(b bool) {
if b {
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]+$"
}
}
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
func good40() {
ret := T40{} // ERROR "stack object ret T40$"
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
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
// Note: ret is live at the printnl because the compiler moves &ret
- 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$"
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)
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
useT40(t)
func f29(b bool) {
if b {
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]+$"
}
}
printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$"
}
}
func good40() {
ret := T40{} // ERROR "stack object ret T40$"
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
t := &ret
printnl() // ERROR "live at call to printnl: ret$"
// Note: ret is live at the printnl because the compiler moves &ret