Reverse rotations with masked negative counts in the generic RTL
simplifier. Keep the count mask so the reversed count stays in range.

gcc/ChangeLog:

        * simplify-rtx.cc (simplify_context::simplify_binary_operation_1):
        Reverse rotations with masked negative counts.
        (selftest::test_masked_negative_rotate): New function.
        (selftest::test_scalar_int_ops): Call it.

gcc/testsuite/ChangeLog:

        * gcc.target/riscv/zbb-rol-ror-10.c: New test.
        * gcc.target/aarch64/rotate-neg-mask.c: Ditto.

---
Requires patch 1/2 of this series.

diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc
index 
e979d4c296411170d3de0ffa5863df7cd07c0b33..1711b55f7376c33fb145d952c1d250e88d61740b
 100644
--- a/gcc/simplify-rtx.cc
+++ b/gcc/simplify-rtx.cc
@@ -4858,6 +4858,44 @@
     case ROTATE:
       if (trueop1 == CONST0_RTX (mode))
        return op0;
+
+      /* Reversing the direction removes a negated count modulo the
+        precision.  Keep the mask so that the new count is in range even
+        on targets that do not truncate rotate counts.  */
+      if (is_a <scalar_int_mode> (mode, &int_mode)
+         && exact_log2 (GET_MODE_PRECISION (int_mode)) >= 0)
+       {
+         rtx count = op1;
+         if ((SUBREG_P (count)
+              && subreg_lowpart_p (count)
+              && !paradoxical_subreg_p (count))
+             || GET_CODE (count) == TRUNCATE)
+           count = XEXP (count, 0);
+
+         scalar_int_mode count_mode;
+         if (GET_CODE (count) == AND
+             && GET_CODE (XEXP (count, 0)) == NEG
+             && CONST_INT_P (XEXP (count, 1))
+             && INTVAL (XEXP (count, 1))
+                == GET_MODE_PRECISION (int_mode) - 1
+             && is_a <scalar_int_mode> (GET_MODE (count), &inner_mode)
+             && is_a <scalar_int_mode> (GET_MODE (op1), &count_mode)
+             && GET_MODE_PRECISION (count_mode)
+                > exact_log2 (GET_MODE_PRECISION (int_mode))
+             && GET_MODE_PRECISION (inner_mode)
+                >= GET_MODE_PRECISION (count_mode))
+           {
+             tem = simplify_gen_binary (AND, inner_mode,
+                                        XEXP (XEXP (count, 0), 0),
+                                        XEXP (count, 1));
+             if (count != op1)
+               tem = simplify_gen_unary (TRUNCATE, count_mode, tem,
+                                         inner_mode);
+             return simplify_gen_binary (code == ROTATE ? ROTATERT : ROTATE,
+                                         mode, op0, tem);
+           }
+       }
+
       /* Canonicalize rotates by constant amount.  If the condition of
         reversing direction is met, then reverse the direction. */
 #if defined(HAVE_rotate) && defined(HAVE_rotatert)
@@ -9352,6 +9390,65 @@
   return gen_rtx_REG (mode, test_reg_num++);
 }
 
+/* Test reversing rotations with a masked negative count.  */
+
+static void
+test_masked_negative_rotate (machine_mode mode)
+{
+  rtx value = make_test_reg (mode);
+  unsigned int precision = GET_MODE_PRECISION (mode).to_constant ();
+  int count_bits = exact_log2 (precision);
+  static const machine_mode count_modes[] = { QImode, SImode, DImode };
+  static const rtx_code codes[] = { ROTATE, ROTATERT };
+
+  for (auto count_mode : count_modes)
+    {
+      rtx reg = make_test_reg (count_mode);
+      rtx neg = gen_rtx_NEG (count_mode, reg);
+      rtx mask = gen_int_mode (precision - 1, count_mode);
+      rtx count = gen_rtx_AND (count_mode, neg, mask);
+      rtx positive = simplify_gen_binary (AND, count_mode, reg, mask);
+
+      for (auto code : codes)
+       {
+         rtx_code reverse = code == ROTATE ? ROTATERT : ROTATE;
+         rtx expected = NULL_RTX;
+         if (count_bits >= 0
+             && GET_MODE_PRECISION (count_mode).to_constant () > count_bits)
+           expected = simplify_gen_binary (reverse, mode, value, positive);
+         ASSERT_RTX_EQ (expected,
+                        simplify_binary_operation (code, mode, value, count));
+
+         /* A bare negative count or a narrower mask must not be folded.  */
+         ASSERT_FALSE (simplify_binary_operation (code, mode, value, neg));
+         rtx narrow = gen_rtx_AND (count_mode, neg,
+                                  gen_int_mode (precision / 2 - 1, 
count_mode));
+         ASSERT_FALSE (simplify_binary_operation (code, mode, value, narrow));
+
+         if (count_mode == QImode)
+           continue;
+         expected = NULL_RTX;
+         if (count_bits >= 0 && GET_MODE_PRECISION (QImode) > count_bits)
+           expected = simplify_gen_binary
+             (reverse, mode, value,
+              simplify_gen_unary (TRUNCATE, QImode, positive, count_mode));
+         rtx low = gen_rtx_SUBREG (QImode, count,
+                                  subreg_lowpart_offset (QImode, count_mode));
+         ASSERT_RTX_EQ (expected,
+                        simplify_binary_operation (code, mode, value, low));
+         rtx trunc = gen_rtx_TRUNCATE (QImode, count);
+         ASSERT_RTX_EQ (expected,
+                        simplify_binary_operation (code, mode, value, trunc));
+         poly_uint64 offset = subreg_highpart_offset (QImode, count_mode);
+         if (validate_subreg (QImode, count_mode, count, offset))
+           {
+             rtx high = gen_rtx_SUBREG (QImode, count, offset);
+             ASSERT_FALSE (simplify_binary_operation (code, mode, value, 
high));
+           }
+       }
+    }
+}
+
 static void
 test_scalar_int_ops (machine_mode mode)
 {
@@ -9418,6 +9515,8 @@
   ASSERT_RTX_EQ (op0, simplify_gen_unary (ZERO_EXTEND, mode, op0, mode));
   ASSERT_RTX_EQ (op0, simplify_gen_unary (SIGN_EXTEND, mode, op0, mode));
   ASSERT_RTX_EQ (op0, lowpart_subreg (mode, op0, mode));
+
+  test_masked_negative_rotate (mode);
 }
 
 /* Verify some simplifications of integer extension/truncation.
diff --git a/gcc/testsuite/gcc.target/riscv/zbb-rol-ror-10.c 
b/gcc/testsuite/gcc.target/riscv/zbb-rol-ror-10.c
new file mode 100644
index 
0000000000000000000000000000000000000000..2db53c6887fdf719dbcb0f0c2e52ae3691e4d704
--- /dev/null
+++ b/gcc/testsuite/gcc.target/riscv/zbb-rol-ror-10.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-march=rv64gc_zbb -mabi=lp64" { target { rv64 } } } */
+/* { dg-options "-march=rv32gc_zbb -mabi=ilp32" { target { rv32 } } } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-Og" } } */
+
+unsigned long
+rol_neg (unsigned long x, unsigned long n)
+{
+  return __builtin_stdc_rotate_left (x, 0UL - (n & 255UL));
+}
+
+unsigned long
+ror_neg (unsigned long x, unsigned long n)
+{
+  return __builtin_stdc_rotate_right (x, 0UL - (n & 255UL));
+}
+
+/* This mask changes the effective count and must be retained.  */
+unsigned long
+ror_neg_mask7 (unsigned long x, unsigned long n)
+{
+  return __builtin_stdc_rotate_right (x, 0UL - (n & 7UL));
+}
+
+#if __riscv_xlen == 64
+unsigned int
+rolw_neg (unsigned int x, unsigned int n)
+{
+  return __builtin_stdc_rotate_left (x, 0U - (n & 31U));
+}
+
+unsigned int
+rorw_neg (unsigned int x, unsigned int n)
+{
+  return __builtin_stdc_rotate_right (x, 0U - (n & 31U));
+}
+#endif
+
+/* { dg-final { scan-assembler-times "rol\t" 2 } } */
+/* { dg-final { scan-assembler-times "ror\t" 1 } } */
+/* { dg-final { scan-assembler-times "rolw\t" 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "rorw\t" 1 { target { rv64 } } } } */
+/* { dg-final { scan-assembler-times "andi\t" 1 } } */
+/* { dg-final { scan-assembler-not "negw?\t" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/rotate-neg-mask.c 
b/gcc/testsuite/gcc.target/aarch64/rotate-neg-mask.c
new file mode 100644
index 
0000000000000000000000000000000000000000..68c63c2768aa8fc57c894c76fd532aa7b63cbaf8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/rotate-neg-mask.c
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { *-*-* } { "-mgeneral-regs-only" } } */
+
+/* Keep SIMD enabled so that SHIFT_COUNT_TRUNCATED is false.  */
+__attribute__((noipa)) unsigned int
+rotate32 (unsigned int x, unsigned int n)
+{
+  return __builtin_stdc_rotate_right (x, 0U - (n & 31U));
+}
+
+__attribute__((noipa)) unsigned long long
+rotate64 (unsigned long long x, unsigned long long n)
+{
+  return __builtin_stdc_rotate_right (x, 0ULL - (n & 63ULL));
+}
+
+__attribute__((noipa)) unsigned long long
+rotate64_count32 (unsigned long long x, unsigned int n)
+{
+  return __builtin_stdc_rotate_right (x, 0U - (n & 63U));
+}
+
+int
+main (void)
+{
+  unsigned int expected32 = 1;
+  unsigned long long expected64 = 1;
+  for (unsigned int n = 0; n < 128; ++n)
+    {
+      if (rotate32 (1, n) != expected32
+         || rotate64 (1, n) != expected64
+         || rotate64_count32 (1, n) != expected64)
+       __builtin_abort ();
+      expected32 = (expected32 << 1) | (expected32 >> 31);
+      expected64 = (expected64 << 1) | (expected64 >> 63);
+    }
+  return 0;
+}

Reply via email to