[RFC PATCH] match: Use wide_int for shift-and-mask folding [PR120524]

Avinal Kumar <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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.

Signed-off-by: Avinal Kumar <[email protected]>
---
Hi, I tried to replace it 1:1. But it isn't working. I think I have hit a wall
with this. The change compiles fine but never gets fired for
any case. Please take a look.

For 64-bit types (long long), the C frontend already performs
this optimization during parsing. it appears in
-fdump-tree-original, so the match.pd pattern was never the
path that optimized (a >> 63) & 1. This means there is no
easy way to test whether the pattern fires for the simpler
case.

Expected variable values for (a >> 127) & 1 with __int128 (prec=128):
 - shiftc = 127
 - mask = 0x1
 - zerobits = ~0 with top bit clear  (wi::mask(128-127, true, 128) = 0xfff...fe)
 - mask & zerobits = 0,  shift_type = unsigned __int128
 - newmask = mask | zerobits = 0xfff...ff
 - newmask + 1 = 2^128 (overflows to 0 in 128-bit precision)

That last point may be the problem, wi::exact_log2 of a 128-bit
zero would return -1, failing the lg > 0 check.  But I haven't been
able to confirm this is the issue since the pattern doesn't appear
to be reached at all.

Thanks

 gcc/match.pd                             | 49 +++++++++++-------------
 gcc/testsuite/gcc.dg/tree-ssa/pr120524.c | 12 ++++++
 2 files changed, 34 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 eae8717bcfe..4ee309b324b 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5643,20 +5643,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))
 	 {
@@ -5680,12 +5679,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
@@ -5693,35 +5689,34 @@ 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 = 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..19993665c92
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr120524.c
@@ -0,0 +1,12 @@
+/* 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; }
+
+/* The arithmetic right shifts should be converted to logical shifts.  */
+/* { dg-final { scan-tree-dump "unsigned __int128"  "optimized" } } */
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.