diff mbox series

[COMMITTED,1/2] Add operand_check_p to range-ops.

Message ID ec0250a6-d9ee-4916-af40-178f324f2a57@redhat.com
State New
Headers show
Series [COMMITTED,1/2] Add operand_check_p to range-ops. | expand

Commit Message

Andrew MacLeod Nov. 29, 2023, 4:51 p.m. UTC
I've been going back and forth with this for the past week, and finally 
settled on a solution

This patch adds an operand_check_p() (lhs_type, op1_type, op2_type) 
method to range_ops which will confirm whether the types of the operands 
being passed to fold_range, op1_range, and op2_range  are properly 
compatible.   For range-ops this basically means the precision matches.

It was a bit tricky to do it any other way because various operations 
allow different precision or even different types in some operand positions.

This patch sets up the operand_check_p to return true by default, which 
means there is no variation from what we do today.  However, I have gone 
in to all the integral/mixed range operators, and added checks for 
things like  X = Y + Z requiring the precision to be the same for all 3 
operands.   however x = y && z only requires OP1 and OP2 to be the same 
precision, and  x = ~y only requires the LHS and OP1 to match.

This call is utilized in a gcc_assert when CHECKING_P is on for 
fold_range(), op1_range() and op2_range() to provide compilation time 
verification while not costing anything for a release build.

Bootstraps on x86_64-pc-linux-gnu with no regressions. committed.

Andrew
diff mbox series

Patch

From 9f1149ef823b64ead6115f79f99ddf8eead1c2f4 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Tue, 28 Nov 2023 09:39:30 -0500
Subject: [PATCH 1/2] Add operand_check_p to range-ops.

Add an optional method to verify operands are compatible, and check
the operands before all range operations.

	* range-op-mixed.h (operator_equal::operand_check_p): New.
	(operator_not_equal::operand_check_p): New.
	(operator_lt::operand_check_p): New.
	(operator_le::operand_check_p): New.
	(operator_gt::operand_check_p): New.
	(operator_ge::operand_check_p): New.
	(operator_plus::operand_check_p): New.
	(operator_abs::operand_check_p): New.
	(operator_minus::operand_check_p): New.
	(operator_negate::operand_check_p): New.
	(operator_mult::operand_check_p): New.
	(operator_bitwise_not::operand_check_p): New.
	(operator_bitwise_xor::operand_check_p): New.
	(operator_bitwise_and::operand_check_p): New.
	(operator_bitwise_or::operand_check_p): New.
	(operator_min::operand_check_p): New.
	(operator_max::operand_check_p): New.
	* range-op.cc (range_op_handler::fold_range): Check operand
	parameter types.
	(range_op_handler::op1_range): Ditto.
	(range_op_handler::op2_range): Ditto.
	(range_op_handler::operand_check_p): New.
	(range_operator::operand_check_p): New.
	(operator_lshift::operand_check_p): New.
	(operator_rshift::operand_check_p): New.
	(operator_logical_and::operand_check_p): New.
	(operator_logical_or::operand_check_p): New.
	(operator_logical_not::operand_check_p): New.
	* range-op.h (range_operator::operand_check_p): New.
	(range_op_handler::operand_check_p): New.
---
 gcc/range-op-mixed.h | 63 +++++++++++++++++++++++++++++++++++++++++---
 gcc/range-op.cc      | 53 ++++++++++++++++++++++++++++++++++---
 gcc/range-op.h       |  5 ++++
 3 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index 45e11df57df..4386a68e946 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -138,6 +138,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_not_equal : public range_operator
@@ -174,6 +177,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_lt :  public range_operator
@@ -207,6 +213,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_le :  public range_operator
@@ -243,6 +252,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_gt :  public range_operator
@@ -278,6 +290,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_ge :  public range_operator
@@ -314,6 +329,9 @@  public:
 				  const frange &) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check op1 and op2 for compatibility.
+  bool operand_check_p (tree, tree t1, tree t2) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 class operator_identity : public range_operator
@@ -409,7 +427,10 @@  public:
 
   virtual bool overflow_free_p (const irange &lh, const irange &rh,
 				relation_trio = TRIO_VARYING) const;
-
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -436,6 +457,9 @@  class operator_abs : public range_operator
 		  relation_trio rel = TRIO_VARYING) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+  // Check compatibility of LHS and op1.
+  bool operand_check_p (tree t1, tree t2, tree) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -477,7 +501,10 @@  public:
 
   virtual bool overflow_free_p (const irange &lh, const irange &rh,
 				relation_trio = TRIO_VARYING) const;
-
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -506,6 +533,9 @@  class operator_negate : public range_operator
   bool op1_range (frange &r, tree type,
 		  const frange &lhs, const frange &op2,
 		  relation_trio rel = TRIO_VARYING) const final override;
+  // Check compatibility of LHS and op1.
+  bool operand_check_p (tree t1, tree t2, tree) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 };
 
 
@@ -557,7 +587,10 @@  public:
 		relation_kind kind) const final override;
   virtual bool overflow_free_p (const irange &lh, const irange &rh,
 				relation_trio = TRIO_VARYING) const;
-
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 };
 
 class operator_addr_expr : public range_operator
@@ -586,6 +619,10 @@  public:
 		  relation_trio rel = TRIO_VARYING) 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 (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 };
 
 class operator_bitwise_xor : public range_operator
@@ -606,6 +643,10 @@  public:
 					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 (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -629,6 +670,10 @@  public:
 				  relation_kind) const override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const override;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 protected:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -651,6 +696,10 @@  public:
 		  relation_trio rel = TRIO_VARYING) const override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const override;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 protected:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -662,6 +711,10 @@  class operator_min : public range_operator
 public:
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const override;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 protected:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -673,6 +726,10 @@  class operator_max : public range_operator
 public:
   void update_bitmask (irange &r, const irange &lh,
       const irange &rh) const override;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 protected:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 6137f2aeed3..a091815997d 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -201,6 +201,10 @@  range_op_handler::fold_range (vrange &r, tree type,
 			      relation_trio rel) const
 {
   gcc_checking_assert (m_operator);
+#if CHECKING_P
+  if (!lh.undefined_p () && !rh.undefined_p ())
+    gcc_assert (m_operator->operand_check_p (type, lh.type (), rh.type ()));
+#endif
   switch (dispatch_kind (r, lh, rh))
     {
       case RO_III:
@@ -237,9 +241,12 @@  range_op_handler::op1_range (vrange &r, tree type,
 			     relation_trio rel) const
 {
   gcc_checking_assert (m_operator);
-
   if (lhs.undefined_p ())
     return false;
+#if CHECKING_P
+  if (!op2.undefined_p ())
+    gcc_assert (m_operator->operand_check_p (lhs.type (), type, op2.type ()));
+#endif
   switch (dispatch_kind (r, lhs, op2))
     {
       case RO_III:
@@ -270,7 +277,10 @@  range_op_handler::op2_range (vrange &r, tree type,
   gcc_checking_assert (m_operator);
   if (lhs.undefined_p ())
     return false;
-
+#if CHECKING_P
+  if (!op1.undefined_p ())
+    gcc_assert (m_operator->operand_check_p (lhs.type (), op1.type (), type));
+#endif
   switch (dispatch_kind (r, lhs, op1))
     {
       case RO_III:
@@ -394,6 +404,13 @@  range_op_handler::overflow_free_p (const vrange &lh,
     }
 }
 
+bool
+range_op_handler::operand_check_p (tree t1, tree t2, tree t3) const
+{
+  gcc_checking_assert (m_operator);
+  return m_operator->operand_check_p (t1, t2, t3);
+}
+
 // Update the known bitmasks in R when applying the operation CODE to
 // LH and RH.
 
@@ -737,6 +754,14 @@  range_operator::update_bitmask (irange &, const irange &,
 {
 }
 
+// Check that operand types are OK.  Default to always OK.
+
+bool
+range_operator::operand_check_p (tree, tree, tree) const
+{
+  return true;
+}
+
 // Create and return a range from a pair of wide-ints that are known
 // to have overflowed (or underflowed).
 
@@ -2466,6 +2491,9 @@  public:
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override
     { update_known_bitmask (r, LSHIFT_EXPR, lh, rh); }
+  // Check compatibility of LHS and op1.
+  bool operand_check_p (tree t1, tree t2, tree) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 } op_lshift;
 
 class operator_rshift : public cross_product_operator
@@ -2495,6 +2523,9 @@  public:
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override
     { update_known_bitmask (r, RSHIFT_EXPR, lh, rh); }
+  // Check compatibility of LHS and op1.
+  bool operand_check_p (tree t1, tree t2, tree) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 } op_rshift;
 
 
@@ -3070,9 +3101,12 @@  public:
 			  const irange &lhs,
 			  const irange &op1,
 			  relation_trio rel = TRIO_VARYING) const;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 } op_logical_and;
 
-
 bool
 operator_logical_and::fold_range (irange &r, tree type,
 				  const irange &lh,
@@ -3082,6 +3116,11 @@  operator_logical_and::fold_range (irange &r, tree type,
   if (empty_range_varying (r, type, lh, rh))
     return true;
 
+  // Precision of LHS and both operands must match.
+  if (TYPE_PRECISION (lh.type ()) != TYPE_PRECISION (type)
+      || TYPE_PRECISION (type) != TYPE_PRECISION (rh.type ()))
+    return false;
+
   // 0 && anything is 0.
   if ((wi::eq_p (lh.lower_bound (), 0) && wi::eq_p (lh.upper_bound (), 0))
       || (wi::eq_p (lh.lower_bound (), 0) && wi::eq_p (rh.upper_bound (), 0)))
@@ -3567,6 +3606,10 @@  public:
 			  const irange &lhs,
 			  const irange &op1,
 			  relation_trio rel = TRIO_VARYING) const;
+  // Check compatibility of all operands.
+  bool operand_check_p (tree t1, tree t2, tree t3) const final override
+    { return (TYPE_PRECISION (t1) == TYPE_PRECISION (t2)
+	      && TYPE_PRECISION (t1) == TYPE_PRECISION (t3)); }
 } op_logical_or;
 
 bool
@@ -3993,6 +4036,9 @@  public:
 			  const irange &lhs,
 			  const irange &op2,
 			  relation_trio rel = TRIO_VARYING) const;
+  // Check compatibility of LHS and op1.
+  bool operand_check_p (tree t1, tree t2, tree) const final override
+    { return TYPE_PRECISION (t1) == TYPE_PRECISION (t2); }
 } op_logical_not;
 
 // Folding a logical NOT, oddly enough, involves doing nothing on the
@@ -4036,7 +4082,6 @@  operator_logical_not::op1_range (irange &r,
   return fold_range (r, type, lhs, op2);
 }
 
-
 bool
 operator_bitwise_not::fold_range (irange &r, tree type,
 				  const irange &lh,
diff --git a/gcc/range-op.h b/gcc/range-op.h
index 282ce386836..ab8f8a36fd5 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -157,6 +157,10 @@  public:
 
   virtual bool overflow_free_p (const irange &lh, const irange &rh,
 				relation_trio = TRIO_VARYING) const;
+
+  // Compatability check for operands.
+  virtual bool operand_check_p (tree, tree, tree) const;
+
 protected:
   // Perform an integral operation between 2 sub-ranges and return it.
   virtual void wi_fold (irange &r, tree type,
@@ -226,6 +230,7 @@  public:
 				  const vrange &op2) const;
   bool overflow_free_p (const vrange &lh, const vrange &rh,
 			relation_trio = TRIO_VARYING) const;
+  bool operand_check_p (tree, tree, tree) const;
 protected:
   unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
 			  const vrange& op2) const;
-- 
2.41.0