]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
New XOR fold routine.
authorAndrew MacLeod <amacleod@redhat.com>
Wed, 29 Oct 2025 22:14:40 +0000 (18:14 -0400)
committerAndrew MacLeod <amacleod@redhat.com>
Thu, 6 Nov 2025 13:54:17 +0000 (08:54 -0500)
XOR goes to VARYING frequently with complex ranges. The other bitwise
operations are improved, so implement XOR using them as well.

PR tree-optimization/113632
gcc/
* range-op-mixed.h (operator_bitwise_xor): Relocate and adjust.
(operator_bitwise_xor::m_and, m_or, m_not): New.
* range-op.cc (operator_bitwise_xor::fold_range): New.

gcc/testsuite/
* gcc.dg/pr113632.c: New.

gcc/range-op-mixed.h
gcc/range-op.cc
gcc/testsuite/gcc.dg/pr113632.c [new file with mode: 0644]

index db31c2bc8c9547fa7f599ab9e7ea27ac6a97f1e2..f79cac8ff5523279562d5f343eee87feae10e871 100644 (file)
@@ -803,35 +803,6 @@ public:
     { return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
 };
 
-class operator_bitwise_xor : public range_operator
-{
-public:
-  using range_operator::op1_range;
-  using range_operator::op2_range;
-  using range_operator::op1_op2_relation_effect;
-  using range_operator::update_bitmask;
-  bool op1_range (irange &r, tree type,
-                 const irange &lhs, const irange &op2,
-                 relation_trio rel = TRIO_VARYING) const final override;
-  bool op2_range (irange &r, tree type,
-                 const irange &lhs, const irange &op1,
-                 relation_trio rel = TRIO_VARYING) const final override;
-  bool op1_op2_relation_effect (irange &lhs_range,
-                                       tree type,
-                                       const irange &op1_range,
-                                       const irange &op2_range,
-                                       relation_kind rel) const final override;
-  void update_bitmask (irange &r, const irange &lh,
-                      const irange &rh) const final override;
-  // Check compatibility of all operands.
-  bool operand_check_p (tree t1, tree t2, tree t3) const final override
-    { return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
-private:
-  void wi_fold (irange &r, tree type, const wide_int &lh_lb,
-               const wide_int &lh_ub, const wide_int &rh_lb,
-               const wide_int &rh_ub) const final override;
-};
-
 class operator_bitwise_and : public range_operator
 {
 public:
@@ -896,6 +867,43 @@ protected:
                const wide_int &rh_ub) const override;
 };
 
+class operator_bitwise_xor : public range_operator
+{
+public:
+  using range_operator::fold_range;
+  using range_operator::op1_range;
+  using range_operator::op2_range;
+  using range_operator::op1_op2_relation_effect;
+  using range_operator::update_bitmask;
+  bool fold_range (irange &r, tree type,
+                  const irange &lh, const irange &rh,
+                  relation_trio rel = TRIO_VARYING) const final override;
+  bool op1_range (irange &r, tree type,
+                 const irange &lhs, const irange &op2,
+                 relation_trio rel = TRIO_VARYING) const final override;
+  bool op2_range (irange &r, tree type,
+                 const irange &lhs, const irange &op1,
+                 relation_trio rel = TRIO_VARYING) const final override;
+  bool op1_op2_relation_effect (irange &lhs_range,
+                                       tree type,
+                                       const irange &op1_range,
+                                       const irange &op2_range,
+                                       relation_kind rel) const final override;
+  void update_bitmask (irange &r, const irange &lh,
+                      const irange &rh) const final override;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return range_compatible_p (t1, t2) && range_compatible_p (t1, t3); }
+private:
+  void wi_fold (irange &r, tree type, const wide_int &lh_lb,
+               const wide_int &lh_ub, const wide_int &rh_lb,
+               const wide_int &rh_ub) const final override;
+  class operator_bitwise_and m_and;
+  class operator_bitwise_or m_or;
+  class operator_bitwise_not m_not;
+};
+
+
 class operator_min : public range_operator
 {
 public:
index cf5b8fe960f8a78638174e2f45aa1ad9c4160bfc..82a994b4ca555a33239a6490e59ed8503025d067 100644 (file)
@@ -4046,6 +4046,83 @@ operator_bitwise_xor::update_bitmask (irange &r, const irange &lh,
   update_known_bitmask (r, BIT_XOR_EXPR, lh, rh);
 }
 
+bool
+operator_bitwise_xor::fold_range (irange &r, tree type,
+                                 const irange &lh, const irange &rh,
+                                 relation_trio rel) const
+{
+  // Handle X ^ UNDEFINED = UNDEFINED.
+  if (lh.undefined_p () || rh.undefined_p ())
+    {
+      r.set_undefined ();
+      return true;
+    }
+
+  // Next, handle X ^ X == [0, 0].
+  if (rel.op1_op2 () == VREL_EQ)
+   {
+     r.set_zero (type);
+     return true;
+   }
+
+  // If either operand is VARYING, the result is VARYING.
+  if (lh.varying_p () || rh.varying_p ())
+    {
+      // If the operands are not equal, zero is not possible.
+      if (rel.op1_op2 () != VREL_NE)
+       r.set_varying (type);
+      else
+       r.set_nonzero (type);
+      return true;
+    }
+
+  // Now deal with X ^ 0 == X.
+  if (lh.zero_p ())
+    {
+      r = rh;
+      return true;
+    }
+  if (rh.zero_p ())
+    {
+      r = lh;
+      return true;
+    }
+
+  // Start with the legacy range.  This can sometimes pick up values
+  // when there are a lot of subranges and fold_range aggregates them.
+  bool res = range_operator::fold_range (r, type, lh, rh, rel);
+
+  // Calculate the XOR identity :   x ^ y = (x | y) & ~(x & y)
+  // AND and OR are already much better optimized.
+  int_range_max tmp1, tmp2, tmp3, new_result;
+  int_range<2> varying;
+  varying.set_varying (type);
+
+  if (m_or.fold_range  (tmp1, type, lh, rh, rel)
+      && m_and.fold_range (tmp2, type, lh, rh, rel)
+      && m_not.fold_range (tmp3, type, tmp2, varying, rel)
+      && m_and.fold_range (new_result, type, tmp1, tmp3, rel))
+    {
+      // If the operands are not equal, or the LH does not contain any
+      // element of the RH, zero is not possible.
+      tmp1 = lh;
+      if (rel.op1_op2 () == VREL_NE
+         || (tmp1.intersect (rh) && tmp1.undefined_p ()))
+       {
+         tmp1.set_nonzero (type);
+         new_result.intersect (tmp1);
+       }
+
+      // Combine with the legacy range if there was one.
+      if (res)
+       r.intersect (new_result);
+      else
+       r = new_result;
+      return true;
+    }
+  return res;
+}
+
 void
 operator_bitwise_xor::wi_fold (irange &r, tree type,
                               const wide_int &lh_lb,
diff --git a/gcc/testsuite/gcc.dg/pr113632.c b/gcc/testsuite/gcc.dg/pr113632.c
new file mode 100644 (file)
index 0000000..dd49b66
--- /dev/null
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+void dummy(void);
+_Bool f(unsigned long a)
+{
+        _Bool cmp = a > 8192;
+        if (cmp) goto then; else goto e;
+then:
+        unsigned long t = __builtin_clzl(a); // [0,50]
+        t^=63; // [13,63]
+        if (t < 13 || t >63)
+          dummy ();
+e:
+  return 0;
+}
+
+void f2(int x)
+{
+  if (x <= 0 || x == 2 || x == 4 || x == 6)
+    return;
+  /* x = [1, 1][3, 3][5, 5][7, 2147483647] */
+  /* x ^ 6  should be non-zero.  */
+  if ((x ^ 6) == 0)
+    dummy ();
+}
+
+/* { dg-final { scan-tree-dump-not "dummy"  "evrp" } } */