aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Macleod <amacleod@gcc.gnu.org>2019-08-19 11:46:45 +0000
committerAndrew Macleod <amacleod@gcc.gnu.org>2019-08-19 11:46:45 +0000
commitd99d7058288b6c38a0e5fe13ed2f5c3a2f2efeb5 (patch)
tree0c7f6332d88a4a1ba2296b6a6a7f397ea8db3f53
parent4227cd462fa4c0165ee29d323779b5cd34bfae8b (diff)
downloadgcc-d99d7058288b6c38a0e5fe13ed2f5c3a2f2efeb5.zip
gcc-d99d7058288b6c38a0e5fe13ed2f5c3a2f2efeb5.tar.gz
gcc-d99d7058288b6c38a0e5fe13ed2f5c3a2f2efeb5.tar.bz2
Rangeops rework
From-SVN: r274669
-rw-r--r--gcc/grange.cc22
-rw-r--r--gcc/grange.h6
-rw-r--r--gcc/range-op.cc2163
-rw-r--r--gcc/range-op.h29
-rw-r--r--gcc/ssa-range-vrp.c6
-rw-r--r--gcc/ssa-range.cc46
-rw-r--r--gcc/tree-vrp.c37
-rw-r--r--gcc/vr-values.c6
8 files changed, 1210 insertions, 1105 deletions
diff --git a/gcc/grange.cc b/gcc/grange.cc
index b1b2a0a..3180235 100644
--- a/gcc/grange.cc
+++ b/gcc/grange.cc
@@ -226,7 +226,7 @@ grange_op::grange_adjust_handler () const
inline range_operator *
grange_op::handler () const
{
- return range_op_handler (gimple_expr_code (this));
+ return range_op_handler (gimple_expr_code (this), gimple_expr_type (this));
}
// Return the first operand of this statement if it is a valid operand
@@ -297,7 +297,7 @@ grange_op::fold (irange &res, const irange &r1, const irange &r2) const
if (grange_adjust_handler ())
adj = grange_adjust_handler()->lhs_adjust (adj_range, this);
if (handler ())
- hand = handler()->fold_range (res, r1, r2);
+ hand = handler()->fold_range (res, gimple_expr_type (this), r1, r2);
// Handle common case first where res was set by handler
// This handles whatever handler() would ahve returned.
@@ -323,15 +323,17 @@ grange_op::calc_op1_irange (irange &r, const irange &lhs_range) const
irange type_range;
gcc_checking_assert (gimple_num_ops (this) < 3);
// An empty range is viral, so return an empty range.
+
+ tree type = TREE_TYPE (operand1 ());
if (lhs_range.undefined_p ())
{
- r.set_undefined (TREE_TYPE (operand1 ()));
+ r.set_undefined (type);
return true;
}
// Unary operations require the type of the first operand in the second range
// position.
- type_range.set_varying (TREE_TYPE (operand1 ()));
- return handler ()->op1_range (r, lhs_range, type_range);
+ type_range.set_varying (type);
+ return handler ()->op1_range (r, type, lhs_range, type_range);
}
// Calculate what we can determine of the range of this statement's first
@@ -346,13 +348,14 @@ grange_op::calc_op1_irange (irange &r, const irange &lhs_range,
// as there are often additional restrictions beyond the type which can
// be imposed. See operator_cast::op1_irange.()
+ tree type = TREE_TYPE (operand1 ());
// An empty range is viral, so return an empty range.
if (op2_range.undefined_p () || lhs_range.undefined_p ())
{
- r.set_undefined (op2_range.type ());
+ r.set_undefined (type);
return true;
}
- return handler ()->op1_range (r, lhs_range, op2_range);
+ return handler ()->op1_range (r, type, lhs_range, op2_range);
}
// Calculate what we can determine of the range of this statement's second
@@ -363,13 +366,14 @@ bool
grange_op::calc_op2_irange (irange &r, const irange &lhs_range,
const irange &op1_range) const
{
+ tree type = TREE_TYPE (operand2 ());
// An empty range is viral, so return an empty range.
if (op1_range.undefined_p () || lhs_range.undefined_p ())
{
- r.set_undefined (op1_range.type ());
+ r.set_undefined (type);
return true;
}
- return handler ()->op2_range (r, lhs_range, op1_range);
+ return handler ()->op2_range (r, type, lhs_range, op1_range);
}
diff --git a/gcc/grange.h b/gcc/grange.h
index 284f0e64..4921c2d 100644
--- a/gcc/grange.h
+++ b/gcc/grange.h
@@ -100,7 +100,8 @@ is_a_helper <const grange_op *>::test (const gimple *gs)
if (dyn_cast<const gassign *> (gs) || dyn_cast<const gcond *>(gs))
{
enum tree_code c = gimple_expr_code (gs);
- return range_op_handler (c) || gimple_range_adjust_handler (c);
+ tree expr_type = gimple_expr_type (gs);
+ return range_op_handler (c, expr_type) || gimple_range_adjust_handler (c);
}
return false;
}
@@ -116,7 +117,8 @@ is_a_helper <grange_op *>::test (gimple *gs)
if (dyn_cast<gassign *> (gs) || dyn_cast<gcond *>(gs))
{
enum tree_code c = gimple_expr_code (gs);
- return range_op_handler (c) || gimple_range_adjust_handler (c);
+ tree expr_type = gimple_expr_type (gs);
+ return range_op_handler (c, expr_type) || gimple_range_adjust_handler (c);
}
return false;
}
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index d2189cd..ad5443b 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -48,88 +48,68 @@ along with GCC; see the file COPYING3. If not see
#include "wide-int-range.h"
-/* Defaults for all operations are to return NULL. This means no
- additional range info is available beyond that of the type. */
-
+// Default wide_int fold operation does nothing.
bool
-range_operator::fold_range (irange &r ATTRIBUTE_UNUSED,
- const irange &op1 ATTRIBUTE_UNUSED,
- const irange &op2 ATTRIBUTE_UNUSED) const
+range_operator::wi_fold (irange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
+ const wide_int &lh_lb ATTRIBUTE_UNUSED,
+ const wide_int &lh_ub ATTRIBUTE_UNUSED,
+ const wide_int &rh_lb ATTRIBUTE_UNUSED,
+ const wide_int &rh_ub ATTRIBUTE_UNUSED) const
{
return false;
}
+// The default for fold is to break all ranges into subranges
+// and invoke the 'wi_fold' method on each subrange pair.
+bool
+range_operator::fold_range (irange &r, tree type, const irange &lh,
+ const irange &rh) const
+{
+ bool res = false;
+
+ // Clear and set result type.
+ r.set_undefined (type);
+
+ if (lh.undefined_p () || rh.undefined_p ())
+ return true;
+
+ for (unsigned x = 0; x < lh.num_pairs (); ++x)
+ for (unsigned y = 0; y < rh.num_pairs (); ++y)
+ {
+ wide_int lh_lb = lh.lower_bound (x);
+ wide_int lh_ub = lh.upper_bound (x);
+ wide_int rh_lb = rh.lower_bound (y);
+ wide_int rh_ub = rh.upper_bound (y);
+ res = wi_fold (r, type, lh_lb, lh_ub, rh_lb, rh_ub);
+ if (!res)
+ return false;
+ }
+
+ return res && !r.varying_p ();
+}
+
+// The default for op1_range is to return false.
bool
range_operator::op1_range (irange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
const irange &lhs ATTRIBUTE_UNUSED,
const irange &op2 ATTRIBUTE_UNUSED) const
{
return false;
}
+// The default for op2_range is to return false.
bool
range_operator::op2_range (irange &r ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED,
const irange &lhs ATTRIBUTE_UNUSED,
const irange &op1 ATTRIBUTE_UNUSED) const
{
return false;
}
-// This class is used to create a range_operator for a tree code and
-// automatically register it with the operator table. Simply inherit
-// from this class and overload whatever routines are required. This
-// provides registration as well as default debug dumping for the tree
-// code and calling op_binary() to resolve fold_range.
-
-class trange_operator : public range_operator
-{
-public:
- trange_operator (enum tree_code c);
- virtual void dump (FILE *f) const;
- virtual bool fold_range (irange &r, const irange &op1,
- const irange &op2) const;
-protected:
- tree_code code;
-};
-
-
-// This implements the range operator table as a local object in this file.
-
-class range_op_table
-{
-public:
- inline range_operator *operator[] (enum tree_code code);
- void register_operator (enum tree_code code, range_operator *);
-private:
- range_operator *m_range_tree[MAX_TREE_CODES];
-} range_tree;
-
-// Return a pointer tto the range_operator instance, if there is one,
-// associated with tree_code CODE.
-
-range_operator *
-range_op_table::operator[] (enum tree_code code)
-{
- gcc_assert (code > 0 && code < MAX_TREE_CODES);
- return m_range_tree[code];
-}
-
-// Add OP to the handler table for CODE.
-
-void
-range_op_table::register_operator (enum tree_code code, range_operator *op)
-{
- gcc_checking_assert (m_range_tree[code] == NULL && op != NULL);
- m_range_tree[code] = op;
-}
-
-/* The table is hidden and accessed via a simple extern function. */
-
-range_operator *
-range_op_handler (enum tree_code code)
-{
- return range_tree[code];
-}
+// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Auxillary routine to return the upper limit for a type.
@@ -148,10 +128,8 @@ min_limit (const_tree type)
return wi::min_value (TYPE_PRECISION (type) , TYPE_SIGN (type));
}
-// If the range of either operand is undefined, set the result to
-// undefined and return true. This is a common routine to most
-// functions as undefined is a viral condition, so if either operand
-// is undefined, so is the result.
+// If the range of either op1 or op2 is undefined, set the result to
+// undefined and return true.
inline bool
empty_range_check (irange &r, const irange &op1, const irange & op2, tree type)
@@ -165,602 +143,160 @@ empty_range_check (irange &r, const irange &op1, const irange & op2, tree type)
return false;
}
-/* Given newly calculated lbound and ubound, examine their respective
- overflow bits to determine how to add [lbound, ubound] into range
- R. */
+// Called when there is either an overflow OR an underflow... which means
+// an anti range must be created to compensate. This does not cover
+// the case where there are 2 possible overflows, or none.
static void
-accumulate_range (irange &r,
- const wide_int &lb, wi::overflow_type ov_lb,
- const wide_int &ub, wi::overflow_type ov_ub,
- bool overflow_wraps = false)
-{
- value_range_kind kind;
- wide_int min = lb, max = ub;
- tree type = r.type ();
- adjust_range_for_overflow (kind, min, max, type, ov_lb, ov_ub,
- overflow_wraps);
- if (kind == VR_VARYING)
+adjust_overflow_bound (irange &r, tree type, const wide_int &wmin,
+ const wide_int &wmax)
+{
+ const signop sgn = TYPE_SIGN (type);
+ const unsigned int prec = TYPE_PRECISION (type);
+
+ wide_int tmin = wide_int::from (wmin, prec, sgn);
+ wide_int tmax = wide_int::from (wmax, prec, sgn);
+
+ bool covers = false;
+ wide_int tem = tmin;
+ tmin = tmax + 1;
+ if (wi::cmp (tmin, tmax, sgn) < 0)
+ covers = true;
+ tmax = tem - 1;
+ if (wi::cmp (tmax, tem, sgn) > 0)
+ covers = true;
+ /* If the anti-range would cover nothing, drop to varying.
+ Likewise if the anti-range bounds are outside of the
+ types values. */
+ if (covers || wi::cmp (tmin, tmax, sgn) > 0)
+ r.set_varying (type);
+ else
{
- r.set_varying (type);
- return;
+ irange tmp (VR_ANTI_RANGE, type, tmin, tmax);
+ r.union_ (tmp);
}
- irange tmp (kind, type, min, max);
- r.union_ (tmp);
return;
}
+// Given newly calculated lbound and ubound, examine their respective
+// overflow bits to determine how to add [lbound, ubound] into range R.
+
static void
-accumulate_range (irange &r, const wide_int &lb, const wide_int &ub)
+accumulate_range (irange &r, tree type, const wide_int &wmin,
+ const wide_int &wmax,
+ wi::overflow_type min_ovf = wi::OVF_NONE,
+ wi::overflow_type max_ovf = wi::OVF_NONE)
{
- accumulate_range (r, lb, wi::OVF_NONE, ub, wi::OVF_NONE, false);
-}
-
-/* Like accumulate_range, but canonicalize the case where the bounds
- are swapped and overflow may wrap. In which case we transform
- [10,5] into [MIN,5][10,MAX].
-
- I am not happy with this. This is basically a workaround for the
- fact that VRP has cripple ranges and wide_int_range_mult_wrapping
- may return swapped ranges, so they must be canonicalized into some
- anti range crap. I'm hoping this will go away when everything is
- an irange. */
-
-static inline void
-accumulate_range_and_canonicalize (signop s,
- irange &r,
- const wide_int &new_lb,
- const wide_int &new_ub)
-{
- /* If the bounds are swapped, set the overflow bit to force
- add_to_range to create the range correctly. This is essentially
- what set_and_canonicalize_value_range was doing. */
- wi::overflow_type overflow;
- bool overflow_wraps = TYPE_OVERFLOW_WRAPS (r.type ());
- if (wi::gt_p (new_lb, new_ub, s))
- {
- overflow = wi::OVF_OVERFLOW;
- overflow_wraps = true;
- }
- else
- overflow = wi::OVF_NONE;
+ const signop sgn = TYPE_SIGN (type);
+ const unsigned int prec = TYPE_PRECISION (type);
+ const bool overflow_wraps = TYPE_OVERFLOW_WRAPS (type);
- accumulate_range (r, new_lb, wi::OVF_NONE, new_ub, overflow, overflow_wraps);
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* irange wrapper for wide_int_range_multiplicative_op. */
-
-static bool
-irange_multiplicative_op (enum tree_code code, signop s, irange &r,
- const wide_int &lh_lb, const wide_int &lh_ub,
- const wide_int &rh_lb, const wide_int &rh_ub)
-{
- wide_int new_lb, new_ub;
- bool overflow_undefined = TYPE_OVERFLOW_UNDEFINED (r.type ());
- unsigned prec = TYPE_PRECISION (r.type ());
- if (wide_int_range_multiplicative_op (new_lb, new_ub,
- code, s, prec,
- lh_lb, lh_ub, rh_lb, rh_ub,
- overflow_undefined))
+ // For one bit precision if max != min, then the range covers all values.
+ if (prec == 1 && wi::ne_p (wmax, wmin))
{
- accumulate_range_and_canonicalize (s, r, new_lb, new_ub);
- return true;
+ r.set_varying (type);
+ return;
}
- return false;
-}
-
-/* For an operation on pointers, this specifies whether the resulting
- operation is null, non-null, or unknown. */
-enum wide_int_range_nullness {
- WIDE_INT_RANGE_UNKNOWN = 0,
- WIDE_INT_RANGE_NULL,
- WIDE_INT_RANGE_NONNULL
-};
-/* FIXME: This was slated to be in wide-int-range.cc, but Richi is
- unconvinced we need to abstract this out. To be discussed later.
-
- See discussion here:
- https://gcc.gnu.org/ml/gcc-patches/2018-09/msg00171.html
-*/
-/* Given a binary operator (CODE) on two pointer ranges, return if the
- result will be zero, non-zero, or unknown. */
-
-static enum wide_int_range_nullness
-wide_int_range_pointer (enum tree_code code,
- signop sign,
- const wide_int &vr0_min,
- const wide_int &vr0_max,
- const wide_int &vr1_min,
- const wide_int &vr1_max,
- bool overflow_wraps)
-{
- unsigned prec = vr0_min.get_precision ();
- if (code == MIN_EXPR || code == MAX_EXPR)
+ if (overflow_wraps)
{
- /* For MIN/MAX expressions with pointers, we only care about
- nullness, if both are non null, then the result is nonnull.
- If both are null, then the result is null. Otherwise they
- are varying. */
- if (!wide_int_range_includes_zero_p (vr0_min, vr0_max, sign)
- && !wide_int_range_includes_zero_p (vr1_min, vr1_max, sign))
- return WIDE_INT_RANGE_NONNULL;
- else if (wide_int_range_zero_p (vr0_min, vr0_max, prec)
- && wide_int_range_zero_p (vr1_min, vr1_max, prec))
- return WIDE_INT_RANGE_NULL;
- else
- return WIDE_INT_RANGE_UNKNOWN;
- }
- else if (code == POINTER_PLUS_EXPR)
- {
- /* For pointer types, we are really only interested in asserting
- whether the expression evaluates to non-NULL.
-
- With -fno-delete-null-pointer-checks we need to be more
- conservative. As some object might reside at address 0,
- then some offset could be added to it and the same offset
- subtracted again and the result would be NULL.
- E.g.
- static int a[12]; where &a[0] is NULL and
- ptr = &a[6];
- ptr -= 6;
- ptr will be NULL here, even when there is POINTER_PLUS_EXPR
- where the first range doesn't include zero and the second one
- doesn't either. As the second operand is sizetype (unsigned),
- consider all ranges where the MSB could be set as possible
- subtractions where the result might be NULL. */
- if ((!wide_int_range_includes_zero_p (vr0_min, vr0_max, sign)
- || !wide_int_range_includes_zero_p (vr1_min, vr1_max, sign))
- && !overflow_wraps
- && (flag_delete_null_pointer_checks
- || !wi::sign_mask (vr1_max)))
- return WIDE_INT_RANGE_NONNULL;
- else if (wide_int_range_zero_p (vr0_min, vr0_max, prec)
- && wide_int_range_zero_p (vr1_min, vr1_max, prec))
- return WIDE_INT_RANGE_NULL;
+ /* If overflow wraps, truncate the values and adjust the
+ range kind and bounds appropriately. */
+ if ((min_ovf != wi::OVF_NONE) == (max_ovf != wi::OVF_NONE))
+ {
+ wide_int tmin = wide_int::from (wmin, prec, sgn);
+ wide_int tmax = wide_int::from (wmax, prec, sgn);
+ /* If the limits are swapped, we wrapped around and cover
+ the entire range. We have a similar check at the end of
+ extract_range_from_binary_expr. */
+ if (wi::gt_p (tmin, tmax, sgn))
+ r.set_varying (type);
+ else
+ {
+ /* No overflow or both overflow or underflow. The
+ range kind stays normal. */
+ irange tmp (type, tmin, tmax);
+ r.union_ (tmp);
+ }
+ }
+ else if ((min_ovf == wi::OVF_UNDERFLOW && max_ovf == wi::OVF_NONE)
+ || (max_ovf == wi::OVF_OVERFLOW && min_ovf == wi::OVF_NONE))
+ adjust_overflow_bound (r, type, wmin, wmax);
else
- return WIDE_INT_RANGE_UNKNOWN;
+ // Other underflow and/or overflow, drop to VR_VARYING.
+ r.set_varying (type);
}
- else if (code == BIT_AND_EXPR)
+ else
{
- /* For pointer types, we are really only interested in asserting
- whether the expression evaluates to non-NULL. */
- if (!wide_int_range_includes_zero_p (vr0_min, vr0_max, sign)
- && !wide_int_range_includes_zero_p (vr1_min, vr1_max, sign))
- return WIDE_INT_RANGE_NONNULL;
- else if (wide_int_range_zero_p (vr0_min, vr0_max, prec)
- || wide_int_range_zero_p (vr1_min, vr1_max, prec))
- return WIDE_INT_RANGE_NULL;
+ /* If overflow does not wrap, saturate to the types min/max
+ value. */
+ wide_int new_lb, new_ub;
+ if (min_ovf == wi::OVF_UNDERFLOW)
+ new_lb = wi::min_value (prec, sgn);
+ else if (min_ovf == wi::OVF_OVERFLOW)
+ new_lb = wi::max_value (prec, sgn);
else
- return WIDE_INT_RANGE_UNKNOWN;
- }
- else
- return WIDE_INT_RANGE_UNKNOWN;
-}
-
-/* irange wrapper for wide_int_range_pointer. */
-
-static void
-irange_pointer_optimization (enum tree_code code, signop s, irange &r,
- const wide_int &lh_lb, const wide_int &lh_ub,
- const wide_int &rh_lb, const wide_int &rh_ub)
-{
- tree type = r.type ();
- wide_int_range_nullness n;
- n = wide_int_range_pointer (code, s, lh_lb, lh_ub, rh_lb, rh_ub,
- TYPE_OVERFLOW_WRAPS (type));
- if (n == WIDE_INT_RANGE_UNKNOWN)
- r.set_varying (type);
- else if (n == WIDE_INT_RANGE_NULL)
- r.union_ (range_zero (type));
- else if (n == WIDE_INT_RANGE_NONNULL)
- r.union_ (range_nonzero (type));
- else
- gcc_unreachable ();
-}
-
-/* The result of a bitwise [lh_lb, lh_ub] .AND. [rh_lb, rh_ub] has
- already been calculated in range R. See if the operation was a
- masking operation, and optimize it further. */
+ new_lb = wmin;
-static void
-irange_adjust_bit_and_mask (irange &r, signop s,
- const wide_int &lh_lb, const wide_int &lh_ub,
- const wide_int &rh_lb, const wide_int &rh_ub)
-{
- /* FIXME: This optimization causes us to miscompare with VRP.
- Disable for now, and then contribute it independently
- upstream. */
- return;
-
- /* If the resulting range contains 0, AND the least significant bit
- of mask is not set, we can improve the range by making the least
- significant bit set the minimum, and adding in the 0.
-
- For example, A & 0x3C returns a range of [0,60]. We can improve
- this to [0,0][4, 60]. */
- wide_int mask, lower_bound, upper_bound;
- int tz;
- tree type = r.type ();
- irange zero = range_zero (type);
- bool range_contains_zero = !range_intersect (r, zero).undefined_p ();
- if (range_contains_zero
- && wide_int_range_get_mask_and_bounds (mask,
- lower_bound,
- upper_bound,
- lh_lb, lh_ub,
- rh_lb, rh_ub)
- && (tz = wi::ctz (mask)) != 0)
- {
- unsigned prec = TYPE_PRECISION (type);
- irange negatives, positives;
- wide_int lb, ub;
- if (s == SIGNED)
- {
- positives = range_positives (type);
- negatives = range_negatives (type);
- positives.intersect (r);
- negatives.intersect (r);
- }
+ if (max_ovf == wi::OVF_UNDERFLOW)
+ new_ub = wi::min_value (prec, sgn);
+ else if (max_ovf == wi::OVF_OVERFLOW)
+ new_ub = wi::max_value (prec, sgn);
else
- {
- positives = r;
- negatives.set_undefined (type);
- }
- if (!positives.undefined_p ())
- {
- // Mask out the positive numbers that can't happen.
- lb = wi::shifted_mask (tz, 1, false, prec);
- ub = positives.upper_bound();
- if (wi::le_p (lb, ub, s))
- positives.intersect (irange (type, lb, ub));
- }
- if (!negatives.undefined_p ())
- {
- // Mask out the negatives numbers that can't happen.
- lb = wi::min_value (prec, s);
- ub = wi::shifted_mask (0, tz, true, prec);
- negatives.intersect (irange (type, lb, ub));
- }
- r = positives;
- r.union_ (negatives);
- r.union_ (zero);
+ new_ub = wmax;
+ irange tmp (type, new_lb, new_ub);
+ r.union_ (tmp);
}
}
-/* Perform an operation CODE on pairs of ranges, and store the result
- in R.
-
- If the operation is a binary op, perform:
- [lh_lb, lh_ub] .CODE. [rh_lb, rh_ub]
-
- If the operation is a unary op, the rh_* bounds are unused.
-
- Return FALSE to stop processing remaining subranges. In this case,
- the result is assumed to span the entire domain (range_for_type). */
+/* Like accumulate_range, but canonicalize the case where the bounds
+ are swapped and overflow may wrap. In which case we transform
+ [10,5] into [MIN,5][10,MAX]. */
-static bool
-op_wide_int (enum tree_code code, irange &r, tree rh_type,
- const wide_int &lh_lb, const wide_int lh_ub,
- const wide_int &rh_lb, const wide_int &rh_ub)
+static inline void
+accumulate_possibly_reversed_range (irange &r, tree type,
+ const wide_int &new_lb,
+ const wide_int &new_ub)
{
- wide_int new_lb, new_ub, tmp;
- wi::overflow_type ov_lb, ov_ub;
- tree type = r.type ();
signop s = TYPE_SIGN (type);
-
- if (POINTER_TYPE_P (type))
- {
- irange_pointer_optimization (code, s, r, lh_lb, lh_ub, rh_lb, rh_ub);
- return true;
- }
-
- switch (code)
- {
- case PLUS_EXPR:
- wide_int_binop (new_lb, code, lh_lb, rh_lb, s, &ov_lb);
- wide_int_binop (new_ub, code, lh_ub, rh_ub, s, &ov_ub);
- accumulate_range (r, new_lb, ov_lb, new_ub, ov_ub,
- TYPE_OVERFLOW_WRAPS (type));
- return true;
-
- case MINUS_EXPR:
- wide_int_binop (new_lb, code, lh_lb, rh_ub, s, &ov_lb);
- wide_int_binop (new_ub, code, lh_ub, rh_lb, s, &ov_ub);
- accumulate_range (r, new_lb, ov_lb, new_ub, ov_ub,
- TYPE_OVERFLOW_WRAPS (type));
- return true;
-
- case MAX_EXPR:
- case MIN_EXPR:
- if (wide_int_range_min_max (new_lb, new_ub, code, s,
- TYPE_PRECISION (type),
- lh_lb, lh_ub, rh_lb, rh_ub))
- {
- accumulate_range (r, new_lb, new_ub);
- return true;
- }
- r.set_varying (type);
- return false;
-
- case MULT_EXPR:
- if (irange_multiplicative_op (code, s, r, lh_lb, lh_ub, rh_lb, rh_ub))
- return true;
- r.set_varying (type);
- return false;
-
- case RSHIFT_EXPR:
- if (!wide_int_range_shift_undefined_p (TYPE_SIGN (rh_type),
- TYPE_PRECISION (type),
- rh_lb, rh_ub)
- && irange_multiplicative_op (code, s, r,
- lh_lb, lh_ub, rh_lb, rh_ub))
- return true;
- r.set_varying (type);
- return false;
-
- case LSHIFT_EXPR:
- if (!wide_int_range_shift_undefined_p (TYPE_SIGN (rh_type),
- TYPE_PRECISION (type),
- rh_lb, rh_ub)
- && wide_int_range_lshift (new_lb, new_ub, s, TYPE_PRECISION (type),
- lh_lb, lh_ub, rh_lb, rh_ub,
- TYPE_OVERFLOW_UNDEFINED (type)))
- {
- accumulate_range_and_canonicalize (s, r, new_lb, new_ub);
- return true;
- }
- r.set_varying (type);
- return false;
-
- case BIT_AND_EXPR:
- {
- /* For pointer types, we are really only interested in asserting
- whether the expression evaluates to non-NULL. */
- wide_int may_be_nonzero_lh, must_be_nonzero_lh;
- wide_int may_be_nonzero_rh, must_be_nonzero_rh;
- wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
- may_be_nonzero_lh,
- must_be_nonzero_lh);
- wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
- may_be_nonzero_rh,
- must_be_nonzero_rh);
- if (wide_int_range_bit_and (new_lb, new_ub, s, TYPE_PRECISION (type),
- lh_lb, lh_ub,
- rh_lb, rh_ub,
- must_be_nonzero_lh,
- may_be_nonzero_lh,
- must_be_nonzero_rh,
- may_be_nonzero_rh))
- {
- // For AND, calculate each subrange separately, and then union
- // the results.
- irange tmp;
- tmp.set_undefined (r.type ());
- accumulate_range (tmp, new_lb, new_ub);
- irange_adjust_bit_and_mask (tmp, s, lh_lb, lh_ub, rh_lb, rh_ub);
- r.union_ (tmp);
- return true;
- }
- r.set_varying (type);
- return false;
- }
-
- case BIT_IOR_EXPR:
- {
- wide_int may_be_nonzero_lh, must_be_nonzero_lh;
- wide_int may_be_nonzero_rh, must_be_nonzero_rh;
- wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
- may_be_nonzero_lh,
- must_be_nonzero_lh);
- wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
- may_be_nonzero_rh,
- must_be_nonzero_rh);
- if (wide_int_range_bit_ior (new_lb, new_ub, s,
- lh_lb, lh_ub,
- rh_lb, rh_ub,
- must_be_nonzero_lh,
- may_be_nonzero_lh,
- must_be_nonzero_rh,
- may_be_nonzero_rh))
- {
- accumulate_range (r, new_lb, new_ub);
- return true;
- }
- r.set_varying (type);
- return false;
- }
-
- case BIT_XOR_EXPR:
- {
- wide_int may_be_nonzero_lh, must_be_nonzero_lh;
- wide_int may_be_nonzero_rh, must_be_nonzero_rh;
- wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
- may_be_nonzero_lh,
- must_be_nonzero_lh);
- wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
- may_be_nonzero_rh,
- must_be_nonzero_rh);
- if (wide_int_range_bit_xor (new_lb, new_ub, s, TYPE_PRECISION (type),
- must_be_nonzero_lh,
- may_be_nonzero_lh,
- must_be_nonzero_rh,
- may_be_nonzero_rh))
- {
- accumulate_range (r, new_lb, new_ub);
- return true;
- }
- r.set_varying (type);
- return false;
- }
-
- case TRUNC_MOD_EXPR:
- if (wide_int_range_zero_p (rh_lb, rh_ub, TYPE_PRECISION (type)))
- {
- /* An empty range means undefined. */
- return false;
- }
- wide_int_range_trunc_mod (new_lb, new_ub, s, TYPE_PRECISION (type),
- lh_lb, lh_ub, rh_lb, rh_ub);
- accumulate_range (r, new_lb, new_ub);
- return true;
-
- case TRUNC_DIV_EXPR:
- case EXACT_DIV_EXPR:
- case FLOOR_DIV_EXPR:
- case ROUND_DIV_EXPR:
- case CEIL_DIV_EXPR:
- {
- /* If we know we will divide by zero, return an empty range,
- which will be interpreted as undefined. */
- if (rh_lb == 0 && rh_ub == 0)
- return false;
-
- wide_int extra_min, extra_max;
- bool extra_range_p;
- if (wide_int_range_div (new_lb, new_ub, code, s,
- TYPE_PRECISION (type),
- lh_lb, lh_ub,
- rh_lb, rh_ub,
- TYPE_OVERFLOW_UNDEFINED (type),
- extra_range_p, extra_min, extra_max))
- {
- accumulate_range (r, new_lb, new_ub);
- if (extra_range_p)
- accumulate_range (r, extra_min, extra_max);
- return true;
- }
- r.set_varying (type);
- return false;
- }
-
- case ABS_EXPR:
- if (wide_int_range_abs (new_lb, new_ub,
- TYPE_SIGN (r.type ()),
- TYPE_PRECISION (r.type ()),
- lh_lb, lh_ub,
- TYPE_OVERFLOW_UNDEFINED (r.type ())))
- {
- r.union_ (irange (r.type (), new_lb, new_ub));
- return true;
- }
- r.set_varying (type);
- return false;
-
- case ABSU_EXPR:
- wide_int_range_absu (new_lb, new_ub, TYPE_PRECISION (r.type ()),
- lh_lb, lh_ub);
- r.union_ (irange (unsigned_type_for (r.type ()), new_lb, new_ub));
- return true;
-
- default:
- return false;
- }
-}
-
-/* Perform an operation between 2 ranges. */
-
-static bool
-op_binary (enum tree_code code, irange &r, const irange &lh, const irange &rh)
-{
- bool res = false;
- tree type = lh.type ();
- // Clear and set result type.
- r.set_undefined (type);
-
- if (lh.undefined_p () || rh.undefined_p ())
- return true;
-
- for (unsigned x = 0; x < lh.num_pairs (); ++x)
- for (unsigned y = 0; y < rh.num_pairs (); ++y)
- {
- wide_int lh_lb = lh.lower_bound (x);
- wide_int lh_ub = lh.upper_bound (x);
- wide_int rh_lb = rh.lower_bound (y);
- wide_int rh_ub = rh.upper_bound (y);
- tree type = rh.type ();
- res = op_wide_int (code, r, type, lh_lb, lh_ub, rh_lb, rh_ub);
- if (!res)
- return false;
- }
-
- return res && !r.varying_p ();
-}
-
-/* Perform a unary operation on a range. TYPE is the type of the
- resulting operation (and thus R). */
-
-static bool
-op_unary (enum tree_code code, irange &r, const irange &lh, tree type)
-{
- bool res = false;
- // Clear and set result type.
- r.set_undefined (type);
-
- if (lh.undefined_p ())
- return true;
-
- for (unsigned x = 0; x < lh.num_pairs (); ++x)
+ // If the bounds are swapped, treat the result as if an overflow occured.
+ if (wi::gt_p (new_lb, new_ub, s))
{
- wide_int lower_bound = lh.lower_bound (x);
- wide_int upper_bound = lh.upper_bound (x);
- res = op_wide_int (code, r, type,
- lower_bound, upper_bound, lower_bound, upper_bound);
- if (!res)
- return false;
+ adjust_overflow_bound (r, type, new_lb, new_ub);
+ return;
}
- return res && !r.varying_p ();
-}
-
-// Construct and register the range_op handler for treee code C.
-
-trange_operator::trange_operator (enum tree_code c)
-{
- code = c;
- range_tree.register_operator (c, this);
-}
-
-// Dump the name of this operation.
-
-void
-trange_operator::dump (FILE *f) const
-{
- fprintf (f," %s ", get_tree_code_name (code));
+ // Otherwise its just a normal range.
+ irange tmp (type, new_lb, new_ub);
+ r.union_ (tmp);
}
-// Perform the default fold operation of LH OP RH, and return it in R.
+// Return an irange instance that is a boolean TRUE.
-bool
-trange_operator::fold_range (irange &r, const irange &lh,
- const irange &rh) const
+static irange
+range_true (tree type)
{
- if (empty_range_check (r, lh, rh, lh.type ()))
- return true;
-
- return op_binary (code, r, lh, rh);
+ unsigned prec = TYPE_PRECISION (type);
+ return irange (type, wi::one (prec), wi::one (prec));
}
-// Return an irange instance that is a boolean TRUE.
+// Return an irange instance that is a boolean FALSE.
static irange
-range_true ()
+range_false (tree type)
{
- unsigned prec = TYPE_PRECISION (boolean_type_node);
- return irange (boolean_type_node, wi::one (prec), wi::one (prec));
+ unsigned prec = TYPE_PRECISION (type);
+ return irange (type, wi::zero (prec), wi::zero (prec));
}
// Return an irange instance that is a boolean FALSE.
static irange
-range_false ()
+range_true_and_false (tree type)
{
- unsigned prec = TYPE_PRECISION (boolean_type_node);
- return irange (boolean_type_node, wi::zero (prec), wi::zero (prec));
+ unsigned prec = TYPE_PRECISION (type);
+ return irange (type, wi::zero (prec), wi::one (prec));
}
-
enum bool_range_state { BRS_FALSE, BRS_TRUE, BRS_EMPTY, BRS_FULL };
/* Return the summary information about boolean range LHS. Return an
@@ -791,24 +327,25 @@ get_bool_state (irange &r, const irange &lhs, tree val_type)
}
-class operator_equal : public trange_operator
+class operator_equal : public range_operator
{
public:
- operator_equal () : trange_operator (EQ_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &val) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &val) const;
} op_equal;
+
+
/* Fold comparison of the 2 ranges. */
bool
-operator_equal::fold_range (irange &r, const irange &op1,
+operator_equal::fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
/* We can be sure the values are always equal or not if both ranges
@@ -817,9 +354,9 @@ operator_equal::fold_range (irange &r, const irange &op1,
&& wi::eq_p (op2.lower_bound (), op2.upper_bound ()))
{
if (wi::eq_p (op1.lower_bound (), op2.upper_bound()))
- r = range_true ();
+ r = range_true (type);
else
- r = range_false ();
+ r = range_false (type);
}
else
{
@@ -827,19 +364,19 @@ operator_equal::fold_range (irange &r, const irange &op1,
we don;t really know anything for sure. */
r = range_intersect (op1, op2);
if (r.undefined_p ())
- r = range_false ();
+ r = range_false (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
}
return true;
}
bool
-operator_equal::op1_range (irange &r, const irange &lhs,
+operator_equal::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_FALSE:
/* If the result is false, the only time we know anything is if OP2 is
@@ -847,7 +384,7 @@ operator_equal::op1_range (irange &r, const irange &lhs,
if (wi::eq_p (op2.lower_bound(), op2.upper_bound()))
r = range_invert (op2);
else
- r.set_varying (op2.type ());
+ r.set_varying (type);
break;
case BRS_TRUE:
@@ -863,33 +400,32 @@ operator_equal::op1_range (irange &r, const irange &lhs,
bool
-operator_equal::op2_range (irange &r, const irange &lhs,
+operator_equal::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_equal::op1_range (r, lhs, op1);
+ return operator_equal::op1_range (r, type, lhs, op1);
}
/* Range operator for def = op1 != op2. */
-class operator_not_equal : public trange_operator
+class operator_not_equal : public range_operator
{
public:
- operator_not_equal () : trange_operator (NE_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_not_equal;
/* Fold comparison of the 2 ranges. */
bool
-operator_not_equal::fold_range (irange &r, const irange &op1,
+operator_not_equal::fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
/* We can be sure the values are always equal or not if both ranges
@@ -898,9 +434,9 @@ operator_not_equal::fold_range (irange &r, const irange &op1,
&& wi::eq_p (op2.lower_bound (), op2.upper_bound ()))
{
if (wi::ne_p (op1.lower_bound (), op2.upper_bound()))
- r = range_true ();
+ r = range_true (type);
else
- r = range_false ();
+ r = range_false (type);
}
else
{
@@ -908,9 +444,9 @@ operator_not_equal::fold_range (irange &r, const irange &op1,
we don;t really know anything for sure. */
r = range_intersect (op1, op2);
if (r.undefined_p ())
- r = range_true ();
+ r = range_true (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
}
return true;
@@ -918,10 +454,10 @@ operator_not_equal::fold_range (irange &r, const irange &op1,
/* Calculate the range of op1 being == to VAL based on LHS. */
bool
-operator_not_equal::op1_range (irange &r, const irange &lhs,
+operator_not_equal::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
/* If the result is true, the only time we know anything is if OP2 is
@@ -929,7 +465,7 @@ operator_not_equal::op1_range (irange &r, const irange &lhs,
if (wi::eq_p (op2.lower_bound(), op2.upper_bound()))
r = range_invert (op2);
else
- r.set_varying (op2.type ());
+ r.set_varying (type);
break;
case BRS_FALSE:
@@ -945,10 +481,10 @@ operator_not_equal::op1_range (irange &r, const irange &lhs,
bool
-operator_not_equal::op2_range (irange &r, const irange &lhs,
+operator_not_equal::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_not_equal::op1_range (r, lhs, op1);
+ return operator_not_equal::op1_range (r, type, lhs, op1);
}
@@ -995,49 +531,48 @@ build_ge (irange &r, tree type, const wide_int &val)
-class operator_lt : public trange_operator
+class operator_lt : public range_operator
{
public:
- operator_lt () : trange_operator (LT_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_lt;
bool
-operator_lt::fold_range (irange &r, const irange &op1, const irange &op2) const
+operator_lt::fold_range (irange &r, tree type, const irange &op1, const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
signop sign = TYPE_SIGN (op1.type ());
gcc_checking_assert (sign == TYPE_SIGN (op2.type ()));
if (wi::lt_p (op1.upper_bound (), op2.lower_bound (), sign))
- r = range_true ();
+ r = range_true (type);
else
if (!wi::lt_p (op1.lower_bound (), op2.upper_bound (), sign))
- r = range_false ();
+ r = range_false (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
return true;
}
bool
-operator_lt::op1_range (irange &r, const irange &lhs, const irange &op2) const
+operator_lt::op1_range (irange &r, tree type, const irange &lhs, const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- build_lt (r, op2.type (), op2.upper_bound ());
+ build_lt (r, type, op2.upper_bound ());
break;
case BRS_FALSE:
- build_ge (r, op2.type (), op2.lower_bound ());
+ build_ge (r, type, op2.lower_bound ());
break;
default:
@@ -1048,16 +583,16 @@ operator_lt::op1_range (irange &r, const irange &lhs, const irange &op2) const
bool
-operator_lt::op2_range (irange &r, const irange &lhs, const irange &op1) const
+operator_lt::op2_range (irange &r, tree type, const irange &lhs, const irange &op1) const
{
- switch (get_bool_state (r, lhs, op1.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_FALSE:
- build_le (r, op1.type (), op1.upper_bound ());
+ build_le (r, type, op1.upper_bound ());
break;
case BRS_TRUE:
- build_gt (r, op1.type (), op1.lower_bound ());
+ build_gt (r, type, op1.lower_bound ());
break;
default:
@@ -1067,48 +602,47 @@ operator_lt::op2_range (irange &r, const irange &lhs, const irange &op1) const
}
-class operator_le : public trange_operator
+class operator_le : public range_operator
{
public:
- operator_le () : trange_operator (LE_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_le;
bool
-operator_le::fold_range (irange &r, const irange &op1, const irange &op2) const
+operator_le::fold_range (irange &r, tree type, const irange &op1, const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
signop sign = TYPE_SIGN (op1.type ());
gcc_checking_assert (sign == TYPE_SIGN (op2.type ()));
if (wi::le_p (op1.upper_bound (), op2.lower_bound (), sign))
- r = range_true ();
+ r = range_true (type);
else
if (!wi::le_p (op1.lower_bound (), op2.upper_bound (), sign))
- r = range_false ();
+ r = range_false (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
return true;
}
bool
-operator_le::op1_range (irange &r, const irange &lhs, const irange &op2) const
+operator_le::op1_range (irange &r, tree type, const irange &lhs, const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- build_le (r, op2.type (), op2.upper_bound ());
+ build_le (r, type, op2.upper_bound ());
break;
case BRS_FALSE:
- build_gt (r, op2.type (), op2.lower_bound ());
+ build_gt (r, type, op2.lower_bound ());
break;
default:
@@ -1119,16 +653,16 @@ operator_le::op1_range (irange &r, const irange &lhs, const irange &op2) const
bool
-operator_le::op2_range (irange &r, const irange &lhs, const irange &op1) const
+operator_le::op2_range (irange &r, tree type, const irange &lhs, const irange &op1) const
{
- switch (get_bool_state (r, lhs, op1.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_FALSE:
- build_lt (r, op1.type (), op1.upper_bound ());
+ build_lt (r, type, op1.upper_bound ());
break;
case BRS_TRUE:
- build_ge (r, op1.type (), op1.lower_bound ());
+ build_ge (r, type, op1.lower_bound ());
break;
default:
@@ -1139,49 +673,48 @@ operator_le::op2_range (irange &r, const irange &lhs, const irange &op1) const
}
-class operator_gt : public trange_operator
+class operator_gt : public range_operator
{
public:
- operator_gt () : trange_operator (GT_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_gt;
bool
-operator_gt::fold_range (irange &r, const irange &op1, const irange &op2) const
+operator_gt::fold_range (irange &r, tree type, const irange &op1, const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
signop sign = TYPE_SIGN (op1.type ());
gcc_checking_assert (sign == TYPE_SIGN (op2.type ()));
if (wi::gt_p (op1.lower_bound (), op2.upper_bound (), sign))
- r = range_true ();
+ r = range_true (type);
else
if (!wi::gt_p (op1.upper_bound (), op2.lower_bound (), sign))
- r = range_false ();
+ r = range_false (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
return true;
}
bool
-operator_gt::op1_range (irange &r, const irange &lhs, const irange &op2) const
+operator_gt::op1_range (irange &r, tree type, const irange &lhs, const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- build_gt (r, op2.type (), op2.lower_bound ());
+ build_gt (r, type, op2.lower_bound ());
break;
case BRS_FALSE:
- build_le (r, op2.type (), op2.upper_bound ());
+ build_le (r, type, op2.upper_bound ());
break;
default:
@@ -1192,16 +725,16 @@ operator_gt::op1_range (irange &r, const irange &lhs, const irange &op2) const
bool
-operator_gt::op2_range (irange &r, const irange &lhs, const irange &op1) const
+operator_gt::op2_range (irange &r, tree type, const irange &lhs, const irange &op1) const
{
- switch (get_bool_state (r, lhs, op1.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_FALSE:
- build_ge (r, op1.type (), op1.lower_bound ());
+ build_ge (r, type, op1.lower_bound ());
break;
case BRS_TRUE:
- build_lt (r, op1.type (), op1.upper_bound ());
+ build_lt (r, type, op1.upper_bound ());
break;
default:
@@ -1212,49 +745,48 @@ operator_gt::op2_range (irange &r, const irange &lhs, const irange &op1) const
}
-class operator_ge : public trange_operator
+class operator_ge : public range_operator
{
public:
- operator_ge () : trange_operator (GE_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_ge;
bool
-operator_ge::fold_range (irange &r, const irange &op1, const irange &op2) const
+operator_ge::fold_range (irange &r, tree type, const irange &op1, const irange &op2) const
{
- if (empty_range_check (r, op1, op2, boolean_type_node))
+ if (empty_range_check (r, op1, op2, type))
return true;
signop sign = TYPE_SIGN (op1.type ());
gcc_checking_assert (sign == TYPE_SIGN (op2.type ()));
if (wi::ge_p (op1.lower_bound (), op2.upper_bound (), sign))
- r = range_true ();
+ r = range_true (type);
else
if (!wi::ge_p (op1.upper_bound (), op2.lower_bound (), sign))
- r = range_false ();
+ r = range_false (type);
else
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
return true;
}
bool
-operator_ge::op1_range (irange &r, const irange &lhs, const irange &op2) const
+operator_ge::op1_range (irange &r, tree type, const irange &lhs, const irange &op2) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_TRUE:
- build_ge (r, op2.type (), op2.lower_bound ());
+ build_ge (r, type, op2.lower_bound ());
break;
case BRS_FALSE:
- build_lt (r, op2.type (), op2.upper_bound ());
+ build_lt (r, type, op2.upper_bound ());
break;
default:
@@ -1265,16 +797,16 @@ operator_ge::op1_range (irange &r, const irange &lhs, const irange &op2) const
bool
-operator_ge::op2_range (irange &r, const irange &lhs, const irange &op1) const
+operator_ge::op2_range (irange &r, tree type, const irange &lhs, const irange &op1) const
{
- switch (get_bool_state (r, lhs, op1.type ()))
+ switch (get_bool_state (r, lhs, type))
{
case BRS_FALSE:
- build_gt (r, op1.type (), op1.lower_bound ());
+ build_gt (r, type, op1.lower_bound ());
break;
case BRS_TRUE:
- build_le (r, op1.type (), op1.upper_bound ());
+ build_le (r, type, op1.upper_bound ());
break;
default:
@@ -1284,78 +816,244 @@ operator_ge::op2_range (irange &r, const irange &lhs, const irange &op1) const
}
+// ----------------------------------------------------------------------------
-class operator_plus : public trange_operator
+class operator_plus : public range_operator
{
public:
- operator_plus () : trange_operator (PLUS_EXPR) { }
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
-
+ bool 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_plus;
+
+bool operator_plus::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
+{
+ wide_int new_lb, new_ub, tmp;
+ wi::overflow_type ov_lb, ov_ub;
+ signop s = TYPE_SIGN (type);
+
+ new_lb = wi::add (lh_lb, rh_lb, s, &ov_lb);
+ new_ub = wi::add (lh_ub, rh_ub, s, &ov_ub);
+ accumulate_range (r, type, new_lb, new_ub, ov_lb, ov_ub);
+ return true;
+}
+
/* Adjust irange to be in terms of op1.
Given [range] = op1 + val, op1 = [range] - val. */
bool
-operator_plus::op1_range (irange &r, const irange &lhs,
+operator_plus::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- return op_binary (MINUS_EXPR, r, lhs, op2);
+ return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op2);
}
bool
-operator_plus::op2_range (irange &r, const irange &lhs,
+operator_plus::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return op_binary (MINUS_EXPR, r, lhs, op1);
+ return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, lhs, op1);
}
+// ----------------------------------------------------------------------------
-class operator_minus : public trange_operator
+class operator_minus : public range_operator
{
public:
- operator_minus () : trange_operator (MINUS_EXPR) { }
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
+ bool 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_minus;
+
+bool
+operator_minus::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
+{
+ wide_int new_lb, new_ub, tmp;
+ wi::overflow_type ov_lb, ov_ub;
+ signop s = TYPE_SIGN (type);
+
+ new_lb = wi::sub (lh_lb, rh_ub, s, &ov_lb);
+ new_ub = wi::sub (lh_ub, rh_lb, s, &ov_ub);
+ accumulate_range (r, type, new_lb, new_ub, ov_lb, ov_ub);
+ return true;
+}
+
/* Adjust irange to be in terms of op1.
Given lhs = op1 - op2, op1 = lhs + op2. */
bool
-operator_minus::op1_range (irange &r, const irange &lhs,
+operator_minus::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- return op_binary (PLUS_EXPR, r, lhs, op2);
+ return range_op_handler (PLUS_EXPR, type)->fold_range (r, type, lhs, op2);
}
/* Adjust irange to be in terms of op2.
Given lhs = op1 - op2, -op2 = lhs - op1, therefore op2 = op1 - lhs. */
bool
-operator_minus::op2_range (irange &r, const irange &lhs,
+operator_minus::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return op_binary (MINUS_EXPR, r, op1 ,lhs);
+ return fold_range (r, type, op1, lhs);
+}
+
+// ----------------------------------------------------------------------------
+
+class operator_min : public range_operator
+{
+public:
+ bool 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_min;
+
+
+bool
+operator_min::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
+{
+
+ wide_int new_lb, new_ub;
+ signop s = TYPE_SIGN (type);
+
+ new_lb = wi::min (lh_lb, rh_lb, s);
+ new_ub = wi::min (lh_ub, rh_ub, s);
+ accumulate_range (r, type, new_lb, new_ub);
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+class operator_max : public range_operator
+{
+public:
+ bool 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_max;
+
+bool
+operator_max::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
+{
+
+ wide_int new_lb, new_ub;
+ signop s = TYPE_SIGN (type);
+
+ new_lb = wi::max (lh_lb, rh_lb, s);
+ new_ub = wi::max (lh_ub, rh_ub, s);
+ accumulate_range (r, type, new_lb, new_ub);
+ return true;
+}
+
+
+// ----------------------------------------------------------------------------
+
+class operator_mult : public range_operator
+{
+public:
+ bool 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_mult;
+
+
+bool
+operator_mult::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
+{
+ bool res;
+ wide_int new_lb, new_ub;
+ signop s = TYPE_SIGN (type);
+
+ if (TYPE_OVERFLOW_UNDEFINED (type))
+ res = wide_int_range_cross_product (new_lb, new_ub,
+ MULT_EXPR, s,
+ lh_lb, lh_ub, rh_lb, rh_ub, true);
+ else
+ res = wide_int_range_mult_wrapping (new_lb, new_ub,
+ s, TYPE_PRECISION (type),
+ lh_lb, lh_ub, rh_lb, rh_ub);
+ if (res)
+ {
+ accumulate_possibly_reversed_range (r, type, new_lb, new_ub);
+ return true;
+ }
+
+ return false;
+}
+
+
+// ----------------------------------------------------------------------------
+
+class operator_div : public range_operator
+{
+public:
+ operator_div (enum tree_code c) { code = c; }
+ bool 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;
+private:
+ enum tree_code code;
+};
+
+operator_div op_trunc_div (TRUNC_DIV_EXPR);
+operator_div op_floor_div(FLOOR_DIV_EXPR);
+operator_div op_round_div (ROUND_DIV_EXPR);
+operator_div op_ceil_div (CEIL_DIV_EXPR);
+
+bool
+operator_div::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
+{
+ wide_int new_lb, new_ub;
+ wide_int extra_min, extra_max;
+ bool extra_range_p;
+
+ /* If we know we will divide by zero, return an empty range,
+ which will be interpreted as undefined. */
+ if (rh_lb == 0 && rh_ub == 0)
+ return true;
+
+ if (wide_int_range_div (new_lb, new_ub, code, TYPE_SIGN (type),
+ TYPE_PRECISION (type),
+ lh_lb, lh_ub,
+ rh_lb, rh_ub,
+ TYPE_OVERFLOW_UNDEFINED (type),
+ extra_range_p, extra_min, extra_max))
+ {
+ accumulate_range (r, type, new_lb, new_ub);
+ if (extra_range_p)
+ accumulate_range (r, type, extra_min, extra_max);
+ return true;
+ }
+ return false;
}
-trange_operator op_mult (MULT_EXPR);
-trange_operator op_trunc_div (TRUNC_DIV_EXPR);
-trange_operator op_floor_div(FLOOR_DIV_EXPR);
-trange_operator op_round_div (ROUND_DIV_EXPR);
-trange_operator op_ceil_div (CEIL_DIV_EXPR);
-trange_operator op_pointer_plus (POINTER_PLUS_EXPR);
-trange_operator op_max (MAX_EXPR);
-trange_operator op_min (MIN_EXPR);
+// ----------------------------------------------------------------------------
-class operator_exact_divide : public trange_operator
+class operator_exact_divide : public operator_div
{
public:
- operator_exact_divide () : trange_operator (EXACT_DIV_EXPR) { }
- virtual bool op1_range (irange &r, const irange &lhs,
+ operator_exact_divide () : operator_div (EXACT_DIV_EXPR) { }
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
} op_exact_div;
@@ -1363,7 +1061,7 @@ public:
// Adjust irange to be in terms of op1.
bool
-operator_exact_divide::op1_range (irange &r,
+operator_exact_divide::op1_range (irange &r, tree type,
const irange &lhs,
const irange &op2) const
{
@@ -1374,91 +1072,132 @@ operator_exact_divide::op1_range (irange &r,
// TRUE accuraacy is [6,6][9,9][12,12]. This is unlikely to matter most of
// the time however.
// If op2 is a multiple of 2, we would be able to set some non-zero bits.
- if (op2.singleton_p (&offset) && op_binary (MULT_EXPR, r, lhs, op2)
+ if (op2.singleton_p (&offset)
+ && range_op_handler (MULT_EXPR, type)->fold_range (r, type, lhs, op2)
&& !integer_zerop (offset))
return true;
return false;
}
+// ----------------------------------------------------------------------------
-class operator_shift : public trange_operator
+class operator_lshift : public range_operator
{
public:
- operator_shift (enum tree_code c) : trange_operator (c) { }
- virtual bool op1_range (irange &r, const irange &lhs,
- const irange &op2) const;
-};
+ bool fold_range (irange& r, tree type, const irange& op1,
+ const irange& op2) const;
-operator_shift op_lshift (LSHIFT_EXPR);
-operator_shift op_rshift (RSHIFT_EXPR);
+ bool 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_lshift;
-bool
-operator_shift::op1_range (irange &r, const irange &lhs,
- const irange &op2) const
-{
- tree type = lhs.type ();
- wide_int w2;
- if (empty_range_check (r, lhs, op2, type))
- return true;
- /* FIXME: Andrew: You probably want to use
- wide_int_range_shift_undefined_p() here instead of these checks.
- But I'm not going to touch anything since this entire function
- isn't doing anything but keeping notes to entertain you in your
- sleepless nights. */
- // Negative shifts are undefined, as well as shift >= precision
- if (wi::lt_p (op2.lower_bound (), 0, TYPE_SIGN (op2.type ())))
- return false;
- if (wi::ge_p (op2.upper_bound (), TYPE_PRECISION (type), UNSIGNED))
+bool
+operator_lshift::fold_range (irange& r, tree type, const irange& op1,
+ const irange& op2) const
+{
+ // Check to see if the shift amount is undefined, and return if so.
+ if (op2.undefined_p () ||
+ wide_int_range_shift_undefined_p (TYPE_SIGN (op2.type ()),
+ TYPE_PRECISION (type),
+ op2.lower_bound (),
+ op2.upper_bound ()))
return false;
- return false;
-#if 0
- // Check if the calculation can be done without overflows.
- // and if so, adjust the bounds to allow for 1's that may have been shifted
- // out.
- wide_int mask;
- if (code == LSHIFT_EXPR)
+ // Otherwise just invoke the normal fold routine.
+ return range_operator::fold_range (r, type, op1, op2);
+
+}
+
+bool
+operator_lshift::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
+{
+ wide_int new_lb, new_ub;
+ signop s = TYPE_SIGN (type);
+
+ if (wide_int_range_lshift (new_lb, new_ub, s, TYPE_PRECISION (type),
+ lh_lb, lh_ub, rh_lb, rh_ub,
+ TYPE_OVERFLOW_UNDEFINED (type)))
{
- res = op_binary (RSHIFT_EXPR, r, lhs, w2);
- if (res)
- {
- mask = wi::mask (op, true, r.get_precision ());
- }
+ accumulate_possibly_reversed_range (r, type, new_lb, new_ub);
+ return true;
}
- else
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+//
+class operator_rshift : public range_operator
+{
+public:
+ bool fold_range (irange& r, tree type, const irange& op1,
+ const irange& op2) const;
+ bool 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_rshift;
+
+
+bool
+operator_rshift::fold_range (irange& r, tree type, const irange& op1,
+ const irange& op2) const
+{
+ // Check to see if the shift amount is undefined, and return if so.
+ if (op2.undefined_p () ||
+ wide_int_range_shift_undefined_p (TYPE_SIGN (op2.type ()),
+ TYPE_PRECISION (type),
+ op2.lower_bound (),
+ op2.upper_bound ()))
+ return false;
+
+ // Otherwise just invoke the normal fold routine.
+ return range_operator::fold_range (r, type, op1, op2);
+
+}
+bool
+operator_rshift::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
+{
+ wide_int new_lb, new_ub;
+ signop s = TYPE_SIGN (type);
+
+ if (wide_int_range_multiplicative_op (new_lb, new_ub,
+ RSHIFT_EXPR, s, TYPE_PRECISION (type),
+ lh_lb, lh_ub, rh_lb, rh_ub,
+ TYPE_OVERFLOW_UNDEFINED (type)))
{
- res = op_binary (LSHIFT_EXPR, r, lhs, w2);
+ accumulate_possibly_reversed_range (r, type, new_lb, new_ub);
+ return true;
}
-
- return res;
-#endif
+ return false;
}
+// ----------------------------------------------------------------------------
-class operator_cast: public trange_operator
+
+class operator_cast: public range_operator
{
public:
- operator_cast (enum tree_code code) : trange_operator (code) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
-};
-
-operator_cast op_nop (NOP_EXPR);
-operator_cast op_convert (CONVERT_EXPR);
+} op_convert;
/* Return the range of lh converted to the type of rh:
r = (type_of(rh)) lh. */
bool
-operator_cast::fold_range (irange &r, const irange &lh, const irange &rh) const
+operator_cast::fold_range (irange &r, tree type, const irange &lh, const irange &rh) const
{
- if (empty_range_check (r, lh, rh, rh.type ()))
+ if (empty_range_check (r, lh, rh, type))
return true;
if (lh.type () != rh.type ())
@@ -1476,29 +1215,29 @@ operator_cast::fold_range (irange &r, const irange &lh, const irange &rh) const
}
bool
-operator_cast::op1_range (irange &r, const irange &lhs,
+operator_cast::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
tree lhs_type = lhs.type ();
- tree op2_type = op2.type ();
+ gcc_checking_assert (op2.type() == type);
irange op_type;
/* If the precision of the LHS is smaller than the precision of the RHS,
then there would be truncation of the value on the RHS, and so we can tell
nothing about it. */
- if (TYPE_PRECISION (lhs_type) < TYPE_PRECISION (op2_type))
+ if (TYPE_PRECISION (lhs_type) < TYPE_PRECISION (type))
{
/* If we've been passed an actual value for the RHS rather than the type
see if it fits the LHS, and if so, then we can allow it. */
r = op2;
r.cast (lhs_type);
- r.cast (op2_type);
+ r.cast (type);
if (r == op2)
{
/* We know the value of the RHS fits in the LHS type, so convert the
left hand side and remove any values that arent in OP2. */
r = lhs;
- r.cast (op2_type);
+ r.cast (type);
r.intersect (op2);
return true;
}
@@ -1515,12 +1254,12 @@ operator_cast::op1_range (irange &r, const irange &lhs,
/* Boolean casts are weird in GCC. It's actually an implied mask with
0x01, so all that is known is whether the rightmost bit is 0 or 1,
which implies the only value *not* in the RHS is 0 or -1. */
- unsigned prec = TYPE_PRECISION (op2_type);
+ unsigned prec = TYPE_PRECISION (type);
if (lhs.zero_p ())
- r = irange (VR_ANTI_RANGE, op2_type,
+ r = irange (VR_ANTI_RANGE, type,
wi::minus_one (prec), wi::minus_one (prec));
else
- r = irange (VR_ANTI_RANGE, op2_type,
+ r = irange (VR_ANTI_RANGE, type,
wi::zero (prec), wi::zero (prec));
/* And intersect it with what we know about op2. */
r.intersect (op2);
@@ -1533,10 +1272,10 @@ operator_cast::op1_range (irange &r, const irange &lhs,
/* If the LHS precision is greater than the rhs precision, the LHS range
is resticted to the range of the RHS by this assignment. */
- if (TYPE_PRECISION (lhs_type) > TYPE_PRECISION (op2_type))
+ if (TYPE_PRECISION (lhs_type) > TYPE_PRECISION (type))
{
/* Cast the range of the RHS to the type of the LHS. */
- op_type.set_varying (op2_type);
+ op_type.set_varying (type);
op_type.cast (lhs_type);
/* Intersect this with the LHS range will produce the RHS range. */
@@ -1546,7 +1285,7 @@ operator_cast::op1_range (irange &r, const irange &lhs,
r = lhs;
/* Cast the calculated range to the type of the RHS. */
- r.cast (op2.type ());
+ r.cast (type);
return true;
}
@@ -1555,30 +1294,29 @@ operator_cast::op1_range (irange &r, const irange &lhs,
// Bitwise and logical ops.
-class operator_logical_and : public trange_operator
+class operator_logical_and : public range_operator
{
public:
- operator_logical_and () : trange_operator (TRUTH_AND_EXPR) { }
- virtual bool fold_range (irange &r, const irange &lh, const irange &rh) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool fold_range (irange &r, tree type, const irange &lh, const irange &rh) const;
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_logical_and;
bool
-operator_logical_and::fold_range (irange &r, const irange &lh,
+operator_logical_and::fold_range (irange &r, tree type, const irange &lh,
const irange &rh) const
{
- if (empty_range_check (r, lh, rh, boolean_type_node))
+ if (empty_range_check (r, lh, rh, type))
return true;
// 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)))
{
- r = range_false ();
+ r = range_false (type);
return true;
}
@@ -1587,9 +1325,9 @@ operator_logical_and::fold_range (irange &r, const irange &lh,
if (lh.contains_p (build_zero_cst (lh.type ()))
|| rh.contains_p (build_zero_cst (rh.type ())))
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
else
- r = range_true ();
+ r = range_true (type);
return true;
}
@@ -1597,79 +1335,116 @@ operator_logical_and::fold_range (irange &r, const irange &lh,
bool
-operator_logical_and::op1_range (irange &r, const irange &lhs,
- const irange &op2) const
+operator_logical_and::op1_range (irange &r, tree type, const irange &lhs,
+ const irange &op2 ATTRIBUTE_UNUSED) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
/* A true result means both sides of the AND must be true. */
case BRS_TRUE:
- r = range_true ();
+ r = range_true (type);
break;
/* Any other result means only one side has to be false, the other
side can be anything. SO we cant be sure of any result here. */
default:
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
break;
}
return true;
}
bool
-operator_logical_and::op2_range (irange &r, const irange &lhs,
+operator_logical_and::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_logical_and::op1_range (r, lhs, op1);
+ return operator_logical_and::op1_range (r, type, lhs, op1);
}
-class operator_bitwise_and : public trange_operator
+class operator_bitwise_and : public range_operator
{
public:
- operator_bitwise_and () : trange_operator (BIT_AND_EXPR) { }
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
+ bool 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_bitwise_and;
+
+bool
+operator_bitwise_and::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
+{
+ wide_int new_lb, new_ub, tmp;
+ signop s = TYPE_SIGN (type);
+
+ wide_int may_be_nonzero_lh, must_be_nonzero_lh;
+ wide_int may_be_nonzero_rh, must_be_nonzero_rh;
+ wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
+ may_be_nonzero_lh,
+ must_be_nonzero_lh);
+ wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
+ may_be_nonzero_rh,
+ must_be_nonzero_rh);
+ if (wide_int_range_bit_and (new_lb, new_ub, s, TYPE_PRECISION (type),
+ lh_lb, lh_ub,
+ rh_lb, rh_ub,
+ must_be_nonzero_lh,
+ may_be_nonzero_lh,
+ must_be_nonzero_rh,
+ may_be_nonzero_rh))
+ {
+ // For AND, calculate each subrange separately, and then union
+ // the results.
+ irange tmp;
+ tmp.set_undefined (type);
+ accumulate_range (tmp, type, new_lb, new_ub);
+ r.union_ (tmp);
+ return true;
+ }
+ return false;
+}
+
bool
-operator_bitwise_and::op1_range (irange &r, const irange &lhs,
+operator_bitwise_and::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- /* If this is really a logical operation, call that. */
- if (types_compatible_p (lhs.type (), boolean_type_node))
- return op_logical_and.op1_range (r, lhs, op2);
+ /* If this is really a logical wi_fold, call that. */
+ if (types_compatible_p (type, boolean_type_node))
+ return op_logical_and.op1_range (r, type, lhs, op2);
/* For now do nothing with bitwise AND of iranges, just return the type. */
- r.set_varying (lhs.type ());
+ r.set_varying (type);
return true;
}
bool
-operator_bitwise_and::op2_range (irange &r, const irange &lhs,
+operator_bitwise_and::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_bitwise_and::op1_range (r, lhs, op1);
+ return operator_bitwise_and::op1_range (r, type, lhs, op1);
}
-class operator_logical_or : public trange_operator
+class operator_logical_or : public range_operator
{
public:
- operator_logical_or () : trange_operator (TRUTH_OR_EXPR) { }
- virtual bool fold_range (irange &r, const irange &lh, const irange &rh) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool fold_range (irange &r, tree type, const irange &lh, const irange &rh) const;
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
} op_logical_or;
bool
-operator_logical_or::fold_range (irange &r, const irange &lh,
+operator_logical_or::fold_range (irange &r, tree type, const irange &lh,
const irange &rh) const
{
- if (empty_range_check (r, lh, rh, boolean_type_node))
+ if (empty_range_check (r, lh, rh, type))
return true;
r = range_union (lh, rh);
@@ -1677,83 +1452,165 @@ operator_logical_or::fold_range (irange &r, const irange &lh,
}
bool
-operator_logical_or::op1_range (irange &r, const irange &lhs,
- const irange &op2) const
+operator_logical_or::op1_range (irange &r, tree type, const irange &lhs,
+ const irange &op2 ATTRIBUTE_UNUSED) const
{
- switch (get_bool_state (r, lhs, op2.type ()))
+ switch (get_bool_state (r, lhs, type))
{
/* A false result means both sides of the OR must be false. */
case BRS_FALSE:
- r = range_false ();
+ r = range_false (type);
break;
/* Any other result means only one side has to be true, the other
side can be anything. SO we cant be sure of any result here. */
default:
- r.set_varying (boolean_type_node);
+ r = range_true_and_false (type);
break;
}
return true;
}
bool
-operator_logical_or::op2_range (irange &r, const irange &lhs,
+operator_logical_or::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_logical_or::op1_range (r, lhs, op1);
+ return operator_logical_or::op1_range (r, type, lhs, op1);
}
-class operator_bitwise_or : public trange_operator
+class operator_bitwise_or : public range_operator
{
public:
- operator_bitwise_or () : trange_operator (BIT_IOR_EXPR) { }
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const;
+ bool 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_bitwise_or;
+
+bool
+operator_bitwise_or::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
+{
+ wide_int new_lb, new_ub, tmp;
+ signop s = TYPE_SIGN (type);
+
+ wide_int may_be_nonzero_lh, must_be_nonzero_lh;
+ wide_int may_be_nonzero_rh, must_be_nonzero_rh;
+ wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
+ may_be_nonzero_lh,
+ must_be_nonzero_lh);
+ wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
+ may_be_nonzero_rh,
+ must_be_nonzero_rh);
+ if (wide_int_range_bit_ior (new_lb, new_ub, s,
+ lh_lb, lh_ub,
+ rh_lb, rh_ub,
+ must_be_nonzero_lh,
+ may_be_nonzero_lh,
+ must_be_nonzero_rh,
+ may_be_nonzero_rh))
+ {
+ accumulate_range (r, type, new_lb, new_ub);
+ return true;
+ }
+ return false;
+}
+
bool
-operator_bitwise_or::op1_range (irange &r, const irange &lhs,
+operator_bitwise_or::op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
{
- /* If this is really a logical operation, call that. */
- if (types_compatible_p (lhs.type (), boolean_type_node))
- return op_logical_or.op1_range (r, lhs, op2);
+ /* If this is really a logical wi_fold, call that. */
+ if (types_compatible_p (type, boolean_type_node))
+ return op_logical_or.op1_range (r, type, lhs, op2);
/* For now do nothing with bitwise OR of iranges, just return the type. */
- r.set_varying (lhs.type ());
+ r.set_varying (type);
return true;
}
bool
-operator_bitwise_or::op2_range (irange &r, const irange &lhs,
+operator_bitwise_or::op2_range (irange &r, tree type, const irange &lhs,
const irange &op1) const
{
- return operator_bitwise_or::op1_range (r, lhs, op1);
+ return operator_bitwise_or::op1_range (r, type, lhs, op1);
}
-class operator_bitwise_xor : public trange_operator
+class operator_bitwise_xor : public range_operator
{
public:
- operator_bitwise_xor (): trange_operator (BIT_XOR_EXPR) { }
- /* FIXME: Andrew can implement the op1_range and op2_range variants
- when he returns from leave :-P. */
+ bool 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_bitwise_xor;
-class operator_trunc_mod : public trange_operator
+
+bool
+operator_bitwise_xor::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
+{
+ wide_int new_lb, new_ub, tmp;
+ signop s = TYPE_SIGN (type);
+
+ wide_int may_be_nonzero_lh, must_be_nonzero_lh;
+ wide_int may_be_nonzero_rh, must_be_nonzero_rh;
+ wide_int_range_set_zero_nonzero_bits (s, lh_lb, lh_ub,
+ may_be_nonzero_lh,
+ must_be_nonzero_lh);
+ wide_int_range_set_zero_nonzero_bits (s, rh_lb, rh_ub,
+ may_be_nonzero_rh,
+ must_be_nonzero_rh);
+ if (wide_int_range_bit_xor (new_lb, new_ub, s, TYPE_PRECISION (type),
+ must_be_nonzero_lh,
+ may_be_nonzero_lh,
+ must_be_nonzero_rh,
+ may_be_nonzero_rh))
+ {
+ accumulate_range (r, type, new_lb, new_ub);
+ return true;
+ }
+ return false;
+}
+
+
+class operator_trunc_mod : public range_operator
{
public:
- operator_trunc_mod (): trange_operator (TRUNC_MOD_EXPR) { }
- /* FIXME: Andrew can implement the op1_range and op2_range variants
- when he returns from leave :-P. */
+ bool 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_trunc_mod;
-class operator_logical_not : public trange_operator
+
+bool
+operator_trunc_mod::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
+{
+ wide_int new_lb, new_ub, tmp;
+ signop s = TYPE_SIGN (type);
+
+ /* Mod 0 is undefined, so no need to accumulate a range. */
+ if (wide_int_range_zero_p (rh_lb, rh_ub, TYPE_PRECISION (type)))
+ return true;
+
+ wide_int_range_trunc_mod (new_lb, new_ub, s, TYPE_PRECISION (type),
+ lh_lb, lh_ub, rh_lb, rh_ub);
+ accumulate_range (r, type, new_lb, new_ub);
+ return true;
+}
+
+
+class operator_logical_not : public range_operator
{
public:
- operator_logical_not () : trange_operator (TRUTH_NOT_EXPR) { }
- virtual bool fold_range (irange &r, const irange &lh, const irange &rh) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool fold_range (irange &r, tree type, const irange &lh, const irange &rh) const;
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
} op_logical_not;
@@ -1772,22 +1629,24 @@ public:
which is the result we are looking for.. so.. pass it thru. */
bool
-operator_logical_not::fold_range (irange &r, const irange &lh,
+operator_logical_not::fold_range (irange &r, tree type, const irange &lh,
const irange &rh ATTRIBUTE_UNUSED) const
{
- if (empty_range_check (r, lh, rh, boolean_type_node))
+ if (empty_range_check (r, lh, rh, type))
return true;
if (lh.varying_p () || lh.undefined_p ())
r = lh;
else
r = range_invert (lh);
+ gcc_checking_assert (lh.type() == type);
return true;
}
bool
-operator_logical_not::op1_range (irange &r, const irange &lhs,
- const irange &op2 ATTRIBUTE_UNUSED) const
+operator_logical_not::op1_range (irange &r, tree type ATTRIBUTE_UNUSED,
+ const irange &lhs,
+ const irange &op2 ATTRIBUTE_UNUSED) const
{
if (lhs.varying_p () || lhs.undefined_p ())
r = lhs;
@@ -1797,20 +1656,18 @@ operator_logical_not::op1_range (irange &r, const irange &lhs,
}
-class operator_bitwise_not : public trange_operator
+class operator_bitwise_not : public range_operator
{
public:
- operator_bitwise_not () : trange_operator (BIT_NOT_EXPR) { }
- virtual bool fold_range (irange &r, const irange &lh, const irange &rh) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool fold_range (irange &r, tree type, const irange &lh, const irange &rh) const;
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
} op_bitwise_not;
bool
-operator_bitwise_not::fold_range (irange &r, const irange &lh,
+operator_bitwise_not::fold_range (irange &r, tree type, const irange &lh,
const irange &rh) const
{
- tree type = lh.type ();
if (empty_range_check (r, lh, rh, type))
return true;
@@ -1818,114 +1675,108 @@ operator_bitwise_not::fold_range (irange &r, const irange &lh,
irange minusone (type,
wi::minus_one (TYPE_PRECISION (type)),
wi::minus_one (TYPE_PRECISION (type)));
- return op_binary (MINUS_EXPR, r, minusone, lh);
+ return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, minusone, lh);
+// return op_binary (MINUS_EXPR, r, type, minusone, lh);
}
bool
-operator_bitwise_not::op1_range (irange &r, const irange &lhs,
- const irange &op2 ATTRIBUTE_UNUSED) const
+operator_bitwise_not::op1_range (irange &r, tree type, const irange &lhs,
+ const irange &op2) const
{
- tree type = lhs.type ();
-
// ~X is -1 - X and since bitwise NOT is involutary...do it again.
- irange minusone (type,
- wi::minus_one (TYPE_PRECISION (type)),
- wi::minus_one (TYPE_PRECISION (type)));
- return op_binary (MINUS_EXPR, r, minusone, lhs);
+ return fold_range (r, type, lhs, op2);
}
/* ---------------------------------------------------------------------- */
-class operator_cst : public trange_operator
+class operator_cst : public range_operator
{
public:
- operator_cst () : trange_operator (INTEGER_CST) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
} op_integer_cst;
bool
-operator_cst::fold_range (irange &r, const irange &lh,
+operator_cst::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
+ const irange &lh,
const irange &rh ATTRIBUTE_UNUSED) const
{
r = lh;
return true;
}
+/* ---------------------------------------------------------------------- */
-class operator_ssa_name : public trange_operator
+
+class operator_identity : public range_operator
{
public:
- operator_ssa_name () : trange_operator (SSA_NAME) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
-} op_ssa_name;
+} op_identity;
bool
-operator_ssa_name::fold_range (irange &r, const irange &lh,
- const irange &rh ATTRIBUTE_UNUSED) const
+operator_identity::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
+ const irange &lh,
+ const irange &rh ATTRIBUTE_UNUSED) const
{
r = lh;
return true;
}
bool
-operator_ssa_name::op1_range (irange &r, const irange &lhs,
- const irange &op2 ATTRIBUTE_UNUSED) const
+operator_identity::op1_range (irange &r, tree type ATTRIBUTE_UNUSED,
+ const irange &lhs,
+ const irange &op2 ATTRIBUTE_UNUSED) const
{
r = lhs;
return true;
}
-// Unary identity function.
-class operator_identity : public trange_operator
-{
- public:
- operator_identity (enum tree_code c) : trange_operator (c) { }
- virtual bool fold_range (irange &r, const irange &op1,
- const irange &op2 ATTRIBUTE_UNUSED) const
- { r = op1; return false; }
- virtual bool op1_range (irange &r, const irange &lhs,
- const irange &op2 ATTRIBUTE_UNUSED) const
- { r = lhs; return false; }
-} op_paren (PAREN_EXPR), op_obj_type_ref (OBJ_TYPE_REF);
+/* ---------------------------------------------------------------------- */
-class operator_abs : public trange_operator
+class operator_abs : public range_operator
{
public:
- operator_abs (enum tree_code code) : trange_operator (code) { }
- virtual bool fold_range (irange &r, const irange &op1,
- const irange &op2) const;
- virtual bool op1_range (irange &r,
- const irange &lhs, const irange &op2) const;
-} op_abs (ABS_EXPR), op_absu (ABSU_EXPR);
+ bool 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;
+ virtual bool op1_range (irange &r, tree type,
+ const irange &lhs, const irange &op2) const;
+} op_abs;
+
bool
-operator_abs::fold_range (irange &r,
- const irange &lh, const irange &rh) const
+operator_abs::wi_fold (irange &r, tree type,
+ const wide_int &lh_lb, const wide_int &lh_ub,
+ const wide_int &rh_lb ATTRIBUTE_UNUSED,
+ const wide_int &rh_ub ATTRIBUTE_UNUSED) const
{
- tree type = rh.type ();
- if (empty_range_check (r, lh, rh, type))
- return true;
+ wide_int new_lb, new_ub, tmp;
- return op_unary (code, r, lh, type);
+ if (wide_int_range_abs (new_lb, new_ub,
+ TYPE_SIGN (type),
+ TYPE_PRECISION (type),
+ lh_lb, lh_ub,
+ TYPE_OVERFLOW_UNDEFINED (type)))
+ {
+ r.union_ (irange (type, new_lb, new_ub));
+ return true;
+ }
+ return false;
}
+
bool
-operator_abs::op1_range (irange &r,
- const irange &lhs, const irange &op2) const
+operator_abs::op1_range (irange &r, tree type,
+ const irange &lhs, const irange &op2) const
{
- // FIXME: ?? Andrew TODO ??
- if (code == ABSU_EXPR)
- return false;
-
- tree type = lhs.type ();
if (empty_range_check (r, lhs, op2, type))
return true;
if (TYPE_UNSIGNED (type))
@@ -1946,151 +1797,393 @@ operator_abs::op1_range (irange &r,
return true;
}
-class operator_negate : public trange_operator
+class operator_absu : public range_operator
+{
+ public:
+ bool 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_absu;
+
+bool
+operator_absu::wi_fold (irange &r, tree type,
+ const wide_int &lh_lb, const wide_int &lh_ub,
+ const wide_int &rh_lb ATTRIBUTE_UNUSED,
+ const wide_int &rh_ub ATTRIBUTE_UNUSED) const
+{
+ wide_int new_lb, new_ub;
+
+ /* Pass through VR0 the easy cases. */
+ if (wi::ges_p (lh_lb, 0))
+ {
+ new_lb = lh_lb;
+ new_ub = lh_ub;
+ }
+ else
+ {
+ new_lb = wi::abs (lh_lb);
+ new_ub = wi::abs (lh_ub);
+
+ /* If the range contains zero then we know that the minimum value in the
+ range will be zero. */
+ if (wi::ges_p (lh_ub, 0))
+ {
+ if (wi::gtu_p (new_lb, new_ub))
+ new_ub = new_lb;
+ new_lb = wi::zero (TYPE_PRECISION (type));
+ }
+ else
+ /* Otherwise, swap MIN and MAX. */
+ std::swap (new_lb, new_ub);
+ }
+
+// r.union_ (irange (unsigned_type_for (type), new_lb, new_ub));
+ gcc_checking_assert (TYPE_UNSIGNED (type));
+ r.union_ (irange (type, new_lb, new_ub));
+ return true;
+}
+
+class operator_negate : public range_operator
{
public:
- operator_negate () : trange_operator (NEGATE_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const
- { return fold_range (r, lhs, op2); } // NEGATE is involutory :-P.
+ { return fold_range (r, type, lhs, op2); } // NEGATE is involutory :-P.
} op_negate;
/* Return the negated range of lh with the type of rh. */
bool
-operator_negate::fold_range (irange &r,
+operator_negate::fold_range (irange &r, tree type,
const irange &lh, const irange &rh) const
{
- tree type = rh.type ();
if (empty_range_check (r, lh, rh, type))
return true;
// -X is simply 0 - X.
- return op_binary (MINUS_EXPR, r, range_zero (type), lh);
+ return range_op_handler (MINUS_EXPR, type)->fold_range (r, type, range_zero (type),
+ lh);
}
-// Disable for now for VRP parity.
-#if 0
-class operator_min_max : public trange_operator
+class operator_addr_expr : public range_operator
{
public:
- operator_min_max (tree_code c) : trange_operator (c) { }
- virtual bool fold_range (irange &r, const irange &op1,
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
const irange &op2) const;
- virtual bool op2_range (irange &r, const irange &lhs,
- const irange &op1) const;
-} op_min (MIN_EXPR), op_max (MAX_EXPR);
-
+} op_addr;
bool
-operator_min_max::fold_range (irange &r, const irange &lh,
- const irange &rh) const
+operator_addr_expr::fold_range (irange &r, tree type, const irange &lh,
+ const irange &rh) const
{
- wide_int lb, ub;
- wi::overflow_type ov;
- tree type = lh.type ();
-
if (empty_range_check (r, lh, rh, type))
return true;
- // Start with the union of both ranges.
- r = range_union (lh, rh);
-
- // For pointer types we are concerned with NULL and NON-NULL.
- // Min max result in this case is a strict union.
- if (POINTER_TYPE_P (type))
- return true;
-
- // Intersect the union with the max/min values of both to get a set
- // of values. This allows MIN ([1,5][20,30] , [0,4][18,60]) to
- // produce [0,5][18,30] rather than [0,30]
- wide_int_binop (lb, code, lh.lower_bound (), rh.lower_bound (),
- TYPE_SIGN (type), &ov);
- wide_int_binop (ub, code, lh.upper_bound (), rh.upper_bound (),
- TYPE_SIGN (type), &ov);
- r.intersect (irange (type, lb, ub));
+ // Return a non-null pointer of the LHS type (passed in op2)
+ if (lh.zero_p ())
+ r = range_zero (type);
+ else
+ if (!lh.contains_p (build_zero_cst (lh.type ())))
+ r = range_nonzero (type);
+ else
+ return false;
return true;
}
+// The same functionality for fold() applies to op1_range...
+// effectively copying the non-nullness.
bool
-operator_min_max::op1_range (irange &r, const irange &lhs,
- const irange &op2) const
+operator_addr_expr::op1_range (irange &r, tree type, const irange &lhs,
+ const irange &op2) const
{
- if (empty_range_check (r, lhs, op2, lhs.type ()))
- return true;
+ return operator_addr_expr::fold_range (r, type, lhs, op2);
+}
- if (POINTER_TYPE_P (lhs.type ()))
- return false;
+// ----------------------------------------------------------------------
- // Until this can be examined closer... Im not convinces this is right
- return false;
+// ---------------------------------------------------------------------------
- wide_int lb = lhs.lower_bound ();
- wide_int ub = lhs.upper_bound ();
- if (code == MIN_EXPR)
- {
- // If the upper bound is set by the other operand, we have no idea
- // what this upper could be, otherwise it HAS to be the upper bound.
- if (wi::eq_p (lhs.upper_bound (), op2.upper_bound ()))
- ub = max_limit (lhs.type ());
- }
+class pointer_plus_operator : public range_operator
+{
+public:
+ virtual bool 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_pointer_plus;
+
+
+bool
+pointer_plus_operator::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
+{
+ unsigned prec = lh_lb.get_precision ();
+ signop sign = TYPE_SIGN (type);
+ /* For pointer types, we are really only interested in asserting
+ whether the expression evaluates to non-NULL.
+
+ With -fno-delete-null-pointer-checks we need to be more
+ conservative. As some object might reside at address 0,
+ then some offset could be added to it and the same offset
+ subtracted again and the result would be NULL.
+ E.g.
+ static int a[12]; where &a[0] is NULL and
+ ptr = &a[6];
+ ptr -= 6;
+ ptr will be NULL here, even when there is POINTER_PLUS_EXPR
+ where the first range doesn't include zero and the second one
+ doesn't either. As the second operand is sizetype (unsigned),
+ consider all ranges where the MSB could be set as possible
+ subtractions where the result might be NULL. */
+ if ((!wide_int_range_includes_zero_p (lh_lb, lh_ub, sign)
+ || !wide_int_range_includes_zero_p (rh_lb, rh_ub, sign))
+ && !TYPE_OVERFLOW_WRAPS (type)
+ && (flag_delete_null_pointer_checks
+ || !wi::sign_mask (rh_ub)))
+ r.union_ (range_nonzero (type));
+ else if (wide_int_range_zero_p (lh_lb, lh_ub, prec)
+ && wide_int_range_zero_p (rh_lb, rh_ub, prec))
+ r.union_ (range_zero (type));
else
- {
- // this operand. Otherwise, it could be any value to MAX_TYPE as the
- // upper bound comes from the other operand.
- if (wi::eq_p (lhs.lower_bound (), op2.lower_bound ()))
- lb = min_limit (lhs.type ());
- }
+ r.set_varying (type);
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+
+class pointer_min_max_operator : public range_operator
+{
+public:
+ virtual bool 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_ptr_min_max;
+
+
+bool
+pointer_min_max_operator::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
+{
+ /* For MIN/MAX expressions with pointers, we only care about
+ nullness, if both are non null, then the result is nonnull.
+ If both are null, then the result is null. Otherwise they
+ are varying. */
+
+ unsigned prec = lh_lb.get_precision ();
+ signop sign = TYPE_SIGN (type);
- r = irange (lhs.type (), lb, ub);
+ if (!wide_int_range_includes_zero_p (lh_lb, lh_ub, sign)
+ && !wide_int_range_includes_zero_p (rh_lb, rh_ub, sign))
+ r.union_ (range_nonzero (type));
+ else if (wide_int_range_zero_p (lh_lb, lh_ub, prec)
+ && wide_int_range_zero_p (rh_lb, rh_ub, prec))
+ r.union_ (range_zero (type));
+ else
+ r.set_varying (type);
return true;
}
-bool
-operator_min_max::op2_range (irange &r, const irange &lhs,
- const irange &op1) const
+// ---------------------------------------------------------------------------
+//
+class pointer_and_operator : public range_operator
+{
+public:
+ virtual bool 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_pointer_and;
+
+
+bool
+pointer_and_operator::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
{
- return operator_min_max::op1_range (r, lhs, op1);
+ unsigned prec = lh_lb.get_precision ();
+ signop sign = TYPE_SIGN (type);
+
+ /* For pointer types, we are really only interested in asserting
+ whether the expression evaluates to non-NULL. */
+
+ if (!wide_int_range_includes_zero_p (lh_lb, lh_ub, sign)
+ && !wide_int_range_includes_zero_p (rh_lb, rh_ub, sign))
+ r.union_ (range_nonzero (type));
+ else if (wide_int_range_zero_p (lh_lb, lh_ub, prec)
+ || wide_int_range_zero_p (lh_lb, lh_ub, prec))
+ r.union_ (range_zero (type));
+ else
+ r.set_varying (type);
+ return true;
}
-#endif // if 0
-class operator_addr_expr : public trange_operator
+
+// -------------------------------------------------------------------------
+
+
+class pointer_or_operator : public range_operator
{
public:
- operator_addr_expr () : trange_operator (ADDR_EXPR) { }
- virtual bool fold_range (irange &r, const irange &op1,
- const irange &op2) const;
- virtual bool op1_range (irange &r, const irange &lhs,
- const irange &op2) const;
-} op_addr;
+ virtual bool 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_pointer_or;
-bool
-operator_addr_expr::fold_range (irange &r, const irange &lh,
- const irange &rh) const
+
+bool
+pointer_or_operator::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
{
- if (empty_range_check (r, lh, rh, rh.type ()))
- return true;
+ unsigned prec = lh_lb.get_precision ();
+ signop sign = TYPE_SIGN (type);
- // Return a non-null pointer of the LHS type (passed in op2)
- if (lh.zero_p ())
- r = range_zero (rh.type ());
+ /* For pointer types, we are really only interested in asserting
+ whether the expression evaluates to non-NULL. */
+
+ if (!wide_int_range_includes_zero_p (lh_lb, lh_ub, sign)
+ && !wide_int_range_includes_zero_p (rh_lb, rh_ub, sign))
+ r.union_ (range_nonzero (type));
+ else if (wide_int_range_zero_p (lh_lb, lh_ub, prec)
+ && wide_int_range_zero_p (rh_lb, rh_ub, prec))
+ r.union_ (range_zero (type));
else
- if (!lh.contains_p (build_zero_cst (lh.type ())))
- r = range_nonzero (rh.type ());
- else
- return false;
+ r.set_varying (type);
return true;
}
-// The same functionality for fold() applies to op1_range...
-// effectively copying the non-nullness.
-bool
-operator_addr_expr::op1_range (irange &r, const irange &lhs,
- const irange &op2) const
+
+// -------------------------------------------------------------------------
+
+// This implements the range operator tables as local objects in this file.
+
+class range_op_table
{
- return operator_addr_expr::fold_range (r, lhs, op2);
+public:
+ inline range_operator *operator[] (enum tree_code code);
+protected:
+ void set (enum tree_code code, range_operator &op);
+private:
+ range_operator *m_range_tree[MAX_TREE_CODES];
+};
+
+// Return a pointer to the range_operator instance, if there is one,
+// associated with tree_code CODE.
+
+range_operator *
+range_op_table::operator[] (enum tree_code code)
+{
+ gcc_assert (code > 0 && code < MAX_TREE_CODES);
+ return m_range_tree[code];
}
+// Add OP to the handler table for CODE.
+
+void
+range_op_table::set (enum tree_code code, range_operator &op)
+{
+ gcc_checking_assert (m_range_tree[code] == NULL);
+ m_range_tree[code] = &op;
+}
+
+// Instantiate a range op table for integral operations.
+class integral_table : public range_op_table
+{
+public:
+ integral_table ();
+} integral_tree_table;
+
+integral_table::integral_table ()
+{
+ set (EQ_EXPR, op_equal);
+ set (NE_EXPR, op_not_equal);
+ set (LT_EXPR, op_lt);
+ set (LE_EXPR, op_le);
+ set (GT_EXPR, op_gt);
+ set (GE_EXPR, op_ge);
+ set (PLUS_EXPR, op_plus);
+ set (MINUS_EXPR, op_minus);
+ set (MIN_EXPR, op_min);
+ set (MAX_EXPR, op_max);
+ set (MULT_EXPR, op_mult);
+ set (TRUNC_DIV_EXPR, op_trunc_div);
+ set (FLOOR_DIV_EXPR, op_floor_div);
+ set (ROUND_DIV_EXPR, op_round_div);
+ set (CEIL_DIV_EXPR, op_ceil_div);
+ set (EXACT_DIV_EXPR, op_exact_div);
+ set (LSHIFT_EXPR, op_lshift);
+ set (RSHIFT_EXPR, op_rshift);
+ set (NOP_EXPR, op_convert);
+ set (CONVERT_EXPR, op_convert);
+ set (TRUTH_AND_EXPR, op_logical_and);
+ set (BIT_AND_EXPR, op_bitwise_and);
+ set (TRUTH_OR_EXPR, op_logical_or);
+ set (BIT_IOR_EXPR, op_bitwise_or);
+ set (BIT_XOR_EXPR, op_bitwise_xor);
+ set (TRUNC_MOD_EXPR, op_trunc_mod);
+ set (TRUTH_NOT_EXPR, op_logical_not);
+ set (BIT_NOT_EXPR, op_bitwise_not);
+ set (INTEGER_CST, op_integer_cst);
+ set (SSA_NAME, op_identity);
+ set (PAREN_EXPR, op_identity);
+ set (OBJ_TYPE_REF, op_identity);
+ set (ABS_EXPR, op_abs);
+ set (ABSU_EXPR, op_absu);
+ set (NEGATE_EXPR, op_negate);
+ set (ADDR_EXPR, op_addr);
+}
+
+
+// Instantiate a range op table for pointer operations.
+class pointer_table : public range_op_table
+{
+public:
+ pointer_table ();
+} pointer_tree_table;
+
+pointer_table::pointer_table ()
+{
+ set (BIT_AND_EXPR, op_pointer_and);
+ set (BIT_IOR_EXPR, op_pointer_or);
+ set (MIN_EXPR, op_ptr_min_max);
+ set (MAX_EXPR, op_ptr_min_max);
+ set (POINTER_PLUS_EXPR, op_pointer_plus);
+
+ set (EQ_EXPR, op_equal);
+ set (NE_EXPR, op_not_equal);
+ set (LT_EXPR, op_lt);
+ set (LE_EXPR, op_le);
+ set (GT_EXPR, op_gt);
+ set (GE_EXPR, op_ge);
+ set (SSA_NAME, op_identity);
+ set (ADDR_EXPR, op_addr);
+ set (NOP_EXPR, op_convert);
+ set (CONVERT_EXPR, op_convert);
+
+ set (BIT_NOT_EXPR, op_bitwise_not);
+ set (BIT_XOR_EXPR, op_bitwise_xor);
+}
+
+
+
+
+/* The tables are hidden and accessed via a simple extern function. */
+
+range_operator *
+range_op_handler (enum tree_code code, tree type)
+{
+ // First check if there is apointer specialization.
+ if (POINTER_TYPE_P (type))
+ return pointer_tree_table[code];
+ return integral_tree_table[code];
+}
+
+
+
+
+
diff --git a/gcc/range-op.h b/gcc/range-op.h
index 64eeb4f..a2b110e 100644
--- a/gcc/range-op.h
+++ b/gcc/range-op.h
@@ -46,26 +46,31 @@ along with GCC; see the file COPYING3. If not see
class range_operator
{
public:
- virtual void dump (FILE *f) const = 0;
-
// Set a range based on this operation between 2 operands.
+ // TYPE is the expected type of the range.
// Return the TRUE if a valid range is created.
- virtual bool fold_range (irange& r, const irange& op1,
- const irange& op2) const;
+ virtual bool fold_range (irange &r, tree type, const irange &op1,
+ const irange &op2) const;
// Set the range for op? in the general case. LHS is the range for
- // the LHS of the expression, VAL is the range for the other
+ // the LHS of the expression, OP[12]is the range for the other
+ // TYPE is the expected type of the range.
// operand, and the result is returned in R.
- // ie [range] = op1 + VAL
- // This is re-formed as new_range = [range] - VAL.
// Return TRUE if the operation could be performed and the range is
// valid.
- virtual bool op1_range (irange& r, const irange& lhs,
- const irange& op2) const;
- virtual bool op2_range (irange& r, const irange& lhs,
- const irange& op1) const;
+ // ie [LHS] = ??? + OP2
+ // is re-formed as R = [LHS] - OP2.
+ virtual bool op1_range (irange &r, tree type, const irange &lhs,
+ const irange &op2) const;
+ virtual bool op2_range (irange &r, tree type, const irange &lhs,
+ const irange &op1) const;
+
+ // Perform this operation on 2 sub ranges, accumulating the result into R.
+ virtual bool 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;
};
-extern range_operator *range_op_handler(enum tree_code code);
+extern range_operator *range_op_handler(enum tree_code code, tree type);
#endif // GCC_RANGE_OP_H
diff --git a/gcc/ssa-range-vrp.c b/gcc/ssa-range-vrp.c
index 775f3a7..36f20e3 100644
--- a/gcc/ssa-range-vrp.c
+++ b/gcc/ssa-range-vrp.c
@@ -139,12 +139,6 @@ rvrp_fold_const_assign (gassign *assign, const irange &r)
match. Consequently, the global range table is being populated
by range kind:1 whereas sometimes Fortran can have other sized
booleans. */
- tree type = TREE_TYPE (gimple_assign_lhs (assign));
- if (!types_compatible_p (type, r.type ()))
- {
- gcc_checking_assert (TREE_CODE (r.type ()) == BOOLEAN_TYPE);
- rhs = fold_convert (type, rhs);
- }
delink_stmt_imm_use (assign);
gimple_assign_set_rhs_code (assign, SSA_NAME);
diff --git a/gcc/ssa-range.cc b/gcc/ssa-range.cc
index 19b24a4..481a45f 100644
--- a/gcc/ssa-range.cc
+++ b/gcc/ssa-range.cc
@@ -387,6 +387,7 @@ ssa_ranger::range_on_edge (irange &r, edge e, tree name)
gcc_checking_assert (valid_ssa_p (name));
range_on_exit (r, e->src, name);
+ gcc_checking_assert (r.type() == TREE_TYPE (name));
// Check to see if NAME is defined on edge e.
if (outgoing_edge_range_p (edge_range, e, name, &r))
@@ -402,30 +403,40 @@ ssa_ranger::range_on_edge (irange &r, edge e, tree name)
bool
ssa_ranger::range_of_stmt (irange &r, gimple *s, tree name)
{
+ bool res = false;
// If name is specified, make sure it a LHS of S.
gcc_checking_assert (name ? SSA_NAME_DEF_STMT (name) == s : true);
if (is_a<grange_op *> (s))
- return range_of_range_op (r, as_a<grange_op *> (s));
- if (is_a<gphi *>(s))
- return range_of_phi (r, as_a<gphi *> (s));
- if (is_a<gcall *>(s))
- return range_of_call (r, as_a<gcall *> (s));
- if (is_a<gassign *> (s) && gimple_assign_rhs_code (s) == COND_EXPR)
- return range_of_cond_expr (r, as_a<gassign *> (s));
-
- // If no name is specified, try the expression kind.
- if (!name)
+ res = range_of_range_op (r, as_a<grange_op *> (s));
+ else if (is_a<gphi *>(s))
+ res = range_of_phi (r, as_a<gphi *> (s));
+ else if (is_a<gcall *>(s))
+ res = range_of_call (r, as_a<gcall *> (s));
+ else if (is_a<gassign *> (s) && gimple_assign_rhs_code (s) == COND_EXPR)
+ res = range_of_cond_expr (r, as_a<gassign *> (s));
+ else
+ {
+ // If no name is specified, try the expression kind.
+ if (!name)
+ {
+ tree t = gimple_expr_type (s);
+ if (!irange::supports_type_p (t))
+ return false;
+ r.set_varying (t);
+ return true;
+ }
+ // We don't understand the stmt, so return the global range.
+ r = range_from_ssa (name);
+ return true;
+ }
+ if (res)
{
- tree t = gimple_expr_type (s);
- if (!irange::supports_type_p (t))
- return false;
- r.set_varying (t);
+ if (name && TREE_TYPE (name) != r.type ())
+ r.cast (TREE_TYPE (name));
return true;
}
- // We don't understand the stmt, so return the global range.
- r = range_from_ssa (name);
- return true;
+ return false;
}
// Calculate a range for statement S and return it in R. If NAME is found
@@ -496,6 +507,7 @@ ssa_ranger::range_on_exit (irange &r, basic_block bb, tree name)
range_on_entry (r, bb, name);
else
gcc_assert (range_of_expr (r, name, s));
+ gcc_checking_assert (r.type() == TREE_TYPE (name));
}
// Calculate a range for range_op statement S given RANGE1 and RANGE2 and
diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
index 05e62d8..33ef25e 100644
--- a/gcc/tree-vrp.c
+++ b/gcc/tree-vrp.c
@@ -1650,21 +1650,6 @@ extract_range_from_binary_expr (value_range_base *vr,
&& ranges_from_anti_range (&vr1, &vrtem0, &vrtem1))
{
extract_range_from_binary_expr (vr, code, expr_type, vr0_, &vrtem0);
- /* On a division, if dividing one sub-range returned UNDEFINED,
- it's a division by zero, and the entire thing is
- UNDEFINED.
-
- Imagine unsigned [X,Y] .DIV. ~[1,1]. This is done as:
- ([X,Y] .DIV. [0,0]) U ([X,Y] .DIV. [2,MAX]). Once we get the
- undefined from the first sub-range, we can bail. */
- if (vr->undefined_p ()
- && (code == TRUNC_DIV_EXPR
- || code == FLOOR_DIV_EXPR
- || code == CEIL_DIV_EXPR
- || code == EXACT_DIV_EXPR
- || code == ROUND_DIV_EXPR
- || code == TRUNC_MOD_EXPR))
- return;
if (!vrtem1.undefined_p ())
{
value_range_base vrres;
@@ -2427,7 +2412,7 @@ range_ops_fold_binary_expr (value_range_base *vr,
{
/* Mimic any behavior users of extract_range_from_unary_expr may
expect. */
- range_operator *op = range_op_handler (code);
+ range_operator *op = range_op_handler (code, expr_type);
if (!op)
{
vr->set_varying (expr_type);
@@ -2461,10 +2446,13 @@ range_ops_fold_binary_expr (value_range_base *vr,
value_range_base n1 = normalize_for_range_ops (vr1);
#if USE_IRANGE
irange ir;
- op->fold_range (ir, n0, n1);
- *vr = ir;
+ if (op->fold_range (ir, expr_type, n0, n1))
+ *vr = ir;
+ else
+ vr->set_varying (expr_type);
#else
- op->fold_range (*vr, n0, n1);
+ if (!op->fold_range (*vr, expr_type, n0, n1))
+ vr->set_varying (expr_type);
#endif
}
@@ -2477,7 +2465,7 @@ range_ops_fold_unary_expr (value_range_base *vr,
{
/* Mimic any behavior users of extract_range_from_unary_expr may
expect. */
- range_operator *op = range_op_handler (code);
+ range_operator *op = range_op_handler (code, expr_type);
if (!op)
{
vr->set_varying (expr_type);
@@ -2525,10 +2513,13 @@ range_ops_fold_unary_expr (value_range_base *vr,
value_range_base n1 (expr_type);
#if USE_IRANGE
irange ir;
- op->fold_range (ir, n0, n1);
- *vr = ir;
+ if (op->fold_range (ir, expr_type, n0, n1))
+ *vr = ir;
+ else
+ vr->set_varying (expr_type);
#else
- op->fold_range (*vr, n0, n1);
+ if (!op->fold_range (*vr, expr_type, n0, n1))
+ vr->set_varying (expr_type);
#endif
}
diff --git a/gcc/vr-values.c b/gcc/vr-values.c
index 6ff877c..8e48178 100644
--- a/gcc/vr-values.c
+++ b/gcc/vr-values.c
@@ -3337,7 +3337,11 @@ range_misc::simplify_min_or_max_using_ranges (gimple_stmt_iterator *gsi,
tree t;
irange res;
enum tree_code code = gimple_assign_rhs_code (stmt);
- range_op_handler (code)->fold_range (res, ir0, ir1);
+ if (!range_op_handler (code, TREE_TYPE (op0))->fold_range (res,
+ TREE_TYPE (op0),
+ ir0, ir1))
+ res.set_varying (TREE_TYPE (op0));
+
if (res == ir0)
t = op0;
else if (res == ir1)