https://gcc.gnu.org/g:3383c738122861c52c150483b012be25d9b9dbac

commit r17-5256-g3383c738122861c52c150483b012be25d9b9dbac
Author: Avinal Kumar <[email protected]>
Date:   Fri Oct 16 07:36:25 2026 -0600

    match.pd: Use wide_int for shift-and-mask folding [PR 
tree-optimization/120524]
    
    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.
    
    ---
    Changes from v2:
    - use operator instead of explicit function calls, wi:: supports operator 
overloading
    - split tests into right shift with negative assetion and left shift with 
positive assertion and regression guard
    
    I also found a case for option 2 as discussed in the last patch:
    
    __int128 f(__int128 a) { return (a << 8) & ((__int128)0xff00); }
    
    Without the patch (128-bit, pattern skipped): keeps & 65280 (0xff00)
    
    With the patch: widens to & 65535 (0xffff - 16-bit mode mask)
    
    this was not required if going for option one, so not included in the patch.
    
            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-1.c: New test.
            * gcc.dg/tree-ssa/pr120524-2.c: New test.
    
    Signed-off-by: Avinal Kumar <[email protected]>

Diff:
---
 gcc/match.pd                               | 51 +++++++++++++++---------------
 gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c | 18 +++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c | 15 +++++++++
 3 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/gcc/match.pd b/gcc/match.pd
index 1cb710974f7e..a28d307c819c 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5859,20 +5859,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))
         {
@@ -5896,12 +5895,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
@@ -5909,35 +5905,38 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
           if (!TYPE_UNSIGNED (TREE_TYPE (@3))
               && prec == TYPE_PRECISION (TREE_TYPE (@3)))
             {
-              if ((mask & zerobits) == 0)
+              if ((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)
       { 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 = (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 (newmask == wi::minus_one (TYPE_PRECISION (type)))
+               lg = TYPE_PRECISION (type);
+             else
+               lg = wi::exact_log2 (newmask + 1);
+             unsigned int newmask_prec = lg >= 0 ? (unsigned int) lg : 0; }
+       (if (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-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c
new file mode 100644
index 000000000000..24ac98090c27
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-1.c
@@ -0,0 +1,18 @@
+/* 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; }
+
+/* Arithmetic right shifts should be converted to logical shifts.  All BIT_AND
+   masks should be folded away.  */
+/* { dg-final { scan-tree-dump "uint128_t" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "& 1" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "& 255" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c
new file mode 100644
index 000000000000..5d01607c04c4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524-2.c
@@ -0,0 +1,15 @@
+/* PR tree-optimization/120524 */
+/* { dg-do compile } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Left shift: (a << 120) & (0xff <<120) folds to just a << 120.  */
+__int128 f1(__int128 a) { return (a << 120) & ((__int128)0xff << 120); }
+
+/* Verify 64-bit case also works, this is folded by C frontend putting it
+   here as regression guard. This should fold to (unsigned long)a >> 63.  */
+__int128 f2(__int128 a) { return (a >> 63) & 1; }
+
+/* Verify expected optimized forms: shift with no AND mask.  */
+/* { dg-final { scan-tree-dump "<< 120" "optimized" } } */
+/* { dg-final { scan-tree-dump ">> 63" "optimized" } } */

Reply via email to