| /* Support routines for value ranges. | |
| Copyright (C) 2019-2026 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 representing subsets of values for various | |
| // supported types, such as the possible values of a variable. | |
| // | |
| // There are subclasses for each of integer, floating point, and pointer | |
| // types, each with their own strategies for efficiently representing | |
| // subsets of values. | |
| // | |
| // For efficiency, we can't precisely represent any arbitrary subset | |
| // of values of a type (which could require 2^N bits for a type of size N) | |
| // Hence operations on the subclasses may introduce imprecision | |
| // due to over-approximating the possible subsets. | |
| // | |
| // 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; | |
| // True if val == 0 may hold for some value in the range; for a float | |
| // range that means +0.0 or -0.0. | |
| virtual bool contains_zero_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; | |
| virtual void verify_range () const { } | |
| protected: | |
| vrange (enum value_range_discriminator d) : m_discriminator (d) { } | |
| enum value_range_kind m_kind : 8; | |
| const enum 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 of a value. Zero bits | |
| // in MASK cover constant values. Set bits in MASK cover unknown | |
| // values. VALUE are the known bits for the bits where MASK is zero, | |
| // and must be zero for the unknown bits where MASK is set (needed as an | |
| // optimization of union and intersect) | |
| // For example: | |
| // VALUE: [..., 0, 1, 0] | |
| // MASK: [..., 1, 0, 0] | |
| // ^ ^ ^ | |
| // | | known bit: {0} | |
| // | known bit: {1} | |
| // unknown bit: {0, 1} | |
| class irange_bitmask | |
| { | |
| public: | |
| irange_bitmask () { /* uninitialized */ } | |
| irange_bitmask (unsigned prec) { set_unknown (prec); } | |
| irange_bitmask (const wide_int &value, const wide_int &mask); | |
| irange_bitmask (tree type, const wide_int &min, const wide_int &max); | |
| 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); | |
| bool 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 range_from_mask (irange &r, tree type) const; | |
| bool member_p (const wide_int &val) 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 compatibility | |
| // 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 (); | |
| } | |
| // Return FALSE if the bitmask intersection is undefined. | |
| inline bool | |
| irange_bitmask::intersect (const irange_bitmask &src) | |
| { | |
| // If we have two known bits that are incompatible, the resulting | |
| // bit and therefore entire range is undefined. Return FALSE. | |
| if (wi::bit_and (~(m_mask | src.m_mask), | |
| m_value ^ src.m_value) != 0) | |
| return false; | |
| else | |
| { | |
| m_mask = m_mask & src.m_mask; | |
| m_value = m_value | src.m_value; | |
| } | |
| if (flag_checking) | |
| verify_mask (); | |
| return true; | |
| } | |
| // A subset of possible values for an integer type, leaving | |
| // allocation of storage to subclasses. | |
| 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 contains_zero_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; | |
| bool 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; | |
| virtual void verify_range () 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 (); | |
| // 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 snap_subranges (); | |
| bool snap (const wide_int &, const wide_int &, wide_int &, wide_int &, | |
| bool &); | |
| 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 final : 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 final : 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 contains_zero_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); | |
| bool invert (); | |
| bool contains_p (const wide_int &) const; | |
| wide_int lower_bound () const; | |
| wide_int upper_bound () const; | |
| virtual void verify_range () const final override; | |
| irange_bitmask get_bitmask () const final override; | |
| void update_bitmask (const irange_bitmask &) final override; | |
| // prange interface to points to information. | |
| // It can point_to or point_away from an object. This represents both | |
| // sides of a conditional. ie: | |
| // if (p == &foo) | |
| // // p points to foo. | |
| // else | |
| // // p points away from foo. | |
| // pt_invariant () and pt_invariant_away () - Return pt if this is invariant. | |
| void set_pt (const prange &r); | |
| void set_pt (tree ptr, bool points_to_p = true); | |
| // unknown_p () is true if no object is pointed to. | |
| void set_pt_unknown (); | |
| bool pt_unknown_p () const; | |
| // Invariant points-to are is_gimple_min_invariant_p (). | |
| // Return expression or NULL_TREE for points-to or away | |
| tree pt_invariant () const; | |
| tree pt_invariant_away () const; | |
| // Return true if THIS and R both point to the same object. | |
| bool pt_invariant_p (const prange &r) const; | |
| // Return true if THIS and R both point away from the same object. | |
| bool pt_invariant_away_p (const prange &r) const; | |
| // Return true if THIS and R refer to the same object, and one is inverted | |
| // from the other, Ie, both to and away. | |
| bool pt_inverted_p (const prange &r) const; | |
| // Invert THIS if it points either to or away from an object. | |
| bool pt_invert (); | |
| // pt_base () - object/allocation the pointer refers into. | |
| tree pt_base () const; | |
| // pt_offset () - possible byte offset range from BASE. | |
| void pt_offset (irange &) const; | |
| // pt_size () - possible size range of the referenced object. | |
| void pt_size (irange &) const; | |
| protected: | |
| bool varying_compatible_p () const; | |
| tree m_type; | |
| wide_int m_min; | |
| wide_int m_max; | |
| irange_bitmask m_bitmask; | |
| // A prange can point to an object, or NOT point to an object. | |
| tree m_pt; // object points-to refers to. | |
| bool m_points_to_p; // Does it point to it (TRUE), or not (FALSE). | |
| // If P has the same points to fields as THIS. | |
| bool pt_equal_p (const class prange &p) const; | |
| }; | |
| // 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 contains_zero_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 possible NAN state of a floating point value as an opaque object. | |
| // This represents one of the four subsets of { -NaN, +NaN }, | |
| // i.e. one of {}, { -NaN }, { +NaN}, or { -NaN, +NaN }. | |
| 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 sub-range in an frange. | |
| struct frange_pair | |
| { | |
| REAL_VALUE_TYPE min; | |
| REAL_VALUE_TYPE max; | |
| }; | |
| // A subset of possible values for a floating point type. | |
| // | |
| // The representation is a handful of disjoint intervals, unioned with a | |
| // subset of { -NaN, +NaN }. | |
| class frange final : 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 contains_zero_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; | |
| bool known_isnormal () const; | |
| bool known_isdenormal_or_zero () const; | |
| virtual void verify_range () const override; | |
| static const unsigned int MAX_PAIRS = 2; | |
| unsigned num_pairs () const { return m_num_ranges; } | |
| const REAL_VALUE_TYPE &lower_bound (unsigned pair) const; | |
| const REAL_VALUE_TYPE &upper_bound (unsigned pair) const; | |
| void widen (tree); | |
| 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; | |
| bool normalize_kind (); | |
| bool union_nans (const frange &); | |
| bool intersect_nans (const frange &); | |
| void set_pairs (frange_pair *, unsigned); | |
| void canonicalize_zeros (frange_pair &); | |
| void set_excluding (tree type, const REAL_VALUE_TYPE &, | |
| const REAL_VALUE_TYPE &, const nan_state &); | |
| tree m_type; | |
| frange_pair m_pairs[MAX_PAIRS]; | |
| unsigned char m_num_ranges; | |
| 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_pairs[0].min; | |
| } | |
| inline const REAL_VALUE_TYPE & | |
| frange::upper_bound () const | |
| { | |
| gcc_checking_assert (!undefined_p () && !known_isnan ()); | |
| return m_pairs[m_num_ranges - 1].max; | |
| } | |
| inline const REAL_VALUE_TYPE & | |
| frange::lower_bound (unsigned pair) const | |
| { | |
| gcc_checking_assert (!undefined_p () && !known_isnan ()); | |
| gcc_checking_assert (pair < m_num_ranges); | |
| return m_pairs[pair].min; | |
| } | |
| inline const REAL_VALUE_TYPE & | |
| frange::upper_bound (unsigned pair) const | |
| { | |
| gcc_checking_assert (!undefined_p () && !known_isnan ()); | |
| gcc_checking_assert (pair < m_num_ranges); | |
| return m_pairs[pair].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 { } | |
| }; | |
| // 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_range_class() 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_range_class(). | |
| 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_range_class (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; | |
| void print (pretty_printer *) 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 contains_zero_p () const { return m_vrange->contains_zero_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); } | |
| void verify_range () const { m_vrange->verify_range (); } | |
| 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_range_class() 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. | |
| // Note that this does *not* set the type of the underlying vrange. | |
| inline void | |
| value_range::set_range_class (tree type) | |
| { | |
| if (m_vrange) | |
| m_vrange->~vrange (); | |
| init (type); | |
| } | |
| // Initialize object to an UNDEFINED range that can hold ranges of | |
| // TYPE. | |
| // Note that this does *not* set the type of the underlying vrange. | |
| 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::contains_zero_p () const | |
| { | |
| if (undefined_p ()) | |
| return false; | |
| wide_int zero = wi::zero (TYPE_PRECISION (type ())); | |
| return contains_p (zero); | |
| } | |
| 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 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; | |
| set_pt_unknown (); | |
| } | |
| 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)); | |
| set_pt_unknown (); | |
| 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)); | |
| set_pt_unknown (); | |
| 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); | |
| set_pt_unknown (); | |
| 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 | |
| { | |
| bool ret = m_kind == VR_RANGE && m_min == 0 && m_max == 0; | |
| return ret; | |
| } | |
| inline bool | |
| prange::contains_zero_p () const | |
| { | |
| if (undefined_p ()) | |
| return false; | |
| wide_int zero = wi::zero (TYPE_PRECISION (type ())); | |
| return contains_p (zero); | |
| } | |
| 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 () && pt_unknown_p ()); | |
| } | |
| inline irange_bitmask | |
| prange::get_bitmask () const | |
| { | |
| return m_bitmask; | |
| } | |
| inline bool | |
| prange::fits_p (const vrange &) const | |
| { | |
| return true; | |
| } | |
| // Set this range's point-to object to PTR, and POINTS_TO_P is TRUE if it | |
| // does point to it, and FALSE if it does not point to it. | |
| inline void | |
| prange::set_pt (const prange &r) | |
| { | |
| // Do not set points-to info if this is zero or undefined. | |
| if (!r.pt_unknown_p () && (undefined_p () || zero_p())) | |
| return; | |
| m_pt = r.m_pt; | |
| m_points_to_p = r.m_points_to_p; | |
| if (r.undefined_p ()) | |
| return; | |
| // Check whether this is now VARYING or not. | |
| if (varying_compatible_p ()) | |
| set_varying (type ()); | |
| else | |
| m_kind = VR_RANGE; | |
| } | |
| // prange_pt methods. | |
| // ------------------------------------------------------------------ | |
| inline void | |
| prange::set_pt_unknown () | |
| { | |
| m_pt = NULL_TREE; | |
| m_points_to_p = false; | |
| } | |
| inline bool | |
| prange::pt_unknown_p () const | |
| { | |
| return (m_pt == NULL_TREE); | |
| } | |
| inline bool | |
| prange::pt_equal_p (const prange &p) const | |
| { | |
| // A prange object invokes vrp_operand_equal_p as we want ranges to compare | |
| // equal to each other if they refer to the same object, even if the | |
| // tree has become unshared. | |
| return (m_points_to_p == p.m_points_to_p | |
| && vrp_operand_equal_p (m_pt, p.m_pt)); | |
| } | |
| inline bool | |
| prange::pt_inverted_p (const prange &r) const | |
| { | |
| return m_pt && vrp_operand_equal_p (m_pt, r.m_pt) | |
| && m_points_to_p != r.m_points_to_p; | |
| } | |
| inline bool | |
| prange::pt_invert () | |
| { | |
| if (m_pt) | |
| { | |
| m_points_to_p = !m_points_to_p; | |
| return true; | |
| } | |
| return false; | |
| } | |
| inline tree | |
| prange::pt_invariant () const | |
| { | |
| if (m_pt && m_points_to_p) | |
| return m_pt; | |
| return NULL_TREE; | |
| } | |
| inline tree | |
| prange::pt_invariant_away () const | |
| { | |
| if (m_pt && !m_points_to_p) | |
| return m_pt; | |
| return NULL_TREE; | |
| } | |
| inline bool | |
| prange::pt_invariant_p (const prange &r) const | |
| { | |
| if (m_pt && m_points_to_p && vrp_operand_equal_p (r.m_pt, m_pt) | |
| && m_points_to_p == r.m_points_to_p) | |
| return true; | |
| return false; | |
| } | |
| inline bool | |
| prange::pt_invariant_away_p (const prange &r) const | |
| { | |
| if (m_pt && !m_points_to_p && vrp_operand_equal_p (r.m_pt, m_pt) | |
| && m_points_to_p == r.m_points_to_p) | |
| return true; | |
| return false; | |
| } | |
| // ----------------------------------------------------------------------- | |
| 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_num_ranges = 1; | |
| m_pairs[0].min = frange_val_min (type); | |
| m_pairs[0].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_num_ranges = 1; | |
| m_pos_nan = false; | |
| m_neg_nan = false; | |
| // Leave the rest undefined; as it speeds up initializing undefined ranges. | |
| 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_num_ranges = 1; | |
| 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 (&lower_bound ()) | |
| && !real_isinf (&upper_bound ())); | |
| } | |
| // Return TRUE if range is known to be normal. | |
| inline bool | |
| frange::known_isnormal () const | |
| { | |
| if (!known_isfinite ()) | |
| return false; | |
| machine_mode mode = TYPE_MODE (type ()); | |
| const REAL_VALUE_TYPE &min = lower_bound (); | |
| const REAL_VALUE_TYPE &max = upper_bound (); | |
| return (!real_isdenormal (&min, mode) && !real_isdenormal (&max, mode) | |
| && !real_iszero (&min) && !real_iszero (&max) | |
| && (!real_isneg (&min) || real_isneg (&max))); | |
| } | |
| // Return TRUE if range is known to be denormal. | |
| inline bool | |
| frange::known_isdenormal_or_zero () const | |
| { | |
| if (!known_isfinite ()) | |
| return false; | |
| machine_mode mode = TYPE_MODE (type ()); | |
| const REAL_VALUE_TYPE &min = lower_bound (); | |
| const REAL_VALUE_TYPE &max = upper_bound (); | |
| return ((real_isdenormal (&min, mode) || real_iszero (&min)) | |
| && (real_isdenormal (&max, mode) || real_iszero (&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 (&lower_bound ()) || real_isinf (&upper_bound ()); | |
| } | |
| // 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 | |
| && m_num_ranges == 1 | |
| && !maybe_isnan () | |
| && real_identical (&m_pairs[0].min, &m_pairs[0].max) | |
| && real_isinf (&m_pairs[0].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 (lower_bound ().sign == upper_bound ().sign) | |
| { | |
| signbit = lower_bound ().sign; | |
| return true; | |
| } | |
| return false; | |
| } | |
| // NAN with known sign. | |
| bool nan_sign = m_neg_nan; | |
| if (known_isnan () | |
| || (nan_sign == lower_bound ().sign | |
| && nan_sign == upper_bound ().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_PRECISION (type1) == TYPE_PRECISION (type2) | |
| && TYPE_SIGN (type1) == TYPE_SIGN (type2)); | |
| } | |
| #endif // GCC_VALUE_RANGE_H |