[V5,1/2] Add overflow API for plus minus mult on range

Message ID 20230718140544.3497370-1-guojiufu@linux.ibm.com
State Unresolved
Headers
Series [V5,1/2] Add overflow API for plus minus mult on range |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Jiufu Guo July 18, 2023, 2:05 p.m. UTC
  Hi,

As discussed in previous reviews, adding overflow APIs to range-op
would be useful. Those APIs could help to check if overflow happens
when operating between two 'range's, like: plus, minus, and mult.

Previous discussions are here:
https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624067.html
https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624701.html

Bootstrap & regtest pass on ppc64{,le} and x86_64.
Is this patch ok for trunk?

BR,
Jeff (Jiufu Guo)

gcc/ChangeLog:

	* range-op-mixed.h (operator_plus::overflow_free_p): New declare.
	(operator_minus::overflow_free_p): New declare.
	(operator_mult::overflow_free_p): New declare.
	* range-op.cc (range_op_handler::overflow_free_p): New function.
	(range_operator::overflow_free_p): New default function.
	(operator_plus::overflow_free_p): New function.
	(operator_minus::overflow_free_p): New function.
	(operator_mult::overflow_free_p): New function.
	* range-op.h (range_op_handler::overflow_free_p): New declare.
	(range_operator::overflow_free_p): New declare.
	* value-range.cc (irange::nonnegative_p): New function.
	(irange::nonpositive_p): New function.
	* value-range.h (irange::nonnegative_p): New declare.
	(irange::nonpositive_p): New declare.

---
 gcc/range-op-mixed.h |  11 ++++
 gcc/range-op.cc      | 124 +++++++++++++++++++++++++++++++++++++++++++
 gcc/range-op.h       |   5 ++
 gcc/value-range.cc   |  12 +++++
 gcc/value-range.h    |   2 +
 5 files changed, 154 insertions(+)
  

Comments

Jiufu Guo Aug. 3, 2023, 2:18 a.m. UTC | #1
Hi,

I would like to have a ping on this patch.

BR,
Jeff (Jiufu Guo)


Jiufu Guo <guojiufu@linux.ibm.com> writes:

> Hi,
>
> As discussed in previous reviews, adding overflow APIs to range-op
> would be useful. Those APIs could help to check if overflow happens
> when operating between two 'range's, like: plus, minus, and mult.
>
> Previous discussions are here:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624067.html
> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624701.html
>
> Bootstrap & regtest pass on ppc64{,le} and x86_64.
> Is this patch ok for trunk?
>
> BR,
> Jeff (Jiufu Guo)
>
> gcc/ChangeLog:
>
> 	* range-op-mixed.h (operator_plus::overflow_free_p): New declare.
> 	(operator_minus::overflow_free_p): New declare.
> 	(operator_mult::overflow_free_p): New declare.
> 	* range-op.cc (range_op_handler::overflow_free_p): New function.
> 	(range_operator::overflow_free_p): New default function.
> 	(operator_plus::overflow_free_p): New function.
> 	(operator_minus::overflow_free_p): New function.
> 	(operator_mult::overflow_free_p): New function.
> 	* range-op.h (range_op_handler::overflow_free_p): New declare.
> 	(range_operator::overflow_free_p): New declare.
> 	* value-range.cc (irange::nonnegative_p): New function.
> 	(irange::nonpositive_p): New function.
> 	* value-range.h (irange::nonnegative_p): New declare.
> 	(irange::nonpositive_p): New declare.
>
> ---
>  gcc/range-op-mixed.h |  11 ++++
>  gcc/range-op.cc      | 124 +++++++++++++++++++++++++++++++++++++++++++
>  gcc/range-op.h       |   5 ++
>  gcc/value-range.cc   |  12 +++++
>  gcc/value-range.h    |   2 +
>  5 files changed, 154 insertions(+)
>
> diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
> index 6944742ecbc..42157ed9061 100644
> --- a/gcc/range-op-mixed.h
> +++ b/gcc/range-op-mixed.h
> @@ -383,6 +383,10 @@ public:
>  				  relation_kind rel) const final override;
>    void update_bitmask (irange &r, const irange &lh,
>  		       const irange &rh) const final override;
> +
> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio = TRIO_VARYING) const;
> +
>  private:
>    void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>  		const wide_int &lh_ub, const wide_int &rh_lb,
> @@ -446,6 +450,10 @@ public:
>  				relation_kind rel) const final override;
>    void update_bitmask (irange &r, const irange &lh,
>  		       const irange &rh) const final override;
> +
> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio = TRIO_VARYING) const;
> +
>  private:
>    void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>  		const wide_int &lh_ub, const wide_int &rh_lb,
> @@ -525,6 +533,9 @@ public:
>  		const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub,
>  		const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub,
>  		relation_kind kind) const final override;
> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio = TRIO_VARYING) const;
> +
>  };
>  
>  class operator_addr_expr : public range_operator
> diff --git a/gcc/range-op.cc b/gcc/range-op.cc
> index cb584314f4c..632b044331b 100644
> --- a/gcc/range-op.cc
> +++ b/gcc/range-op.cc
> @@ -366,6 +366,22 @@ range_op_handler::op1_op2_relation (const vrange &lhs) const
>      }
>  }
>  
> +bool
> +range_op_handler::overflow_free_p (const vrange &lh,
> +				   const vrange &rh,
> +				   relation_trio rel) const
> +{
> +  gcc_checking_assert (m_operator);
> +  switch (dispatch_kind (lh, lh, rh))
> +    {
> +      case RO_III:
> +	return m_operator->overflow_free_p(as_a <irange> (lh),
> +					   as_a <irange> (rh),
> +					   rel);
> +      default:
> +	return false;
> +    }
> +}
>  
>  // Convert irange bitmasks into a VALUE MASK pair suitable for calling CCP.
>  
> @@ -688,6 +704,13 @@ range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED,
>    return false;
>  }
>  
> +bool
> +range_operator::overflow_free_p (const irange &, const irange &,
> +				 relation_trio) const
> +{
> +  return false;
> +}
> +
>  // Apply any known bitmask updates based on this operator.
>  
>  void
> @@ -4311,6 +4334,107 @@ range_op_table::initialize_integral_ops ()
>  
>  }
>  
> +bool
> +operator_plus::overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio) const
> +{
> +  if (lh.undefined_p () || rh.undefined_p ())
> +    return false;
> +
> +  tree type = lh.type ();
> +  if (TYPE_OVERFLOW_UNDEFINED (type))
> +    return true;
> +
> +  wi::overflow_type ovf;
> +  signop sgn = TYPE_SIGN (type);
> +  wide_int wmax0 = lh.upper_bound ();
> +  wide_int wmax1 = rh.upper_bound ();
> +  wi::add (wmax0, wmax1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  if (TYPE_UNSIGNED (type))
> +    return true;
> +
> +  wide_int wmin0 = lh.lower_bound ();
> +  wide_int wmin1 = rh.lower_bound ();
> +  wi::add (wmin0, wmin1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  return true;
> +}
> +
> +bool
> +operator_minus::overflow_free_p (const irange &lh, const irange &rh,
> +				 relation_trio) const
> +{
> +  if (lh.undefined_p () || rh.undefined_p ())
> +    return false;
> +
> +  tree type = lh.type ();
> +  if (TYPE_OVERFLOW_UNDEFINED (type))
> +    return true;
> +
> +  wi::overflow_type ovf;
> +  signop sgn = TYPE_SIGN (type);
> +  wide_int wmin0 = lh.lower_bound ();
> +  wide_int wmax1 = rh.upper_bound ();
> +  wi::sub (wmin0, wmax1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  if (TYPE_UNSIGNED (type))
> +    return true;
> +
> +  wide_int wmax0 = lh.upper_bound ();
> +  wide_int wmin1 = rh.lower_bound ();
> +  wi::sub (wmax0, wmin1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  return true;
> +}
> +
> +bool
> +operator_mult::overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio) const
> +{
> +  if (lh.undefined_p () || rh.undefined_p ())
> +    return false;
> +
> +  tree type = lh.type ();
> +  if (TYPE_OVERFLOW_UNDEFINED (type))
> +    return true;
> +
> +  wi::overflow_type ovf;
> +  signop sgn = TYPE_SIGN (type);
> +  wide_int wmax0 = lh.upper_bound ();
> +  wide_int wmax1 = rh.upper_bound ();
> +  wi::mul (wmax0, wmax1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  if (TYPE_UNSIGNED (type))
> +    return true;
> +
> +  wide_int wmin0 = lh.lower_bound ();
> +  wide_int wmin1 = rh.lower_bound ();
> +  wi::mul (wmin0, wmin1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  wi::mul (wmin0, wmax1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  wi::mul (wmax0, wmin1, sgn, &ovf);
> +  if (ovf != wi::OVF_NONE)
> +    return false;
> +
> +  return true;
> +}
> +
>  #if CHECKING_P
>  #include "selftest.h"
>  
> diff --git a/gcc/range-op.h b/gcc/range-op.h
> index af94c2756a7..db3b03f28a5 100644
> --- a/gcc/range-op.h
> +++ b/gcc/range-op.h
> @@ -147,6 +147,9 @@ public:
>  
>    virtual relation_kind op1_op2_relation (const irange &lhs) const;
>    virtual relation_kind op1_op2_relation (const frange &lhs) const;
> +
> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
> +				relation_trio = TRIO_VARYING) const;
>  protected:
>    // Perform an integral operation between 2 sub-ranges and return it.
>    virtual void wi_fold (irange &r, tree type,
> @@ -214,6 +217,8 @@ public:
>  				  const vrange &op2,
>  				  relation_kind = VREL_VARYING) const;
>    relation_kind op1_op2_relation (const vrange &lhs) const;
> +  bool overflow_free_p (const vrange &lh, const vrange &rh,
> +			relation_trio = TRIO_VARYING) const;
>  protected:
>    unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
>  			  const vrange& op2) const;
> diff --git a/gcc/value-range.cc b/gcc/value-range.cc
> index 011bdbdeae6..5ae4e044194 100644
> --- a/gcc/value-range.cc
> +++ b/gcc/value-range.cc
> @@ -313,6 +313,18 @@ add_vrange (const vrange &v, inchash::hash &hstate,
>  
>  } //namespace inchash
>  
> +bool
> +irange::nonnegative_p () const
> +{
> +  return wi::ge_p (lower_bound (), 0, TYPE_SIGN (type ()));
> +}
> +
> +bool
> +irange::nonpositive_p () const
> +{
> +  return wi::le_p (upper_bound (), 0, TYPE_SIGN (type ()));
> +}
> +
>  bool
>  irange::supports_type_p (const_tree type) const
>  {
> diff --git a/gcc/value-range.h b/gcc/value-range.h
> index 0170188201b..a12dea514e4 100644
> --- a/gcc/value-range.h
> +++ b/gcc/value-range.h
> @@ -280,6 +280,8 @@ public:
>    virtual bool singleton_p (tree *result = NULL) const override;
>    bool singleton_p (wide_int &) const;
>    bool contains_p (const wide_int &) const;
> +  bool nonnegative_p () const;
> +  bool nonpositive_p () const;
>  
>    // In-place operators.
>    virtual bool union_ (const vrange &) override;
  
Andrew MacLeod Aug. 3, 2023, 1:18 p.m. UTC | #2
This is OK.


On 8/2/23 22:18, Jiufu Guo wrote:
> Hi,
>
> I would like to have a ping on this patch.
>
> BR,
> Jeff (Jiufu Guo)
>
>
> Jiufu Guo <guojiufu@linux.ibm.com> writes:
>
>> Hi,
>>
>> As discussed in previous reviews, adding overflow APIs to range-op
>> would be useful. Those APIs could help to check if overflow happens
>> when operating between two 'range's, like: plus, minus, and mult.
>>
>> Previous discussions are here:
>> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624067.html
>> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624701.html
>>
>> Bootstrap & regtest pass on ppc64{,le} and x86_64.
>> Is this patch ok for trunk?
>>
>> BR,
>> Jeff (Jiufu Guo)
>>
>> gcc/ChangeLog:
>>
>> 	* range-op-mixed.h (operator_plus::overflow_free_p): New declare.
>> 	(operator_minus::overflow_free_p): New declare.
>> 	(operator_mult::overflow_free_p): New declare.
>> 	* range-op.cc (range_op_handler::overflow_free_p): New function.
>> 	(range_operator::overflow_free_p): New default function.
>> 	(operator_plus::overflow_free_p): New function.
>> 	(operator_minus::overflow_free_p): New function.
>> 	(operator_mult::overflow_free_p): New function.
>> 	* range-op.h (range_op_handler::overflow_free_p): New declare.
>> 	(range_operator::overflow_free_p): New declare.
>> 	* value-range.cc (irange::nonnegative_p): New function.
>> 	(irange::nonpositive_p): New function.
>> 	* value-range.h (irange::nonnegative_p): New declare.
>> 	(irange::nonpositive_p): New declare.
>>
>> ---
>>   gcc/range-op-mixed.h |  11 ++++
>>   gcc/range-op.cc      | 124 +++++++++++++++++++++++++++++++++++++++++++
>>   gcc/range-op.h       |   5 ++
>>   gcc/value-range.cc   |  12 +++++
>>   gcc/value-range.h    |   2 +
>>   5 files changed, 154 insertions(+)
>>
>> diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
>> index 6944742ecbc..42157ed9061 100644
>> --- a/gcc/range-op-mixed.h
>> +++ b/gcc/range-op-mixed.h
>> @@ -383,6 +383,10 @@ public:
>>   				  relation_kind rel) const final override;
>>     void update_bitmask (irange &r, const irange &lh,
>>   		       const irange &rh) const final override;
>> +
>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio = TRIO_VARYING) const;
>> +
>>   private:
>>     void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>>   		const wide_int &lh_ub, const wide_int &rh_lb,
>> @@ -446,6 +450,10 @@ public:
>>   				relation_kind rel) const final override;
>>     void update_bitmask (irange &r, const irange &lh,
>>   		       const irange &rh) const final override;
>> +
>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio = TRIO_VARYING) const;
>> +
>>   private:
>>     void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>>   		const wide_int &lh_ub, const wide_int &rh_lb,
>> @@ -525,6 +533,9 @@ public:
>>   		const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub,
>>   		const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub,
>>   		relation_kind kind) const final override;
>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio = TRIO_VARYING) const;
>> +
>>   };
>>   
>>   class operator_addr_expr : public range_operator
>> diff --git a/gcc/range-op.cc b/gcc/range-op.cc
>> index cb584314f4c..632b044331b 100644
>> --- a/gcc/range-op.cc
>> +++ b/gcc/range-op.cc
>> @@ -366,6 +366,22 @@ range_op_handler::op1_op2_relation (const vrange &lhs) const
>>       }
>>   }
>>   
>> +bool
>> +range_op_handler::overflow_free_p (const vrange &lh,
>> +				   const vrange &rh,
>> +				   relation_trio rel) const
>> +{
>> +  gcc_checking_assert (m_operator);
>> +  switch (dispatch_kind (lh, lh, rh))
>> +    {
>> +      case RO_III:
>> +	return m_operator->overflow_free_p(as_a <irange> (lh),
>> +					   as_a <irange> (rh),
>> +					   rel);
>> +      default:
>> +	return false;
>> +    }
>> +}
>>   
>>   // Convert irange bitmasks into a VALUE MASK pair suitable for calling CCP.
>>   
>> @@ -688,6 +704,13 @@ range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED,
>>     return false;
>>   }
>>   
>> +bool
>> +range_operator::overflow_free_p (const irange &, const irange &,
>> +				 relation_trio) const
>> +{
>> +  return false;
>> +}
>> +
>>   // Apply any known bitmask updates based on this operator.
>>   
>>   void
>> @@ -4311,6 +4334,107 @@ range_op_table::initialize_integral_ops ()
>>   
>>   }
>>   
>> +bool
>> +operator_plus::overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio) const
>> +{
>> +  if (lh.undefined_p () || rh.undefined_p ())
>> +    return false;
>> +
>> +  tree type = lh.type ();
>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>> +    return true;
>> +
>> +  wi::overflow_type ovf;
>> +  signop sgn = TYPE_SIGN (type);
>> +  wide_int wmax0 = lh.upper_bound ();
>> +  wide_int wmax1 = rh.upper_bound ();
>> +  wi::add (wmax0, wmax1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  if (TYPE_UNSIGNED (type))
>> +    return true;
>> +
>> +  wide_int wmin0 = lh.lower_bound ();
>> +  wide_int wmin1 = rh.lower_bound ();
>> +  wi::add (wmin0, wmin1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  return true;
>> +}
>> +
>> +bool
>> +operator_minus::overflow_free_p (const irange &lh, const irange &rh,
>> +				 relation_trio) const
>> +{
>> +  if (lh.undefined_p () || rh.undefined_p ())
>> +    return false;
>> +
>> +  tree type = lh.type ();
>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>> +    return true;
>> +
>> +  wi::overflow_type ovf;
>> +  signop sgn = TYPE_SIGN (type);
>> +  wide_int wmin0 = lh.lower_bound ();
>> +  wide_int wmax1 = rh.upper_bound ();
>> +  wi::sub (wmin0, wmax1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  if (TYPE_UNSIGNED (type))
>> +    return true;
>> +
>> +  wide_int wmax0 = lh.upper_bound ();
>> +  wide_int wmin1 = rh.lower_bound ();
>> +  wi::sub (wmax0, wmin1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  return true;
>> +}
>> +
>> +bool
>> +operator_mult::overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio) const
>> +{
>> +  if (lh.undefined_p () || rh.undefined_p ())
>> +    return false;
>> +
>> +  tree type = lh.type ();
>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>> +    return true;
>> +
>> +  wi::overflow_type ovf;
>> +  signop sgn = TYPE_SIGN (type);
>> +  wide_int wmax0 = lh.upper_bound ();
>> +  wide_int wmax1 = rh.upper_bound ();
>> +  wi::mul (wmax0, wmax1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  if (TYPE_UNSIGNED (type))
>> +    return true;
>> +
>> +  wide_int wmin0 = lh.lower_bound ();
>> +  wide_int wmin1 = rh.lower_bound ();
>> +  wi::mul (wmin0, wmin1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  wi::mul (wmin0, wmax1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  wi::mul (wmax0, wmin1, sgn, &ovf);
>> +  if (ovf != wi::OVF_NONE)
>> +    return false;
>> +
>> +  return true;
>> +}
>> +
>>   #if CHECKING_P
>>   #include "selftest.h"
>>   
>> diff --git a/gcc/range-op.h b/gcc/range-op.h
>> index af94c2756a7..db3b03f28a5 100644
>> --- a/gcc/range-op.h
>> +++ b/gcc/range-op.h
>> @@ -147,6 +147,9 @@ public:
>>   
>>     virtual relation_kind op1_op2_relation (const irange &lhs) const;
>>     virtual relation_kind op1_op2_relation (const frange &lhs) const;
>> +
>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>> +				relation_trio = TRIO_VARYING) const;
>>   protected:
>>     // Perform an integral operation between 2 sub-ranges and return it.
>>     virtual void wi_fold (irange &r, tree type,
>> @@ -214,6 +217,8 @@ public:
>>   				  const vrange &op2,
>>   				  relation_kind = VREL_VARYING) const;
>>     relation_kind op1_op2_relation (const vrange &lhs) const;
>> +  bool overflow_free_p (const vrange &lh, const vrange &rh,
>> +			relation_trio = TRIO_VARYING) const;
>>   protected:
>>     unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
>>   			  const vrange& op2) const;
>> diff --git a/gcc/value-range.cc b/gcc/value-range.cc
>> index 011bdbdeae6..5ae4e044194 100644
>> --- a/gcc/value-range.cc
>> +++ b/gcc/value-range.cc
>> @@ -313,6 +313,18 @@ add_vrange (const vrange &v, inchash::hash &hstate,
>>   
>>   } //namespace inchash
>>   
>> +bool
>> +irange::nonnegative_p () const
>> +{
>> +  return wi::ge_p (lower_bound (), 0, TYPE_SIGN (type ()));
>> +}
>> +
>> +bool
>> +irange::nonpositive_p () const
>> +{
>> +  return wi::le_p (upper_bound (), 0, TYPE_SIGN (type ()));
>> +}
>> +
>>   bool
>>   irange::supports_type_p (const_tree type) const
>>   {
>> diff --git a/gcc/value-range.h b/gcc/value-range.h
>> index 0170188201b..a12dea514e4 100644
>> --- a/gcc/value-range.h
>> +++ b/gcc/value-range.h
>> @@ -280,6 +280,8 @@ public:
>>     virtual bool singleton_p (tree *result = NULL) const override;
>>     bool singleton_p (wide_int &) const;
>>     bool contains_p (const wide_int &) const;
>> +  bool nonnegative_p () const;
>> +  bool nonpositive_p () const;
>>   
>>     // In-place operators.
>>     virtual bool union_ (const vrange &) override;
  
Jiufu Guo Aug. 31, 2023, 5:07 a.m. UTC | #3
On 2023-08-03 21:18, Andrew MacLeod wrote:
> This is OK.
> 

Thanks a lot!  Committed via r14-3582.


BR,
Jeff (Jiufu Guo)

> 
> On 8/2/23 22:18, Jiufu Guo wrote:
>> Hi,
>> 
>> I would like to have a ping on this patch.
>> 
>> BR,
>> Jeff (Jiufu Guo)
>> 
>> 
>> Jiufu Guo <guojiufu@linux.ibm.com> writes:
>> 
>>> Hi,
>>> 
>>> As discussed in previous reviews, adding overflow APIs to range-op
>>> would be useful. Those APIs could help to check if overflow happens
>>> when operating between two 'range's, like: plus, minus, and mult.
>>> 
>>> Previous discussions are here:
>>> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624067.html
>>> https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624701.html
>>> 
>>> Bootstrap & regtest pass on ppc64{,le} and x86_64.
>>> Is this patch ok for trunk?
>>> 
>>> BR,
>>> Jeff (Jiufu Guo)
>>> 
>>> gcc/ChangeLog:
>>> 
>>> 	* range-op-mixed.h (operator_plus::overflow_free_p): New declare.
>>> 	(operator_minus::overflow_free_p): New declare.
>>> 	(operator_mult::overflow_free_p): New declare.
>>> 	* range-op.cc (range_op_handler::overflow_free_p): New function.
>>> 	(range_operator::overflow_free_p): New default function.
>>> 	(operator_plus::overflow_free_p): New function.
>>> 	(operator_minus::overflow_free_p): New function.
>>> 	(operator_mult::overflow_free_p): New function.
>>> 	* range-op.h (range_op_handler::overflow_free_p): New declare.
>>> 	(range_operator::overflow_free_p): New declare.
>>> 	* value-range.cc (irange::nonnegative_p): New function.
>>> 	(irange::nonpositive_p): New function.
>>> 	* value-range.h (irange::nonnegative_p): New declare.
>>> 	(irange::nonpositive_p): New declare.
>>> 
>>> ---
>>>   gcc/range-op-mixed.h |  11 ++++
>>>   gcc/range-op.cc      | 124 
>>> +++++++++++++++++++++++++++++++++++++++++++
>>>   gcc/range-op.h       |   5 ++
>>>   gcc/value-range.cc   |  12 +++++
>>>   gcc/value-range.h    |   2 +
>>>   5 files changed, 154 insertions(+)
>>> 
>>> diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
>>> index 6944742ecbc..42157ed9061 100644
>>> --- a/gcc/range-op-mixed.h
>>> +++ b/gcc/range-op-mixed.h
>>> @@ -383,6 +383,10 @@ public:
>>>   				  relation_kind rel) const final override;
>>>     void update_bitmask (irange &r, const irange &lh,
>>>   		       const irange &rh) const final override;
>>> +
>>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio = TRIO_VARYING) const;
>>> +
>>>   private:
>>>     void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>>>   		const wide_int &lh_ub, const wide_int &rh_lb,
>>> @@ -446,6 +450,10 @@ public:
>>>   				relation_kind rel) const final override;
>>>     void update_bitmask (irange &r, const irange &lh,
>>>   		       const irange &rh) const final override;
>>> +
>>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio = TRIO_VARYING) const;
>>> +
>>>   private:
>>>     void wi_fold (irange &r, tree type, const wide_int &lh_lb,
>>>   		const wide_int &lh_ub, const wide_int &rh_lb,
>>> @@ -525,6 +533,9 @@ public:
>>>   		const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub,
>>>   		const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub,
>>>   		relation_kind kind) const final override;
>>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio = TRIO_VARYING) const;
>>> +
>>>   };
>>>     class operator_addr_expr : public range_operator
>>> diff --git a/gcc/range-op.cc b/gcc/range-op.cc
>>> index cb584314f4c..632b044331b 100644
>>> --- a/gcc/range-op.cc
>>> +++ b/gcc/range-op.cc
>>> @@ -366,6 +366,22 @@ range_op_handler::op1_op2_relation (const vrange 
>>> &lhs) const
>>>       }
>>>   }
>>>   +bool
>>> +range_op_handler::overflow_free_p (const vrange &lh,
>>> +				   const vrange &rh,
>>> +				   relation_trio rel) const
>>> +{
>>> +  gcc_checking_assert (m_operator);
>>> +  switch (dispatch_kind (lh, lh, rh))
>>> +    {
>>> +      case RO_III:
>>> +	return m_operator->overflow_free_p(as_a <irange> (lh),
>>> +					   as_a <irange> (rh),
>>> +					   rel);
>>> +      default:
>>> +	return false;
>>> +    }
>>> +}
>>>     // Convert irange bitmasks into a VALUE MASK pair suitable for 
>>> calling CCP.
>>>   @@ -688,6 +704,13 @@ range_operator::op1_op2_relation_effect 
>>> (irange &lhs_range ATTRIBUTE_UNUSED,
>>>     return false;
>>>   }
>>>   +bool
>>> +range_operator::overflow_free_p (const irange &, const irange &,
>>> +				 relation_trio) const
>>> +{
>>> +  return false;
>>> +}
>>> +
>>>   // Apply any known bitmask updates based on this operator.
>>>     void
>>> @@ -4311,6 +4334,107 @@ range_op_table::initialize_integral_ops ()
>>>     }
>>>   +bool
>>> +operator_plus::overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio) const
>>> +{
>>> +  if (lh.undefined_p () || rh.undefined_p ())
>>> +    return false;
>>> +
>>> +  tree type = lh.type ();
>>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>>> +    return true;
>>> +
>>> +  wi::overflow_type ovf;
>>> +  signop sgn = TYPE_SIGN (type);
>>> +  wide_int wmax0 = lh.upper_bound ();
>>> +  wide_int wmax1 = rh.upper_bound ();
>>> +  wi::add (wmax0, wmax1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  if (TYPE_UNSIGNED (type))
>>> +    return true;
>>> +
>>> +  wide_int wmin0 = lh.lower_bound ();
>>> +  wide_int wmin1 = rh.lower_bound ();
>>> +  wi::add (wmin0, wmin1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  return true;
>>> +}
>>> +
>>> +bool
>>> +operator_minus::overflow_free_p (const irange &lh, const irange &rh,
>>> +				 relation_trio) const
>>> +{
>>> +  if (lh.undefined_p () || rh.undefined_p ())
>>> +    return false;
>>> +
>>> +  tree type = lh.type ();
>>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>>> +    return true;
>>> +
>>> +  wi::overflow_type ovf;
>>> +  signop sgn = TYPE_SIGN (type);
>>> +  wide_int wmin0 = lh.lower_bound ();
>>> +  wide_int wmax1 = rh.upper_bound ();
>>> +  wi::sub (wmin0, wmax1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  if (TYPE_UNSIGNED (type))
>>> +    return true;
>>> +
>>> +  wide_int wmax0 = lh.upper_bound ();
>>> +  wide_int wmin1 = rh.lower_bound ();
>>> +  wi::sub (wmax0, wmin1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  return true;
>>> +}
>>> +
>>> +bool
>>> +operator_mult::overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio) const
>>> +{
>>> +  if (lh.undefined_p () || rh.undefined_p ())
>>> +    return false;
>>> +
>>> +  tree type = lh.type ();
>>> +  if (TYPE_OVERFLOW_UNDEFINED (type))
>>> +    return true;
>>> +
>>> +  wi::overflow_type ovf;
>>> +  signop sgn = TYPE_SIGN (type);
>>> +  wide_int wmax0 = lh.upper_bound ();
>>> +  wide_int wmax1 = rh.upper_bound ();
>>> +  wi::mul (wmax0, wmax1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  if (TYPE_UNSIGNED (type))
>>> +    return true;
>>> +
>>> +  wide_int wmin0 = lh.lower_bound ();
>>> +  wide_int wmin1 = rh.lower_bound ();
>>> +  wi::mul (wmin0, wmin1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  wi::mul (wmin0, wmax1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  wi::mul (wmax0, wmin1, sgn, &ovf);
>>> +  if (ovf != wi::OVF_NONE)
>>> +    return false;
>>> +
>>> +  return true;
>>> +}
>>> +
>>>   #if CHECKING_P
>>>   #include "selftest.h"
>>>   diff --git a/gcc/range-op.h b/gcc/range-op.h
>>> index af94c2756a7..db3b03f28a5 100644
>>> --- a/gcc/range-op.h
>>> +++ b/gcc/range-op.h
>>> @@ -147,6 +147,9 @@ public:
>>>       virtual relation_kind op1_op2_relation (const irange &lhs) 
>>> const;
>>>     virtual relation_kind op1_op2_relation (const frange &lhs) const;
>>> +
>>> +  virtual bool overflow_free_p (const irange &lh, const irange &rh,
>>> +				relation_trio = TRIO_VARYING) const;
>>>   protected:
>>>     // Perform an integral operation between 2 sub-ranges and return 
>>> it.
>>>     virtual void wi_fold (irange &r, tree type,
>>> @@ -214,6 +217,8 @@ public:
>>>   				  const vrange &op2,
>>>   				  relation_kind = VREL_VARYING) const;
>>>     relation_kind op1_op2_relation (const vrange &lhs) const;
>>> +  bool overflow_free_p (const vrange &lh, const vrange &rh,
>>> +			relation_trio = TRIO_VARYING) const;
>>>   protected:
>>>     unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
>>>   			  const vrange& op2) const;
>>> diff --git a/gcc/value-range.cc b/gcc/value-range.cc
>>> index 011bdbdeae6..5ae4e044194 100644
>>> --- a/gcc/value-range.cc
>>> +++ b/gcc/value-range.cc
>>> @@ -313,6 +313,18 @@ add_vrange (const vrange &v, inchash::hash 
>>> &hstate,
>>>     } //namespace inchash
>>>   +bool
>>> +irange::nonnegative_p () const
>>> +{
>>> +  return wi::ge_p (lower_bound (), 0, TYPE_SIGN (type ()));
>>> +}
>>> +
>>> +bool
>>> +irange::nonpositive_p () const
>>> +{
>>> +  return wi::le_p (upper_bound (), 0, TYPE_SIGN (type ()));
>>> +}
>>> +
>>>   bool
>>>   irange::supports_type_p (const_tree type) const
>>>   {
>>> diff --git a/gcc/value-range.h b/gcc/value-range.h
>>> index 0170188201b..a12dea514e4 100644
>>> --- a/gcc/value-range.h
>>> +++ b/gcc/value-range.h
>>> @@ -280,6 +280,8 @@ public:
>>>     virtual bool singleton_p (tree *result = NULL) const override;
>>>     bool singleton_p (wide_int &) const;
>>>     bool contains_p (const wide_int &) const;
>>> +  bool nonnegative_p () const;
>>> +  bool nonpositive_p () const;
>>>       // In-place operators.
>>>     virtual bool union_ (const vrange &) override;
  

Patch

diff --git a/gcc/range-op-mixed.h b/gcc/range-op-mixed.h
index 6944742ecbc..42157ed9061 100644
--- a/gcc/range-op-mixed.h
+++ b/gcc/range-op-mixed.h
@@ -383,6 +383,10 @@  public:
 				  relation_kind rel) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+
+  virtual bool overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio = TRIO_VARYING) const;
+
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -446,6 +450,10 @@  public:
 				relation_kind rel) const final override;
   void update_bitmask (irange &r, const irange &lh,
 		       const irange &rh) const final override;
+
+  virtual bool overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio = TRIO_VARYING) const;
+
 private:
   void wi_fold (irange &r, tree type, const wide_int &lh_lb,
 		const wide_int &lh_ub, const wide_int &rh_lb,
@@ -525,6 +533,9 @@  public:
 		const REAL_VALUE_TYPE &lh_lb, const REAL_VALUE_TYPE &lh_ub,
 		const REAL_VALUE_TYPE &rh_lb, const REAL_VALUE_TYPE &rh_ub,
 		relation_kind kind) const final override;
+  virtual bool overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio = TRIO_VARYING) const;
+
 };
 
 class operator_addr_expr : public range_operator
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index cb584314f4c..632b044331b 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -366,6 +366,22 @@  range_op_handler::op1_op2_relation (const vrange &lhs) const
     }
 }
 
+bool
+range_op_handler::overflow_free_p (const vrange &lh,
+				   const vrange &rh,
+				   relation_trio rel) const
+{
+  gcc_checking_assert (m_operator);
+  switch (dispatch_kind (lh, lh, rh))
+    {
+      case RO_III:
+	return m_operator->overflow_free_p(as_a <irange> (lh),
+					   as_a <irange> (rh),
+					   rel);
+      default:
+	return false;
+    }
+}
 
 // Convert irange bitmasks into a VALUE MASK pair suitable for calling CCP.
 
@@ -688,6 +704,13 @@  range_operator::op1_op2_relation_effect (irange &lhs_range ATTRIBUTE_UNUSED,
   return false;
 }
 
+bool
+range_operator::overflow_free_p (const irange &, const irange &,
+				 relation_trio) const
+{
+  return false;
+}
+
 // Apply any known bitmask updates based on this operator.
 
 void
@@ -4311,6 +4334,107 @@  range_op_table::initialize_integral_ops ()
 
 }
 
+bool
+operator_plus::overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio) const
+{
+  if (lh.undefined_p () || rh.undefined_p ())
+    return false;
+
+  tree type = lh.type ();
+  if (TYPE_OVERFLOW_UNDEFINED (type))
+    return true;
+
+  wi::overflow_type ovf;
+  signop sgn = TYPE_SIGN (type);
+  wide_int wmax0 = lh.upper_bound ();
+  wide_int wmax1 = rh.upper_bound ();
+  wi::add (wmax0, wmax1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  if (TYPE_UNSIGNED (type))
+    return true;
+
+  wide_int wmin0 = lh.lower_bound ();
+  wide_int wmin1 = rh.lower_bound ();
+  wi::add (wmin0, wmin1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  return true;
+}
+
+bool
+operator_minus::overflow_free_p (const irange &lh, const irange &rh,
+				 relation_trio) const
+{
+  if (lh.undefined_p () || rh.undefined_p ())
+    return false;
+
+  tree type = lh.type ();
+  if (TYPE_OVERFLOW_UNDEFINED (type))
+    return true;
+
+  wi::overflow_type ovf;
+  signop sgn = TYPE_SIGN (type);
+  wide_int wmin0 = lh.lower_bound ();
+  wide_int wmax1 = rh.upper_bound ();
+  wi::sub (wmin0, wmax1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  if (TYPE_UNSIGNED (type))
+    return true;
+
+  wide_int wmax0 = lh.upper_bound ();
+  wide_int wmin1 = rh.lower_bound ();
+  wi::sub (wmax0, wmin1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  return true;
+}
+
+bool
+operator_mult::overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio) const
+{
+  if (lh.undefined_p () || rh.undefined_p ())
+    return false;
+
+  tree type = lh.type ();
+  if (TYPE_OVERFLOW_UNDEFINED (type))
+    return true;
+
+  wi::overflow_type ovf;
+  signop sgn = TYPE_SIGN (type);
+  wide_int wmax0 = lh.upper_bound ();
+  wide_int wmax1 = rh.upper_bound ();
+  wi::mul (wmax0, wmax1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  if (TYPE_UNSIGNED (type))
+    return true;
+
+  wide_int wmin0 = lh.lower_bound ();
+  wide_int wmin1 = rh.lower_bound ();
+  wi::mul (wmin0, wmin1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  wi::mul (wmin0, wmax1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  wi::mul (wmax0, wmin1, sgn, &ovf);
+  if (ovf != wi::OVF_NONE)
+    return false;
+
+  return true;
+}
+
 #if CHECKING_P
 #include "selftest.h"
 
diff --git a/gcc/range-op.h b/gcc/range-op.h
index af94c2756a7..db3b03f28a5 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -147,6 +147,9 @@  public:
 
   virtual relation_kind op1_op2_relation (const irange &lhs) const;
   virtual relation_kind op1_op2_relation (const frange &lhs) const;
+
+  virtual bool overflow_free_p (const irange &lh, const irange &rh,
+				relation_trio = TRIO_VARYING) const;
 protected:
   // Perform an integral operation between 2 sub-ranges and return it.
   virtual void wi_fold (irange &r, tree type,
@@ -214,6 +217,8 @@  public:
 				  const vrange &op2,
 				  relation_kind = VREL_VARYING) const;
   relation_kind op1_op2_relation (const vrange &lhs) const;
+  bool overflow_free_p (const vrange &lh, const vrange &rh,
+			relation_trio = TRIO_VARYING) const;
 protected:
   unsigned dispatch_kind (const vrange &lhs, const vrange &op1,
 			  const vrange& op2) const;
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 011bdbdeae6..5ae4e044194 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -313,6 +313,18 @@  add_vrange (const vrange &v, inchash::hash &hstate,
 
 } //namespace inchash
 
+bool
+irange::nonnegative_p () const
+{
+  return wi::ge_p (lower_bound (), 0, TYPE_SIGN (type ()));
+}
+
+bool
+irange::nonpositive_p () const
+{
+  return wi::le_p (upper_bound (), 0, TYPE_SIGN (type ()));
+}
+
 bool
 irange::supports_type_p (const_tree type) const
 {
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 0170188201b..a12dea514e4 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -280,6 +280,8 @@  public:
   virtual bool singleton_p (tree *result = NULL) const override;
   bool singleton_p (wide_int &) const;
   bool contains_p (const wide_int &) const;
+  bool nonnegative_p () const;
+  bool nonpositive_p () const;
 
   // In-place operators.
   virtual bool union_ (const vrange &) override;