From: Kyrylo Tkachov <[email protected]>

aarch64_cm<optab>di has a general register alternative, but its second
operand only accepts a register there, while the predicate also allows a
constant zero.  A comparison against zero therefore has no general
register alternative and the value is moved through a vector register.

Accept the zero in that alternative.  The split already builds the
comparison with aarch64_gen_compare_reg, which handles a zero operand.

  long f (long x) { return -(long) (x == 0); }

aarch64 -O2:

  before                          after
    fmov  d31, x0                   cmp    x0, 0
    cmeq  d31, d31, #0              csetm  x0, eq
    fmov  x0, d31

The two moves cross the general and vector register files, which is the
expensive part.  The vector alternatives are unchanged, so a comparison
whose operands are already in vector registers still uses cmeq.

Constant time selection code in OpenSSL, Botan and libgcrypt builds masks
this way.

Bootstrapped and tested on aarch64-none-linux-gnu.

gcc/ChangeLog:

        * config/aarch64/aarch64-simd.md (aarch64_cm<optab>di): Accept a
        zero second operand in the general register alternative.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/cmpdi-neg-1.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/config/aarch64/aarch64-simd.md            |  2 +-
 .../gcc.target/aarch64/cmpdi-neg-1.c          | 58 +++++++++++++++++++
 2 files changed, 59 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/cmpdi-neg-1.c

diff --git a/gcc/config/aarch64/aarch64-simd.md 
b/gcc/config/aarch64/aarch64-simd.md
index aa6c1fa0735..12861333b22 100644
--- a/gcc/config/aarch64/aarch64-simd.md
+++ b/gcc/config/aarch64/aarch64-simd.md
@@ -7945,7 +7945,7 @@
        (neg:DI
          (COMPARISONS:DI
            (match_operand:DI 1 "register_operand" "w,w,r")
-           (match_operand:DI 2 "aarch64_simd_reg_or_zero" "w,ZDz,r")
+           (match_operand:DI 2 "aarch64_simd_reg_or_zero" "w,ZDz,rZ")
          )))
      (clobber (reg:CC CC_REGNUM))]
   "TARGET_SIMD"
diff --git a/gcc/testsuite/gcc.target/aarch64/cmpdi-neg-1.c 
b/gcc/testsuite/gcc.target/aarch64/cmpdi-neg-1.c
new file mode 100644
index 00000000000..cfe0eb0ac0b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/cmpdi-neg-1.c
@@ -0,0 +1,58 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/* A 64-bit comparison against zero whose result is negated has to stay in
+   the general registers.  Before, only the vector alternative accepted the
+   zero, so the value was moved to and from a vector register.  */
+
+/*
+** f1:
+**     cmp     x0, 0
+**     csetm   x0, eq
+**     ret
+*/
+long
+f1 (long x)
+{
+  return -(long) (x == 0);
+}
+
+/*
+** f2:
+**     cmp     x0, 0
+**     csetm   x0, ne
+**     ret
+*/
+long
+f2 (long x)
+{
+  return -(long) (x != 0);
+}
+
+/*
+** f3:
+**     cmp     x0, 0
+**     csetm   x0, le
+**     ret
+*/
+long
+f3 (long x)
+{
+  return -(long) (x <= 0);
+}
+
+/*
+** f4:
+**     cmp     x0, x1
+**     csetm   x0, eq
+**     ret
+*/
+long
+f4 (long x, long y)
+{
+  return -(long) (x == y);
+}
+
+/* { dg-final { scan-assembler-not "\\tfmov\\t" } } */
+/* { dg-final { scan-assembler-not "\\tcmeq\\t" } } */
-- 
2.50.1 (Apple Git-155)

Reply via email to