Commit fbfc2031 authored by Martin Möhrmann's avatar Martin Möhrmann

cmd/compile: specialize map creation for small hint sizes

Handle make(map[any]any) and make(map[any]any, hint) where
hint <= BUCKETSIZE special to allow for faster map initialization
and to improve binary size by using runtime calls with fewer arguments.

Given hint is smaller or equal to BUCKETSIZE in which case
overLoadFactor(hint, 0)  is false and no buckets would be allocated by makemap:
* If hmap needs to be allocated on the stack then only hmap's hash0
  field needs to be initialized and no call to makemap is needed.
* If hmap needs to be allocated on the heap then a new special
  makehmap function will allocate hmap and intialize hmap's
  hash0 field.

Reduces size of the godoc by ~36kb.

AMD64
name         old time/op    new time/op    delta
NewEmptyMap    16.6ns ± 2%     5.5ns ± 2%  -66.72%  (p=0.000 n=10+10)
NewSmallMap    64.8ns ± 1%    56.5ns ± 1%  -12.75%  (p=0.000 n=9+10)

Updates #6853

Change-Id: I624e90da6775afaa061178e95db8aca674f44e9b
Reviewed-on: https://go-review.googlesource.com/61190
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 1e83f883
...@@ -72,85 +72,87 @@ var runtimeDecls = [...]struct { ...@@ -72,85 +72,87 @@ var runtimeDecls = [...]struct {
{"panicnildottype", funcTag, 55}, {"panicnildottype", funcTag, 55},
{"ifaceeq", funcTag, 58}, {"ifaceeq", funcTag, 58},
{"efaceeq", funcTag, 58}, {"efaceeq", funcTag, 58},
{"makemap64", funcTag, 60}, {"fastrand", funcTag, 60},
{"makemap", funcTag, 61}, {"makemap64", funcTag, 62},
{"mapaccess1", funcTag, 62}, {"makemap", funcTag, 63},
{"mapaccess1_fast32", funcTag, 63}, {"makemap_small", funcTag, 64},
{"mapaccess1_fast64", funcTag, 63}, {"mapaccess1", funcTag, 65},
{"mapaccess1_faststr", funcTag, 63}, {"mapaccess1_fast32", funcTag, 66},
{"mapaccess1_fat", funcTag, 64}, {"mapaccess1_fast64", funcTag, 66},
{"mapaccess2", funcTag, 65}, {"mapaccess1_faststr", funcTag, 66},
{"mapaccess2_fast32", funcTag, 66}, {"mapaccess1_fat", funcTag, 67},
{"mapaccess2_fast64", funcTag, 66}, {"mapaccess2", funcTag, 68},
{"mapaccess2_faststr", funcTag, 66}, {"mapaccess2_fast32", funcTag, 69},
{"mapaccess2_fat", funcTag, 67}, {"mapaccess2_fast64", funcTag, 69},
{"mapassign", funcTag, 62}, {"mapaccess2_faststr", funcTag, 69},
{"mapassign_fast32", funcTag, 63}, {"mapaccess2_fat", funcTag, 70},
{"mapassign_fast64", funcTag, 63}, {"mapassign", funcTag, 65},
{"mapassign_faststr", funcTag, 63}, {"mapassign_fast32", funcTag, 66},
{"mapiterinit", funcTag, 68}, {"mapassign_fast64", funcTag, 66},
{"mapdelete", funcTag, 68}, {"mapassign_faststr", funcTag, 66},
{"mapdelete_fast32", funcTag, 69}, {"mapiterinit", funcTag, 71},
{"mapdelete_fast64", funcTag, 69}, {"mapdelete", funcTag, 71},
{"mapdelete_faststr", funcTag, 69}, {"mapdelete_fast32", funcTag, 72},
{"mapiternext", funcTag, 70}, {"mapdelete_fast64", funcTag, 72},
{"makechan64", funcTag, 72}, {"mapdelete_faststr", funcTag, 72},
{"makechan", funcTag, 73}, {"mapiternext", funcTag, 73},
{"chanrecv1", funcTag, 75}, {"makechan64", funcTag, 75},
{"chanrecv2", funcTag, 76}, {"makechan", funcTag, 76},
{"chansend1", funcTag, 78}, {"chanrecv1", funcTag, 78},
{"chanrecv2", funcTag, 79},
{"chansend1", funcTag, 81},
{"closechan", funcTag, 23}, {"closechan", funcTag, 23},
{"writeBarrier", varTag, 80}, {"writeBarrier", varTag, 83},
{"writebarrierptr", funcTag, 81}, {"writebarrierptr", funcTag, 84},
{"typedmemmove", funcTag, 82}, {"typedmemmove", funcTag, 85},
{"typedmemclr", funcTag, 83}, {"typedmemclr", funcTag, 86},
{"typedslicecopy", funcTag, 84}, {"typedslicecopy", funcTag, 87},
{"selectnbsend", funcTag, 85}, {"selectnbsend", funcTag, 88},
{"selectnbrecv", funcTag, 86}, {"selectnbrecv", funcTag, 89},
{"selectnbrecv2", funcTag, 88}, {"selectnbrecv2", funcTag, 91},
{"newselect", funcTag, 89}, {"newselect", funcTag, 92},
{"selectsend", funcTag, 90}, {"selectsend", funcTag, 93},
{"selectrecv", funcTag, 91}, {"selectrecv", funcTag, 94},
{"selectdefault", funcTag, 55}, {"selectdefault", funcTag, 55},
{"selectgo", funcTag, 92}, {"selectgo", funcTag, 95},
{"block", funcTag, 5}, {"block", funcTag, 5},
{"makeslice", funcTag, 94}, {"makeslice", funcTag, 97},
{"makeslice64", funcTag, 95}, {"makeslice64", funcTag, 98},
{"growslice", funcTag, 96}, {"growslice", funcTag, 99},
{"memmove", funcTag, 97}, {"memmove", funcTag, 100},
{"memclrNoHeapPointers", funcTag, 98}, {"memclrNoHeapPointers", funcTag, 101},
{"memclrHasPointers", funcTag, 98}, {"memclrHasPointers", funcTag, 101},
{"memequal", funcTag, 99}, {"memequal", funcTag, 102},
{"memequal8", funcTag, 100}, {"memequal8", funcTag, 103},
{"memequal16", funcTag, 100}, {"memequal16", funcTag, 103},
{"memequal32", funcTag, 100}, {"memequal32", funcTag, 103},
{"memequal64", funcTag, 100}, {"memequal64", funcTag, 103},
{"memequal128", funcTag, 100}, {"memequal128", funcTag, 103},
{"int64div", funcTag, 101}, {"int64div", funcTag, 104},
{"uint64div", funcTag, 102}, {"uint64div", funcTag, 105},
{"int64mod", funcTag, 101}, {"int64mod", funcTag, 104},
{"uint64mod", funcTag, 102}, {"uint64mod", funcTag, 105},
{"float64toint64", funcTag, 103}, {"float64toint64", funcTag, 106},
{"float64touint64", funcTag, 104}, {"float64touint64", funcTag, 107},
{"float64touint32", funcTag, 106}, {"float64touint32", funcTag, 108},
{"int64tofloat64", funcTag, 107}, {"int64tofloat64", funcTag, 109},
{"uint64tofloat64", funcTag, 108}, {"uint64tofloat64", funcTag, 110},
{"uint32tofloat64", funcTag, 109}, {"uint32tofloat64", funcTag, 111},
{"complex128div", funcTag, 110}, {"complex128div", funcTag, 112},
{"racefuncenter", funcTag, 111}, {"racefuncenter", funcTag, 113},
{"racefuncexit", funcTag, 5}, {"racefuncexit", funcTag, 5},
{"raceread", funcTag, 111}, {"raceread", funcTag, 113},
{"racewrite", funcTag, 111}, {"racewrite", funcTag, 113},
{"racereadrange", funcTag, 112}, {"racereadrange", funcTag, 114},
{"racewriterange", funcTag, 112}, {"racewriterange", funcTag, 114},
{"msanread", funcTag, 112}, {"msanread", funcTag, 114},
{"msanwrite", funcTag, 112}, {"msanwrite", funcTag, 114},
{"support_popcnt", varTag, 11}, {"support_popcnt", varTag, 11},
{"support_sse41", varTag, 11}, {"support_sse41", varTag, 11},
} }
func runtimeTypes() []*types.Type { func runtimeTypes() []*types.Type {
var typs [113]*types.Type var typs [115]*types.Type
typs[0] = types.Bytetype typs[0] = types.Bytetype
typs[1] = types.NewPtr(typs[0]) typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[TANY] typs[2] = types.Types[TANY]
...@@ -210,59 +212,61 @@ func runtimeTypes() []*types.Type { ...@@ -210,59 +212,61 @@ func runtimeTypes() []*types.Type {
typs[56] = types.NewPtr(typs[48]) typs[56] = types.NewPtr(typs[48])
typs[57] = types.Types[TUNSAFEPTR] typs[57] = types.Types[TUNSAFEPTR]
typs[58] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[57]), anonfield(typs[57])}, []*Node{anonfield(typs[11])}) typs[58] = functype(nil, []*Node{anonfield(typs[56]), anonfield(typs[57]), anonfield(typs[57])}, []*Node{anonfield(typs[11])})
typs[59] = types.NewMap(typs[2], typs[2]) typs[59] = types.Types[TUINT32]
typs[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[59])}) typs[60] = functype(nil, nil, []*Node{anonfield(typs[59])})
typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[59])}) typs[61] = types.NewMap(typs[2], typs[2])
typs[62] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3])}) typs[62] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[61])})
typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3])}) typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[61])})
typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])}) typs[64] = functype(nil, nil, []*Node{anonfield(typs[61])})
typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])}) typs[67] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, nil) typs[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, nil) typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[70] = functype(nil, []*Node{anonfield(typs[3])}, nil) typs[70] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
typs[71] = types.NewChan(typs[2], types.Cboth) typs[71] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[3])}, nil)
typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[71])}) typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[61]), anonfield(typs[2])}, nil)
typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[71])}) typs[73] = functype(nil, []*Node{anonfield(typs[3])}, nil)
typs[74] = types.NewChan(typs[2], types.Crecv) typs[74] = types.NewChan(typs[2], types.Cboth)
typs[75] = functype(nil, []*Node{anonfield(typs[74]), anonfield(typs[3])}, nil) typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[74])})
typs[76] = functype(nil, []*Node{anonfield(typs[74]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[74])})
typs[77] = types.NewChan(typs[2], types.Csend) typs[77] = types.NewChan(typs[2], types.Crecv)
typs[78] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, nil) typs[78] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, nil)
typs[79] = types.NewArray(typs[0], 3) typs[79] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[80] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[79]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])}) typs[80] = types.NewChan(typs[2], types.Csend)
typs[81] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[2])}, nil) typs[81] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, nil)
typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil) typs[82] = types.NewArray(typs[0], 3)
typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil) typs[83] = tostruct([]*Node{namedfield("enabled", typs[11]), namedfield("pad", typs[82]), namedfield("needed", typs[11]), namedfield("cgo", typs[11]), namedfield("alignme", typs[17])})
typs[84] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])}) typs[84] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[2])}, nil)
typs[85] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) typs[85] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
typs[86] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[74])}, []*Node{anonfield(typs[11])}) typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
typs[87] = types.NewPtr(typs[11]) typs[87] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
typs[88] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[87]), anonfield(typs[74])}, []*Node{anonfield(typs[11])}) typs[88] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[89] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[8])}, nil) typs[89] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[77])}, []*Node{anonfield(typs[11])})
typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[77]), anonfield(typs[3])}, nil) typs[90] = types.NewPtr(typs[11])
typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[74]), anonfield(typs[3]), anonfield(typs[87])}, nil) typs[91] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[90]), anonfield(typs[77])}, []*Node{anonfield(typs[11])})
typs[92] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[32])}) typs[92] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[8])}, nil)
typs[93] = types.NewSlice(typs[2]) typs[93] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[80]), anonfield(typs[3])}, nil)
typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[93])}) typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[77]), anonfield(typs[3]), anonfield(typs[90])}, nil)
typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[93])}) typs[95] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[32])})
typs[96] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[93]), anonfield(typs[32])}, []*Node{anonfield(typs[93])}) typs[96] = types.NewSlice(typs[2])
typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, nil) typs[97] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[96])})
typs[98] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[48])}, nil) typs[98] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[96])})
typs[99] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, []*Node{anonfield(typs[11])}) typs[99] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[96]), anonfield(typs[32])}, []*Node{anonfield(typs[96])})
typs[100] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])}) typs[100] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, nil)
typs[101] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])}) typs[101] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[48])}, nil)
typs[102] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])}) typs[102] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, []*Node{anonfield(typs[11])})
typs[103] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])}) typs[103] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
typs[104] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])}) typs[104] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
typs[105] = types.Types[TUINT32] typs[105] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])})
typs[106] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[105])}) typs[106] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])})
typs[107] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])}) typs[107] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])})
typs[108] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])}) typs[108] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[59])})
typs[109] = functype(nil, []*Node{anonfield(typs[105])}, []*Node{anonfield(typs[13])}) typs[109] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])})
typs[110] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])}) typs[110] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])})
typs[111] = functype(nil, []*Node{anonfield(typs[48])}, nil) typs[111] = functype(nil, []*Node{anonfield(typs[59])}, []*Node{anonfield(typs[13])})
typs[112] = functype(nil, []*Node{anonfield(typs[48]), anonfield(typs[48])}, nil) typs[112] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])})
typs[113] = functype(nil, []*Node{anonfield(typs[48])}, nil)
typs[114] = functype(nil, []*Node{anonfield(typs[48]), anonfield(typs[48])}, nil)
return typs[:] return typs[:]
} }
...@@ -91,9 +91,12 @@ func panicnildottype(want *byte) ...@@ -91,9 +91,12 @@ func panicnildottype(want *byte)
func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool) func ifaceeq(tab *uintptr, x, y unsafe.Pointer) (ret bool)
func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool) func efaceeq(typ *uintptr, x, y unsafe.Pointer) (ret bool)
func fastrand() uint32
// *byte is really *runtime.Type // *byte is really *runtime.Type
func makemap64(mapType *byte, hint int64, mapbuf *any) (hmap map[any]any) func makemap64(mapType *byte, hint int64, mapbuf *any) (hmap map[any]any)
func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any) func makemap(mapType *byte, hint int, mapbuf *any) (hmap map[any]any)
func makemap_small() (hmap map[any]any)
func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any) func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any) func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any)
func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any) func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
......
...@@ -252,8 +252,8 @@ func hmap(t *types.Type) *types.Type { ...@@ -252,8 +252,8 @@ func hmap(t *types.Type) *types.Type {
makefield("flags", types.Types[TUINT8]), makefield("flags", types.Types[TUINT8]),
makefield("B", types.Types[TUINT8]), makefield("B", types.Types[TUINT8]),
makefield("noverflow", types.Types[TUINT16]), makefield("noverflow", types.Types[TUINT16]),
makefield("hash0", types.Types[TUINT32]), makefield("hash0", types.Types[TUINT32]), // Used in walk.go for OMAKEMAP.
makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for makemap. makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for OMAKEMAP.
makefield("oldbuckets", types.NewPtr(bmap)), makefield("oldbuckets", types.NewPtr(bmap)),
makefield("nevacuate", types.Types[TUINTPTR]), makefield("nevacuate", types.Types[TUINTPTR]),
makefield("extra", types.Types[TUNSAFEPTR]), makefield("extra", types.Types[TUNSAFEPTR]),
......
...@@ -1574,6 +1574,12 @@ func (s *state) expr(n *Node) *ssa.Value { ...@@ -1574,6 +1574,12 @@ func (s *state) expr(n *Node) *ssa.Value {
return v return v
} }
// map <--> *hmap
if to.Etype == TMAP && from.IsPtr() &&
to.MapType().Hmap == from.Elem() {
return v
}
dowidth(from) dowidth(from)
dowidth(to) dowidth(to)
if from.Width != to.Width { if from.Width != to.Width {
......
...@@ -927,6 +927,14 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op { ...@@ -927,6 +927,14 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
return OCONVNOP return OCONVNOP
} }
// 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.Etype == TMAP && dst.IsPtr() &&
src.MapType().Hmap == dst.Elem() {
return OCONVNOP
}
return 0 return 0
} }
......
...@@ -1467,10 +1467,44 @@ opswitch: ...@@ -1467,10 +1467,44 @@ opswitch:
na = typecheck(na, Etop) na = typecheck(na, Etop)
init.Append(na) init.Append(na)
} }
}
if Isconst(hint, CTINT) && hint.Val().U.(*Mpint).CmpInt64(BUCKETSIZE) <= 0 {
// Handling make(map[any]any) and
// make(map[any]any, hint) where hint <= BUCKETSIZE
// special allows for faster map initialization and
// improves binary size by using calls with fewer arguments.
// For hint <= BUCKETSIZE overLoadFactor(hint, 0) is false
// and no buckets will be allocated by makemap. Therefore,
// no buckets need to be allocated in this code path.
if n.Esc == EscNone {
// Only need to initialize h.hash0 since
// hmap h has been allocated on the stack already.
// h.hash0 = fastrand()
rand := mkcall("fastrand", types.Types[TUINT32], init)
hashsym := hmapType.Field(4).Sym // hmap.hash0 see reflect.go:hmap
a := nod(OAS, nodSym(ODOT, h, hashsym), rand)
a = typecheck(a, Etop)
a = walkexpr(a, init)
init.Append(a)
n = nod(OCONVNOP, h, nil)
n.Type = t
n = typecheck(n, Erv)
} else { } else {
// h = nil // Call runtime.makehmap to allocate an
// hmap on the heap and initialize hmap's hash0 field.
fn := syslook("makemap_small")
fn = substArgTypes(fn, t.Key(), t.Val())
n = mkcall1(fn, n.Type, init)
}
} else {
if n.Esc != EscNone {
h = nodnil() h = nodnil()
} }
// Map initialization with a variable or large hint is
// more complicated. We therefore generate a call to
// runtime.makemap to intialize hmap and allocate the
// map buckets.
// When hint fits into int, use makemap instead of // When hint fits into int, use makemap instead of
// makemap64, which is faster and shorter on 32 bit platforms. // makemap64, which is faster and shorter on 32 bit platforms.
...@@ -1489,6 +1523,7 @@ opswitch: ...@@ -1489,6 +1523,7 @@ opswitch:
fn := syslook(fnname) fn := syslook(fnname)
fn = substArgTypes(fn, hmapType, t.Key(), t.Val()) fn = substArgTypes(fn, hmapType, t.Key(), t.Val())
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h) n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h)
}
case OMAKESLICE: case OMAKESLICE:
l := n.Left l := n.Left
......
...@@ -377,11 +377,16 @@ func (rw *RWMutex) Unlock() { ...@@ -377,11 +377,16 @@ func (rw *RWMutex) Unlock() {
rw.rw.unlock() rw.rw.unlock()
} }
func MapBuckets(m map[int]int) int { func MapBucketsCount(m map[int]int) int {
h := *(**hmap)(unsafe.Pointer(&m)) h := *(**hmap)(unsafe.Pointer(&m))
return 1 << h.B return 1 << h.B
} }
func MapBucketsPointerIsNil(m map[int]int) bool {
h := *(**hmap)(unsafe.Pointer(&m))
return h.buckets == nil
}
func LockOSCounts() (external, internal uint32) { func LockOSCounts() (external, internal uint32) {
g := getg() g := getg()
if g.m.lockedExt+g.m.lockedInt == 0 { if g.m.lockedExt+g.m.lockedInt == 0 {
......
...@@ -281,7 +281,16 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap { ...@@ -281,7 +281,16 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap {
return makemap(t, int(hint), h) return makemap(t, int(hint), h)
} }
// makemap implements a Go map creation make(map[k]v, hint) // makehmap_small implements Go map creation for make(map[k]v) and
// make(map[k]v, hint) when hint is known to be at most bucketCnt
// at compile time and the map needs to be allocated on the heap.
func makemap_small() *hmap {
h := new(hmap)
h.hash0 = fastrand()
return h
}
// makemap implements Go map creation for make(map[k]v, hint).
// If the compiler has determined that the map or the first bucket // If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil. // can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h. // If h != nil, the map can be created directly in h.
......
...@@ -596,33 +596,132 @@ func TestIgnoreBogusMapHint(t *testing.T) { ...@@ -596,33 +596,132 @@ func TestIgnoreBogusMapHint(t *testing.T) {
} }
} }
var mapSink map[int]int
var mapBucketTests = [...]struct {
n int // n is the number of map elements
noescape int // number of expected buckets for non-escaping map
escape int // number of expected buckets for escaping map
}{
{-(1 << 30), 1, 1},
{-1, 1, 1},
{0, 1, 1},
{1, 1, 1},
{8, 1, 1},
{9, 2, 2},
{13, 2, 2},
{14, 4, 4},
{26, 4, 4},
}
func TestMapBuckets(t *testing.T) { func TestMapBuckets(t *testing.T) {
// Test that maps of different sizes have the right number of buckets. // Test that maps of different sizes have the right number of buckets.
// Non-escaping maps with small buckets (like map[int]int) never
// have a nil bucket pointer due to starting with preallocated buckets
// on the stack. Escaping maps start with a non-nil bucket pointer if
// hint size is above bucketCnt and thereby have more than one bucket.
// These tests depend on bucketCnt and loadFactor* in hashmap.go. // These tests depend on bucketCnt and loadFactor* in hashmap.go.
for _, tt := range [...]struct { t.Run("mapliteral", func(t *testing.T) {
n, b int for _, tt := range mapBucketTests {
}{ localMap := map[int]int{}
{8, 1}, if runtime.MapBucketsPointerIsNil(localMap) {
{9, 2}, t.Errorf("no escape: buckets pointer is nil for non-escaping map")
{13, 2}, }
{14, 4},
{26, 4},
} {
m := map[int]int{}
for i := 0; i < tt.n; i++ { for i := 0; i < tt.n; i++ {
m[i] = i localMap[i] = i
} }
if got := runtime.MapBuckets(m); got != tt.b { if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no hint n=%d want %d buckets, got %d", tt.n, tt.b, got) t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
escapingMap := map[int]int{}
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
} }
m = make(map[int]int, tt.n)
for i := 0; i < tt.n; i++ { for i := 0; i < tt.n; i++ {
m[i] = i escapingMap[i] = i
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
mapSink = escapingMap
}
})
t.Run("nohint", func(t *testing.T) {
for _, tt := range mapBucketTests {
localMap := make(map[int]int)
if runtime.MapBucketsPointerIsNil(localMap) {
t.Errorf("no escape: buckets pointer is nil for non-escaping map")
}
for i := 0; i < tt.n; i++ {
localMap[i] = i
}
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
escapingMap := make(map[int]int)
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
} }
if got := runtime.MapBuckets(m); got != tt.b { for i := 0; i < tt.n; i++ {
t.Errorf("hint n=%d want %d buckets, got %d", tt.n, tt.b, got) escapingMap[i] = i
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
} }
mapSink = escapingMap
} }
})
t.Run("makemap", func(t *testing.T) {
for _, tt := range mapBucketTests {
localMap := make(map[int]int, tt.n)
if runtime.MapBucketsPointerIsNil(localMap) {
t.Errorf("no escape: buckets pointer is nil for non-escaping map")
}
for i := 0; i < tt.n; i++ {
localMap[i] = i
}
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
escapingMap := make(map[int]int, tt.n)
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
for i := 0; i < tt.n; i++ {
escapingMap[i] = i
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
mapSink = escapingMap
}
})
t.Run("makemap64", func(t *testing.T) {
for _, tt := range mapBucketTests {
localMap := make(map[int]int, int64(tt.n))
if runtime.MapBucketsPointerIsNil(localMap) {
t.Errorf("no escape: buckets pointer is nil for non-escaping map")
}
for i := 0; i < tt.n; i++ {
localMap[i] = i
}
if got := runtime.MapBucketsCount(localMap); got != tt.noescape {
t.Errorf("no escape: n=%d want %d buckets, got %d", tt.n, tt.noescape, got)
}
escapingMap := make(map[int]int, tt.n)
if count := runtime.MapBucketsCount(escapingMap); count > 1 && runtime.MapBucketsPointerIsNil(escapingMap) {
t.Errorf("escape: buckets pointer is nil for n=%d buckets", count)
}
for i := 0; i < tt.n; i++ {
escapingMap[i] = i
}
if got := runtime.MapBucketsCount(escapingMap); got != tt.escape {
t.Errorf("escape: n=%d want %d buckets, got %d", tt.n, tt.escape, got)
}
mapSink = escapingMap
}
})
} }
func benchmarkMapPop(b *testing.B, n int) { func benchmarkMapPop(b *testing.B, n int) {
......
...@@ -76,7 +76,7 @@ import "fmt" ...@@ -76,7 +76,7 @@ import "fmt"
import "runtime" import "runtime"
var gslice []string var gslice []string
func main() { func main() {
mapvar := make(map[string]string,5) mapvar := make(map[string]string, 13)
mapvar["abc"] = "def" mapvar["abc"] = "def"
mapvar["ghi"] = "jkl" mapvar["ghi"] = "jkl"
strvar := "abc" strvar := "abc"
...@@ -198,8 +198,10 @@ func testGdbPython(t *testing.T, cgo bool) { ...@@ -198,8 +198,10 @@ func testGdbPython(t *testing.T, cgo bool) {
t.Fatalf("info goroutines failed: %s", bl) t.Fatalf("info goroutines failed: %s", bl)
} }
printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`) printMapvarRe1 := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) { printMapvarRe2 := regexp.MustCompile(`\Q = map[string]string = {["ghi"] = "jkl", ["abc"] = "def"}\E$`)
if bl := blocks["print mapvar"]; !printMapvarRe1.MatchString(bl) &&
!printMapvarRe2.MatchString(bl) {
t.Fatalf("print mapvar failed: %s", bl) t.Fatalf("print mapvar failed: %s", bl)
} }
......
...@@ -644,7 +644,7 @@ func useT40(*T40) ...@@ -644,7 +644,7 @@ func useT40(*T40)
func newT40() *T40 { func newT40() *T40 {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$" ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: &ret$"
return &ret return &ret
} }
...@@ -656,7 +656,7 @@ func bad40() { ...@@ -656,7 +656,7 @@ func bad40() {
func good40() { func good40() {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$" ret.m = make(map[int]int) // ERROR "live at call to fastrand: .autotmp_[0-9]+ ret$"
t := &ret t := &ret
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$" printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$" useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
......
...@@ -23,7 +23,7 @@ type T40 struct { ...@@ -23,7 +23,7 @@ type T40 struct {
func newT40() *T40 { func newT40() *T40 {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: &ret$" ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: &ret$"
return &ret return &ret
} }
...@@ -35,7 +35,7 @@ func bad40() { ...@@ -35,7 +35,7 @@ func bad40() {
func good40() { func good40() {
ret := T40{} ret := T40{}
ret.m = make(map[int]int) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$" ret.m = make(map[int]int, 42) // ERROR "live at call to makemap: .autotmp_[0-9]+ ret$"
t := &ret t := &ret
printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$" printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$" useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment