]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: specialize map creation for small hint sizes
authorMartin Möhrmann <moehrmann@google.com>
Sat, 2 Sep 2017 16:46:59 +0000 (18:46 +0200)
committerMartin Möhrmann <moehrmann@google.com>
Thu, 2 Nov 2017 17:03:45 +0000 (17:03 +0000)
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: Keith Randall <khr@golang.org>
12 files changed:
src/cmd/compile/internal/gc/builtin.go
src/cmd/compile/internal/gc/builtin/runtime.go
src/cmd/compile/internal/gc/reflect.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/subr.go
src/cmd/compile/internal/gc/walk.go
src/runtime/export_test.go
src/runtime/hashmap.go
src/runtime/map_test.go
src/runtime/runtime-gdb_test.go
test/live.go
test/live2.go

index a72b36b1fded5d79b4693cf60b2ce6507c4a3803..b865d2f3baa28758039ec578754675dff072c34f 100644 (file)
@@ -72,85 +72,87 @@ var runtimeDecls = [...]struct {
        {"panicnildottype", funcTag, 55},
        {"ifaceeq", funcTag, 58},
        {"efaceeq", funcTag, 58},
-       {"makemap64", funcTag, 60},
-       {"makemap", funcTag, 61},
-       {"mapaccess1", funcTag, 62},
-       {"mapaccess1_fast32", funcTag, 63},
-       {"mapaccess1_fast64", funcTag, 63},
-       {"mapaccess1_faststr", funcTag, 63},
-       {"mapaccess1_fat", funcTag, 64},
-       {"mapaccess2", funcTag, 65},
-       {"mapaccess2_fast32", funcTag, 66},
-       {"mapaccess2_fast64", funcTag, 66},
-       {"mapaccess2_faststr", funcTag, 66},
-       {"mapaccess2_fat", funcTag, 67},
-       {"mapassign", funcTag, 62},
-       {"mapassign_fast32", funcTag, 63},
-       {"mapassign_fast64", funcTag, 63},
-       {"mapassign_faststr", funcTag, 63},
-       {"mapiterinit", funcTag, 68},
-       {"mapdelete", funcTag, 68},
-       {"mapdelete_fast32", funcTag, 69},
-       {"mapdelete_fast64", funcTag, 69},
-       {"mapdelete_faststr", funcTag, 69},
-       {"mapiternext", funcTag, 70},
-       {"makechan64", funcTag, 72},
-       {"makechan", funcTag, 73},
-       {"chanrecv1", funcTag, 75},
-       {"chanrecv2", funcTag, 76},
-       {"chansend1", funcTag, 78},
+       {"fastrand", funcTag, 60},
+       {"makemap64", funcTag, 62},
+       {"makemap", funcTag, 63},
+       {"makemap_small", funcTag, 64},
+       {"mapaccess1", funcTag, 65},
+       {"mapaccess1_fast32", funcTag, 66},
+       {"mapaccess1_fast64", funcTag, 66},
+       {"mapaccess1_faststr", funcTag, 66},
+       {"mapaccess1_fat", funcTag, 67},
+       {"mapaccess2", funcTag, 68},
+       {"mapaccess2_fast32", funcTag, 69},
+       {"mapaccess2_fast64", funcTag, 69},
+       {"mapaccess2_faststr", funcTag, 69},
+       {"mapaccess2_fat", funcTag, 70},
+       {"mapassign", funcTag, 65},
+       {"mapassign_fast32", funcTag, 66},
+       {"mapassign_fast64", funcTag, 66},
+       {"mapassign_faststr", funcTag, 66},
+       {"mapiterinit", funcTag, 71},
+       {"mapdelete", funcTag, 71},
+       {"mapdelete_fast32", funcTag, 72},
+       {"mapdelete_fast64", funcTag, 72},
+       {"mapdelete_faststr", funcTag, 72},
+       {"mapiternext", funcTag, 73},
+       {"makechan64", funcTag, 75},
+       {"makechan", funcTag, 76},
+       {"chanrecv1", funcTag, 78},
+       {"chanrecv2", funcTag, 79},
+       {"chansend1", funcTag, 81},
        {"closechan", funcTag, 23},
-       {"writeBarrier", varTag, 80},
-       {"writebarrierptr", funcTag, 81},
-       {"typedmemmove", funcTag, 82},
-       {"typedmemclr", funcTag, 83},
-       {"typedslicecopy", funcTag, 84},
-       {"selectnbsend", funcTag, 85},
-       {"selectnbrecv", funcTag, 86},
-       {"selectnbrecv2", funcTag, 88},
-       {"newselect", funcTag, 89},
-       {"selectsend", funcTag, 90},
-       {"selectrecv", funcTag, 91},
+       {"writeBarrier", varTag, 83},
+       {"writebarrierptr", funcTag, 84},
+       {"typedmemmove", funcTag, 85},
+       {"typedmemclr", funcTag, 86},
+       {"typedslicecopy", funcTag, 87},
+       {"selectnbsend", funcTag, 88},
+       {"selectnbrecv", funcTag, 89},
+       {"selectnbrecv2", funcTag, 91},
+       {"newselect", funcTag, 92},
+       {"selectsend", funcTag, 93},
+       {"selectrecv", funcTag, 94},
        {"selectdefault", funcTag, 55},
-       {"selectgo", funcTag, 92},
+       {"selectgo", funcTag, 95},
        {"block", funcTag, 5},
-       {"makeslice", funcTag, 94},
-       {"makeslice64", funcTag, 95},
-       {"growslice", funcTag, 96},
-       {"memmove", funcTag, 97},
-       {"memclrNoHeapPointers", funcTag, 98},
-       {"memclrHasPointers", funcTag, 98},
-       {"memequal", funcTag, 99},
-       {"memequal8", funcTag, 100},
-       {"memequal16", funcTag, 100},
-       {"memequal32", funcTag, 100},
-       {"memequal64", funcTag, 100},
-       {"memequal128", funcTag, 100},
-       {"int64div", funcTag, 101},
-       {"uint64div", funcTag, 102},
-       {"int64mod", funcTag, 101},
-       {"uint64mod", funcTag, 102},
-       {"float64toint64", funcTag, 103},
-       {"float64touint64", funcTag, 104},
-       {"float64touint32", funcTag, 106},
-       {"int64tofloat64", funcTag, 107},
-       {"uint64tofloat64", funcTag, 108},
-       {"uint32tofloat64", funcTag, 109},
-       {"complex128div", funcTag, 110},
-       {"racefuncenter", funcTag, 111},
+       {"makeslice", funcTag, 97},
+       {"makeslice64", funcTag, 98},
+       {"growslice", funcTag, 99},
+       {"memmove", funcTag, 100},
+       {"memclrNoHeapPointers", funcTag, 101},
+       {"memclrHasPointers", funcTag, 101},
+       {"memequal", funcTag, 102},
+       {"memequal8", funcTag, 103},
+       {"memequal16", funcTag, 103},
+       {"memequal32", funcTag, 103},
+       {"memequal64", funcTag, 103},
+       {"memequal128", funcTag, 103},
+       {"int64div", funcTag, 104},
+       {"uint64div", funcTag, 105},
+       {"int64mod", funcTag, 104},
+       {"uint64mod", funcTag, 105},
+       {"float64toint64", funcTag, 106},
+       {"float64touint64", funcTag, 107},
+       {"float64touint32", funcTag, 108},
+       {"int64tofloat64", funcTag, 109},
+       {"uint64tofloat64", funcTag, 110},
+       {"uint32tofloat64", funcTag, 111},
+       {"complex128div", funcTag, 112},
+       {"racefuncenter", funcTag, 113},
        {"racefuncexit", funcTag, 5},
-       {"raceread", funcTag, 111},
-       {"racewrite", funcTag, 111},
-       {"racereadrange", funcTag, 112},
-       {"racewriterange", funcTag, 112},
-       {"msanread", funcTag, 112},
-       {"msanwrite", funcTag, 112},
+       {"raceread", funcTag, 113},
+       {"racewrite", funcTag, 113},
+       {"racereadrange", funcTag, 114},
+       {"racewriterange", funcTag, 114},
+       {"msanread", funcTag, 114},
+       {"msanwrite", funcTag, 114},
        {"support_popcnt", varTag, 11},
        {"support_sse41", varTag, 11},
 }
 
 func runtimeTypes() []*types.Type {
-       var typs [113]*types.Type
+       var typs [115]*types.Type
        typs[0] = types.Bytetype
        typs[1] = types.NewPtr(typs[0])
        typs[2] = types.Types[TANY]
@@ -210,59 +212,61 @@ func runtimeTypes() []*types.Type {
        typs[56] = types.NewPtr(typs[48])
        typs[57] = types.Types[TUNSAFEPTR]
        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[60] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[3])}, []*Node{anonfield(typs[59])})
-       typs[61] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[3])}, []*Node{anonfield(typs[59])})
-       typs[62] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3])})
-       typs[63] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
-       typs[64] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3])})
-       typs[65] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
-       typs[66] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
-       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[68] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[3])}, nil)
-       typs[69] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[59]), anonfield(typs[2])}, nil)
-       typs[70] = functype(nil, []*Node{anonfield(typs[3])}, nil)
-       typs[71] = types.NewChan(typs[2], types.Cboth)
-       typs[72] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[71])})
-       typs[73] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[71])})
-       typs[74] = types.NewChan(typs[2], types.Crecv)
-       typs[75] = functype(nil, []*Node{anonfield(typs[74]), anonfield(typs[3])}, nil)
-       typs[76] = functype(nil, []*Node{anonfield(typs[74]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
-       typs[77] = types.NewChan(typs[2], types.Csend)
+       typs[59] = types.Types[TUINT32]
+       typs[60] = functype(nil, nil, []*Node{anonfield(typs[59])})
+       typs[61] = types.NewMap(typs[2], typs[2])
+       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[32]), anonfield(typs[3])}, []*Node{anonfield(typs[61])})
+       typs[64] = functype(nil, nil, []*Node{anonfield(typs[61])})
+       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[61]), anonfield(typs[2])}, []*Node{anonfield(typs[3])})
+       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[61]), anonfield(typs[3])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
+       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[1]), anonfield(typs[61]), anonfield(typs[3]), anonfield(typs[1])}, []*Node{anonfield(typs[3]), anonfield(typs[11])})
+       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[61]), anonfield(typs[2])}, nil)
+       typs[73] = functype(nil, []*Node{anonfield(typs[3])}, nil)
+       typs[74] = types.NewChan(typs[2], types.Cboth)
+       typs[75] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15])}, []*Node{anonfield(typs[74])})
+       typs[76] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32])}, []*Node{anonfield(typs[74])})
+       typs[77] = types.NewChan(typs[2], types.Crecv)
        typs[78] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, nil)
-       typs[79] = types.NewArray(typs[0], 3)
-       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[81] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[2])}, nil)
-       typs[82] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
-       typs[83] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
-       typs[84] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[2]), anonfield(typs[2])}, []*Node{anonfield(typs[32])})
-       typs[85] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
-       typs[86] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[74])}, []*Node{anonfield(typs[11])})
-       typs[87] = types.NewPtr(typs[11])
-       typs[88] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[87]), anonfield(typs[74])}, []*Node{anonfield(typs[11])})
-       typs[89] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[8])}, nil)
-       typs[90] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[77]), anonfield(typs[3])}, nil)
-       typs[91] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[74]), anonfield(typs[3]), anonfield(typs[87])}, nil)
-       typs[92] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[32])})
-       typs[93] = types.NewSlice(typs[2])
-       typs[94] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[32]), anonfield(typs[32])}, []*Node{anonfield(typs[93])})
-       typs[95] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[93])})
-       typs[96] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[93]), anonfield(typs[32])}, []*Node{anonfield(typs[93])})
-       typs[97] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, nil)
-       typs[98] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[48])}, nil)
-       typs[99] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3]), anonfield(typs[48])}, []*Node{anonfield(typs[11])})
-       typs[100] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
-       typs[101] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
-       typs[102] = functype(nil, []*Node{anonfield(typs[17]), anonfield(typs[17])}, []*Node{anonfield(typs[17])})
-       typs[103] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[15])})
-       typs[104] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])})
-       typs[105] = types.Types[TUINT32]
-       typs[106] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[105])})
-       typs[107] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])})
-       typs[108] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])})
-       typs[109] = functype(nil, []*Node{anonfield(typs[105])}, []*Node{anonfield(typs[13])})
-       typs[110] = functype(nil, []*Node{anonfield(typs[19]), anonfield(typs[19])}, []*Node{anonfield(typs[19])})
-       typs[111] = functype(nil, []*Node{anonfield(typs[48])}, nil)
-       typs[112] = functype(nil, []*Node{anonfield(typs[48]), anonfield(typs[48])}, nil)
+       typs[79] = functype(nil, []*Node{anonfield(typs[77]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
+       typs[80] = types.NewChan(typs[2], types.Csend)
+       typs[81] = functype(nil, []*Node{anonfield(typs[80]), anonfield(typs[3])}, nil)
+       typs[82] = types.NewArray(typs[0], 3)
+       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[3]), anonfield(typs[2])}, nil)
+       typs[85] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3]), anonfield(typs[3])}, nil)
+       typs[86] = functype(nil, []*Node{anonfield(typs[1]), anonfield(typs[3])}, nil)
+       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[80]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
+       typs[89] = functype(nil, []*Node{anonfield(typs[3]), anonfield(typs[77])}, []*Node{anonfield(typs[11])})
+       typs[90] = types.NewPtr(typs[11])
+       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]), anonfield(typs[15]), anonfield(typs[8])}, nil)
+       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[77]), anonfield(typs[3]), anonfield(typs[90])}, nil)
+       typs[95] = functype(nil, []*Node{anonfield(typs[1])}, []*Node{anonfield(typs[32])})
+       typs[96] = types.NewSlice(typs[2])
+       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[1]), anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[96])})
+       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]), anonfield(typs[48])}, nil)
+       typs[101] = functype(nil, []*Node{anonfield(typs[57]), anonfield(typs[48])}, nil)
+       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[3]), anonfield(typs[3])}, []*Node{anonfield(typs[11])})
+       typs[104] = functype(nil, []*Node{anonfield(typs[15]), anonfield(typs[15])}, []*Node{anonfield(typs[15])})
+       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[15])})
+       typs[107] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[17])})
+       typs[108] = functype(nil, []*Node{anonfield(typs[13])}, []*Node{anonfield(typs[59])})
+       typs[109] = functype(nil, []*Node{anonfield(typs[15])}, []*Node{anonfield(typs[13])})
+       typs[110] = functype(nil, []*Node{anonfield(typs[17])}, []*Node{anonfield(typs[13])})
+       typs[111] = functype(nil, []*Node{anonfield(typs[59])}, []*Node{anonfield(typs[13])})
+       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[:]
 }
index 5220e251fa67d7cc3b1e3a8c7eeb73354090f1e4..a27abcafa2fe35f8c8c1228165627d7098676545 100644 (file)
@@ -91,9 +91,12 @@ func panicnildottype(want *byte)
 func ifaceeq(tab *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
 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_small() (hmap map[any]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_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
index 41dcfe994a7d48184c467e881fb0c4f5e1a65ba6..faed5bd9a5f2b7014c28ae42b5d64a335cc829cc 100644 (file)
@@ -252,8 +252,8 @@ func hmap(t *types.Type) *types.Type {
                makefield("flags", types.Types[TUINT8]),
                makefield("B", types.Types[TUINT8]),
                makefield("noverflow", types.Types[TUINT16]),
-               makefield("hash0", types.Types[TUINT32]),
-               makefield("buckets", types.NewPtr(bmap)), // Used in walk.go for makemap.
+               makefield("hash0", 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("nevacuate", types.Types[TUINTPTR]),
                makefield("extra", types.Types[TUNSAFEPTR]),
index 34c74a281b1d21f0c609aaf374ef03ae588e81b9..4bb88b62efddd9ae3519a8b66338a28263f07165 100644 (file)
@@ -1574,6 +1574,12 @@ func (s *state) expr(n *Node) *ssa.Value {
                        return v
                }
 
+               // map <--> *hmap
+               if to.Etype == TMAP && from.IsPtr() &&
+                       to.MapType().Hmap == from.Elem() {
+                       return v
+               }
+
                dowidth(from)
                dowidth(to)
                if from.Width != to.Width {
index a3d8df8ffc8a4c8a83ba6cfa7f8b9ccd5a762f58..4eb2dae5562c6efeddecaeb4b39021debe0b77c4 100644 (file)
@@ -927,6 +927,14 @@ func convertop(src *types.Type, dst *types.Type, why *string) Op {
                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
 }
 
index 0f75473b3f18c9239edea74c9b2a73811faebee7..11825d9eac4f4c2bda7a53da7e016daf8446d63c 100644 (file)
@@ -1467,28 +1467,63 @@ opswitch:
                                na = typecheck(na, Etop)
                                init.Append(na)
                        }
-               } else {
-                       // h = nil
-                       h = nodnil()
                }
 
-               // When hint fits into int, use makemap instead of
-               // makemap64, which is faster and shorter on 32 bit platforms.
-               fnname := "makemap64"
-               argtype := types.Types[TINT64]
+               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 {
+                               // 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()
+                       }
+                       // 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.
 
-               // Type checking guarantees that TIDEAL hint is positive and fits in an int.
-               // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function.
-               // The case of hint overflow when converting TUINT or TUINTPTR to TINT
-               // will be handled by the negative range checks in makemap during runtime.
-               if hint.Type.IsKind(TIDEAL) || maxintval[hint.Type.Etype].Cmp(maxintval[TUINT]) <= 0 {
-                       fnname = "makemap"
-                       argtype = types.Types[TINT]
-               }
+                       // When hint fits into int, use makemap instead of
+                       // makemap64, which is faster and shorter on 32 bit platforms.
+                       fnname := "makemap64"
+                       argtype := types.Types[TINT64]
 
-               fn := syslook(fnname)
-               fn = substArgTypes(fn, hmapType, t.Key(), t.Val())
-               n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h)
+                       // Type checking guarantees that TIDEAL hint is positive and fits in an int.
+                       // See checkmake call in TMAP case of OMAKE case in OpSwitch in typecheck1 function.
+                       // The case of hint overflow when converting TUINT or TUINTPTR to TINT
+                       // will be handled by the negative range checks in makemap during runtime.
+                       if hint.Type.IsKind(TIDEAL) || maxintval[hint.Type.Etype].Cmp(maxintval[TUINT]) <= 0 {
+                               fnname = "makemap"
+                               argtype = types.Types[TINT]
+                       }
+
+                       fn := syslook(fnname)
+                       fn = substArgTypes(fn, hmapType, t.Key(), t.Val())
+                       n = mkcall1(fn, n.Type, init, typename(n.Type), conv(hint, argtype), h)
+               }
 
        case OMAKESLICE:
                l := n.Left
index 599ac2d84a3a0847c3daf20f5d3015481ac514f1..385c569ed88fabdecd1763442fbec72dfb314c2c 100644 (file)
@@ -377,11 +377,16 @@ func (rw *RWMutex) Unlock() {
        rw.rw.unlock()
 }
 
-func MapBuckets(m map[int]int) int {
+func MapBucketsCount(m map[int]int) int {
        h := *(**hmap)(unsafe.Pointer(&m))
        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) {
        g := getg()
        if g.m.lockedExt+g.m.lockedInt == 0 {
index f537098854a4f1b2da1af87411cac2e606899a1e..dee5dd581676ef9449b7c01ea5465bc994c11885 100644 (file)
@@ -281,7 +281,16 @@ func makemap64(t *maptype, hint int64, h *hmap) *hmap {
        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
 // 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.
index 0529cb8e8697e24cb65a08940833caac606b73dc..6ed655de0ab661d7866399c4876cb8348d60a1bb 100644 (file)
@@ -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) {
        // 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.
-       for _, tt := range [...]struct {
-               n, b int
-       }{
-               {8, 1},
-               {9, 2},
-               {13, 2},
-               {14, 4},
-               {26, 4},
-       } {
-               m := map[int]int{}
-               for i := 0; i < tt.n; i++ {
-                       m[i] = i
+       t.Run("mapliteral", func(t *testing.T) {
+               for _, tt := range mapBucketTests {
+                       localMap := 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 := 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)
+                       }
+                       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
                }
-               if got := runtime.MapBuckets(m); got != tt.b {
-                       t.Errorf("no hint n=%d want %d buckets, got %d", tt.n, tt.b, got)
+       })
+       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)
+                       }
+                       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
                }
-               m = make(map[int]int, tt.n)
-               for i := 0; i < tt.n; i++ {
-                       m[i] = i
+       })
+       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
                }
-               if got := runtime.MapBuckets(m); got != tt.b {
-                       t.Errorf("hint n=%d want %d buckets, got %d", tt.n, tt.b, got)
+       })
+       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) {
index 03194bcd58e03a602cd2deb7ef1a114a17e59fe7..476f9a791febd4562b1f5a3cc0ef450e2f779a96 100644 (file)
@@ -76,7 +76,7 @@ import "fmt"
 import "runtime"
 var gslice []string
 func main() {
-       mapvar := make(map[string]string,5)
+       mapvar := make(map[string]string, 13)
        mapvar["abc"] = "def"
        mapvar["ghi"] = "jkl"
        strvar := "abc"
@@ -198,8 +198,10 @@ func testGdbPython(t *testing.T, cgo bool) {
                t.Fatalf("info goroutines failed: %s", bl)
        }
 
-       printMapvarRe := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
-       if bl := blocks["print mapvar"]; !printMapvarRe.MatchString(bl) {
+       printMapvarRe1 := regexp.MustCompile(`\Q = map[string]string = {["abc"] = "def", ["ghi"] = "jkl"}\E$`)
+       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)
        }
 
index dd45e38025fc2a1e00a9d98535f0c5557d5d564e..e54336ead7f5a13495104effc091c5d829a8feca 100644 (file)
@@ -644,7 +644,7 @@ func useT40(*T40)
 
 func newT40() *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
 }
 
@@ -656,7 +656,7 @@ func bad40() {
 
 func good40() {
        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
        printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
        useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"
index 5c5706d225765064ee589b16ebf3fdf8721bc9ef..cc1b0b7acf580cb1902c71ee143b658b747bd255 100644 (file)
@@ -23,7 +23,7 @@ type T40 struct {
 
 func newT40() *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
 }
 
@@ -35,7 +35,7 @@ func bad40() {
 
 func good40() {
        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
        printnl() // ERROR "live at call to printnl: .autotmp_[0-9]+ ret$"
        useT40(t) // ERROR "live at call to useT40: .autotmp_[0-9]+ ret$"