The op1_range/op2_range range operators for logical AND and OR miss an important case exposed by the follow-on patch in this set.

For logical AND, a false result normally tells us only that at least one operand is false. However, if the other operand is known to be true, the operand being recomputed must be false:

|false = op1 && true|

therefore:

|op1 = false|

Similarly, a true logical OR result is normally ambiguous, but if the other operand is known to be false, the operand being recomputed must be true:

|true = op1 || false|

therefore:

|op1 = true|

This patch adds those cases to |operator_logical_and::op1_range|and |operator_logical_or::op1_range|. The existing |op2_range|implementations transpose the operands and reuse |op1_range|, so the improvement applies symmetrically to both operands.

This allows backward range evaluation to preserve information that was previously lost, and allows the follow on patch to look for relations when one operand matches this pattern.

Bootstrapped onĀ  x86_64-pc-linux-gnu with no new regressions. pushed.

Andrew

||
From 9d526af0cead9b34950831a41da290ee404309aa Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Thu, 16 Jul 2026 16:43:40 -0400
Subject: [PATCH 1/3] Enhance logical AND and OR op1_range.

The rangeops should recognize that
  [0, 0] = op1 & TRUE    -->  op1 must be [0, 0]
  [1, 1] = op1 | FALSE   -->  op1 must be [1, 1]

op2_range simple transposes the operands, so this covers both cases.

	PR tree-optimization/126212
	* range-op.cc (operator_logical_and::op1_range): Add case for
	[0, 0] = op1 & [1, 1]
	(operator_logical_or::op1_range): Add case for [1, 1] = op1 | [0, 0]
---
 gcc/range-op.cc | 72 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 27 deletions(-)

diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 94edfdc9870..a1a479a8319 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -3420,22 +3420,31 @@ operator_logical_and::fold_range (irange &r, tree type,
 bool
 operator_logical_and::op1_range (irange &r, tree type,
 				 const irange &lhs,
-				 const irange &op2 ATTRIBUTE_UNUSED,
+				 const irange &op2,
 				 relation_trio) const
 {
-   switch (get_bool_state (r, lhs, type))
-     {
-     case BRS_TRUE:
-       // A true result means both sides of the AND must be true.
-       r = range_true (type);
-       break;
-     default:
-       // Any other result means only one side has to be false, the
-       // other side can be anything.  So we cannot be sure of any
-       // result here.
-       r = range_true_and_false (type);
-       break;
-     }
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_TRUE:
+      // A TRUE result means both sides of the AND must be true.
+      r = range_true (type);
+      return true;
+
+    case BRS_FALSE:
+      // A FALSE result when op2 is TRUE, must have op1 FALSE.
+      if (!op2.contains_p (wi::zero (TYPE_PRECISION (op2.type ()))))
+	{
+	  r = range_false (type);
+	  return true;
+	}
+      break;
+
+    default:
+      break;
+    }
+
+  // Any other result means we cannot be sure of any result.
+  r = range_true_and_false (type);
   return true;
 }
 
@@ -3950,22 +3959,31 @@ operator_logical_or::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
 bool
 operator_logical_or::op1_range (irange &r, tree type,
 				const irange &lhs,
-				const irange &op2 ATTRIBUTE_UNUSED,
+				const irange &op2,
 				relation_trio) const
 {
-   switch (get_bool_state (r, lhs, type))
-     {
-     case BRS_FALSE:
-       // A false result means both sides of the OR must be false.
-       r = range_false (type);
-       break;
-     default:
-       // Any other result means only one side has to be true, the
-       // other side can be anything. so we can't be sure of any result
-       // here.
-       r = range_true_and_false (type);
-       break;
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_FALSE:
+      // A false result means both sides of the OR must be false.
+      r = range_false (type);
+      return true;
+
+    case BRS_TRUE:
+      // A TRUE result when op2 is FALSE must have op1 TRUE.
+      if (op2.zero_p ())
+	{
+	  r = range_true (type);
+	  return true;
+	}
+      break;
+
+    default:
+      break;
     }
+
+  // Any other result means we cannot be sure of any result.
+  r = range_true_and_false (type);
   return true;
 }
 
-- 
2.45.0

Reply via email to