diff options
author | Tamar Christina <tamar.christina@arm.com> | 2023-03-12 18:40:12 +0000 |
---|---|---|
committer | Tamar Christina <tamar.christina@arm.com> | 2023-03-12 18:40:12 +0000 |
commit | 03c6ba86757f0684c5419c90651106900f5ecb5a (patch) | |
tree | 6dd54fee375493a7fd5ea179cab4f9f987441e7f /gcc | |
parent | 2246d576f922bae3629da0fe1dbfcc6ff06769ad (diff) | |
download | gcc-03c6ba86757f0684c5419c90651106900f5ecb5a.zip gcc-03c6ba86757f0684c5419c90651106900f5ecb5a.tar.gz gcc-03c6ba86757f0684c5419c90651106900f5ecb5a.tar.bz2 |
ranger: Add range-ops for widen addition and widen multiplication [PR108583]
This adds range-ops for widening addition and widening multiplication.
I couldn't figure out how to write a test for this. It looks like there are
self tests but not a way to write standalone ones? I did create testcases in
the patch 3/4 which tests the end result.
gcc/ChangeLog:
PR target/108583
* gimple-range-op.h (gimple_range_op_handler): Add maybe_non_standard.
* gimple-range-op.cc (gimple_range_op_handler::gimple_range_op_handler):
Use it.
(gimple_range_op_handler::maybe_non_standard): New.
* range-op.cc (class operator_widen_plus_signed,
operator_widen_plus_signed::wi_fold, class operator_widen_plus_unsigned,
operator_widen_plus_unsigned::wi_fold, class operator_widen_mult_signed,
operator_widen_mult_signed::wi_fold, class operator_widen_mult_unsigned,
operator_widen_mult_unsigned::wi_fold,
ptr_op_widen_mult_signed, ptr_op_widen_mult_unsigned,
ptr_op_widen_plus_signed, ptr_op_widen_plus_unsigned): New.
* range-op.h (ptr_op_widen_mult_signed, ptr_op_widen_mult_unsigned,
ptr_op_widen_plus_signed, ptr_op_widen_plus_unsigned): New
Co-Authored-By: Andrew MacLeod <amacleod@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/gimple-range-op.cc | 53 | ||||
-rw-r--r-- | gcc/gimple-range-op.h | 1 | ||||
-rw-r--r-- | gcc/range-op.cc | 131 | ||||
-rw-r--r-- | gcc/range-op.h | 4 |
4 files changed, 189 insertions, 0 deletions
diff --git a/gcc/gimple-range-op.cc b/gcc/gimple-range-op.cc index d9dfdc5..a5d6253 100644 --- a/gcc/gimple-range-op.cc +++ b/gcc/gimple-range-op.cc @@ -179,6 +179,8 @@ gimple_range_op_handler::gimple_range_op_handler (gimple *s) // statements. if (is_a <gcall *> (m_stmt)) maybe_builtin_call (); + else + maybe_non_standard (); } // Calculate what we can determine of the range of this unary @@ -764,6 +766,57 @@ public: } } op_cfn_parity; +// Set up a gimple_range_op_handler for any nonstandard function which can be +// supported via range-ops. + +void +gimple_range_op_handler::maybe_non_standard () +{ + range_operator *signed_op = ptr_op_widen_mult_signed; + range_operator *unsigned_op = ptr_op_widen_mult_unsigned; + if (gimple_code (m_stmt) == GIMPLE_ASSIGN) + switch (gimple_assign_rhs_code (m_stmt)) + { + case WIDEN_PLUS_EXPR: + { + signed_op = ptr_op_widen_plus_signed; + unsigned_op = ptr_op_widen_plus_unsigned; + } + gcc_fallthrough (); + case WIDEN_MULT_EXPR: + { + m_valid = false; + m_op1 = gimple_assign_rhs1 (m_stmt); + m_op2 = gimple_assign_rhs2 (m_stmt); + tree ret = gimple_assign_lhs (m_stmt); + bool signed1 = TYPE_SIGN (TREE_TYPE (m_op1)) == SIGNED; + bool signed2 = TYPE_SIGN (TREE_TYPE (m_op2)) == SIGNED; + bool signed_ret = TYPE_SIGN (TREE_TYPE (ret)) == SIGNED; + + /* Normally these operands should all have the same sign, but + some passes and violate this by taking mismatched sign args. At + the moment the only one that's possible is mismatch inputs and + unsigned output. Once ranger supports signs for the operands we + can properly fix it, for now only accept the case we can do + correctly. */ + if ((signed1 ^ signed2) && signed_ret) + return; + + m_valid = true; + if (signed2 && !signed1) + std::swap (m_op1, m_op2); + + if (signed1 || signed2) + m_int = signed_op; + else + m_int = unsigned_op; + break; + } + default: + break; + } +} + // Set up a gimple_range_op_handler for any built in function which can be // supported via range-ops. diff --git a/gcc/gimple-range-op.h b/gcc/gimple-range-op.h index 743b858..1bf63c5 100644 --- a/gcc/gimple-range-op.h +++ b/gcc/gimple-range-op.h @@ -41,6 +41,7 @@ public: relation_trio = TRIO_VARYING); private: void maybe_builtin_call (); + void maybe_non_standard (); gimple *m_stmt; tree m_op1, m_op2; }; diff --git a/gcc/range-op.cc b/gcc/range-op.cc index 5c67bce..718ccb6 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -1556,6 +1556,73 @@ operator_plus::op2_range (irange &r, tree type, return op1_range (r, type, lhs, op1, rel.swap_op1_op2 ()); } +class operator_widen_plus_signed : public range_operator +{ +public: + virtual 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; +} op_widen_plus_signed; +range_operator *ptr_op_widen_plus_signed = &op_widen_plus_signed; + +void +operator_widen_plus_signed::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 +{ + wi::overflow_type ov_lb, ov_ub; + signop s = TYPE_SIGN (type); + + wide_int lh_wlb + = wide_int::from (lh_lb, wi::get_precision (lh_lb) * 2, SIGNED); + wide_int lh_wub + = wide_int::from (lh_ub, wi::get_precision (lh_ub) * 2, SIGNED); + wide_int rh_wlb = wide_int::from (rh_lb, wi::get_precision (rh_lb) * 2, s); + wide_int rh_wub = wide_int::from (rh_ub, wi::get_precision (rh_ub) * 2, s); + + wide_int new_lb = wi::add (lh_wlb, rh_wlb, s, &ov_lb); + wide_int new_ub = wi::add (lh_wub, rh_wub, s, &ov_ub); + + r = int_range<2> (type, new_lb, new_ub); +} + +class operator_widen_plus_unsigned : public range_operator +{ +public: + virtual 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; +} op_widen_plus_unsigned; +range_operator *ptr_op_widen_plus_unsigned = &op_widen_plus_unsigned; + +void +operator_widen_plus_unsigned::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 +{ + wi::overflow_type ov_lb, ov_ub; + signop s = TYPE_SIGN (type); + + wide_int lh_wlb + = wide_int::from (lh_lb, wi::get_precision (lh_lb) * 2, UNSIGNED); + wide_int lh_wub + = wide_int::from (lh_ub, wi::get_precision (lh_ub) * 2, UNSIGNED); + wide_int rh_wlb = wide_int::from (rh_lb, wi::get_precision (rh_lb) * 2, s); + wide_int rh_wub = wide_int::from (rh_ub, wi::get_precision (rh_ub) * 2, s); + + wide_int new_lb = wi::add (lh_wlb, rh_wlb, s, &ov_lb); + wide_int new_ub = wi::add (lh_wub, rh_wub, s, &ov_ub); + + r = int_range<2> (type, new_lb, new_ub); +} class operator_minus : public range_operator { @@ -2031,6 +2098,70 @@ operator_mult::wi_fold (irange &r, tree type, } } +class operator_widen_mult_signed : public range_operator +{ +public: + virtual 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; +} op_widen_mult_signed; +range_operator *ptr_op_widen_mult_signed = &op_widen_mult_signed; + +void +operator_widen_mult_signed::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 +{ + signop s = TYPE_SIGN (type); + + wide_int lh_wlb = wide_int::from (lh_lb, wi::get_precision (lh_lb) * 2, SIGNED); + wide_int lh_wub = wide_int::from (lh_ub, wi::get_precision (lh_ub) * 2, SIGNED); + wide_int rh_wlb = wide_int::from (rh_lb, wi::get_precision (rh_lb) * 2, s); + wide_int rh_wub = wide_int::from (rh_ub, wi::get_precision (rh_ub) * 2, s); + + /* We don't expect a widening multiplication to be able to overflow but range + calculations for multiplications are complicated. After widening the + operands lets call the base class. */ + return op_mult.wi_fold (r, type, lh_wlb, lh_wub, rh_wlb, rh_wub); +} + + +class operator_widen_mult_unsigned : public range_operator +{ +public: + virtual 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; +} op_widen_mult_unsigned; +range_operator *ptr_op_widen_mult_unsigned = &op_widen_mult_unsigned; + +void +operator_widen_mult_unsigned::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 +{ + signop s = TYPE_SIGN (type); + + wide_int lh_wlb = wide_int::from (lh_lb, wi::get_precision (lh_lb) * 2, UNSIGNED); + wide_int lh_wub = wide_int::from (lh_ub, wi::get_precision (lh_ub) * 2, UNSIGNED); + wide_int rh_wlb = wide_int::from (rh_lb, wi::get_precision (rh_lb) * 2, s); + wide_int rh_wub = wide_int::from (rh_ub, wi::get_precision (rh_ub) * 2, s); + + /* We don't expect a widening multiplication to be able to overflow but range + calculations for multiplications are complicated. After widening the + operands lets call the base class. */ + return op_mult.wi_fold (r, type, lh_wlb, lh_wub, rh_wlb, rh_wub); +} class operator_div : public cross_product_operator { diff --git a/gcc/range-op.h b/gcc/range-op.h index f00b747..b1eeac7 100644 --- a/gcc/range-op.h +++ b/gcc/range-op.h @@ -311,4 +311,8 @@ private: // This holds the range op table for floating point operations. extern floating_op_table *floating_tree_table; +extern range_operator *ptr_op_widen_mult_signed; +extern range_operator *ptr_op_widen_mult_unsigned; +extern range_operator *ptr_op_widen_plus_signed; +extern range_operator *ptr_op_widen_plus_unsigned; #endif // GCC_RANGE_OP_H |