]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add arch-specific inlining for runtime.memmove
authorRuslan Andreev <ruslan.andreev@huawei.com>
Tue, 19 Jan 2021 14:30:10 +0000 (22:30 +0800)
committerKeith Randall <khr@golang.org>
Wed, 12 May 2021 16:23:30 +0000 (16:23 +0000)
This CL add runtime.memmove inlining for AMD64 and ARM64.
According to ssa dump from testcases generic rules can't inline
memmomve properly due to one of the arguments is Phi operation. But this
Phi op will be optimized out by later optimization stages. As a result
memmove can be inlined during arch-specific rules.
The commit add new optimization rules to arch-specific rules that can
inline runtime.memmove if it possible during lowering stage.
Optimization fires 5 times in Go source-code using regabi.

Fixes #41662

Change-Id: Iaffaf4c482d068b5f0683d141863892202cc8824
Reviewed-on: https://go-review.googlesource.com/c/go/+/289151
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: David Chase <drchase@google.com>

src/cmd/compile/internal/ssa/gen/AMD64.rules
src/cmd/compile/internal/ssa/gen/ARM64.rules
src/cmd/compile/internal/ssa/gen/generic.rules
src/cmd/compile/internal/ssa/rewriteAMD64.go
src/cmd/compile/internal/ssa/rewriteARM64.go
test/codegen/copy.go

index ec91ea1513b08c17bfc9e1b146edaff9a83958b4..4cd00732fc3f5e5f8a4098f6a0b0261f01c9a4a8 100644 (file)
 (MOVOstore [dstOff] {dstSym} ptr (MOVOload [srcOff] {srcSym} (SB) _) mem) && symIsRO(srcSym) =>
   (MOVQstore [dstOff+8] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff)+8, config.ctxt.Arch.ByteOrder))])
     (MOVQstore [dstOff] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff), config.ctxt.Arch.ByteOrder))]) mem))
+
+// Arch-specific inlining for small or disjoint runtime.memmove
+// Match post-lowering calls, memory version.
+(SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
+       && sc.Val64() >= 0
+       && isSameCall(sym, "runtime.memmove")
+       && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
+       && isInlinableMemmove(dst, src, sc.Val64(), config)
+       && clobber(s1, s2, s3, call)
+       => (Move [sc.Val64()] dst src mem)
+
+// Match post-lowering calls, register version.
+(SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
+       && sz >= 0
+       && isSameCall(sym, "runtime.memmove")
+       && call.Uses == 1
+       && isInlinableMemmove(dst, src, sz, config)
+       && clobber(call)
+       => (Move [sz] dst src mem)
index 3d2759493e5a230bf5c80945a670f297ab37faaf..62699f290c2149e6308c4f0d3d82b9b20e3d7738 100644 (file)
 (MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))])
 (MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))])
 (MOVDload  [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))])
+
+// Arch-specific inlining for small or disjoint runtime.memmove
+(SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore  _ src s3:(MOVDstore {t} _ dst mem)))))
+       && sz >= 0
+       && isSameCall(sym, "runtime.memmove")
+       && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
+       && isInlinableMemmove(dst, src, sz, config)
+       && clobber(s1, s2, s3, call)
+       => (Move [sz] dst src mem)
index aad7600d793d77bb44acaf76f101fa8e10d95e37..5cbc70cf41d1c68c928c203c9fc77c53baa8882d 100644 (file)
 (SelectN [0] call:(StaticCall {sym} s1:(Store _ (Const(64|32) [sz]) s2:(Store  _ src s3:(Store {t} _ dst mem)))))
        && sz >= 0
        && isSameCall(sym, "runtime.memmove")
-       && t.IsPtr() // avoids TUINTPTR, see issue 30061
+       && t.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(s1, s2, s3, call)
        && sz >= 0
        && call.Uses == 1 // this will exclude all calls with results
        && isSameCall(sym, "runtime.memmove")
-       && dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
+       && dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(call)
        => (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
        && sz >= 0
        && call.Uses == 1 // this will exclude all calls with results
        && isSameCall(sym, "runtime.memmove")
-       && dst.Type.IsPtr() // avoids TUINTPTR, see issue 30061
+       && dst.Type.IsPtr() // avoids TUNSAFEPTR, see issue 30061
        && isInlinableMemmove(dst, src, int64(sz), config)
        && clobber(call)
        => (Move {dst.Type.Elem()} [int64(sz)] dst src mem)
index efb5d2714516950db2912b7b1ebf67f1f3af820b..5045ba7351f447ce13814b8ee6b3580cb9ed7ea6 100644 (file)
@@ -1038,6 +1038,8 @@ func rewriteValueAMD64(v *Value) bool {
                return rewriteValueAMD64_OpSelect0(v)
        case OpSelect1:
                return rewriteValueAMD64_OpSelect1(v)
+       case OpSelectN:
+               return rewriteValueAMD64_OpSelectN(v)
        case OpSignExt16to32:
                v.Op = OpAMD64MOVWQSX
                return true
@@ -32981,6 +32983,78 @@ func rewriteValueAMD64_OpSelect1(v *Value) bool {
        }
        return false
 }
+func rewriteValueAMD64_OpSelectN(v *Value) bool {
+       v_0 := v.Args[0]
+       b := v.Block
+       config := b.Func.Config
+       // match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVQstoreconst _ [sc] s2:(MOVQstore _ src s3:(MOVQstore _ dst mem)))))
+       // cond: sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)
+       // result: (Move [sc.Val64()] dst src mem)
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpAMD64CALLstatic || len(call.Args) != 1 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               s1 := call.Args[0]
+               if s1.Op != OpAMD64MOVQstoreconst {
+                       break
+               }
+               sc := auxIntToValAndOff(s1.AuxInt)
+               _ = s1.Args[1]
+               s2 := s1.Args[1]
+               if s2.Op != OpAMD64MOVQstore {
+                       break
+               }
+               _ = s2.Args[2]
+               src := s2.Args[1]
+               s3 := s2.Args[2]
+               if s3.Op != OpAMD64MOVQstore {
+                       break
+               }
+               mem := s3.Args[2]
+               dst := s3.Args[1]
+               if !(sc.Val64() >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sc.Val64(), config) && clobber(s1, s2, s3, call)) {
+                       break
+               }
+               v.reset(OpMove)
+               v.AuxInt = int64ToAuxInt(sc.Val64())
+               v.AddArg3(dst, src, mem)
+               return true
+       }
+       // match: (SelectN [0] call:(CALLstatic {sym} dst src (MOVQconst [sz]) mem))
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)
+       // result: (Move [sz] dst src mem)
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpAMD64CALLstatic || len(call.Args) != 4 {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               mem := call.Args[3]
+               dst := call.Args[0]
+               src := call.Args[1]
+               call_2 := call.Args[2]
+               if call_2.Op != OpAMD64MOVQconst {
+                       break
+               }
+               sz := auxIntToInt64(call_2.AuxInt)
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && call.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(call)) {
+                       break
+               }
+               v.reset(OpMove)
+               v.AuxInt = int64ToAuxInt(sz)
+               v.AddArg3(dst, src, mem)
+               return true
+       }
+       return false
+}
 func rewriteValueAMD64_OpSlicemask(v *Value) bool {
        v_0 := v.Args[0]
        b := v.Block
index 0ba3951df57418ffce9c9b9db3239b8fd4c7a4df..3cdc4d36cb5a400fbb419aa5b896074bc5243d61 100644 (file)
@@ -984,6 +984,8 @@ func rewriteValueARM64(v *Value) bool {
                return rewriteValueARM64_OpSelect0(v)
        case OpSelect1:
                return rewriteValueARM64_OpSelect1(v)
+       case OpSelectN:
+               return rewriteValueARM64_OpSelectN(v)
        case OpSignExt16to32:
                v.Op = OpARM64MOVHreg
                return true
@@ -25983,6 +25985,54 @@ func rewriteValueARM64_OpSelect1(v *Value) bool {
        }
        return false
 }
+func rewriteValueARM64_OpSelectN(v *Value) bool {
+       v_0 := v.Args[0]
+       b := v.Block
+       config := b.Func.Config
+       // match: (SelectN [0] call:(CALLstatic {sym} s1:(MOVDstore _ (MOVDconst [sz]) s2:(MOVDstore _ src s3:(MOVDstore {t} _ dst mem)))))
+       // cond: sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)
+       // result: (Move [sz] dst src mem)
+       for {
+               if auxIntToInt64(v.AuxInt) != 0 {
+                       break
+               }
+               call := v_0
+               if call.Op != OpARM64CALLstatic {
+                       break
+               }
+               sym := auxToCall(call.Aux)
+               s1 := call.Args[0]
+               if s1.Op != OpARM64MOVDstore {
+                       break
+               }
+               _ = s1.Args[2]
+               s1_1 := s1.Args[1]
+               if s1_1.Op != OpARM64MOVDconst {
+                       break
+               }
+               sz := auxIntToInt64(s1_1.AuxInt)
+               s2 := s1.Args[2]
+               if s2.Op != OpARM64MOVDstore {
+                       break
+               }
+               _ = s2.Args[2]
+               src := s2.Args[1]
+               s3 := s2.Args[2]
+               if s3.Op != OpARM64MOVDstore {
+                       break
+               }
+               mem := s3.Args[2]
+               dst := s3.Args[1]
+               if !(sz >= 0 && isSameCall(sym, "runtime.memmove") && s1.Uses == 1 && s2.Uses == 1 && s3.Uses == 1 && isInlinableMemmove(dst, src, sz, config) && clobber(s1, s2, s3, call)) {
+                       break
+               }
+               v.reset(OpMove)
+               v.AuxInt = int64ToAuxInt(sz)
+               v.AddArg3(dst, src, mem)
+               return true
+       }
+       return false
+}
 func rewriteValueARM64_OpSlicemask(v *Value) bool {
        v_0 := v.Args[0]
        b := v.Block
index 0cd86d1161a9f32a58759e166b0f14d8afd6e01e..ea8a01f803f7b92998e73d051ba75f11d311a13c 100644 (file)
@@ -97,6 +97,42 @@ func moveDisjointNoOverlap(a *[256]byte) {
        copy(a[:], a[128:])
 }
 
+// Check arch-specific memmove lowering. See issue 41662 fot details
+
+func moveArchLowering1(b []byte, x *[1]byte) {
+       _ = b[1]
+       // amd64:-".*memmove"
+       // arm64:-".*memmove"
+       copy(b, x[:])
+}
+
+func moveArchLowering2(b []byte, x *[2]byte) {
+       _ = b[2]
+       // amd64:-".*memmove"
+       // arm64:-".*memmove"
+       copy(b, x[:])
+}
+
+func moveArchLowering4(b []byte, x *[4]byte) {
+       _ = b[4]
+       // amd64:-".*memmove"
+       // arm64:-".*memmove"
+       copy(b, x[:])
+}
+
+func moveArchLowering8(b []byte, x *[8]byte) {
+       _ = b[8]
+       // amd64:-".*memmove"
+       // arm64:-".*memmove"
+       copy(b, x[:])
+}
+
+func moveArchLowering16(b []byte, x *[16]byte) {
+       _ = b[16]
+       // amd64:-".*memmove"
+       copy(b, x[:])
+}
+
 // Check that no branches are generated when the pointers are [not] equal.
 
 func ptrEqual() {