mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
cmd/compile: avoid using destination pointer base type in memmove optimization
The type of the source and destination of a memmove call isn't always accurate. It will always be a pointer (or an unsafe.Pointer), but the base type might not be accurate. This comes about because multiple copies of a pointer with different base types are coalesced into a single value. In the failing example, the IData selector of the input argument is a *[32]byte in one branch of the type switch, and a *[]byte in the other branch. During the expand_calls pass both IDatas become just copies of the input register. Those copies are deduped and an arbitrary one wins (in this case, *[]byte is the unfortunate winner). Generally an op v can rely on v.Type during rewrite rules. But relying on v.Args[i].Type is discouraged. Fixes #55122 Change-Id: I348fd9accf2058a87cd191eec01d39cda612f120 Reviewed-on: https://go-review.googlesource.com/c/go/+/431496 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> Run-TryBot: Keith Randall <khr@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
0053ec452d
commit
e283473ebb
@ -2112,7 +2112,13 @@
|
|||||||
|
|
||||||
// Inline small or disjoint runtime.memmove calls with constant length.
|
// Inline small or disjoint runtime.memmove calls with constant length.
|
||||||
// See the comment in op Move in genericOps.go for discussion of the type.
|
// See the comment in op Move in genericOps.go for discussion of the type.
|
||||||
|
//
|
||||||
|
// Note that we've lost any knowledge of the type and alignment requirements
|
||||||
|
// of the source and destination. We only know the size, and that the type
|
||||||
|
// contains no pointers.
|
||||||
|
// The type of the move is not necessarily v.Args[0].Type().Elem()!
|
||||||
|
// See issue 55122 for details.
|
||||||
|
//
|
||||||
// Because expand calls runs after prove, constants useful to this pattern may not appear.
|
// Because expand calls runs after prove, constants useful to this pattern may not appear.
|
||||||
// Both versions need to exist; the memory and register variants.
|
// Both versions need to exist; the memory and register variants.
|
||||||
//
|
//
|
||||||
@ -2120,31 +2126,28 @@
|
|||||||
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
(SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||||
&& sz >= 0
|
&& sz >= 0
|
||||||
&& isSameCall(sym, "runtime.memmove")
|
&& isSameCall(sym, "runtime.memmove")
|
||||||
&& t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
|
||||||
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
&& s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
|
||||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||||
&& clobber(s1, s2, s3, call)
|
&& clobber(s1, s2, s3, call)
|
||||||
=> (Move {t.Elem()} [int64(sz)] dst src mem)
|
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
|
|
||||||
// Match post-expansion calls, register version.
|
// Match post-expansion calls, register version.
|
||||||
(SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
|
(SelectN [0] call:(StaticCall {sym} dst src (Const(64|32) [sz]) mem))
|
||||||
&& sz >= 0
|
&& sz >= 0
|
||||||
&& call.Uses == 1 // this will exclude all calls with results
|
&& call.Uses == 1 // this will exclude all calls with results
|
||||||
&& isSameCall(sym, "runtime.memmove")
|
&& isSameCall(sym, "runtime.memmove")
|
||||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
|
||||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||||
&& clobber(call)
|
&& clobber(call)
|
||||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
|
|
||||||
// Match pre-expansion calls.
|
// Match pre-expansion calls.
|
||||||
(SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
|
(SelectN [0] call:(StaticLECall {sym} dst src (Const(64|32) [sz]) mem))
|
||||||
&& sz >= 0
|
&& sz >= 0
|
||||||
&& call.Uses == 1 // this will exclude all calls with results
|
&& call.Uses == 1 // this will exclude all calls with results
|
||||||
&& isSameCall(sym, "runtime.memmove")
|
&& isSameCall(sym, "runtime.memmove")
|
||||||
&& dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
|
|
||||||
&& isInlinableMemmove(dst, src, int64(sz), config)
|
&& isInlinableMemmove(dst, src, int64(sz), config)
|
||||||
&& clobber(call)
|
&& clobber(call)
|
||||||
=> (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
=> (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
|
|
||||||
// De-virtualize late-expanded interface calls into late-expanded static calls.
|
// De-virtualize late-expanded interface calls into late-expanded static calls.
|
||||||
// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
|
// Note that (ITab (IMake)) doesn't get rewritten until after the first opt pass,
|
||||||
|
@ -26382,8 +26382,8 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const64 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26413,21 +26413,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
if s3.Op != OpStore {
|
if s3.Op != OpStore {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
t := auxToType(s3.Aux)
|
|
||||||
mem := s3.Args[2]
|
mem := s3.Args[2]
|
||||||
dst := s3.Args[1]
|
dst := s3.Args[1]
|
||||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(t.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
// match: (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const32 [sz]) s2:(Store _ src s3:(Store {t} _ dst mem)))))
|
||||||
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
// cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)
|
||||||
// result: (Move {t.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26457,21 +26456,20 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
if s3.Op != OpStore {
|
if s3.Op != OpStore {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
t := auxToType(s3.Aux)
|
|
||||||
mem := s3.Args[2]
|
mem := s3.Args[2]
|
||||||
dst := s3.Args[1]
|
dst := s3.Args[1]
|
||||||
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && t.IsPtr() && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, int64(sz), config) && clobber(s1, s2, s3, call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(t.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
|
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const64 [sz]) mem))
|
||||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26489,18 +26487,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
sz := auxIntToInt64(call_2.AuxInt)
|
sz := auxIntToInt64(call_2.AuxInt)
|
||||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(dst.Type.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
|
// match: (SelectN [0] call:(StaticCall {sym} dst src (Const32 [sz]) mem))
|
||||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26518,18 +26516,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
sz := auxIntToInt32(call_2.AuxInt)
|
sz := auxIntToInt32(call_2.AuxInt)
|
||||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(dst.Type.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
|
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const64 [sz]) mem))
|
||||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26547,18 +26545,18 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
sz := auxIntToInt64(call_2.AuxInt)
|
sz := auxIntToInt64(call_2.AuxInt)
|
||||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(dst.Type.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
|
// match: (SelectN [0] call:(StaticLECall {sym} dst src (Const32 [sz]) mem))
|
||||||
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
// cond: sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)
|
||||||
// result: (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
|
// result: (Move {types.Types[types.TUINT8]} [int64(sz)] dst src mem)
|
||||||
for {
|
for {
|
||||||
if auxIntToInt64(v.AuxInt) != 0 {
|
if auxIntToInt64(v.AuxInt) != 0 {
|
||||||
break
|
break
|
||||||
@ -26576,12 +26574,12 @@ func rewriteValuegeneric_OpSelectN(v *Value) bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
sz := auxIntToInt32(call_2.AuxInt)
|
sz := auxIntToInt32(call_2.AuxInt)
|
||||||
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && dst.Type.IsPtr() && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
if !(sz >= 0 && call.Uses == 1 && isSameCall(sym, "runtime.memmove") && isInlinableMemmove(dst, src, int64(sz), config) && clobber(call)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v.reset(OpMove)
|
v.reset(OpMove)
|
||||||
v.AuxInt = int64ToAuxInt(int64(sz))
|
v.AuxInt = int64ToAuxInt(int64(sz))
|
||||||
v.Aux = typeToAux(dst.Type.Elem())
|
v.Aux = typeToAux(types.Types[types.TUINT8])
|
||||||
v.AddArg3(dst, src, mem)
|
v.AddArg3(dst, src, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
42
test/fixedbugs/issue55122.go
Normal file
42
test/fixedbugs/issue55122.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
h(i)
|
||||||
|
sink = make([]byte, 1024) // generate some garbage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func h(iter int) {
|
||||||
|
var x [32]byte
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
x[i] = 99
|
||||||
|
}
|
||||||
|
g(&x)
|
||||||
|
if x == ([32]byte{}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
println(x[i])
|
||||||
|
}
|
||||||
|
panic(iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func g(x interface{}) {
|
||||||
|
switch e := x.(type) {
|
||||||
|
case *[32]byte:
|
||||||
|
var c [32]byte
|
||||||
|
*e = c
|
||||||
|
case *[]byte:
|
||||||
|
*e = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sink []byte
|
43
test/fixedbugs/issue55122b.go
Normal file
43
test/fixedbugs/issue55122b.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
h(i)
|
||||||
|
sink = make([]byte, 1024) // generate some garbage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func h(iter int) {
|
||||||
|
var x [32]byte
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
x[i] = 99
|
||||||
|
}
|
||||||
|
g(&x)
|
||||||
|
if x == ([32]byte{}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
println(x[i])
|
||||||
|
}
|
||||||
|
panic(iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:noinline
|
||||||
|
func g(x interface{}) {
|
||||||
|
switch e := x.(type) {
|
||||||
|
case *[32]byte:
|
||||||
|
var c [32]byte
|
||||||
|
*e = c
|
||||||
|
case *[3]*byte:
|
||||||
|
var c [3]*byte
|
||||||
|
*e = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sink []byte
|
Loading…
x
Reference in New Issue
Block a user