mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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
8 changed files with 138 additions and 151 deletions
|
|
@ -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
|
||||||
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 {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
// 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 {
|
||||||
// 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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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,44 +1292,48 @@ 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
|
||||||
|
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
|
if p == "" {
|
||||||
p := ""
|
// standard version takes key by reference.
|
||||||
if t.Val().Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
|
// orderexpr made sure key is addressable.
|
||||||
switch algtype(t.Key()) {
|
key = nod(OADDR, key, nil)
|
||||||
case AMEM32:
|
p = "mapaccess1"
|
||||||
p = "mapaccess1_fast32"
|
}
|
||||||
case AMEM64:
|
|
||||||
p = "mapaccess1_fast64"
|
if w := t.Val().Width; w <= 1024 { // 1024 must match ../../../../runtime/hashmap.go:maxZero
|
||||||
case ASTRING:
|
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key)
|
||||||
p = "mapaccess1_faststr"
|
} else {
|
||||||
|
p = "mapaccess1_fat"
|
||||||
|
z := zeroaddr(w)
|
||||||
|
n = mkcall1(mapfn(p, t), ptrto(t.Val()), init, typename(t), map_, key, z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
n.Type = ptrto(t.Val())
|
||||||
var key *Node
|
n.NonNil = true // mapaccess1* and mapassign always return non-nil pointers.
|
||||||
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 = 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 {
|
||||||
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
|
||||||
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:
|
||||||
|
|
||||||
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))
|
val = 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)
|
|
||||||
goto done
|
goto done
|
||||||
}
|
}
|
||||||
ovf := b.overflow(t)
|
ovf := b.overflow(t)
|
||||||
|
|
@ -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:
|
||||||
}
|
}
|
||||||
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:
|
||||||
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 {
|
||||||
|
|
||||||
//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
|
||||||
|
|
|
||||||
33
test/live.go
33
test/live.go
|
|
@ -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() {
|
||||||
// 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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue