Commit 442de98c authored by Keith Randall's avatar Keith Randall

cmd/compile,runtime: redo how map assignments work

To compile:
  m[k] = v
instead of:
  mapassign(maptype, m, &k, &v), do
do:
  *mapassign(maptype, m, &k) = v

mapassign returns a pointer to the value slot in the map.  It is just
like mapaccess except that it will allocate a new slot if k is not
already present in the map.

This makes map accesses faster but potentially larger (codewise).

It is faster because the write into the map is done when the compiler
knows the concrete type, so it can be done with a few store
instructions instead of calling typedmemmove.  We also potentially
avoid stack temporaries to hold v.

The code can be larger when the map has pointers in its value type,
since there is a write barrier call in addition to the mapassign call.
That makes the code at the callsite a bit bigger (go binary is 0.3%
bigger).

This CL is in preparation for doing operations like m[k] += v with
only a single runtime call.  That will roughly double the speed of
such operations.

Update #17133
Update #5147

Change-Id: Ia435f032090a2ed905dac9234e693972fe8c2dc5
Reviewed-on: https://go-review.googlesource.com/30815
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: default avatarMatthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 55ef67f2
...@@ -57,52 +57,52 @@ const runtimeimport = "" + ...@@ -57,52 +57,52 @@ const runtimeimport = "" +
"ast64\x00\x06\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00:\xc6\x01\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t#m" + "ast64\x00\x06\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00:\xc6\x01\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t#m" +
"apaccess2_faststr\x00\x06\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00:\xc6\x01\x00\x00\x04\x17:" + "apaccess2_faststr\x00\x06\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00:\xc6\x01\x00\x00\x04\x17:" +
"\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x1bmapaccess2_fat\x00\b\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00" + "\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x1bmapaccess2_fat\x00\b\x17\"\xc2\x01\x00\x00\x1d::\xc4\x01\x00\x00" +
"\x17:\xc6\x01\x00\x00\x17\"\rzero·6\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x13mapassig" + "\x17:\xc6\x01\x00\x00\x17\"\rzero·6\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x11mapassig" +
"n1\x00\b\x17\"\x13mapType·1\x00\x00\x1d::\rhmap·2\x00\x00\x17:\vkey·" + "n\x00\x06\x17\"\xa4\x01\x00\x00\x1d::\xb0\x01\x00\x00\x17:\xb2\x01\x00\x00\x02\x17:\xb4\x01\x00\x00\t\x15mapiterin" +
"3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15mapiterinit\x00\x06\x17\"\xd6\x01\x00\x00\x1d::\xd8" + "it\x00\x06\x17\"\x13mapType·1\x00\x00\x1d::\rhmap·2\x00\x00\x17:\x0fhiter" +
"\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11mapdelete\x00\x06\x17\"\xd6\x01\x00\x00\x1d::\xd8" + "·3\x00\x00\x00\t\x11mapdelete\x00\x06\x17\"\xd8\x01\x00\x00\x1d::\xda\x01\x00\x00\x17:\vkey·" +
"\x01\x00\x00\x17:\xda\x01\x00\x00\x00\t\x15mapiternext\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t" + "3\x00\x00\x00\t\x15mapiternext\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakec" +
"\x0fmakechan\x00\x04\x17\"\x15chanType·2\x00\x00\n\xa6\x01\x00\x00\x02\x1f\x06:\x0fhch" + "han\x00\x04\x17\"\x15chanType·2\x00\x00\n\xa6\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00" +
"an·1\x00\x00\t\x11chanrecv1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0f" + "\x00\t\x11chanrecv1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan\xc2" +
"hchan·2\x00\x00\x17:j\x00\x00\x00\t\x11chanrecv2\x00\x06\x17\"\xea\x01\x00\x00\x1f\x02:\x0fh" + "\xb72\x00\x00\x17:j\x00\x00\x00\t\x11chanrecv2\x00\x06\x17\"\xe8\x01\x00\x00\x1f\x02:\x0fhchan·" +
"chan·3\x00\x00\x17:\relem·4\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf0" + "3\x00\x00\x17:\relem·4\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xee\x01\x00\x00\x1f\x04:" +
"\x01\x00\x00\x1f\x04:\xf2\x01\x00\x00\x17:j\x00\x00\x00\t\x11closechan\x00\x02:\xec\x01\x00\x00\x00\a\x17wri" + "\xf0\x01\x00\x00\x17:j\x00\x00\x00\t\x11closechan\x00\x02:\xea\x01\x00\x00\x00\a\x17writeBarr" +
"teBarrier\x00\x15\x06\renabled\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00" + "ier\x00\x15\x06\renabled\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwrit" +
"\t\x1dwritebarrierptr\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00" + "ebarrierptr\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typ" +
"\x00\t\x17typedmemmove\x00\x06\x17\"t\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc\xc2" + "edmemmove\x00\x06\x17\"t\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t" +
"\xb73\x00\x00\x00\t\x1btypedslicecopy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00:\v" + "\x1btypedslicecopy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00:\vsrc·4" +
"src·4\x00\x00\x01\x02\x00\t\x17selectnbsend\x00\x06\x17\"\xea\x01\x00\x00\x1f\x04:\xf6\x01\x00\x00" + "\x00\x00\x01\x02\x00\t\x17selectnbsend\x00\x06\x17\"\xe8\x01\x00\x00\x1f\x04:\xf4\x01\x00\x00\x17:\xf6\x01\x00\x00" +
"\x17:\xf8\x01\x00\x00\x01\x00\x00\t\x17selectnbrecv\x00\x06\x17\"\xea\x01\x00\x00\x17:j\x00\x00\x1f\x02:\x0f" + "\x01\x00\x00\t\x17selectnbrecv\x00\x06\x17\"\xe8\x01\x00\x00\x17:j\x00\x00\x1f\x02:\x0fhchan\xc2" +
"hchan·4\x00\x00\x01\x00\x00\t\x19selectnbrecv2\x00\b\x17\"\xea\x01\x00\x00\x17:j\x00" + "\xb74\x00\x00\x01\x00\x00\t\x19selectnbrecv2\x00\b\x17\"\xe8\x01\x00\x00\x17:j\x00\x00\x17\x00\x15re" +
"\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11news" + "ceived·4\x00\x00\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00" +
"elect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsize·2\x00\x00\b\rsize·" + "\x06\x17\"\vsel·1\x00\x00\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13" +
"3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xf6\x01\x00\x00\x17:\xf8\x01" + "selectsend\x00\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xf4\x01\x00\x00\x17:\xf6\x01\x00\x00\x02\x00\x15s" +
"\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13selectrecv\x00\x06\x17\"\xae\x02\x00\x00\x1f\x02" + "elected·1\x00\x00\t\x13selectrecv\x00\x06\x17\"\xac\x02\x00\x00\x1f\x02:\xf4\x01\x00\x00\x17" +
":\xf6\x01\x00\x00\x17:\xf8\x01\x00\x00\x02\x00\xb0\x02\x00\x00\t\x15selectrecv2\x00\b\x17\"\xae\x02\x00\x00\x1f\x02" + ":\xf6\x01\x00\x00\x02\x00\xae\x02\x00\x00\t\x15selectrecv2\x00\b\x17\"\xac\x02\x00\x00\x1f\x02:\xf4\x01\x00\x00\x17" +
":\xf6\x01\x00\x00\x17:\xf8\x01\x00\x00\x17\x00\x15received·5\x00\x00\x02\x00\xb0\x02\x00\x00\t\x19selec" + ":\xf6\x01\x00\x00\x17\x00\x15received·5\x00\x00\x02\x00\xae\x02\x00\x00\t\x19selectdefau" +
"tdefault\x00\x02\x17\"\xae\x02\x00\x00\x02\x00\xb0\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xa6\x02\x00\x00" + "lt\x00\x02\x17\"\xac\x02\x00\x00\x02\x00\xae\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xa4\x02\x00\x00\x00\t\tblo" +
"\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00\x00\x02" + "ck\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00\x00\x02\vcap·" +
"\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x15makeslice64\x00\x06\x17\"\x06\x00" + "4\x00\x00\x02\x11:\vary·1\x00\x00\t\x15makeslice64\x00\x06\x17\"\x06\x00\x00\n\xbe\x02\x00\x00" +
"\x00\n\xc0\x02\x00\x00\n\xc2\x02\x00\x00\x02\x11:\xc4\x02\x00\x00\t\x11growslice\x00\x06\x17\"\x06\x00\x00\x11:\vo" + "\n\xc0\x02\x00\x00\x02\x11:\xc2\x02\x00\x00\t\x11growslice\x00\x06\x17\"\x06\x00\x00\x11:\vold·3\x00" +
"ld·3\x00\x00\x02\xc2\x02\x00\x00\x02\x11:\xc4\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00" + "\x00\x02\xc0\x02\x00\x00\x02\x11:\xc2\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm" +
"\x17:\vfrm·2\x00\x00\x16\x11length·3\x00^\x00\t\vmemclr\x00\x04\x17\"\vpt" + "·2\x00\x00\x16\x11length·3\x00^\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00" +
"r·1\x00\x00\x16\x11length·2\x00^\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00" + "\x16\x11length·2\x00^\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay\xc2" +
"\x00\x17:\ay·3\x00\x00\x16\rsize·4\x00^\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xdc" + "\xb73\x00\x00\x16\rsize·4\x00^\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc" +
"\x02\x00\x00\x17:\xde\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04\x17:\xdc\x02\x00\x00\x17:\xde\x02\x00\x00\x01\x00" + "\x02\x00\x00\x01\x00\x00\t\x13memequal16\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc\x02\x00\x00\x01\x00\x00\t\x13mem" +
"\x00\t\x13memequal32\x00\x04\x17:\xdc\x02\x00\x00\x17:\xde\x02\x00\x00\x01\x00\x00\t\x13memequal" + "equal32\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc\x02\x00\x00\x01\x00\x00\t\x13memequal64\x00\x04\x17:" +
"64\x00\x04\x17:\xdc\x02\x00\x00\x17:\xde\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xdc\x02\x00\x00" + "\xda\x02\x00\x00\x17:\xdc\x02\x00\x00\x01\x00\x00\t\x15memequal128\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc\x02\x00\x00" +
"\x17:\xde\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00" + "\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\x03\x14\x00\x14\x00\x01" +
"\x03\x14\x00\x14\x00\x01\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03" + "\x14\x00\t\x0fint64mod\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64mod\x00\x03\x14\x00\x14\x00\x01\x14" +
"\x14\x00\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64t" + "\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint6" +
"ouint64\x00\x01\x1a\x00\x01\x14\x00\t\x1dfloat64touint32\x00\x01\x1a\x00\x01\x12\x00\t\x1b" + "4\x00\x01\x1a\x00\x01\x14\x00\t\x1dfloat64touint32\x00\x01\x1a\x00\x01\x12\x00\t\x1bint64t" +
"int64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00\x01" + "ofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t" +
"\x14\x00\x01\x1a\x00\t\x1duint32tofloat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex12" + "\x1duint32tofloat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex128div\x00\x04" +
"8div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19r" + "\x1e\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefun" +
"acefuncenter\x00\x01\x16^\x00\t\x17racefuncexit\x00\x00\x00\t\x0frace" + "center\x00\x01\x16^\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01" +
"read\x00\x01\x16^\x00\t\x11racewrite\x00\x01\x16^\x00\t\x19racereadrange" + "\x16^\x00\t\x11racewrite\x00\x01\x16^\x00\t\x19racereadrange\x00\x04\x16\rad" +
"\x00\x04\x16\raddr·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterang" + "dr·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterange\x00\x04\x16\x90\x03" +
"e\x00\x04\x16\x92\x03\x00^\x16\x94\x03\x00^\x00\t\x0fmsanread\x00\x04\x16\x92\x03\x00^\x16\x94\x03\x00^\x00\t\x11m" + "\x00^\x16\x92\x03\x00^\x00\t\x0fmsanread\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\t\x11msanwri" +
"sanwrite\x00\x04\x16\x92\x03\x00^\x16\x94\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n" "te\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n"
const unsafeimport = "" + const unsafeimport = "" +
"version 2\n\n\x00\x00\x01\vunsafe\x00\t\x0fOffsetof\x00\x01:\x00\x01\x16\x00\t" + "version 2\n\n\x00\x00\x01\vunsafe\x00\t\x0fOffsetof\x00\x01:\x00\x01\x16\x00\t" +
......
...@@ -93,7 +93,7 @@ func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres ...@@ -93,7 +93,7 @@ func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres
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 mapaccess2_fat(mapType *byte, hmap map[any]any, key *any, zero *byte) (val *any, pres bool) func mapaccess2_fat(mapType *byte, hmap map[any]any, key *any, zero *byte) (val *any, pres bool)
func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any) func mapassign(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)
......
...@@ -188,9 +188,9 @@ func isaddrokay(n *Node) bool { ...@@ -188,9 +188,9 @@ func isaddrokay(n *Node) bool {
return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n)) return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n))
} }
// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines. // Orderaddrtemp ensures that n is okay to pass by address to runtime routines.
// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits // If the original argument n is not okay, orderaddrtemp creates a tmp, emits
// tmp = *np, and then sets *np to the tmp variable. // tmp = n, and then returns tmp.
func orderaddrtemp(n *Node, order *Order) *Node { func orderaddrtemp(n *Node, order *Order) *Node {
if isaddrokay(n) { if isaddrokay(n) {
return n return n
...@@ -395,13 +395,8 @@ func ordercall(n *Node, order *Order) { ...@@ -395,13 +395,8 @@ func ordercall(n *Node, order *Order) {
} }
// Ordermapassign appends n to order->out, introducing temporaries // Ordermapassign appends n to order->out, introducing temporaries
// to make sure that all map assignments have the form m[k] = x, // to make sure that all map assignments have the form m[k] = x.
// where x is addressable. // (Note: orderexpr has already been called on n, so we know k is addressable.)
// (Orderexpr has already been called on n, so we know k is addressable.)
//
// If n is m[k] = x where x is not addressable, the rewrite is:
// tmp = x
// m[k] = tmp
// //
// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is // If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
// t1 = m // t1 = m
...@@ -428,7 +423,7 @@ func ordermapassign(n *Node, order *Order) { ...@@ -428,7 +423,7 @@ func ordermapassign(n *Node, order *Order) {
// We call writebarrierfat only for values > 4 pointers long. See walk.go. // We call writebarrierfat only for values > 4 pointers long. See walk.go.
// TODO(mdempsky): writebarrierfat doesn't exist anymore, but removing that // TODO(mdempsky): writebarrierfat doesn't exist anymore, but removing that
// logic causes net/http's tests to become flaky; see CL 21242. // logic causes net/http's tests to become flaky; see CL 21242.
if (n.Left.Op == OINDEXMAP || (needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr))) && !isaddrokay(n.Right) { if needwritebarrier(n.Left, n.Right) && n.Left.Type.Width > int64(4*Widthptr) && !isaddrokay(n.Right) {
m := n.Left m := n.Left
n.Left = ordertemp(m.Type, order, false) n.Left = ordertemp(m.Type, order, false)
a := nod(OAS, m, n.Left) a := nod(OAS, m, n.Left)
...@@ -1061,8 +1056,14 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node { ...@@ -1061,8 +1056,14 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
// key must be addressable // key must be addressable
case OINDEXMAP: case OINDEXMAP:
n.Left = orderexpr(n.Left, order, nil) n.Left = orderexpr(n.Left, order, nil)
n.Right = orderexpr(n.Right, order, nil) n.Right = orderexpr(n.Right, order, nil)
needCopy := false
if n.Etype == 0 && instrumenting {
// Race detector needs the copy so it can
// call treecopy on the result.
needCopy = true
}
// For x = m[string(k)] where k is []byte, the allocation of // For x = m[string(k)] where k is []byte, the allocation of
// backing bytes for the string can be avoided by reusing // backing bytes for the string can be avoided by reusing
...@@ -1076,12 +1077,13 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node { ...@@ -1076,12 +1077,13 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
// conversion (by the ordercopyexpr a few lines below). // conversion (by the ordercopyexpr a few lines below).
if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR { if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR {
n.Right.Op = OARRAYBYTESTRTMP n.Right.Op = OARRAYBYTESTRTMP
needCopy = true
} }
// Map calls need to take the address of the key.
n.Right = orderaddrtemp(n.Right, order) n.Right = orderaddrtemp(n.Right, order)
if n.Etype == 0 {
// use of value (not being assigned); if needCopy {
// make copy in temporary.
n = ordercopyexpr(n, n.Type, order, 0) n = ordercopyexpr(n, n.Type, order, 0)
} }
......
...@@ -474,8 +474,8 @@ func isartificial(n *Node) bool { ...@@ -474,8 +474,8 @@ func isartificial(n *Node) bool {
func callinstr(np **Node, init *Nodes, wr int, skip int) bool { func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
n := *np n := *np
//print("callinstr for %+N [ %O ] etype=%E class=%d\n", //fmt.Printf("callinstr for %v [ %v ] etype=%v class=%v\n",
// n, n->op, n->type ? n->type->etype : -1, n->class); // n, n.Op, n.Type.Etype, n.Class)
if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL { if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL {
return false return false
......
...@@ -52,7 +52,7 @@ type Node struct { ...@@ -52,7 +52,7 @@ type Node struct {
Op Op Op Op
Ullman uint8 // sethi/ullman number Ullman uint8 // sethi/ullman number
Addable bool // addressable Addable bool // addressable
Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN, for OINDEXMAP 1=LHS,0=RHS
Bounded bool // bounds check unnecessary Bounded bool // bounds check unnecessary
NonNil bool // guaranteed to be non-nil NonNil bool // guaranteed to be non-nil
Class Class // PPARAM, PAUTO, PEXTERN, etc Class Class // PPARAM, PAUTO, PEXTERN, etc
......
...@@ -1292,13 +1292,19 @@ opswitch: ...@@ -1292,13 +1292,19 @@ opswitch:
} }
case OINDEXMAP: case OINDEXMAP:
if n.Etype == 1 { // Replace m[k] with *map{access1,assign}(maptype, m, &k)
break
}
n.Left = walkexpr(n.Left, init) n.Left = walkexpr(n.Left, init)
n.Right = walkexpr(n.Right, init) n.Right = walkexpr(n.Right, init)
map_ := n.Left
t := n.Left.Type key := n.Right
t := map_.Type
if n.Etype == 1 {
// This m[k] expression is on the left-hand side of an assignment.
// orderexpr made sure key is addressable.
key = nod(OADDR, key, nil)
n = mkcall1(mapfn("mapassign", t), nil, init, typename(t), map_, key)
} else {
// m[k] is not the target of an assignment.
p := "" p := ""
if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing. if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
switch algtype(t.Key()) { switch algtype(t.Key()) {
...@@ -1311,25 +1317,23 @@ opswitch: ...@@ -1311,25 +1317,23 @@ opswitch:
} }
} }
var key *Node if p == "" {
if p != "" {
// fast versions take key by value
key = n.Right
} else {
// standard version takes key by reference. // standard version takes key by reference.
// orderexpr made sure key is addressable. // orderexpr made sure key is addressable.
key = nod(OADDR, n.Right, nil) key = nod(OADDR, key, nil)
p = "mapaccess1" p = "mapaccess1"
} }
if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), n.Left, key) n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key)
} else { } else {
p = "mapaccess1_fat" p = "mapaccess1_fat"
z := zeroaddr(w) z := zeroaddr(w)
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), n.Left, key, z) n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key, z)
} }
n.NonNil = true // mapaccess always returns a non-nil pointer }
n.Type = ptrto(t.Val())
n.NonNil = true // mapaccess1* and mapassign always return non-nil pointers.
n = nod(OIND, n, nil) n = nod(OIND, n, nil)
n.Type = t.Val() n.Type = t.Val()
n.Typecheck = 1 n.Typecheck = 1
...@@ -2306,22 +2310,6 @@ func convas(n *Node, init *Nodes) *Node { ...@@ -2306,22 +2310,6 @@ func convas(n *Node, init *Nodes) *Node {
goto out goto out
} }
if n.Left.Op == OINDEXMAP {
map_ := n.Left.Left
key := n.Left.Right
val := n.Right
map_ = walkexpr(map_, init)
key = walkexpr(key, init)
val = walkexpr(val, init)
// orderexpr made sure key and val are addressable.
key = nod(OADDR, key, nil)
val = nod(OADDR, val, nil)
n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val)
goto out
}
if !eqtype(lt, rt) { if !eqtype(lt, rt) {
n.Right = assignconv(n.Right, lt, "assignment") n.Right = assignconv(n.Right, lt, "assignment")
n.Right = walkexpr(n.Right, init) n.Right = walkexpr(n.Right, init)
......
...@@ -481,20 +481,19 @@ func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Point ...@@ -481,20 +481,19 @@ func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Point
return v, true return v, true
} }
func mapassign1(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) { // Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
if h == nil { if h == nil {
panic(plainError("assignment to entry in nil map")) panic(plainError("assignment to entry in nil map"))
} }
if raceenabled { if raceenabled {
callerpc := getcallerpc(unsafe.Pointer(&t)) callerpc := getcallerpc(unsafe.Pointer(&t))
pc := funcPC(mapassign1) pc := funcPC(mapassign)
racewritepc(unsafe.Pointer(h), callerpc, pc) racewritepc(unsafe.Pointer(h), callerpc, pc)
raceReadObjectPC(t.key, key, callerpc, pc) raceReadObjectPC(t.key, key, callerpc, pc)
raceReadObjectPC(t.elem, val, callerpc, pc)
} }
if msanenabled { if msanenabled {
msanread(key, t.key.size) msanread(key, t.key.size)
msanread(val, t.elem.size)
} }
if h.flags&hashWriting != 0 { if h.flags&hashWriting != 0 {
throw("concurrent map writes") throw("concurrent map writes")
...@@ -521,35 +520,29 @@ again: ...@@ -521,35 +520,29 @@ again:
var inserti *uint8 var inserti *uint8
var insertk unsafe.Pointer var insertk unsafe.Pointer
var insertv unsafe.Pointer var val unsafe.Pointer
for { for {
for i := uintptr(0); i < bucketCnt; i++ { for i := uintptr(0); i < bucketCnt; i++ {
if b.tophash[i] != top { if b.tophash[i] != top {
if b.tophash[i] == empty && inserti == nil { if b.tophash[i] == empty && inserti == nil {
inserti = &b.tophash[i] inserti = &b.tophash[i]
insertk = add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize)) insertk = add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
insertv = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize)) val = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
} }
continue continue
} }
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize)) k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
k2 := k
if t.indirectkey { if t.indirectkey {
k2 = *((*unsafe.Pointer)(k2)) k = *((*unsafe.Pointer)(k))
} }
if !alg.equal(key, k2) { if !alg.equal(key, k) {
continue continue
} }
// already have a mapping for key. Update it. // already have a mapping for key. Update it.
if t.needkeyupdate { if t.needkeyupdate {
typedmemmove(t.key, k2, key) typedmemmove(t.key, k, key)
}
v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
v2 := v
if t.indirectvalue {
v2 = *((*unsafe.Pointer)(v2))
} }
typedmemmove(t.elem, v2, val) val = add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize))
goto done goto done
} }
ovf := b.overflow(t) ovf := b.overflow(t)
...@@ -574,7 +567,7 @@ again: ...@@ -574,7 +567,7 @@ again:
h.setoverflow(t, b, newb) h.setoverflow(t, b, newb)
inserti = &newb.tophash[0] inserti = &newb.tophash[0]
insertk = add(unsafe.Pointer(newb), dataOffset) insertk = add(unsafe.Pointer(newb), dataOffset)
insertv = add(insertk, bucketCnt*uintptr(t.keysize)) val = add(insertk, bucketCnt*uintptr(t.keysize))
} }
// store new key/value at insert position // store new key/value at insert position
...@@ -585,11 +578,9 @@ again: ...@@ -585,11 +578,9 @@ again:
} }
if t.indirectvalue { if t.indirectvalue {
vmem := newobject(t.elem) vmem := newobject(t.elem)
*(*unsafe.Pointer)(insertv) = vmem *(*unsafe.Pointer)(val) = vmem
insertv = vmem
} }
typedmemmove(t.key, insertk, key) typedmemmove(t.key, insertk, key)
typedmemmove(t.elem, insertv, val)
*inserti = top *inserti = top
h.count++ h.count++
...@@ -598,6 +589,10 @@ done: ...@@ -598,6 +589,10 @@ done:
throw("concurrent map writes") throw("concurrent map writes")
} }
h.flags &^= hashWriting h.flags &^= hashWriting
if t.indirectvalue {
val = *((*unsafe.Pointer)(val))
}
return val
} }
func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) { func mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {
...@@ -1128,7 +1123,8 @@ func reflect_mapaccess(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { ...@@ -1128,7 +1123,8 @@ func reflect_mapaccess(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
//go:linkname reflect_mapassign reflect.mapassign //go:linkname reflect_mapassign reflect.mapassign
func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) { func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, val unsafe.Pointer) {
mapassign1(t, h, key, val) p := mapassign(t, h, key)
typedmemmove(t.elem, p, val)
} }
//go:linkname reflect_mapdelete reflect.mapdelete //go:linkname reflect_mapdelete reflect.mapdelete
......
...@@ -268,33 +268,34 @@ var m2 map[[2]string]*byte ...@@ -268,33 +268,34 @@ var m2 map[[2]string]*byte
var x2 [2]string var x2 [2]string
var bp *byte var bp *byte
func f17a() { func f17a(p *byte) { // ERROR "live at entry to f17a: p$"
// value temporary only
if b { if b {
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2[x2] = p // ERROR "live at call to mapassign: p$"
} }
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2[x2] = p // ERROR "live at call to mapassign: p$"
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2[x2] = p // ERROR "live at call to mapassign: p$"
} }
func f17b() { func f17b(p *byte) { // ERROR "live at entry to f17b: p$"
// key temporary only // key temporary
if b { if b {
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2s["x"] = p // ERROR "live at call to mapassign: p autotmp_[0-9]+$"
} }
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2s["x"] = p // ERROR "live at call to mapassign: p autotmp_[0-9]+$"
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$" m2s["x"] = p // ERROR "live at call to mapassign: p autotmp_[0-9]+$"
} }
func f17c() { func f17c() {
// key and value temporaries // key and value temporaries
if b { if b {
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2s["x"] = f17d() // ERROR "live at call to f17d: autotmp_[0-9]+$" "live at call to mapassign: autotmp_[0-9]+ autotmp_[0-9]+$"
} }
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2s["x"] = f17d() // ERROR "live at call to f17d: autotmp_[0-9]+$" "live at call to mapassign: autotmp_[0-9]+ autotmp_[0-9]+$"
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2s["x"] = f17d() // ERROR "live at call to f17d: autotmp_[0-9]+$" "live at call to mapassign: autotmp_[0-9]+ autotmp_[0-9]+$"
} }
func f17d() *byte
func g18() [2]string func g18() [2]string
func f18() { func f18() {
...@@ -360,10 +361,10 @@ func f24() { ...@@ -360,10 +361,10 @@ func f24() {
// key temporary for map access using array literal key. // key temporary for map access using array literal key.
// value temporary too. // value temporary too.
if b { if b {
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: autotmp_[0-9]+$"
} }
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: autotmp_[0-9]+$"
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$" m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign: autotmp_[0-9]+$"
} }
// defer should not cause spurious ambiguously live variables // defer should not cause spurious ambiguously live variables
......
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