https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96669

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org

--- Comment #3 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
Seems we already since 2014 had a rule for this when the result is compared
against 0.

Anyway, here is a generalization of that plus an attempt at handling this
specific case without the comparison:

--- gcc/match.pd.jj     2021-01-15 14:00:21.567135280 +0100
+++ gcc/match.pd        2021-01-15 16:55:30.658692184 +0100
@@ -3117,13 +3117,33 @@ (define_operator_list COND_TERNARY
       (op @0 { build_int_cst (TREE_TYPE (@1), low); })))))))


-/* ((1 << A) & 1) != 0 -> A == 0
-   ((1 << A) & 1) == 0 -> A != 0 */
+/* Simplify (CST << x) & 1 to 0 if CST is even or to x == 0 if it is odd.  /
+(simplify
+ (bit_and (lshift INTEGER_CST@1 @0) integer_onep)
+  (if ((wi::to_wide (@1) & 1) != 0)
+   (eq @0 { build_zero_cst (TREE_TYPE (@0)); })
+   ({ build_zero_cst (type); })))
+
+/* Simplify ((C << x) & D) != 0 where C and D are power of two constants,
+   either to false if D is smaller (unsigned comparison) than C, or to
+   x == log2 (D) - log2 (C).  Similarly for right shifts.  */
 (for cmp (ne eq)
      icmp (eq ne)
  (simplify
-  (cmp (bit_and (lshift integer_onep @0) integer_onep) integer_zerop)
-  (icmp @0 { build_zero_cst (TREE_TYPE (@0)); })))
+  (cmp (bit_and (lshift integer_pow2p@1 @0) integer_pow2p@2) integer_zerop)
+   (with { int c1 = wi::clz (wi::to_wide (@1));
+          int c2 = wi::clz (wi::to_wide (@2)); }
+    (if (c1 < c2)
+     { constant_boolean_node (cmp == NE_EXPR ? false : true, type); }
+     (icmp @0 { build_int_cst (TREE_TYPE (@0), c1 - c2); }))))
+ (simplify
+  (cmp (bit_and (rshift integer_pow2p@1 @0) integer_pow2p@2) integer_zerop)
+   (if (tree_int_cst_sgn (@1) > 0)
+    (with { int c1 = wi::clz (wi::to_wide (@1));
+           int c2 = wi::clz (wi::to_wide (@2)); }
+     (if (c1 > c2)
+      { constant_boolean_node (cmp == NE_EXPR ? false : true, type); }
+      (icmp @0 { build_int_cst (TREE_TYPE (@0), c2 - c1); }))))))

 /* (CST1 << A) == CST2 -> A == ctz (CST2) - ctz (CST1)
    (CST1 << A) != CST2 -> A != ctz (CST2) - ctz (CST1)
Unfortunately, while that extended second simplification seems to work fine,
have been using:
int
f1 (int a)
{
  return (1 << a) & 1;
}

int
f2 (int a)
{
  return (2 << a) & 1;
}

int
f3 (int a)
{
  return ((1 << a) & 1) != 0;
}

int
f4 (int a)
{
  return ((2 << a) & 1) == 0;
}

int
f5 (int a)
{
  return ((2 << a) & 16) != 0;
}

int
f6 (int a)
{
  return ((16 << a) & 32) == 0;
}

int
f7 (int a)
{
  return (1 >> a) & 1;
}

int
f8 (int a)
{
  return (2 >> a) & 1;
}

int
f9 (int a)
{
  return ((1 >> a) & 1) == 0;
}

int
f10 (int a)
{
  return ((2 >> a) & 1) != 0;
}

int
f11 (int a)
{
  return ((1 >> a) & 2) != 0;
}

int
f12 (int a)
{
  return ((32 >> a) & 16) == 0;
}
as testcase, the first simplification doesn't show up in *-match.c at all.  Any
ideas why?

Reply via email to