mirror of
https://github.com/golang/go
synced 2024-11-22 14:34:45 -07:00
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: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
55ef67f2f8
commit
442de98c14
@ -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" +
|
||||
"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" +
|
||||
"\x17:\xc6\x01\x00\x00\x17\"\rzero·6\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x13mapassig" +
|
||||
"n1\x00\b\x17\"\x13mapType·1\x00\x00\x1d::\rhmap·2\x00\x00\x17:\vkey·" +
|
||||
"3\x00\x00\x17:\vval·4\x00\x00\x00\t\x15mapiterinit\x00\x06\x17\"\xd6\x01\x00\x00\x1d::\xd8" +
|
||||
"\x01\x00\x00\x17:\x0fhiter·3\x00\x00\x00\t\x11mapdelete\x00\x06\x17\"\xd6\x01\x00\x00\x1d::\xd8" +
|
||||
"\x01\x00\x00\x17:\xda\x01\x00\x00\x00\t\x15mapiternext\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t" +
|
||||
"\x0fmakechan\x00\x04\x17\"\x15chanType·2\x00\x00\n\xa6\x01\x00\x00\x02\x1f\x06:\x0fhch" +
|
||||
"an·1\x00\x00\t\x11chanrecv1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0f" +
|
||||
"hchan·2\x00\x00\x17:j\x00\x00\x00\t\x11chanrecv2\x00\x06\x17\"\xea\x01\x00\x00\x1f\x02:\x0fh" +
|
||||
"chan·3\x00\x00\x17:\relem·4\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xf0" +
|
||||
"\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" +
|
||||
"teBarrier\x00\x15\x06\renabled\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00" +
|
||||
"\t\x1dwritebarrierptr\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00" +
|
||||
"\x00\t\x17typedmemmove\x00\x06\x17\"t\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc\xc2" +
|
||||
"\xb73\x00\x00\x00\t\x1btypedslicecopy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00:\v" +
|
||||
"src·4\x00\x00\x01\x02\x00\t\x17selectnbsend\x00\x06\x17\"\xea\x01\x00\x00\x1f\x04:\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" +
|
||||
"hchan·4\x00\x00\x01\x00\x00\t\x19selectnbrecv2\x00\b\x17\"\xea\x01\x00\x00\x17:j\x00" +
|
||||
"\x00\x17\x00\x15received·4\x00\x00\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11news" +
|
||||
"elect\x00\x06\x17\"\vsel·1\x00\x00\n\x13selsize·2\x00\x00\b\rsize·" +
|
||||
"3\x00\x00\x00\t\x13selectsend\x00\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xf6\x01\x00\x00\x17:\xf8\x01" +
|
||||
"\x00\x00\x02\x00\x15selected·1\x00\x00\t\x13selectrecv\x00\x06\x17\"\xae\x02\x00\x00\x1f\x02" +
|
||||
":\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\x17:\xf8\x01\x00\x00\x17\x00\x15received·5\x00\x00\x02\x00\xb0\x02\x00\x00\t\x19selec" +
|
||||
"tdefault\x00\x02\x17\"\xae\x02\x00\x00\x02\x00\xb0\x02\x00\x00\t\x0fselectgo\x00\x02\x17\"\xa6\x02\x00\x00" +
|
||||
"\x00\t\tblock\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00\x00\x02" +
|
||||
"\vcap·4\x00\x00\x02\x11:\vary·1\x00\x00\t\x15makeslice64\x00\x06\x17\"\x06\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" +
|
||||
"ld·3\x00\x00\x02\xc2\x02\x00\x00\x02\x11:\xc4\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00" +
|
||||
"\x17:\vfrm·2\x00\x00\x16\x11length·3\x00^\x00\t\vmemclr\x00\x04\x17\"\vpt" +
|
||||
"r·1\x00\x00\x16\x11length·2\x00^\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00" +
|
||||
"\x00\x17:\ay·3\x00\x00\x16\rsize·4\x00^\x01\x00\x00\t\x11memequal8\x00\x04\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" +
|
||||
"\x00\t\x13memequal32\x00\x04\x17:\xdc\x02\x00\x00\x17:\xde\x02\x00\x00\x01\x00\x00\t\x13memequal" +
|
||||
"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" +
|
||||
"\x17:\xde\x02\x00\x00\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00" +
|
||||
"\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\x14\x00\x01\x14\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64t" +
|
||||
"ouint64\x00\x01\x1a\x00\x01\x14\x00\t\x1dfloat64touint32\x00\x01\x1a\x00\x01\x12\x00\t\x1b" +
|
||||
"int64tofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00\x01" +
|
||||
"\x14\x00\x01\x1a\x00\t\x1duint32tofloat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex12" +
|
||||
"8div\x00\x04\x1e\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19r" +
|
||||
"acefuncenter\x00\x01\x16^\x00\t\x17racefuncexit\x00\x00\x00\t\x0frace" +
|
||||
"read\x00\x01\x16^\x00\t\x11racewrite\x00\x01\x16^\x00\t\x19racereadrange" +
|
||||
"\x00\x04\x16\raddr·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterang" +
|
||||
"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" +
|
||||
"sanwrite\x00\x04\x16\x92\x03\x00^\x16\x94\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n"
|
||||
"\x17:\xc6\x01\x00\x00\x17\"\rzero·6\x00\x00\x04\x17:\xb4\x01\x00\x00\x00\xc8\x01\x00\x00\t\x11mapassig" +
|
||||
"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" +
|
||||
"it\x00\x06\x17\"\x13mapType·1\x00\x00\x1d::\rhmap·2\x00\x00\x17:\x0fhiter" +
|
||||
"·3\x00\x00\x00\t\x11mapdelete\x00\x06\x17\"\xd8\x01\x00\x00\x1d::\xda\x01\x00\x00\x17:\vkey·" +
|
||||
"3\x00\x00\x00\t\x15mapiternext\x00\x02\x17:\x0fhiter·1\x00\x00\x00\t\x0fmakec" +
|
||||
"han\x00\x04\x17\"\x15chanType·2\x00\x00\n\xa6\x01\x00\x00\x02\x1f\x06:\x0fhchan·1\x00" +
|
||||
"\x00\t\x11chanrecv1\x00\x06\x17\"\x15chanType·1\x00\x00\x1f\x02:\x0fhchan\xc2" +
|
||||
"\xb72\x00\x00\x17:j\x00\x00\x00\t\x11chanrecv2\x00\x06\x17\"\xe8\x01\x00\x00\x1f\x02:\x0fhchan·" +
|
||||
"3\x00\x00\x17:\relem·4\x00\x00\x01\x00\x00\t\x11chansend1\x00\x06\x17\"\xee\x01\x00\x00\x1f\x04:" +
|
||||
"\xf0\x01\x00\x00\x17:j\x00\x00\x00\t\x11closechan\x00\x02:\xea\x01\x00\x00\x00\a\x17writeBarr" +
|
||||
"ier\x00\x15\x06\renabled\x00\x00\x00\vneeded\x00\x00\x00\x05cgo\x00\x00\x00\t\x1dwrit" +
|
||||
"ebarrierptr\x00\x04\x17:\vdst·1\x00\x00:\vsrc·2\x00\x00\x00\t\x17typ" +
|
||||
"edmemmove\x00\x06\x17\"t\x00\x00\x17:\vdst·2\x00\x00\x17:\vsrc·3\x00\x00\x00\t" +
|
||||
"\x1btypedslicecopy\x00\x06\x17\"\x06\x00\x00:\vdst·3\x00\x00:\vsrc·4" +
|
||||
"\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" +
|
||||
"\x01\x00\x00\t\x17selectnbrecv\x00\x06\x17\"\xe8\x01\x00\x00\x17:j\x00\x00\x1f\x02:\x0fhchan\xc2" +
|
||||
"\xb74\x00\x00\x01\x00\x00\t\x19selectnbrecv2\x00\b\x17\"\xe8\x01\x00\x00\x17:j\x00\x00\x17\x00\x15re" +
|
||||
"ceived·4\x00\x00\x1f\x02:\x0fhchan·5\x00\x00\x01\x00\x00\t\x11newselect\x00" +
|
||||
"\x06\x17\"\vsel·1\x00\x00\n\x13selsize·2\x00\x00\b\rsize·3\x00\x00\x00\t\x13" +
|
||||
"selectsend\x00\x06\x17\"\vsel·2\x00\x00\x1f\x04:\xf4\x01\x00\x00\x17:\xf6\x01\x00\x00\x02\x00\x15s" +
|
||||
"elected·1\x00\x00\t\x13selectrecv\x00\x06\x17\"\xac\x02\x00\x00\x1f\x02:\xf4\x01\x00\x00\x17" +
|
||||
":\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\x00\x15received·5\x00\x00\x02\x00\xae\x02\x00\x00\t\x19selectdefau" +
|
||||
"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" +
|
||||
"ck\x00\x00\x00\t\x11makeslice\x00\x06\x17\"\x06\x00\x00\x02\vlen·3\x00\x00\x02\vcap·" +
|
||||
"4\x00\x00\x02\x11:\vary·1\x00\x00\t\x15makeslice64\x00\x06\x17\"\x06\x00\x00\n\xbe\x02\x00\x00" +
|
||||
"\n\xc0\x02\x00\x00\x02\x11:\xc2\x02\x00\x00\t\x11growslice\x00\x06\x17\"\x06\x00\x00\x11:\vold·3\x00" +
|
||||
"\x00\x02\xc0\x02\x00\x00\x02\x11:\xc2\x02\x00\x00\t\rmemmove\x00\x06\x17:\tto·1\x00\x00\x17:\vfrm" +
|
||||
"·2\x00\x00\x16\x11length·3\x00^\x00\t\vmemclr\x00\x04\x17\"\vptr·1\x00\x00" +
|
||||
"\x16\x11length·2\x00^\x00\t\x0fmemequal\x00\x06\x17:\ax·2\x00\x00\x17:\ay\xc2" +
|
||||
"\xb73\x00\x00\x16\rsize·4\x00^\x01\x00\x00\t\x11memequal8\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc" +
|
||||
"\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" +
|
||||
"equal32\x00\x04\x17:\xda\x02\x00\x00\x17:\xdc\x02\x00\x00\x01\x00\x00\t\x13memequal64\x00\x04\x17:" +
|
||||
"\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" +
|
||||
"\x01\x00\x00\t\x0fint64div\x00\x03\n\x00\n\x00\x01\n\x00\t\x11uint64div\x00\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\x14\x00\x01\x14" +
|
||||
"\x00\t\x1bfloat64toint64\x00\x01\x1a\x00\x01\n\x00\t\x1dfloat64touint6" +
|
||||
"4\x00\x01\x1a\x00\x01\x14\x00\t\x1dfloat64touint32\x00\x01\x1a\x00\x01\x12\x00\t\x1bint64t" +
|
||||
"ofloat64\x00\x01\n\x00\x01\x1a\x00\t\x1duint64tofloat64\x00\x01\x14\x00\x01\x1a\x00\t" +
|
||||
"\x1duint32tofloat64\x00\x01\x12\x00\x01\x1a\x00\t\x19complex128div\x00\x04" +
|
||||
"\x1e\vnum·2\x00\x00\x1e\vden·3\x00\x00\x02\x1e\vquo·1\x00\x00\t\x19racefun" +
|
||||
"center\x00\x01\x16^\x00\t\x17racefuncexit\x00\x00\x00\t\x0fraceread\x00\x01" +
|
||||
"\x16^\x00\t\x11racewrite\x00\x01\x16^\x00\t\x19racereadrange\x00\x04\x16\rad" +
|
||||
"dr·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterange\x00\x04\x16\x90\x03" +
|
||||
"\x00^\x16\x92\x03\x00^\x00\t\x0fmsanread\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\t\x11msanwri" +
|
||||
"te\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n"
|
||||
|
||||
const unsafeimport = "" +
|
||||
"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
|
||||
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_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 mapdelete(mapType *byte, hmap map[any]any, key *any)
|
||||
func mapiternext(hiter *any)
|
||||
|
@ -188,9 +188,9 @@ func isaddrokay(n *Node) bool {
|
||||
return islvalue(n) && (n.Op != ONAME || n.Class == PEXTERN || istemp(n))
|
||||
}
|
||||
|
||||
// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines.
|
||||
// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits
|
||||
// tmp = *np, and then sets *np to the tmp variable.
|
||||
// Orderaddrtemp ensures that n is okay to pass by address to runtime routines.
|
||||
// If the original argument n is not okay, orderaddrtemp creates a tmp, emits
|
||||
// tmp = n, and then returns tmp.
|
||||
func orderaddrtemp(n *Node, order *Order) *Node {
|
||||
if isaddrokay(n) {
|
||||
return n
|
||||
@ -395,13 +395,8 @@ func ordercall(n *Node, order *Order) {
|
||||
}
|
||||
|
||||
// Ordermapassign appends n to order->out, introducing temporaries
|
||||
// to make sure that all map assignments have the form m[k] = x,
|
||||
// where x 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
|
||||
// to make sure that all map assignments have the form m[k] = x.
|
||||
// (Note: orderexpr has already been called on n, so we know k is addressable.)
|
||||
//
|
||||
// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
|
||||
// t1 = m
|
||||
@ -428,7 +423,7 @@ func ordermapassign(n *Node, order *Order) {
|
||||
// We call writebarrierfat only for values > 4 pointers long. See walk.go.
|
||||
// TODO(mdempsky): writebarrierfat doesn't exist anymore, but removing that
|
||||
// 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
|
||||
n.Left = ordertemp(m.Type, order, false)
|
||||
a := nod(OAS, m, n.Left)
|
||||
@ -1061,8 +1056,14 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
|
||||
// key must be addressable
|
||||
case OINDEXMAP:
|
||||
n.Left = orderexpr(n.Left, 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
|
||||
// backing bytes for the string can be avoided by reusing
|
||||
@ -1076,12 +1077,13 @@ func orderexpr(n *Node, order *Order, lhs *Node) *Node {
|
||||
// conversion (by the ordercopyexpr a few lines below).
|
||||
if n.Etype == 0 && n.Right.Op == OARRAYBYTESTR {
|
||||
n.Right.Op = OARRAYBYTESTRTMP
|
||||
needCopy = true
|
||||
}
|
||||
|
||||
// Map calls need to take the address of the key.
|
||||
n.Right = orderaddrtemp(n.Right, order)
|
||||
if n.Etype == 0 {
|
||||
// use of value (not being assigned);
|
||||
// make copy in temporary.
|
||||
|
||||
if needCopy {
|
||||
n = ordercopyexpr(n, n.Type, order, 0)
|
||||
}
|
||||
|
||||
|
@ -474,8 +474,8 @@ func isartificial(n *Node) bool {
|
||||
func callinstr(np **Node, init *Nodes, wr int, skip int) bool {
|
||||
n := *np
|
||||
|
||||
//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
|
||||
// n, n->op, n->type ? n->type->etype : -1, n->class);
|
||||
//fmt.Printf("callinstr for %v [ %v ] etype=%v class=%v\n",
|
||||
// n, n.Op, n.Type.Etype, n.Class)
|
||||
|
||||
if skip != 0 || n.Type == nil || n.Type.Etype >= TIDEAL {
|
||||
return false
|
||||
|
@ -52,7 +52,7 @@ type Node struct {
|
||||
Op Op
|
||||
Ullman uint8 // sethi/ullman number
|
||||
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
|
||||
NonNil bool // guaranteed to be non-nil
|
||||
Class Class // PPARAM, PAUTO, PEXTERN, etc
|
||||
|
@ -1292,44 +1292,48 @@ opswitch:
|
||||
}
|
||||
|
||||
case OINDEXMAP:
|
||||
if n.Etype == 1 {
|
||||
break
|
||||
}
|
||||
// Replace m[k] with *map{access1,assign}(maptype, m, &k)
|
||||
n.Left = walkexpr(n.Left, init)
|
||||
n.Right = walkexpr(n.Right, init)
|
||||
map_ := n.Left
|
||||
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 := ""
|
||||
if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
|
||||
switch algtype(t.Key()) {
|
||||
case AMEM32:
|
||||
p = "mapaccess1_fast32"
|
||||
case AMEM64:
|
||||
p = "mapaccess1_fast64"
|
||||
case ASTRING:
|
||||
p = "mapaccess1_faststr"
|
||||
}
|
||||
}
|
||||
|
||||
t := n.Left.Type
|
||||
p := ""
|
||||
if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
|
||||
switch algtype(t.Key()) {
|
||||
case AMEM32:
|
||||
p = "mapaccess1_fast32"
|
||||
case AMEM64:
|
||||
p = "mapaccess1_fast64"
|
||||
case ASTRING:
|
||||
p = "mapaccess1_faststr"
|
||||
if p == "" {
|
||||
// standard version takes key by reference.
|
||||
// orderexpr made sure key is addressable.
|
||||
key = nod(OADDR, key, nil)
|
||||
p = "mapaccess1"
|
||||
}
|
||||
|
||||
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), map_, key)
|
||||
} else {
|
||||
p = "mapaccess1_fat"
|
||||
z := zeroaddr(w)
|
||||
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key, z)
|
||||
}
|
||||
}
|
||||
|
||||
var key *Node
|
||||
if p != "" {
|
||||
// fast versions take key by value
|
||||
key = n.Right
|
||||
} else {
|
||||
// standard version takes key by reference.
|
||||
// orderexpr made sure key is addressable.
|
||||
key = nod(OADDR, n.Right, nil)
|
||||
p = "mapaccess1"
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
p = "mapaccess1_fat"
|
||||
z := zeroaddr(w)
|
||||
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), n.Left, 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.Type = t.Val()
|
||||
n.Typecheck = 1
|
||||
@ -2306,22 +2310,6 @@ func convas(n *Node, init *Nodes) *Node {
|
||||
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) {
|
||||
n.Right = assignconv(n.Right, lt, "assignment")
|
||||
n.Right = walkexpr(n.Right, init)
|
||||
|
@ -481,20 +481,19 @@ func mapaccess2_fat(t *maptype, h *hmap, key, zero unsafe.Pointer) (unsafe.Point
|
||||
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 {
|
||||
panic(plainError("assignment to entry in nil map"))
|
||||
}
|
||||
if raceenabled {
|
||||
callerpc := getcallerpc(unsafe.Pointer(&t))
|
||||
pc := funcPC(mapassign1)
|
||||
pc := funcPC(mapassign)
|
||||
racewritepc(unsafe.Pointer(h), callerpc, pc)
|
||||
raceReadObjectPC(t.key, key, callerpc, pc)
|
||||
raceReadObjectPC(t.elem, val, callerpc, pc)
|
||||
}
|
||||
if msanenabled {
|
||||
msanread(key, t.key.size)
|
||||
msanread(val, t.elem.size)
|
||||
}
|
||||
if h.flags&hashWriting != 0 {
|
||||
throw("concurrent map writes")
|
||||
@ -521,35 +520,29 @@ again:
|
||||
|
||||
var inserti *uint8
|
||||
var insertk unsafe.Pointer
|
||||
var insertv unsafe.Pointer
|
||||
var val unsafe.Pointer
|
||||
for {
|
||||
for i := uintptr(0); i < bucketCnt; i++ {
|
||||
if b.tophash[i] != top {
|
||||
if b.tophash[i] == empty && inserti == nil {
|
||||
inserti = &b.tophash[i]
|
||||
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
|
||||
}
|
||||
k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize))
|
||||
k2 := k
|
||||
if t.indirectkey {
|
||||
k2 = *((*unsafe.Pointer)(k2))
|
||||
k = *((*unsafe.Pointer)(k))
|
||||
}
|
||||
if !alg.equal(key, k2) {
|
||||
if !alg.equal(key, k) {
|
||||
continue
|
||||
}
|
||||
// already have a mapping for key. Update it.
|
||||
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
|
||||
}
|
||||
ovf := b.overflow(t)
|
||||
@ -574,7 +567,7 @@ again:
|
||||
h.setoverflow(t, b, newb)
|
||||
inserti = &newb.tophash[0]
|
||||
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
|
||||
@ -585,11 +578,9 @@ again:
|
||||
}
|
||||
if t.indirectvalue {
|
||||
vmem := newobject(t.elem)
|
||||
*(*unsafe.Pointer)(insertv) = vmem
|
||||
insertv = vmem
|
||||
*(*unsafe.Pointer)(val) = vmem
|
||||
}
|
||||
typedmemmove(t.key, insertk, key)
|
||||
typedmemmove(t.elem, insertv, val)
|
||||
*inserti = top
|
||||
h.count++
|
||||
|
||||
@ -598,6 +589,10 @@ done:
|
||||
throw("concurrent map writes")
|
||||
}
|
||||
h.flags &^= hashWriting
|
||||
if t.indirectvalue {
|
||||
val = *((*unsafe.Pointer)(val))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
//go:linkname reflect_mapassign reflect.mapassign
|
||||
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
|
||||
|
33
test/live.go
33
test/live.go
@ -268,33 +268,34 @@ var m2 map[[2]string]*byte
|
||||
var x2 [2]string
|
||||
var bp *byte
|
||||
|
||||
func f17a() {
|
||||
// value temporary only
|
||||
func f17a(p *byte) { // ERROR "live at entry to f17a: p$"
|
||||
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] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
|
||||
m2[x2] = p // ERROR "live at call to mapassign: p$"
|
||||
m2[x2] = p // ERROR "live at call to mapassign: p$"
|
||||
}
|
||||
|
||||
func f17b() {
|
||||
// key temporary only
|
||||
func f17b(p *byte) { // ERROR "live at entry to f17b: p$"
|
||||
// key temporary
|
||||
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"] = 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"] = p // ERROR "live at call to mapassign: p autotmp_[0-9]+$"
|
||||
}
|
||||
|
||||
func f17c() {
|
||||
// key and value temporaries
|
||||
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"] = 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"] = 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 f18() {
|
||||
@ -360,10 +361,10 @@ func f24() {
|
||||
// key temporary for map access using array literal key.
|
||||
// value temporary too.
|
||||
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 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 mapassign: autotmp_[0-9]+$"
|
||||
}
|
||||
|
||||
// defer should not cause spurious ambiguously live variables
|
||||
|
Loading…
Reference in New Issue
Block a user