The pattern that folds (X >> C1) & C2 and (X << C1) & C2 was limited
to types with precision <= HOST_BITS_PER_WIDE_INT because it used
unsigned HOST_WIDE_INT for mask and zerobits arithmetic.  This prevents
the optimization from firing on __int128 and wider types at the GIMPLE
level.

Convert the pattern to use wide_int and remove the TYPE_PRECISION guard.
Replace the mode-mask loop with wi::exact_log2 (newmask + 1) to check
whether newmask is (2^n - 1) for a power-of-two n >= BITS_PER_UNIT.
Handle the case where newmask is all-ones at full type precision
separately, since newmask + 1 overflows to zero in that case.

        PR120524
        PR tree-optimization/120524

gcc/ChangeLog:

        * match.pd: Use wide_int for shift-and-mask folding [PR120524]

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr120524.c: New test.

Signed-off-by: Avinal Kumar <[email protected]>
---
Chnages from RFC:
- fixed pattern not hitting due to overflow
- added more tests and a regression guard

Tested on x86_64 machine. Optimization result:

avinal@cfarm422:~/gcc-work/build$ ./gcc/xgcc -B ./gcc -O2 
-fdump-tree-optimized=/dev/stdout 
~/gcc-work/gcc/gcc/testsuite/gcc.dg/tree-ssa/pr120524.c -c -o /dev/null

;; Function f1 (f1, funcdef_no=0, decl_uid=2966, cgraph_uid=1, symbol_order=0)

__int128 f1 (__int128 a)
{
  uint128_t a.0_1;
  uint128_t _2;
  __int128 _4;

  <bb 2> [local count: 1073741824]:
  a.0_1 = (uint128_t) a_3(D);
  _2 = a.0_1 >> 127;
  _4 = (__int128) _2;
  return _4;

}

;; Function f2 (f2, funcdef_no=1, decl_uid=2969, cgraph_uid=2, symbol_order=1)

__int128 f2 (__int128 a)
{
  uint128_t a.1_1;
  uint128_t _2;
  __int128 _4;

  <bb 2> [local count: 1073741824]:
  a.1_1 = (uint128_t) a_3(D);
  _2 = a.1_1 >> 120;
  _4 = (__int128) _2;
  return _4;

}

;; Function f3 (f3, funcdef_no=2, decl_uid=2972, cgraph_uid=3, symbol_order=2)

__int128 f3 (__int128 a)
{
  __int128 _2;

  <bb 2> [local count: 1073741824]:
  _2 = a_1(D) << 120;
  return _2;

}

;; Function f4 (f4, funcdef_no=3, decl_uid=2975, cgraph_uid=4, symbol_order=3)

long long int f4 (long long int a)
{
  unsigned long a.2_1;
  unsigned long _2;
  long long int _4;

  <bb 2> [local count: 1073741824]:
  a.2_1 = (unsigned long) a_3(D);
  _2 = a.2_1 >> 63;
  _4 = (long long int) _2;
  return _4;

}

 gcc/match.pd                             | 55 ++++++++++++------------
 gcc/testsuite/gcc.dg/tree-ssa/pr120524.c | 28 ++++++++++++
 2 files changed, 56 insertions(+), 27 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr120524.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 2a5f948c965..5825ebafaf6 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5670,20 +5670,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
   (bit_and (convert?:s@4 (shift:s@5 (convert1?@3 @0) INTEGER_CST@1))
            INTEGER_CST@2)
    (if (tree_nop_conversion_p (TREE_TYPE (@4), TREE_TYPE (@5))
-       && TYPE_PRECISION (type) <= HOST_BITS_PER_WIDE_INT
        && tree_fits_uhwi_p (@1)
        && tree_to_uhwi (@1) > 0
        && tree_to_uhwi (@1) < TYPE_PRECISION (type))
     (with
      {
        unsigned int shiftc = tree_to_uhwi (@1);
-       unsigned HOST_WIDE_INT mask = TREE_INT_CST_LOW (@2);
-       unsigned HOST_WIDE_INT newmask, zerobits = 0;
+       wide_int mask = wi::to_wide (@2);
+       wide_int newmask, zerobits = wi::zero (TYPE_PRECISION (type));
        tree shift_type = TREE_TYPE (@3);
        unsigned int prec;
 
        if (shift == LSHIFT_EXPR)
-        zerobits = ((HOST_WIDE_INT_1U << shiftc) - 1);
+        zerobits = wi::mask (shiftc, false, TYPE_PRECISION (type));
        else if (shift == RSHIFT_EXPR
                && type_has_mode_precision_p (shift_type))
         {
@@ -5707,12 +5706,9 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
                      is all ones.  */
                 }
             }
-          zerobits = HOST_WIDE_INT_M1U;
+          zerobits = wi::minus_one (TYPE_PRECISION (type));
           if (shiftc < prec)
-            {
-              zerobits >>= HOST_BITS_PER_WIDE_INT - shiftc;
-              zerobits <<= prec - shiftc;
-            }
+             zerobits = wi::mask (prec - shiftc, true, TYPE_PRECISION (type));
           /* For arithmetic shift if sign bit could be set, zerobits
              can contain actually sign bits, so no transformation is
              possible, unless MASK masks them all away.  In that
@@ -5720,35 +5716,40 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
           if (!TYPE_UNSIGNED (TREE_TYPE (@3))
               && prec == TYPE_PRECISION (TREE_TYPE (@3)))
             {
-              if ((mask & zerobits) == 0)
+              if (wi::eq_p (wi::bit_and (mask, zerobits),
+                  wi::zero (TYPE_PRECISION (type))))
                 shift_type = unsigned_type_for (TREE_TYPE (@3));
               else
-                zerobits = 0;
+                zerobits = wi::zero (TYPE_PRECISION (type));
             }
         }
      }
      /* ((X << 16) & 0xff00) is (X, 0).  */
-     (if ((mask & zerobits) == mask)
+     (if (wi::eq_p (wi::bit_and (mask, zerobits), mask))
       { build_int_cst (type, 0); }
-      (with { newmask = mask | zerobits; }
-       (if (newmask != mask && (newmask & (newmask + 1)) == 0)
-        (with
-        {
-          /* Only do the transformation if NEWMASK is some integer
-             mode's mask.  */
-          for (prec = BITS_PER_UNIT;
-               prec < HOST_BITS_PER_WIDE_INT; prec <<= 1)
-            if (newmask == (HOST_WIDE_INT_1U << prec) - 1)
-              break;
-        }
-        (if (prec < HOST_BITS_PER_WIDE_INT
-             || newmask == HOST_WIDE_INT_M1U)
+      (with { newmask = wi::bit_or (mask, zerobits);
+             /* Only do the transformation if NEWMASK is some integer
+                mode's mask, i.e.  an all-ones value (2^n - 1) where n
+                is a power of 2 precision >= BITS_PER_UNIT.  We check
+                this by testing whether newmask + 1 is a power of two
+                via wi::exact_log2.  */
+             int lg;
+             /* newmask + 1 overflows at full type precision (e.g.  128-bit
+                all-ones), so handle that case directly.  */
+             if (wi::eq_p (newmask, wi::minus_one (TYPE_PRECISION (type))))
+               lg = TYPE_PRECISION (type);
+             else
+               lg = wi::exact_log2 (wi::add (newmask, 1));
+             unsigned int newmask_prec = lg >= 0 ? (unsigned int) lg : 0; }
+       (if (!wi::eq_p (newmask, mask)
+           && lg > 0 && (newmask_prec & (newmask_prec - 1)) == 0
+           && newmask_prec >= BITS_PER_UNIT)
          (with
-          { tree newmaskt = build_int_cst_type (TREE_TYPE (@2), newmask); }
+          { tree newmaskt = wide_int_to_tree (TREE_TYPE (@2), newmask); }
           (if (!tree_int_cst_equal (newmaskt, @2))
            (if (shift_type != TREE_TYPE (@3))
             (bit_and (convert (shift:shift_type (convert @3) @1)) { newmaskt; 
})
-            (bit_and @4 { newmaskt; })))))))))))))
+            (bit_and @4 { newmaskt; })))))))))))
 
 /* ((1 << n) & M) != 0  -> n == log2 (M) */
 (for cmp (ne eq)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr120524.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr120524.c
new file mode 100644
index 00000000000..b9d9bae74ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/120524 */
+/* { dg-do compile } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Verify (a >> 127) & 1 is optimized to (unsigned __int128)a >> 127.
+   The mask 1 covers all sign-extended bits, so the arithmetic right shift
+   can be converted to a logical right shift.  */
+__int128 f1(__int128 a) { return (a >> 127) & 1; }
+
+/* Verify (a >> 120) & 0xff is similarly optimized.  */
+__int128 f2(__int128 a) { return (a >> 120) & 0xff; }
+
+/* Verify the left shift path of the same pattern also works for 128-bit
+   types.  The mask isolates only the shifted bits, so the redundant lower
+   bits can be folded into the mask.  */
+__int128 f3(__int128 a) { return (a << 120) & ((__int128)0xff << 120); }
+
+/* f1, f2: arithmetic right shifts should be converted to logical shifts.  */
+/* { dg-final { scan-tree-dump "uint128_t" "optimized" } } */
+
+/* Verify 64-bit case also works, this is folded by C frontend putting it
+   here as regression guard.  */
+long long f4(long long a) { return (a >> 63) & 1; }
+
+/* All BIT_AND masks should be folded away.  */
+/* { dg-final { scan-tree-dump-not "& 1" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "& 255" "optimized" } } */
-- 
2.55.0

Reply via email to