blob: 44cdbd717f4c5f38e5f51924351ce125d18dedf6 [file] [log] [blame]
/* Support routines for value ranges.
Copyright (C) 2019-2024 Free Software Foundation, Inc.
Contributed by Aldy Hernandez <aldyh@redhat.com> and
Andrew Macleod <amacleod@redhat.com>.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_VALUE_RANGE_H
#define GCC_VALUE_RANGE_H
class irange;
// Types of value ranges.
enum value_range_kind
{
/* Empty range. */
VR_UNDEFINED,
/* Range spans the entire domain. */
VR_VARYING,
/* Range is [MIN, MAX]. */
VR_RANGE,
/* Range is ~[MIN, MAX]. */
VR_ANTI_RANGE,
/* Range is a NAN. */
VR_NAN,
/* Range is a nice guy. */
VR_LAST
};
// Discriminator between different vrange types.
enum value_range_discriminator
{
// Range holds an integer or pointer.
VR_IRANGE,
// Pointer range.
VR_PRANGE,
// Floating point range.
VR_FRANGE,
// Range holds an unsupported type.
VR_UNKNOWN
};
// Abstract class for ranges of any of the supported types.
//
// To query what types ranger and the entire ecosystem can support,
// use Value_Range::supports_type_p(tree type). This is a static
// method available independently of any vrange object.
//
// To query what a given vrange variant can support, use:
// irange::supports_p ()
// frange::supports_p ()
// etc
//
// To query what a range object can support, use:
// void foo (vrange &v, irange &i, frange &f)
// {
// if (v.supports_type_p (type)) ...
// if (i.supports_type_p (type)) ...
// if (f.supports_type_p (type)) ...
// }
class vrange
{
template <typename T> friend bool is_a (vrange &);
friend class Value_Range;
friend void streamer_write_vrange (struct output_block *, const vrange &);
friend class range_op_handler;
public:
virtual void accept (const class vrange_visitor &v) const = 0;
virtual void set (tree, tree, value_range_kind = VR_RANGE) = 0;
virtual tree type () const = 0;
virtual bool supports_type_p (const_tree type) const = 0;
virtual void set_varying (tree type) = 0;
virtual void set_undefined () = 0;
virtual bool union_ (const vrange &) = 0;
virtual bool intersect (const vrange &) = 0;
virtual bool singleton_p (tree *result = NULL) const = 0;
virtual bool contains_p (tree cst) const = 0;
virtual bool zero_p () const = 0;
virtual bool nonzero_p () const = 0;
virtual void set_nonzero (tree type) = 0;
virtual void set_zero (tree type) = 0;
virtual void set_nonnegative (tree type) = 0;
virtual bool fits_p (const vrange &r) const = 0;
virtual ~vrange () { }
virtual tree lbound () const = 0;
virtual tree ubound () const = 0;
virtual void update_bitmask (const class irange_bitmask &);
virtual irange_bitmask get_bitmask () const;
wide_int get_nonzero_bits () const;
void set_nonzero_bits (const wide_int &bits);
bool varying_p () const;
bool undefined_p () const;
vrange& operator= (const vrange &);
bool operator== (const vrange &) const;
bool operator!= (const vrange &r) const { return !(*this == r); }
void dump (FILE *) const;
protected:
vrange (enum value_range_discriminator d) : m_discriminator (d) { }
ENUM_BITFIELD(value_range_kind) m_kind : 8;
const ENUM_BITFIELD(value_range_discriminator) m_discriminator : 4;
};
namespace inchash
{
extern void add_vrange (const vrange &, hash &, unsigned flags = 0);
}
// A pair of values representing the known bits in a range. Zero bits
// in MASK cover constant values. Set bits in MASK cover unknown
// values. VALUE are the known bits.
//
// Set bits in MASK (no meaningful information) must have their
// corresponding bits in VALUE cleared, as this speeds up union and
// intersect.
class irange_bitmask
{
public:
irange_bitmask () { /* uninitialized */ }
irange_bitmask (unsigned prec) { set_unknown (prec); }
irange_bitmask (const wide_int &value, const wide_int &mask);
wide_int value () const { return m_value; }
wide_int mask () const { return m_mask; }
void set_unknown (unsigned prec);
bool unknown_p () const;
unsigned get_precision () const;
void union_ (const irange_bitmask &src);
void intersect (const irange_bitmask &src);
bool operator== (const irange_bitmask &src) const;
bool operator!= (const irange_bitmask &src) const { return !(*this == src); }
void verify_mask () const;
void dump (FILE *) const;
bool member_p (const wide_int &val) const;
void adjust_range (irange &r) const;
// Convenience functions for nonzero bitmask compatibility.
wide_int get_nonzero_bits () const;
void set_nonzero_bits (const wide_int &bits);
private:
wide_int m_value;
wide_int m_mask;
};
inline void
irange_bitmask::set_unknown (unsigned prec)
{
m_value = wi::zero (prec);
m_mask = wi::minus_one (prec);
if (flag_checking)
verify_mask ();
}
// Return TRUE if THIS does not have any meaningful information.
inline bool
irange_bitmask::unknown_p () const
{
return m_mask == -1;
}
inline
irange_bitmask::irange_bitmask (const wide_int &value, const wide_int &mask)
{
m_value = value;
m_mask = mask;
if (flag_checking)
verify_mask ();
}
inline unsigned
irange_bitmask::get_precision () const
{
return m_mask.get_precision ();
}
// The following two functions are meant for backwards compatability
// with the nonzero bitmask. A cleared bit means the value must be 0.
// A set bit means we have no information for the bit.
// Return the nonzero bits.
inline wide_int
irange_bitmask::get_nonzero_bits () const
{
return m_value | m_mask;
}
// Set the bitmask to the nonzero bits in BITS.
inline void
irange_bitmask::set_nonzero_bits (const wide_int &bits)
{
m_value = wi::zero (bits.get_precision ());
m_mask = bits;
if (flag_checking)
verify_mask ();
}
// Return TRUE if val could be a valid value with this bitmask.
inline bool
irange_bitmask::member_p (const wide_int &val) const
{
if (unknown_p ())
return true;
wide_int res = m_mask & val;
if (m_value != 0)
res |= ~m_mask & m_value;
return res == val;
}
inline bool
irange_bitmask::operator== (const irange_bitmask &src) const
{
bool unknown1 = unknown_p ();
bool unknown2 = src.unknown_p ();
if (unknown1 || unknown2)
return unknown1 == unknown2;
return m_value == src.m_value && m_mask == src.m_mask;
}
inline void
irange_bitmask::union_ (const irange_bitmask &src)
{
m_mask = (m_mask | src.m_mask) | (m_value ^ src.m_value);
m_value = m_value & src.m_value;
if (flag_checking)
verify_mask ();
}
inline void
irange_bitmask::intersect (const irange_bitmask &src)
{
// If we have two known bits that are incompatible, the resulting
// bit is undefined. It is unclear whether we should set the entire
// range to UNDEFINED, or just a subset of it. For now, set the
// entire bitmask to unknown (VARYING).
if (wi::bit_and (~(m_mask | src.m_mask),
m_value ^ src.m_value) != 0)
{
unsigned prec = m_mask.get_precision ();
m_mask = wi::minus_one (prec);
m_value = wi::zero (prec);
}
else
{
m_mask = m_mask & src.m_mask;
m_value = m_value | src.m_value;
}
if (flag_checking)
verify_mask ();
}
// An integer range without any storage.
class irange : public vrange
{
friend class irange_storage;
friend class vrange_printer;
public:
// In-place setters.
void set (tree type, const wide_int &, const wide_int &,
value_range_kind = VR_RANGE);
virtual void set_nonzero (tree type) override;
virtual void set_zero (tree type) override;
virtual void set_nonnegative (tree type) override;
virtual void set_varying (tree type) override;
virtual void set_undefined () override;
// Range types.
static bool supports_p (const_tree type);
virtual bool supports_type_p (const_tree type) const override;
virtual tree type () const override;
// Iteration over sub-ranges.
unsigned num_pairs () const;
wide_int lower_bound (unsigned = 0) const;
wide_int upper_bound (unsigned) const;
wide_int upper_bound () const;
virtual tree lbound () const override;
virtual tree ubound () const override;
// Predicates.
virtual bool zero_p () const override;
virtual bool nonzero_p () const override;
virtual bool singleton_p (tree *result = NULL) const override;
bool singleton_p (wide_int &) const;
bool contains_p (const wide_int &) const;
bool nonnegative_p () const;
bool nonpositive_p () const;
// In-place operators.
virtual bool union_ (const vrange &) override;
virtual bool intersect (const vrange &) override;
void invert ();
// Operator overloads.
irange& operator= (const irange &);
bool operator== (const irange &) const;
bool operator!= (const irange &r) const { return !(*this == r); }
// Misc methods.
virtual bool fits_p (const vrange &r) const override;
virtual void accept (const vrange_visitor &v) const override;
virtual void update_bitmask (const class irange_bitmask &) override;
virtual irange_bitmask get_bitmask () const override;
protected:
void maybe_resize (int needed);
virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
virtual bool contains_p (tree cst) const override;
irange (wide_int *, unsigned nranges, bool resizable);
// In-place operators.
bool irange_contains_p (const irange &) const;
bool irange_single_pair_union (const irange &r);
void normalize_kind ();
void verify_range ();
// Hard limit on max ranges allowed.
static const int HARD_MAX_RANGES = 255;
private:
bool varying_compatible_p () const;
bool intersect_bitmask (const irange &r);
bool union_bitmask (const irange &r);
bool set_range_from_bitmask ();
bool intersect (const wide_int& lb, const wide_int& ub);
bool union_append (const irange &r);
unsigned char m_num_ranges;
bool m_resizable;
unsigned char m_max_ranges;
tree m_type;
irange_bitmask m_bitmask;
protected:
wide_int *m_base;
};
// Here we describe an irange with N pairs of ranges. The storage for
// the pairs is embedded in the class as an array.
//
// If RESIZABLE is true, the storage will be resized on the heap when
// the number of ranges needed goes past N up to a max of
// HARD_MAX_RANGES. This new storage is freed upon destruction.
template<unsigned N, bool RESIZABLE = false>
class int_range : public irange
{
public:
int_range ();
int_range (tree type, const wide_int &, const wide_int &,
value_range_kind = VR_RANGE);
int_range (tree type);
int_range (const int_range &);
int_range (const irange &);
~int_range () final override;
int_range& operator= (const int_range &);
protected:
int_range (tree, tree, value_range_kind = VR_RANGE);
private:
wide_int m_ranges[N*2];
};
class prange : public vrange
{
friend class prange_storage;
friend class vrange_printer;
public:
prange ();
prange (const prange &);
prange (tree type);
prange (tree type, const wide_int &, const wide_int &,
value_range_kind = VR_RANGE);
static bool supports_p (const_tree type);
virtual bool supports_type_p (const_tree type) const final override;
virtual void accept (const vrange_visitor &v) const final override;
virtual void set_undefined () final override;
virtual void set_varying (tree type) final override;
virtual void set_nonzero (tree type) final override;
virtual void set_zero (tree type) final override;
virtual void set_nonnegative (tree type) final override;
virtual bool contains_p (tree cst) const final override;
virtual bool fits_p (const vrange &v) const final override;
virtual bool singleton_p (tree *result = NULL) const final override;
virtual bool zero_p () const final override;
virtual bool nonzero_p () const final override;
virtual void set (tree, tree, value_range_kind = VR_RANGE) final override;
virtual tree type () const final override;
virtual bool union_ (const vrange &v) final override;
virtual bool intersect (const vrange &v) final override;
virtual tree lbound () const final override;
virtual tree ubound () const final override;
prange& operator= (const prange &);
bool operator== (const prange &) const;
void set (tree type, const wide_int &, const wide_int &,
value_range_kind = VR_RANGE);
void invert ();
bool contains_p (const wide_int &) const;
wide_int lower_bound () const;
wide_int upper_bound () const;
void verify_range () const;
irange_bitmask get_bitmask () const final override;
void update_bitmask (const irange_bitmask &) final override;
protected:
bool varying_compatible_p () const;
tree m_type;
wide_int m_min;
wide_int m_max;
irange_bitmask m_bitmask;
};
// Unsupported temporaries may be created by ranger before it's known
// they're unsupported, or by vr_values::get_value_range.
class unsupported_range : public vrange
{
public:
unsupported_range ()
: vrange (VR_UNKNOWN)
{
set_undefined ();
}
unsupported_range (const unsupported_range &src)
: vrange (VR_UNKNOWN)
{
unsupported_range::operator= (src);
}
void set (tree min, tree, value_range_kind = VR_RANGE) final override;
tree type () const final override;
bool supports_type_p (const_tree) const final override;
void set_varying (tree) final override;
void set_undefined () final override;
void accept (const vrange_visitor &v) const final override;
bool union_ (const vrange &r) final override;
bool intersect (const vrange &r) final override;
bool singleton_p (tree * = NULL) const final override;
bool contains_p (tree) const final override;
bool zero_p () const final override;
bool nonzero_p () const final override;
void set_nonzero (tree type) final override;
void set_zero (tree type) final override;
void set_nonnegative (tree type) final override;
bool fits_p (const vrange &) const final override;
unsupported_range& operator= (const unsupported_range &r);
tree lbound () const final override;
tree ubound () const final override;
};
// The NAN state as an opaque object.
class nan_state
{
public:
nan_state (bool);
nan_state (bool pos_nan, bool neg_nan);
bool neg_p () const;
bool pos_p () const;
private:
bool m_pos_nan;
bool m_neg_nan;
};
// Set NAN state to +-NAN if NAN_P is true. Otherwise set NAN state
// to false.
inline
nan_state::nan_state (bool nan_p)
{
m_pos_nan = nan_p;
m_neg_nan = nan_p;
}
// Constructor initializing the object to +NAN if POS_NAN is set, -NAN
// if NEG_NAN is set, or +-NAN if both are set. Otherwise POS_NAN and
// NEG_NAN are clear, and the object cannot be a NAN.
inline
nan_state::nan_state (bool pos_nan, bool neg_nan)
{
m_pos_nan = pos_nan;
m_neg_nan = neg_nan;
}
// Return if +NAN is possible.
inline bool
nan_state::pos_p () const
{
return m_pos_nan;
}
// Return if -NAN is possible.
inline bool
nan_state::neg_p () const
{
return m_neg_nan;
}
// A floating point range.
//
// The representation is a type with a couple of endpoints, unioned
// with the set of { -NAN, +Nan }.
class frange : public vrange
{
friend class frange_storage;
friend class vrange_printer;
public:
frange ();
frange (const frange &);
frange (tree, tree, value_range_kind = VR_RANGE);
frange (tree type);
frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
value_range_kind = VR_RANGE);
static bool supports_p (const_tree type)
{
// ?? Decimal floats can have multiple representations for the
// same number. Supporting them may be as simple as just
// disabling them in singleton_p. No clue.
return SCALAR_FLOAT_TYPE_P (type) && !DECIMAL_FLOAT_TYPE_P (type);
}
virtual tree type () const override;
void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
value_range_kind = VR_RANGE);
void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
const nan_state &, value_range_kind = VR_RANGE);
void set_nan (tree type);
void set_nan (tree type, bool sign);
void set_nan (tree type, const nan_state &);
virtual void set_varying (tree type) override;
virtual void set_undefined () override;
virtual bool union_ (const vrange &) override;
virtual bool intersect (const vrange &) override;
bool contains_p (const REAL_VALUE_TYPE &) const;
virtual bool singleton_p (tree *result = NULL) const override;
bool singleton_p (REAL_VALUE_TYPE &r) const;
virtual bool supports_type_p (const_tree type) const override;
virtual void accept (const vrange_visitor &v) const override;
virtual bool zero_p () const override;
virtual bool nonzero_p () const override;
virtual void set_nonzero (tree type) override;
virtual void set_zero (tree type) override;
virtual void set_nonnegative (tree type) override;
virtual bool fits_p (const vrange &) const override;
frange& operator= (const frange &);
bool operator== (const frange &) const;
bool operator!= (const frange &r) const { return !(*this == r); }
const REAL_VALUE_TYPE &lower_bound () const;
const REAL_VALUE_TYPE &upper_bound () const;
virtual tree lbound () const override;
virtual tree ubound () const override;
nan_state get_nan_state () const;
void update_nan ();
void update_nan (bool sign);
void update_nan (tree) = delete; // Disallow silent conversion to bool.
void update_nan (const nan_state &);
void clear_nan ();
void flush_denormals_to_zero ();
// fpclassify like API
bool known_isfinite () const;
bool known_isnan () const;
bool known_isinf () const;
bool maybe_isnan () const;
bool maybe_isnan (bool sign) const;
bool maybe_isinf () const;
bool signbit_p (bool &signbit) const;
bool nan_signbit_p (bool &signbit) const;
protected:
virtual bool contains_p (tree cst) const override;
virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
private:
bool internal_singleton_p (REAL_VALUE_TYPE * = NULL) const;
void verify_range ();
bool normalize_kind ();
bool union_nans (const frange &);
bool intersect_nans (const frange &);
bool combine_zeros (const frange &, bool union_p);
tree m_type;
REAL_VALUE_TYPE m_min;
REAL_VALUE_TYPE m_max;
bool m_pos_nan;
bool m_neg_nan;
};
inline const REAL_VALUE_TYPE &
frange::lower_bound () const
{
gcc_checking_assert (!undefined_p () && !known_isnan ());
return m_min;
}
inline const REAL_VALUE_TYPE &
frange::upper_bound () const
{
gcc_checking_assert (!undefined_p () && !known_isnan ());
return m_max;
}
// Return the NAN state.
inline nan_state
frange::get_nan_state () const
{
return nan_state (m_pos_nan, m_neg_nan);
}
// is_a<> and as_a<> implementation for vrange.
// Anything we haven't specialized is a hard fail.
template <typename T>
inline bool
is_a (vrange &)
{
gcc_unreachable ();
return false;
}
template <typename T>
inline bool
is_a (const vrange &v)
{
// Reuse is_a <vrange> to implement the const version.
const T &derived = static_cast<const T &> (v);
return is_a <T> (const_cast<T &> (derived));
}
template <typename T>
inline T &
as_a (vrange &v)
{
gcc_checking_assert (is_a <T> (v));
return static_cast <T &> (v);
}
template <typename T>
inline const T &
as_a (const vrange &v)
{
gcc_checking_assert (is_a <T> (v));
return static_cast <const T &> (v);
}
// Specializations for the different range types.
template <>
inline bool
is_a <irange> (vrange &v)
{
return v.m_discriminator == VR_IRANGE;
}
template <>
inline bool
is_a <prange> (vrange &v)
{
return v.m_discriminator == VR_PRANGE;
}
template <>
inline bool
is_a <frange> (vrange &v)
{
return v.m_discriminator == VR_FRANGE;
}
template <>
inline bool
is_a <unsupported_range> (vrange &v)
{
return v.m_discriminator == VR_UNKNOWN;
}
// For resizable ranges, resize the range up to HARD_MAX_RANGES if the
// NEEDED pairs is greater than the current capacity of the range.
inline void
irange::maybe_resize (int needed)
{
if (!m_resizable || m_max_ranges == HARD_MAX_RANGES)
return;
if (needed > m_max_ranges)
{
m_max_ranges = HARD_MAX_RANGES;
wide_int *newmem = new wide_int[m_max_ranges * 2];
unsigned n = num_pairs () * 2;
for (unsigned i = 0; i < n; ++i)
newmem[i] = m_base[i];
m_base = newmem;
}
}
template<unsigned N, bool RESIZABLE>
inline
int_range<N, RESIZABLE>::~int_range ()
{
if (RESIZABLE && m_base != m_ranges)
delete[] m_base;
}
// This is an "infinite" precision irange for use in temporary
// calculations. It starts with a sensible default covering 99% of
// uses, and goes up to HARD_MAX_RANGES when needed. Any allocated
// storage is freed upon destruction.
typedef int_range<3, /*RESIZABLE=*/true> int_range_max;
class vrange_visitor
{
public:
virtual void visit (const irange &) const { }
virtual void visit (const prange &) const { }
virtual void visit (const frange &) const { }
virtual void visit (const unsupported_range &) const { }
};
typedef int_range<2> value_range;
// This is an "infinite" precision range object for use in temporary
// calculations for any of the handled types. The object can be
// transparently used as a vrange.
//
// Using any of the various constructors initializes the object
// appropriately, but the default constructor is uninitialized and
// must be initialized either with set_type() or by assigning into it.
//
// Assigning between incompatible types is allowed. For example if a
// temporary holds an irange, you can assign an frange into it, and
// all the right things will happen. However, before passing this
// object to a function accepting a vrange, the correct type must be
// set. If it isn't, you can do so with set_type().
class Value_Range
{
public:
Value_Range ();
Value_Range (const vrange &r);
Value_Range (tree type);
Value_Range (tree, tree, value_range_kind kind = VR_RANGE);
Value_Range (const Value_Range &);
~Value_Range ();
void set_type (tree type);
vrange& operator= (const vrange &);
Value_Range& operator= (const Value_Range &);
bool operator== (const Value_Range &r) const;
bool operator!= (const Value_Range &r) const;
operator vrange &();
operator const vrange &() const;
void dump (FILE *) const;
static bool supports_type_p (const_tree type);
tree type () { return m_vrange->type (); }
bool varying_p () const { return m_vrange->varying_p (); }
bool undefined_p () const { return m_vrange->undefined_p (); }
void set_varying (tree type) { init (type); m_vrange->set_varying (type); }
void set_undefined () { m_vrange->set_undefined (); }
bool union_ (const vrange &r) { return m_vrange->union_ (r); }
bool intersect (const vrange &r) { return m_vrange->intersect (r); }
bool contains_p (tree cst) const { return m_vrange->contains_p (cst); }
bool singleton_p (tree *result = NULL) const
{ return m_vrange->singleton_p (result); }
void set_zero (tree type) { init (type); return m_vrange->set_zero (type); }
void set_nonzero (tree type)
{ init (type); return m_vrange->set_nonzero (type); }
bool nonzero_p () const { return m_vrange->nonzero_p (); }
bool zero_p () const { return m_vrange->zero_p (); }
tree lbound () const { return m_vrange->lbound (); }
tree ubound () const { return m_vrange->ubound (); }
irange_bitmask get_bitmask () const { return m_vrange->get_bitmask (); }
void update_bitmask (const class irange_bitmask &bm)
{ return m_vrange->update_bitmask (bm); }
void accept (const vrange_visitor &v) const { m_vrange->accept (v); }
private:
void init (tree type);
void init (const vrange &);
vrange *m_vrange;
union buffer_type {
int_range_max ints;
frange floats;
unsupported_range unsupported;
prange pointers;
buffer_type () { }
~buffer_type () { }
} m_buffer;
};
// The default constructor is uninitialized and must be initialized
// with either set_type() or with an assignment into it.
inline
Value_Range::Value_Range ()
: m_buffer ()
{
m_vrange = NULL;
}
// Copy constructor.
inline
Value_Range::Value_Range (const Value_Range &r)
{
init (*r.m_vrange);
}
// Copy constructor from a vrange.
inline
Value_Range::Value_Range (const vrange &r)
{
init (r);
}
// Construct an UNDEFINED range that can hold ranges of TYPE. If TYPE
// is not supported, default to unsupported_range.
inline
Value_Range::Value_Range (tree type)
{
init (type);
}
// Construct a range that can hold a range of [MIN, MAX], where MIN
// and MAX are trees.
inline
Value_Range::Value_Range (tree min, tree max, value_range_kind kind)
{
init (TREE_TYPE (min));
m_vrange->set (min, max, kind);
}
inline
Value_Range::~Value_Range ()
{
if (m_vrange)
m_vrange->~vrange ();
}
// Initialize object to an UNDEFINED range that can hold ranges of
// TYPE. Clean-up memory if there was a previous object.
inline void
Value_Range::set_type (tree type)
{
if (m_vrange)
m_vrange->~vrange ();
init (type);
}
// Initialize object to an UNDEFINED range that can hold ranges of
// TYPE.
inline void
Value_Range::init (tree type)
{
gcc_checking_assert (TYPE_P (type));
if (irange::supports_p (type))
m_vrange = new (&m_buffer.ints) int_range_max ();
else if (prange::supports_p (type))
m_vrange = new (&m_buffer.pointers) prange ();
else if (frange::supports_p (type))
m_vrange = new (&m_buffer.floats) frange ();
else
m_vrange = new (&m_buffer.unsupported) unsupported_range ();
}
// Initialize object with a copy of R.
inline void
Value_Range::init (const vrange &r)
{
if (is_a <irange> (r))
m_vrange = new (&m_buffer.ints) int_range_max (as_a <irange> (r));
else if (is_a <prange> (r))
m_vrange = new (&m_buffer.pointers) prange (as_a <prange> (r));
else if (is_a <frange> (r))
m_vrange = new (&m_buffer.floats) frange (as_a <frange> (r));
else
m_vrange = new (&m_buffer.unsupported)
unsupported_range (as_a <unsupported_range> (r));
}
// Assignment operator. Copying incompatible types is allowed. That
// is, assigning an frange to an object holding an irange does the
// right thing.
inline vrange &
Value_Range::operator= (const vrange &r)
{
if (m_vrange)
m_vrange->~vrange ();
init (r);
return *m_vrange;
}
inline Value_Range &
Value_Range::operator= (const Value_Range &r)
{
// No need to call the m_vrange destructor here, as we will do so in
// the assignment below.
*this = *r.m_vrange;
return *this;
}
inline bool
Value_Range::operator== (const Value_Range &r) const
{
return *m_vrange == *r.m_vrange;
}
inline bool
Value_Range::operator!= (const Value_Range &r) const
{
return *m_vrange != *r.m_vrange;
}
inline
Value_Range::operator vrange &()
{
return *m_vrange;
}
inline
Value_Range::operator const vrange &() const
{
return *m_vrange;
}
// Return TRUE if TYPE is supported by the vrange infrastructure.
inline bool
Value_Range::supports_type_p (const_tree type)
{
return irange::supports_p (type)
|| prange::supports_p (type)
|| frange::supports_p (type);
}
extern value_range_kind get_legacy_range (const vrange &, tree &min, tree &max);
extern void dump_value_range (FILE *, const vrange *);
extern bool vrp_operand_equal_p (const_tree, const_tree);
inline REAL_VALUE_TYPE frange_val_min (const_tree type);
inline REAL_VALUE_TYPE frange_val_max (const_tree type);
// Number of sub-ranges in a range.
inline unsigned
irange::num_pairs () const
{
return m_num_ranges;
}
inline tree
irange::type () const
{
gcc_checking_assert (m_num_ranges > 0);
return m_type;
}
inline bool
irange::varying_compatible_p () const
{
if (m_num_ranges != 1)
return false;
const wide_int &l = m_base[0];
const wide_int &u = m_base[1];
tree t = m_type;
if (m_kind == VR_VARYING)
return true;
unsigned prec = TYPE_PRECISION (t);
signop sign = TYPE_SIGN (t);
if (INTEGRAL_TYPE_P (t) || POINTER_TYPE_P (t))
return (l == wi::min_value (prec, sign)
&& u == wi::max_value (prec, sign)
&& m_bitmask.unknown_p ());
return true;
}
inline bool
vrange::varying_p () const
{
return m_kind == VR_VARYING;
}
inline bool
vrange::undefined_p () const
{
return m_kind == VR_UNDEFINED;
}
inline bool
irange::zero_p () const
{
return (m_kind == VR_RANGE && m_num_ranges == 1
&& lower_bound (0) == 0
&& upper_bound (0) == 0);
}
inline bool
irange::nonzero_p () const
{
if (undefined_p ())
return false;
wide_int zero = wi::zero (TYPE_PRECISION (type ()));
return *this == int_range<2> (type (), zero, zero, VR_ANTI_RANGE);
}
inline bool
irange::supports_p (const_tree type)
{
return INTEGRAL_TYPE_P (type);
}
inline bool
irange::contains_p (tree cst) const
{
return contains_p (wi::to_wide (cst));
}
inline bool
range_includes_zero_p (const vrange &vr)
{
if (vr.undefined_p ())
return false;
if (vr.varying_p ())
return true;
return vr.contains_p (build_zero_cst (vr.type ()));
}
// Constructors for irange
inline
irange::irange (wide_int *base, unsigned nranges, bool resizable)
: vrange (VR_IRANGE),
m_resizable (resizable),
m_max_ranges (nranges)
{
m_base = base;
set_undefined ();
}
// Constructors for int_range<>.
template<unsigned N, bool RESIZABLE>
inline
int_range<N, RESIZABLE>::int_range ()
: irange (m_ranges, N, RESIZABLE)
{
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>::int_range (const int_range &other)
: irange (m_ranges, N, RESIZABLE)
{
irange::operator= (other);
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>::int_range (tree min, tree max, value_range_kind kind)
: irange (m_ranges, N, RESIZABLE)
{
irange::set (min, max, kind);
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>::int_range (tree type)
: irange (m_ranges, N, RESIZABLE)
{
set_varying (type);
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>::int_range (tree type, const wide_int &wmin, const wide_int &wmax,
value_range_kind kind)
: irange (m_ranges, N, RESIZABLE)
{
set (type, wmin, wmax, kind);
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>::int_range (const irange &other)
: irange (m_ranges, N, RESIZABLE)
{
irange::operator= (other);
}
template<unsigned N, bool RESIZABLE>
int_range<N, RESIZABLE>&
int_range<N, RESIZABLE>::operator= (const int_range &src)
{
irange::operator= (src);
return *this;
}
inline void
irange::set_undefined ()
{
m_kind = VR_UNDEFINED;
m_num_ranges = 0;
}
inline void
irange::set_varying (tree type)
{
m_kind = VR_VARYING;
m_num_ranges = 1;
m_bitmask.set_unknown (TYPE_PRECISION (type));
if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
{
m_type = type;
// Strict enum's require varying to be not TYPE_MIN/MAX, but rather
// min_value and max_value.
m_base[0] = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
m_base[1] = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
}
else
m_type = error_mark_node;
}
// Return the lower bound of a sub-range. PAIR is the sub-range in
// question.
inline wide_int
irange::lower_bound (unsigned pair) const
{
gcc_checking_assert (m_num_ranges > 0);
gcc_checking_assert (pair + 1 <= num_pairs ());
return m_base[pair * 2];
}
// Return the upper bound of a sub-range. PAIR is the sub-range in
// question.
inline wide_int
irange::upper_bound (unsigned pair) const
{
gcc_checking_assert (m_num_ranges > 0);
gcc_checking_assert (pair + 1 <= num_pairs ());
return m_base[pair * 2 + 1];
}
// Return the highest bound of a range.
inline wide_int
irange::upper_bound () const
{
unsigned pairs = num_pairs ();
gcc_checking_assert (pairs > 0);
return upper_bound (pairs - 1);
}
// Set value range VR to a nonzero range of type TYPE.
inline void
irange::set_nonzero (tree type)
{
unsigned prec = TYPE_PRECISION (type);
if (TYPE_UNSIGNED (type))
{
m_type = type;
m_kind = VR_RANGE;
m_base[0] = wi::one (prec);
m_base[1] = wi::minus_one (prec);
m_bitmask.set_unknown (prec);
m_num_ranges = 1;
if (flag_checking)
verify_range ();
}
else
{
wide_int zero = wi::zero (prec);
set (type, zero, zero, VR_ANTI_RANGE);
}
}
// Set value range VR to a ZERO range of type TYPE.
inline void
irange::set_zero (tree type)
{
wide_int zero = wi::zero (TYPE_PRECISION (type));
set (type, zero, zero);
}
// Normalize a range to VARYING or UNDEFINED if possible.
inline void
irange::normalize_kind ()
{
if (m_num_ranges == 0)
set_undefined ();
else if (varying_compatible_p ())
{
if (m_kind == VR_RANGE)
m_kind = VR_VARYING;
else if (m_kind == VR_ANTI_RANGE)
set_undefined ();
}
if (flag_checking)
verify_range ();
}
inline bool
contains_zero_p (const irange &r)
{
if (r.undefined_p ())
return false;
wide_int zero = wi::zero (TYPE_PRECISION (r.type ()));
return r.contains_p (zero);
}
inline wide_int
irange_val_min (const_tree type)
{
gcc_checking_assert (irange::supports_p (type));
return wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type));
}
inline wide_int
irange_val_max (const_tree type)
{
gcc_checking_assert (irange::supports_p (type));
return wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type));
}
inline
prange::prange ()
: vrange (VR_PRANGE)
{
set_undefined ();
}
inline
prange::prange (const prange &r)
: vrange (VR_PRANGE)
{
*this = r;
}
inline
prange::prange (tree type)
: vrange (VR_PRANGE)
{
set_varying (type);
}
inline
prange::prange (tree type, const wide_int &lb, const wide_int &ub,
value_range_kind kind)
: vrange (VR_PRANGE)
{
set (type, lb, ub, kind);
}
inline bool
prange::supports_p (const_tree type)
{
return POINTER_TYPE_P (type);
}
inline bool
prange::supports_type_p (const_tree type) const
{
return POINTER_TYPE_P (type);
}
inline void
prange::set_undefined ()
{
m_kind = VR_UNDEFINED;
}
inline void
prange::set_varying (tree type)
{
m_kind = VR_VARYING;
m_type = type;
m_min = wi::zero (TYPE_PRECISION (type));
m_max = wi::max_value (TYPE_PRECISION (type), UNSIGNED);
m_bitmask.set_unknown (TYPE_PRECISION (type));
if (flag_checking)
verify_range ();
}
inline void
prange::set_nonzero (tree type)
{
m_kind = VR_RANGE;
m_type = type;
m_min = wi::one (TYPE_PRECISION (type));
m_max = wi::max_value (TYPE_PRECISION (type), UNSIGNED);
m_bitmask.set_unknown (TYPE_PRECISION (type));
if (flag_checking)
verify_range ();
}
inline void
prange::set_zero (tree type)
{
m_kind = VR_RANGE;
m_type = type;
wide_int zero = wi::zero (TYPE_PRECISION (type));
m_min = m_max = zero;
m_bitmask = irange_bitmask (zero, zero);
if (flag_checking)
verify_range ();
}
inline bool
prange::contains_p (tree cst) const
{
return contains_p (wi::to_wide (cst));
}
inline bool
prange::zero_p () const
{
return m_kind == VR_RANGE && m_min == 0 && m_max == 0;
}
inline bool
prange::nonzero_p () const
{
return m_kind == VR_RANGE && m_min == 1 && m_max == -1;
}
inline tree
prange::type () const
{
gcc_checking_assert (!undefined_p ());
return m_type;
}
inline wide_int
prange::lower_bound () const
{
gcc_checking_assert (!undefined_p ());
return m_min;
}
inline wide_int
prange::upper_bound () const
{
gcc_checking_assert (!undefined_p ());
return m_max;
}
inline bool
prange::varying_compatible_p () const
{
return (!undefined_p ()
&& m_min == 0 && m_max == -1 && get_bitmask ().unknown_p ());
}
inline irange_bitmask
prange::get_bitmask () const
{
return m_bitmask;
}
inline bool
prange::fits_p (const vrange &) const
{
return true;
}
inline
frange::frange ()
: vrange (VR_FRANGE)
{
set_undefined ();
}
inline
frange::frange (const frange &src)
: vrange (VR_FRANGE)
{
*this = src;
}
inline
frange::frange (tree type)
: vrange (VR_FRANGE)
{
set_varying (type);
}
// frange constructor from REAL_VALUE_TYPE endpoints.
inline
frange::frange (tree type,
const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
value_range_kind kind)
: vrange (VR_FRANGE)
{
set (type, min, max, kind);
}
// frange constructor from trees.
inline
frange::frange (tree min, tree max, value_range_kind kind)
: vrange (VR_FRANGE)
{
set (min, max, kind);
}
inline tree
frange::type () const
{
gcc_checking_assert (!undefined_p ());
return m_type;
}
inline void
frange::set_varying (tree type)
{
m_kind = VR_VARYING;
m_type = type;
m_min = frange_val_min (type);
m_max = frange_val_max (type);
if (HONOR_NANS (m_type))
{
m_pos_nan = true;
m_neg_nan = true;
}
else
{
m_pos_nan = false;
m_neg_nan = false;
}
}
inline void
frange::set_undefined ()
{
m_kind = VR_UNDEFINED;
m_type = NULL;
m_pos_nan = false;
m_neg_nan = false;
// m_min and m_min are uninitialized as they are REAL_VALUE_TYPE ??.
if (flag_checking)
verify_range ();
}
// Set the NAN bits to NAN and adjust the range.
inline void
frange::update_nan (const nan_state &nan)
{
gcc_checking_assert (!undefined_p ());
if (HONOR_NANS (m_type))
{
m_pos_nan = nan.pos_p ();
m_neg_nan = nan.neg_p ();
normalize_kind ();
if (flag_checking)
verify_range ();
}
}
// Set the NAN bit to +-NAN.
inline void
frange::update_nan ()
{
gcc_checking_assert (!undefined_p ());
nan_state nan (true);
update_nan (nan);
}
// Like above, but set the sign of the NAN.
inline void
frange::update_nan (bool sign)
{
gcc_checking_assert (!undefined_p ());
nan_state nan (/*pos=*/!sign, /*neg=*/sign);
update_nan (nan);
}
inline bool
frange::contains_p (tree cst) const
{
return contains_p (*TREE_REAL_CST_PTR (cst));
}
// Clear the NAN bit and adjust the range.
inline void
frange::clear_nan ()
{
gcc_checking_assert (!undefined_p ());
m_pos_nan = false;
m_neg_nan = false;
normalize_kind ();
if (flag_checking)
verify_range ();
}
// Set R to maximum representable value for TYPE.
inline REAL_VALUE_TYPE
real_max_representable (const_tree type)
{
REAL_VALUE_TYPE r;
char buf[128];
get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
buf, sizeof (buf), false);
int res = real_from_string (&r, buf);
gcc_checking_assert (!res);
return r;
}
// Return the minimum representable value for TYPE.
inline REAL_VALUE_TYPE
real_min_representable (const_tree type)
{
REAL_VALUE_TYPE r = real_max_representable (type);
r = real_value_negate (&r);
return r;
}
// Return the minimum value for TYPE.
inline REAL_VALUE_TYPE
frange_val_min (const_tree type)
{
if (HONOR_INFINITIES (type))
return dconstninf;
else
return real_min_representable (type);
}
// Return the maximum value for TYPE.
inline REAL_VALUE_TYPE
frange_val_max (const_tree type)
{
if (HONOR_INFINITIES (type))
return dconstinf;
else
return real_max_representable (type);
}
// Return TRUE if R is the minimum value for TYPE.
inline bool
frange_val_is_min (const REAL_VALUE_TYPE &r, const_tree type)
{
REAL_VALUE_TYPE min = frange_val_min (type);
return real_identical (&min, &r);
}
// Return TRUE if R is the max value for TYPE.
inline bool
frange_val_is_max (const REAL_VALUE_TYPE &r, const_tree type)
{
REAL_VALUE_TYPE max = frange_val_max (type);
return real_identical (&max, &r);
}
// Build a NAN with a state of NAN.
inline void
frange::set_nan (tree type, const nan_state &nan)
{
gcc_checking_assert (nan.pos_p () || nan.neg_p ());
if (HONOR_NANS (type))
{
m_kind = VR_NAN;
m_type = type;
m_neg_nan = nan.neg_p ();
m_pos_nan = nan.pos_p ();
if (flag_checking)
verify_range ();
}
else
set_undefined ();
}
// Build a signless NAN of type TYPE.
inline void
frange::set_nan (tree type)
{
nan_state nan (true);
set_nan (type, nan);
}
// Build a NAN of type TYPE with SIGN.
inline void
frange::set_nan (tree type, bool sign)
{
nan_state nan (/*pos=*/!sign, /*neg=*/sign);
set_nan (type, nan);
}
// Return TRUE if range is known to be finite.
inline bool
frange::known_isfinite () const
{
if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE)
return false;
return (!maybe_isnan () && !real_isinf (&m_min) && !real_isinf (&m_max));
}
// Return TRUE if range may be infinite.
inline bool
frange::maybe_isinf () const
{
if (undefined_p () || m_kind == VR_ANTI_RANGE || m_kind == VR_NAN)
return false;
if (varying_p ())
return true;
return real_isinf (&m_min) || real_isinf (&m_max);
}
// Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF].
inline bool
frange::known_isinf () const
{
return (m_kind == VR_RANGE
&& !maybe_isnan ()
&& real_identical (&m_min, &m_max)
&& real_isinf (&m_min));
}
// Return TRUE if range is possibly a NAN.
inline bool
frange::maybe_isnan () const
{
if (undefined_p ())
return false;
return m_pos_nan || m_neg_nan;
}
// Return TRUE if range is possibly a NAN with SIGN.
inline bool
frange::maybe_isnan (bool sign) const
{
if (undefined_p ())
return false;
if (sign)
return m_neg_nan;
return m_pos_nan;
}
// Return TRUE if range is a +NAN or -NAN.
inline bool
frange::known_isnan () const
{
return m_kind == VR_NAN;
}
// If the signbit for the range is known, set it in SIGNBIT and return
// TRUE.
inline bool
frange::signbit_p (bool &signbit) const
{
if (undefined_p ())
return false;
// NAN with unknown sign.
if (m_pos_nan && m_neg_nan)
return false;
// No NAN.
if (!m_pos_nan && !m_neg_nan)
{
if (m_min.sign == m_max.sign)
{
signbit = m_min.sign;
return true;
}
return false;
}
// NAN with known sign.
bool nan_sign = m_neg_nan;
if (known_isnan ()
|| (nan_sign == m_min.sign && nan_sign == m_max.sign))
{
signbit = nan_sign;
return true;
}
return false;
}
// If range has a NAN with a known sign, set it in SIGNBIT and return
// TRUE.
inline bool
frange::nan_signbit_p (bool &signbit) const
{
if (undefined_p ())
return false;
if (m_pos_nan == m_neg_nan)
return false;
signbit = m_neg_nan;
return true;
}
void frange_nextafter (enum machine_mode, REAL_VALUE_TYPE &,
const REAL_VALUE_TYPE &);
void frange_arithmetic (enum tree_code, tree, REAL_VALUE_TYPE &,
const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
const REAL_VALUE_TYPE &);
// Return true if TYPE1 and TYPE2 are compatible range types.
inline bool
range_compatible_p (tree type1, tree type2)
{
// types_compatible_p requires conversion in both directions to be useless.
// GIMPLE only requires a cast one way in order to be compatible.
// Ranges really only need the sign and precision to be the same.
return TYPE_SIGN (type1) == TYPE_SIGN (type2)
&& (TYPE_PRECISION (type1) == TYPE_PRECISION (type2)
// FIXME: As PR112788 shows, for now on rs6000 _Float128 has
// type precision 128 while long double has type precision 127
// but both have the same mode so their precision is actually
// the same, workaround it temporarily.
|| (SCALAR_FLOAT_TYPE_P (type1)
&& TYPE_MODE (type1) == TYPE_MODE (type2)));
}
#endif // GCC_VALUE_RANGE_H