Commit 3278dc15 authored by Keith Randall's avatar Keith Randall

runtime: pass key/value to map accessors by reference, not by value.

This change is part of the plan to get rid of all vararg C calls
which are a pain for getting exact stack scanning.

We allocate a chunk of zero memory to return a pointer to when a
map access doesn't find the key.  This is simpler than returning nil
and fixing things up in the caller.  Linker magic allocates a single
zero memory area that is shared by all (non-reflect-generated) map
types.

Passing things by reference gets rid of some copies, so it speeds
up code with big keys/values.

benchmark             old ns/op    new ns/op    delta
BenchmarkBigKeyMap           34           31   -8.48%
BenchmarkBigValMap           37           30  -18.62%
BenchmarkSmallKeyMap         26           23  -11.28%

R=golang-dev, dvyukov, khr, rsc
CC=golang-dev
https://golang.org/cl/14794043
parent de0fd9ac
...@@ -60,20 +60,18 @@ char *runtimeimport = ...@@ -60,20 +60,18 @@ char *runtimeimport =
"func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
"func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n" "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n"
"func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n" "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n"
"func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 any)\n" "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n"
"func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
"func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
"func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
"func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 any, @\"\".pres·2 bool)\n" "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 *any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
"func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" "func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
"func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" "func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
"func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n" "func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
"func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any, @\"\".val·4 any)\n" "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any, @\"\".val·4 *any)\n"
"func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n" "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n"
"func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any)\n" "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n"
"func @\"\".mapiternext (@\"\".hiter·1 *any)\n" "func @\"\".mapiternext (@\"\".hiter·1 *any)\n"
"func @\"\".mapiter1 (@\"\".hiter·2 *any) (@\"\".key·1 any)\n"
"func @\"\".mapiter2 (@\"\".hiter·3 *any) (@\"\".key·1 any, @\"\".val·2 any)\n"
"func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n"
"func @\"\".chanrecv1 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any) (@\"\".elem·1 any)\n" "func @\"\".chanrecv1 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any) (@\"\".elem·1 any)\n"
"func @\"\".chanrecv2 (@\"\".chanType·3 *byte, @\"\".hchan·4 <-chan any) (@\"\".elem·1 any, @\"\".received·2 bool)\n" "func @\"\".chanrecv2 (@\"\".chanType·3 *byte, @\"\".hchan·4 <-chan any) (@\"\".elem·1 any, @\"\".received·2 bool)\n"
......
...@@ -706,6 +706,10 @@ typefmt(Fmt *fp, Type *t) ...@@ -706,6 +706,10 @@ typefmt(Fmt *fp, Type *t)
t = t->hmap; t = t->hmap;
return fmtprint(fp, "map.bucket[%T]%T", t->down, t->type); return fmtprint(fp, "map.bucket[%T]%T", t->down, t->type);
} }
if(t->hiter != T) {
t = t->hiter;
return fmtprint(fp, "map.iter[%T]%T", t->down, t->type);
}
if(t->funarg) { if(t->funarg) {
fmtstrcpy(fp, "("); fmtstrcpy(fp, "(");
......
...@@ -190,6 +190,7 @@ struct Type ...@@ -190,6 +190,7 @@ struct Type
// TMAP // TMAP
Type* bucket; // internal type representing a hash bucket Type* bucket; // internal type representing a hash bucket
Type* hmap; // internal type representing a Hmap (map header object) Type* hmap; // internal type representing a Hmap (map header object)
Type* hiter; // internal type representing hash iterator state
int32 maplineno; // first use of TFORW as map key int32 maplineno; // first use of TFORW as map key
int32 embedlineno; // first use of TFORW as embedded type int32 embedlineno; // first use of TFORW as embedded type
...@@ -1274,6 +1275,7 @@ Sym* tracksym(Type *t); ...@@ -1274,6 +1275,7 @@ Sym* tracksym(Type *t);
Sym* typesymprefix(char *prefix, Type *t); Sym* typesymprefix(char *prefix, Type *t);
int haspointers(Type *t); int haspointers(Type *t);
void usefield(Node*); void usefield(Node*);
Type* hiter(Type* t);
/* /*
* select.c * select.c
......
...@@ -111,6 +111,8 @@ walkrange(Node *n) ...@@ -111,6 +111,8 @@ walkrange(Node *n)
Node *hb; // hidden bool Node *hb; // hidden bool
Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 Node *a, *v1, *v2; // not hidden aggregate, val 1, 2
Node *fn, *tmp; Node *fn, *tmp;
Node *keyname, *valname;
Node *key, *val;
NodeList *body, *init; NodeList *body, *init;
Type *th, *t; Type *th, *t;
int lno; int lno;
...@@ -182,37 +184,33 @@ walkrange(Node *n) ...@@ -182,37 +184,33 @@ walkrange(Node *n)
break; break;
case TMAP: case TMAP:
th = typ(TARRAY); // allocate an iterator state structure on the stack
th->type = ptrto(types[TUINT8]); th = hiter(t);
// see ../../pkg/runtime/hashmap.c:/hash_iter
// Size of hash_iter in # of pointers.
th->bound = 11;
hit = temp(th); hit = temp(th);
keyname = newname(th->type->sym); // depends on layout of iterator struct. See reflect.c:hiter
valname = newname(th->type->down->sym); // ditto
fn = syslook("mapiterinit", 1); fn = syslook("mapiterinit", 1);
argtype(fn, t->down); argtype(fn, t->down);
argtype(fn, t->type); argtype(fn, t->type);
argtype(fn, th); argtype(fn, th);
init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N))); init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N)));
n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil()); n->ntest = nod(ONE, nod(ODOT, hit, keyname), nodnil());
fn = syslook("mapiternext", 1); fn = syslook("mapiternext", 1);
argtype(fn, th); argtype(fn, th);
n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N)); n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
key = nod(ODOT, hit, keyname);
key = nod(OIND, key, N);
if(v2 == N) { if(v2 == N) {
fn = syslook("mapiter1", 1); a = nod(OAS, v1, key);
argtype(fn, th);
argtype(fn, t->down);
a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N)));
} else { } else {
fn = syslook("mapiter2", 1); val = nod(ODOT, hit, valname);
argtype(fn, th); val = nod(OIND, val, N);
argtype(fn, t->down);
argtype(fn, t->type);
a = nod(OAS2, N, N); a = nod(OAS2, N, N);
a->list = list(list1(v1), v2); a->list = list(list1(v1), v2);
a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N))); a->rlist = list(list1(key), val);
} }
body = list1(a); body = list1(a);
break; break;
......
...@@ -233,6 +233,85 @@ hmap(Type *t) ...@@ -233,6 +233,85 @@ hmap(Type *t)
return h; return h;
} }
Type*
hiter(Type *t)
{
int32 n, off;
Type *field[7];
Type *i;
if(t->hiter != T)
return t->hiter;
// build a struct:
// hash_iter {
// key *Key
// val *Value
// t *MapType
// h *Hmap
// buckets *Bucket
// bptr *Bucket
// other [5]uintptr
// }
// must match ../../pkg/runtime/hashmap.c:hash_iter.
field[0] = typ(TFIELD);
field[0]->type = ptrto(t->down);
field[0]->sym = mal(sizeof(Sym));
field[0]->sym->name = "key";
field[1] = typ(TFIELD);
field[1]->type = ptrto(t->type);
field[1]->sym = mal(sizeof(Sym));
field[1]->sym->name = "val";
field[2] = typ(TFIELD);
field[2]->type = ptrto(types[TUINT8]); // TODO: is there a Type type?
field[2]->sym = mal(sizeof(Sym));
field[2]->sym->name = "t";
field[3] = typ(TFIELD);
field[3]->type = ptrto(hmap(t));
field[3]->sym = mal(sizeof(Sym));
field[3]->sym->name = "h";
field[4] = typ(TFIELD);
field[4]->type = ptrto(mapbucket(t));
field[4]->sym = mal(sizeof(Sym));
field[4]->sym->name = "buckets";
field[5] = typ(TFIELD);
field[5]->type = ptrto(mapbucket(t));
field[5]->sym = mal(sizeof(Sym));
field[5]->sym->name = "bptr";
// all other non-pointer fields
field[6] = typ(TFIELD);
field[6]->type = typ(TARRAY);
field[6]->type->type = types[TUINTPTR];
field[6]->type->bound = 5;
field[6]->type->width = 5 * widthptr;
field[6]->sym = mal(sizeof(Sym));
field[6]->sym->name = "other";
// build iterator struct holding the above fields
i = typ(TSTRUCT);
i->noalg = 1;
i->type = field[0];
off = 0;
for(n = 0; n < 6; n++) {
field[n]->down = field[n+1];
field[n]->width = off;
off += field[n]->type->width;
}
field[6]->down = T;
off += field[6]->type->width;
if(off != 11 * widthptr)
yyerror("hash_iter size not correct %d %d", off, 11 * widthptr);
t->hiter = i;
i->hiter = t;
return i;
}
/* /*
* f is method type, with receiver. * f is method type, with receiver.
* return function type, receiver as first argument (or not). * return function type, receiver as first argument (or not).
...@@ -656,7 +735,7 @@ static int ...@@ -656,7 +735,7 @@ static int
dcommontype(Sym *s, int ot, Type *t) dcommontype(Sym *s, int ot, Type *t)
{ {
int i, alg, sizeofAlg; int i, alg, sizeofAlg;
Sym *sptr, *algsym; Sym *sptr, *algsym, *zero;
static Sym *algarray; static Sym *algarray;
char *p; char *p;
...@@ -677,6 +756,18 @@ dcommontype(Sym *s, int ot, Type *t) ...@@ -677,6 +756,18 @@ dcommontype(Sym *s, int ot, Type *t)
else else
sptr = weaktypesym(ptrto(t)); sptr = weaktypesym(ptrto(t));
// All (non-reflect-allocated) Types share the same zero object.
// Each place in the compiler where a pointer to the zero object
// might be returned by a runtime call (map access return value,
// 2-arg type cast) declares the size of the zerovalue it needs.
// The linker magically takes the max of all the sizes.
zero = pkglookup("zerovalue", runtimepkg);
ggloblsym(zero, 0, 1, 1);
// We use size 0 here so we get the pointer to the zero value,
// but don't allocate space for the zero value unless we need it.
// TODO: how do we get this symbol into bss? We really want
// a read-only bss, but I don't think such a thing exists.
// ../../pkg/reflect/type.go:/^type.commonType // ../../pkg/reflect/type.go:/^type.commonType
// actual type structure // actual type structure
// type commonType struct { // type commonType struct {
...@@ -691,6 +782,7 @@ dcommontype(Sym *s, int ot, Type *t) ...@@ -691,6 +782,7 @@ dcommontype(Sym *s, int ot, Type *t)
// string *string // string *string
// *extraType // *extraType
// ptrToThis *Type // ptrToThis *Type
// zero unsafe.Pointer
// } // }
ot = duintptr(s, ot, t->width); ot = duintptr(s, ot, t->width);
ot = duint32(s, ot, typehash(t)); ot = duint32(s, ot, typehash(t));
...@@ -728,6 +820,7 @@ dcommontype(Sym *s, int ot, Type *t) ...@@ -728,6 +820,7 @@ dcommontype(Sym *s, int ot, Type *t)
ot += widthptr; ot += widthptr;
ot = dsymptr(s, ot, sptr, 0); // ptrto type ot = dsymptr(s, ot, sptr, 0); // ptrto type
ot = dsymptr(s, ot, zero, 0); // ptr to zero value
return ot; return ot;
} }
...@@ -893,7 +986,7 @@ ok: ...@@ -893,7 +986,7 @@ ok:
switch(t->etype) { switch(t->etype) {
default: default:
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
break; break;
case TARRAY: case TARRAY:
...@@ -905,7 +998,7 @@ ok: ...@@ -905,7 +998,7 @@ ok:
t2->bound = -1; // slice t2->bound = -1; // slice
s2 = dtypesym(t2); s2 = dtypesym(t2);
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s1, 0);
ot = dsymptr(s, ot, s2, 0); ot = dsymptr(s, ot, s2, 0);
ot = duintptr(s, ot, t->bound); ot = duintptr(s, ot, t->bound);
...@@ -913,7 +1006,7 @@ ok: ...@@ -913,7 +1006,7 @@ ok:
// ../../pkg/runtime/type.go:/SliceType // ../../pkg/runtime/type.go:/SliceType
s1 = dtypesym(t->type); s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s1, 0);
} }
break; break;
...@@ -922,7 +1015,7 @@ ok: ...@@ -922,7 +1015,7 @@ ok:
// ../../pkg/runtime/type.go:/ChanType // ../../pkg/runtime/type.go:/ChanType
s1 = dtypesym(t->type); s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s1, 0);
ot = duintptr(s, ot, t->chan); ot = duintptr(s, ot, t->chan);
break; break;
...@@ -939,7 +1032,7 @@ ok: ...@@ -939,7 +1032,7 @@ ok:
dtypesym(t1->type); dtypesym(t1->type);
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = duint8(s, ot, isddd); ot = duint8(s, ot, isddd);
// two slice headers: in and out. // two slice headers: in and out.
...@@ -971,7 +1064,7 @@ ok: ...@@ -971,7 +1064,7 @@ ok:
// ../../pkg/runtime/type.go:/InterfaceType // ../../pkg/runtime/type.go:/InterfaceType
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint);
...@@ -990,7 +1083,7 @@ ok: ...@@ -990,7 +1083,7 @@ ok:
s3 = dtypesym(mapbucket(t)); s3 = dtypesym(mapbucket(t));
s4 = dtypesym(hmap(t)); s4 = dtypesym(hmap(t));
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s1, 0);
ot = dsymptr(s, ot, s2, 0); ot = dsymptr(s, ot, s2, 0);
ot = dsymptr(s, ot, s3, 0); ot = dsymptr(s, ot, s3, 0);
...@@ -1007,7 +1100,7 @@ ok: ...@@ -1007,7 +1100,7 @@ ok:
// ../../pkg/runtime/type.go:/PtrType // ../../pkg/runtime/type.go:/PtrType
s1 = dtypesym(t->type); s1 = dtypesym(t->type);
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s1, 0);
break; break;
...@@ -1020,7 +1113,7 @@ ok: ...@@ -1020,7 +1113,7 @@ ok:
n++; n++;
} }
ot = dcommontype(s, ot, t); ot = dcommontype(s, ot, t);
xt = ot - 2*widthptr; xt = ot - 3*widthptr;
ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint);
ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint);
......
...@@ -83,20 +83,18 @@ func equal(typ *byte, x1, x2 any) (ret bool) ...@@ -83,20 +83,18 @@ func equal(typ *byte, x1, x2 any) (ret bool)
// *byte is really *runtime.Type // *byte is really *runtime.Type
func makemap(mapType *byte, hint int64) (hmap map[any]any) func makemap(mapType *byte, hint int64) (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)
func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any) func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any)
func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool) func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool)
func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool) func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
func mapassign1(mapType *byte, hmap map[any]any, key any, val any) func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any)
func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
func mapdelete(mapType *byte, hmap map[any]any, key any) func mapdelete(mapType *byte, hmap map[any]any, key *any)
func mapiternext(hiter *any) func mapiternext(hiter *any)
func mapiter1(hiter *any) (key any)
func mapiter2(hiter *any) (key any, val any)
// *byte is really *runtime.Type // *byte is really *runtime.Type
func makechan(chanType *byte, hint int64) (hchan chan any) func makechan(chanType *byte, hint int64) (hchan chan any)
......
...@@ -341,13 +341,14 @@ void ...@@ -341,13 +341,14 @@ void
walkexpr(Node **np, NodeList **init) walkexpr(Node **np, NodeList **init)
{ {
Node *r, *l, *var, *a; Node *r, *l, *var, *a;
Node *map, *key, *keyvar;
NodeList *ll, *lr, *lpost; NodeList *ll, *lr, *lpost;
Type *t; Type *t;
int et, old_safemode; int et, old_safemode;
int64 v; int64 v;
int32 lno; int32 lno;
Node *n, *fn, *n1, *n2; Node *n, *fn, *n1, *n2;
Sym *sym; Sym *sym, *zero;
char buf[100], *p; char buf[100], *p;
n = *np; n = *np;
...@@ -657,6 +658,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -657,6 +658,7 @@ walkexpr(Node **np, NodeList **init)
r = n->rlist->n; r = n->rlist->n;
walkexprlistsafe(n->list, init); walkexprlistsafe(n->list, init);
walkexpr(&r->left, init); walkexpr(&r->left, init);
walkexpr(&r->right, init);
t = r->left->type; t = r->left->type;
p = nil; p = nil;
if(t->type->width <= 128) { // Check ../../pkg/runtime/hashmap.c:MAXVALUESIZE before changing. if(t->type->width <= 128) { // Check ../../pkg/runtime/hashmap.c:MAXVALUESIZE before changing.
...@@ -675,41 +677,65 @@ walkexpr(Node **np, NodeList **init) ...@@ -675,41 +677,65 @@ walkexpr(Node **np, NodeList **init)
} }
} }
if(p != nil) { if(p != nil) {
// from: // fast versions take key by value
// a,b = m[i] key = r->right;
// to: } else {
// var,b = mapaccess2_fast*(t, m, i) // standard version takes key by reference
// a = *var if(islvalue(r->right)) {
a = n->list->n; key = nod(OADDR, r->right, N);
var = temp(ptrto(t->type)); } else {
var->typecheck = 1; keyvar = temp(t->down);
n1 = nod(OAS, keyvar, r->right);
fn = mapfn(p, t); typecheck(&n1, Etop);
r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, r->right); *init = list(*init, n1);
n->rlist = list1(r); key = nod(OADDR, keyvar, N);
n->op = OAS2FUNC; }
n->list->n = var; p = "mapaccess2";
walkexpr(&n, init); }
*init = list(*init, n);
// from:
n = nod(OAS, a, nod(OIND, var, N)); // a,b = m[i]
typecheck(&n, Etop); // to:
walkexpr(&n, init); // var,b = mapaccess2*(t, m, i)
goto ret; // a = *var
} a = n->list->n;
fn = mapfn("mapaccess2", t); var = temp(ptrto(t->type));
r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, r->right); var->typecheck = 1;
fn = mapfn(p, t);
r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key);
n->rlist = list1(r); n->rlist = list1(r);
n->op = OAS2FUNC; n->op = OAS2FUNC;
goto as2func; n->list->n = var;
walkexpr(&n, init);
*init = list(*init, n);
n = nod(OAS, a, nod(OIND, var, N));
typecheck(&n, Etop);
walkexpr(&n, init);
// mapaccess needs a zero value to be at least this big.
zero = pkglookup("zerovalue", runtimepkg);
ggloblsym(zero, t->type->width, 1, 1);
// TODO: ptr is always non-nil, so disable nil check for this OIND op.
goto ret;
case ODELETE: case ODELETE:
*init = concat(*init, n->ninit); *init = concat(*init, n->ninit);
n->ninit = nil; n->ninit = nil;
l = n->list->n; map = n->list->n;
r = n->list->next->n; key = n->list->next->n;
t = l->type; walkexpr(&map, init);
n = mkcall1(mapfndel("mapdelete", t), t->down, init, typename(t), l, r); walkexpr(&key, init);
if(islvalue(key)) {
key = nod(OADDR, key, N);
} else {
keyvar = temp(key->type);
n1 = nod(OAS, keyvar, key);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
t = map->type;
n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key);
goto ret; goto ret;
case OAS2DOTTYPE: case OAS2DOTTYPE:
...@@ -1063,6 +1089,8 @@ walkexpr(Node **np, NodeList **init) ...@@ -1063,6 +1089,8 @@ walkexpr(Node **np, NodeList **init)
case OINDEXMAP: case OINDEXMAP:
if(n->etype == 1) if(n->etype == 1)
goto ret; goto ret;
walkexpr(&n->left, init);
walkexpr(&n->right, init);
t = n->left->type; t = n->left->type;
p = nil; p = nil;
...@@ -1082,16 +1110,28 @@ walkexpr(Node **np, NodeList **init) ...@@ -1082,16 +1110,28 @@ walkexpr(Node **np, NodeList **init)
} }
} }
if(p != nil) { if(p != nil) {
// use fast version. The fast versions return a pointer to the value - we need // fast versions take key by value
// to dereference it to get the result. key = n->right;
n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, n->right);
n = nod(OIND, n, N);
n->type = t->type;
n->typecheck = 1;
} else { } else {
// no fast version for this key // standard version takes key by reference
n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right); if(islvalue(n->right)) {
} key = nod(OADDR, n->right, N);
} else {
keyvar = temp(t->down);
n1 = nod(OAS, keyvar, n->right);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
p = "mapaccess1";
}
n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key);
n = nod(OIND, n, N);
n->type = t->type;
n->typecheck = 1;
// mapaccess needs a zero value to be at least this big.
zero = pkglookup("zerovalue", runtimepkg);
ggloblsym(zero, t->type->width, 1, 1);
goto ret; goto ret;
case ORECV: case ORECV:
...@@ -1911,6 +1951,8 @@ static Node* ...@@ -1911,6 +1951,8 @@ static Node*
convas(Node *n, NodeList **init) convas(Node *n, NodeList **init)
{ {
Type *lt, *rt; Type *lt, *rt;
Node *map, *key, *keyvar, *val, *valvar;
Node *n1;
if(n->op != OAS) if(n->op != OAS)
fatal("convas: not OAS %O", n->op); fatal("convas: not OAS %O", n->op);
...@@ -1931,9 +1973,32 @@ convas(Node *n, NodeList **init) ...@@ -1931,9 +1973,32 @@ convas(Node *n, NodeList **init)
} }
if(n->left->op == OINDEXMAP) { if(n->left->op == OINDEXMAP) {
n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init, map = n->left->left;
typename(n->left->left->type), key = n->left->right;
n->left->left, n->left->right, n->right); val = n->right;
walkexpr(&map, init);
walkexpr(&key, init);
walkexpr(&val, init);
if(islvalue(key)) {
key = nod(OADDR, key, N);
} else {
keyvar = temp(key->type);
n1 = nod(OAS, keyvar, key);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
if(islvalue(val)) {
val = nod(OADDR, val, N);
} else {
valvar = temp(val->type);
n1 = nod(OAS, valvar, val);
typecheck(&n1, Etop);
*init = list(*init, n1);
val = nod(OADDR, valvar, N);
}
n = mkcall1(mapfn("mapassign1", map->type), T, init,
typename(map->type), map, key, val);
goto out; goto out;
} }
......
...@@ -252,6 +252,7 @@ type rtype struct { ...@@ -252,6 +252,7 @@ type rtype struct {
string *string // string form; unnecessary but undeniably useful string *string // string form; unnecessary but undeniably useful
*uncommonType // (relatively) uncommon fields *uncommonType // (relatively) uncommon fields
ptrToThis *rtype // type for pointer to this type, if used in binary or has methods ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
zero unsafe.Pointer // pointer to zero value
} }
// Method on non-interface type // Method on non-interface type
...@@ -1089,6 +1090,7 @@ func (t *rtype) ptrTo() *rtype { ...@@ -1089,6 +1090,7 @@ func (t *rtype) ptrTo() *rtype {
p.uncommonType = nil p.uncommonType = nil
p.ptrToThis = nil p.ptrToThis = nil
p.zero = unsafe.Pointer(&make([]byte, p.size)[0])
p.elem = t p.elem = t
if t.kind&kindNoPointers != 0 { if t.kind&kindNoPointers != 0 {
...@@ -1475,6 +1477,7 @@ func ChanOf(dir ChanDir, t Type) Type { ...@@ -1475,6 +1477,7 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.elem = typ ch.elem = typ
ch.uncommonType = nil ch.uncommonType = nil
ch.ptrToThis = nil ch.ptrToThis = nil
ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0])
ch.gc = unsafe.Pointer(&chanGC{ ch.gc = unsafe.Pointer(&chanGC{
width: ch.size, width: ch.size,
...@@ -1534,6 +1537,7 @@ func MapOf(key, elem Type) Type { ...@@ -1534,6 +1537,7 @@ func MapOf(key, elem Type) Type {
mt.hmap = hMapOf(mt.bucket) mt.hmap = hMapOf(mt.bucket)
mt.uncommonType = nil mt.uncommonType = nil
mt.ptrToThis = nil mt.ptrToThis = nil
mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0])
// INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
// fail when mt.gc is wrong. // fail when mt.gc is wrong.
...@@ -1709,6 +1713,7 @@ func SliceOf(t Type) Type { ...@@ -1709,6 +1713,7 @@ func SliceOf(t Type) Type {
slice.elem = typ slice.elem = typ
slice.uncommonType = nil slice.uncommonType = nil
slice.ptrToThis = nil slice.ptrToThis = nil
slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0])
if typ.size == 0 { if typ.size == 0 {
slice.gc = unsafe.Pointer(&sliceEmptyGCProg) slice.gc = unsafe.Pointer(&sliceEmptyGCProg)
...@@ -1778,6 +1783,7 @@ func arrayOf(count int, elem Type) Type { ...@@ -1778,6 +1783,7 @@ func arrayOf(count int, elem Type) Type {
// TODO: array.gc // TODO: array.gc
array.uncommonType = nil array.uncommonType = nil
array.ptrToThis = nil array.ptrToThis = nil
array.zero = unsafe.Pointer(&make([]byte, array.size)[0])
array.len = uintptr(count) array.len = uintptr(count)
array.slice = slice.(*rtype) array.slice = slice.(*rtype)
......
...@@ -2143,7 +2143,7 @@ func Zero(typ Type) Value { ...@@ -2143,7 +2143,7 @@ func Zero(typ Type) Value {
if t.size <= ptrSize { if t.size <= ptrSize {
return Value{t, nil, fl} return Value{t, nil, fl}
} }
return Value{t, unsafe_New(typ.(*rtype)), fl | flagIndir} return Value{t, t.zero, fl | flagIndir}
} }
// New returns a Value representing a pointer to a new zero value // New returns a Value representing a pointer to a new zero value
......
This diff is collapsed.
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
// Fast hashmap lookup specialized to a specific key type. // Fast hashmap lookup specialized to a specific key type.
// Included by hashmap.c once for each specialized type. // Included by hashmap.c once for each specialized type.
// Note that this code differs from hash_lookup in that
// it returns a pointer to the result, not the result itself.
// The returned pointer is only valid until the next GC
// point, so the caller must dereference it before then.
// +build ignore // +build ignore
#pragma textflag NOSPLIT #pragma textflag NOSPLIT
...@@ -31,7 +26,7 @@ HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value) ...@@ -31,7 +26,7 @@ HASH_LOOKUP1(MapType *t, Hmap *h, KEYTYPE key, byte *value)
runtime·prints("\n"); runtime·prints("\n");
} }
if(h == nil || h->count == 0) { if(h == nil || h->count == 0) {
value = empty_value; value = t->elem->zero;
FLUSH(&value); FLUSH(&value);
return; return;
} }
...@@ -120,7 +115,7 @@ dohash: ...@@ -120,7 +115,7 @@ dohash:
b = b->overflow; b = b->overflow;
} while(b != nil); } while(b != nil);
} }
value = empty_value; value = t->elem->zero;
FLUSH(&value); FLUSH(&value);
} }
...@@ -143,7 +138,7 @@ HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res) ...@@ -143,7 +138,7 @@ HASH_LOOKUP2(MapType *t, Hmap *h, KEYTYPE key, byte *value, bool res)
runtime·prints("\n"); runtime·prints("\n");
} }
if(h == nil || h->count == 0) { if(h == nil || h->count == 0) {
value = empty_value; value = t->elem->zero;
res = false; res = false;
FLUSH(&value); FLUSH(&value);
FLUSH(&res); FLUSH(&res);
...@@ -242,7 +237,7 @@ dohash: ...@@ -242,7 +237,7 @@ dohash:
b = b->overflow; b = b->overflow;
} while(b != nil); } while(b != nil);
} }
value = empty_value; value = t->elem->zero;
res = false; res = false;
FLUSH(&value); FLUSH(&value);
FLUSH(&res); FLUSH(&res);
......
...@@ -268,3 +268,33 @@ func BenchmarkSameLengthMap(b *testing.B) { ...@@ -268,3 +268,33 @@ func BenchmarkSameLengthMap(b *testing.B) {
_ = m[s1] _ = m[s1]
} }
} }
type BigKey [3]int64
func BenchmarkBigKeyMap(b *testing.B) {
m := make(map[BigKey]bool)
k := BigKey{3, 4, 5}
m[k] = true
for i := 0; i < b.N; i++ {
_ = m[k]
}
}
type BigVal [3]int64
func BenchmarkBigValMap(b *testing.B) {
m := make(map[BigKey]BigVal)
k := BigKey{3, 4, 5}
m[k] = BigVal{6, 7, 8}
for i := 0; i < b.N; i++ {
_ = m[k]
}
}
func BenchmarkSmallKeyMap(b *testing.B) {
m := make(map[int16]bool)
m[5] = true
for i := 0; i < b.N; i++ {
_ = m[5]
}
}
...@@ -1037,12 +1037,6 @@ void runtime·osyield(void); ...@@ -1037,12 +1037,6 @@ void runtime·osyield(void);
void runtime·lockOSThread(void); void runtime·lockOSThread(void);
void runtime·unlockOSThread(void); void runtime·unlockOSThread(void);
void runtime·mapassign(MapType*, Hmap*, byte*, byte*);
void runtime·mapaccess(MapType*, Hmap*, byte*, byte*, bool*);
void runtime·mapiternext(struct hash_iter*);
bool runtime·mapiterkey(struct hash_iter*, void*);
Hmap* runtime·makemap_c(MapType*, int64);
Hchan* runtime·makechan_c(ChanType*, int64); Hchan* runtime·makechan_c(ChanType*, int64);
void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*); void runtime·chansend(ChanType*, Hchan*, byte*, bool*, void*);
void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*); void runtime·chanrecv(ChanType*, Hchan*, byte*, bool*, bool*);
......
...@@ -31,6 +31,7 @@ struct Type ...@@ -31,6 +31,7 @@ struct Type
String *string; String *string;
UncommonType *x; UncommonType *x;
Type *ptrto; Type *ptrto;
byte *zero; // ptr to the zero value for this type
}; };
struct Method struct Method
......
...@@ -36,6 +36,6 @@ enum { ...@@ -36,6 +36,6 @@ enum {
KindNoPointers = 1<<7, KindNoPointers = 1<<7,
// size of Type structure. // size of Type structure.
CommonSize = 6*PtrSize + 8, CommonSize = 7*PtrSize + 8,
}; };
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