/* Support routines for value ranges.
Copyright (C) 2019-2020 Free Software Foundation, Inc.
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
. */
#ifndef GCC_VALUE_RANGE_H
#define GCC_VALUE_RANGE_H
/* 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 nice guy. */
VR_LAST
};
// Helper for is_a<> to distinguish between irange and widest_irange.
enum irange_discriminator
{
IRANGE_KIND_UNKNOWN,
IRANGE_KIND_INT,
IRANGE_KIND_WIDEST_INT
};
// Range of values that can be associated with an SSA_NAME.
//
// This is the base class without any storage.
class irange
{
public:
void set (tree, tree, value_range_kind = VR_RANGE);
void set (tree);
void set_nonzero (tree);
void set_zero (tree);
enum value_range_kind kind () const;
tree min () const;
tree max () const;
/* Types of value ranges. */
bool symbolic_p () const;
bool constant_p () const;
bool undefined_p () const;
bool varying_p () const;
void set_varying (tree type);
void set_undefined ();
void union_ (const irange *);
void intersect (const irange *);
void union_ (const irange &);
void intersect (const irange &);
irange& operator= (const irange &);
bool operator== (const irange &) const;
bool operator!= (const irange &r) const { return !(*this == r); }
bool equal_p (const irange &) const;
/* Misc methods. */
tree type () const;
bool may_contain_p (tree) const;
bool zero_p () const;
bool nonzero_p () const;
bool singleton_p (tree *result = NULL) const;
void dump (FILE * = stderr) const;
void simple_dump (FILE *) const;
static bool supports_type_p (tree);
void normalize_symbolics ();
void normalize_addresses ();
bool contains_p (tree) const;
unsigned num_pairs () const;
wide_int lower_bound (unsigned = 0) const;
wide_int upper_bound (unsigned) const;
wide_int upper_bound () const;
void invert ();
protected:
void check ();
// Returns true for an old-school value_range with anti ranges.
bool simple_ranges_p () const { return m_max_ranges == 1; }
irange (tree *, unsigned);
irange (tree *, unsigned, const irange &);
private:
int value_inside_range (tree) const;
void intersect_from_wide_ints (const wide_int &, const wide_int &);
bool maybe_anti_range (const irange &) const;
void multi_range_set_anti_range (tree, tree);
void multi_range_union (const irange &);
void multi_range_intersect (const irange &);
tree tree_lower_bound (unsigned = 0) const;
tree tree_upper_bound (unsigned) const;
bool compatible_copy_p (const irange &) const;
void copy_compatible_range (const irange &);
void copy_simple_range (const irange &);
public:
ENUM_BITFIELD(irange_discriminator) m_discriminator : 8;
protected:
unsigned char m_num_ranges;
unsigned char m_max_ranges;
ENUM_BITFIELD(value_range_kind) m_kind : 8;
tree *m_base;
};
// int_range describes an irange with N pairs of ranges.
template
class GTY((user)) int_range : public irange
{
public:
int_range ();
int_range (tree, tree, value_range_kind = VR_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& operator= (const int_range &);
private:
template friend void gt_ggc_mx (int_range *);
template friend void gt_pch_nx (int_range *);
template friend void gt_pch_nx (int_range *,
gt_pointer_operator, void *);
/* ?? hash-traits.h has its own extern for these, which is causing
them to never be picked up by the templates. For now, define
elsewhere. */
//template friend void gt_ggc_mx (int_range *&);
//template friend void gt_pch_nx (int_range *&);
friend void gt_ggc_mx (int_range<1> *&);
friend void gt_pch_nx (int_range<1> *&);
tree m_ranges[N*2];
};
// This is a special int_range<> with only one pair, plus
// VR_ANTI_RANGE magic to describe slightly more than can be described
// in one pair. It is described in the code as a "simple range" (as
// opposed to multi-ranges which have multiple sub-ranges). It is
// provided for backward compatibility with the more limited,
// traditional value_range's.
//
// simple_ranges_p() returns true for value_range's.
//
// There are copy methods to seamlessly copy to/fro multi-ranges.
typedef int_range<1> value_range;
// An irange with "unlimited" sub-ranges. In reality we are limited
// by the number of values that fit in an `m_num_ranges'.
//
// A widest_irange starts with a handful of sub-ranges in local
// storage and will grow into the heap as necessary.
class widest_irange : public irange
{
public:
widest_irange ();
widest_irange (tree, tree, value_range_kind = VR_RANGE);
widest_irange (tree, const wide_int &, const wide_int &,
value_range_kind = VR_RANGE);
widest_irange (tree type);
widest_irange (const widest_irange &);
widest_irange (const irange &);
~widest_irange ();
widest_irange& operator= (const widest_irange &);
void resize_if_needed (unsigned);
#if CHECKING_P
static void stats_dump (FILE *);
#endif
private:
static const unsigned m_sub_ranges_in_local_storage = 5;
void init_widest_irange ();
// Memory usage stats.
void stats_register_use (void);
static int stats_used_buckets[11];
tree *m_blob;
tree m_ranges[m_sub_ranges_in_local_storage*2];
};
value_range union_helper (const value_range *, const value_range *);
value_range intersect_helper (const value_range *, const value_range *);
extern bool range_has_numeric_bounds_p (const irange *);
extern bool ranges_from_anti_range (const value_range *,
value_range *, value_range *);
extern void dump_value_range (FILE *, const irange *);
extern void dump_value_range_stats (FILE *);
extern bool vrp_val_is_min (const_tree);
extern bool vrp_val_is_max (const_tree);
extern tree vrp_val_min (const_tree);
extern tree vrp_val_max (const_tree);
extern bool vrp_operand_equal_p (const_tree, const_tree);
template
inline
int_range::int_range ()
: irange (m_ranges, N)
{
m_kind = VR_UNDEFINED;
m_num_ranges = 0;
}
inline value_range_kind
irange::kind () const
{
if (simple_ranges_p ())
return m_kind;
if (undefined_p ())
return VR_UNDEFINED;
if (varying_p ())
return VR_VARYING;
if (m_kind == VR_ANTI_RANGE)
{
// VR_ANTI_RANGE's are only valid for symbolics.
gcc_checking_assert (m_num_ranges == 1);
gcc_checking_assert (!range_has_numeric_bounds_p (this));
return VR_ANTI_RANGE;
}
return VR_RANGE;
}
inline tree
irange::type () const
{
gcc_checking_assert (!undefined_p ());
return TREE_TYPE (m_base[0]);
}
inline tree
irange::tree_lower_bound (unsigned i) const
{
return m_base[i * 2];
}
inline tree
irange::tree_upper_bound (unsigned i) const
{
return m_base[i * 2 + 1];
}
inline tree
irange::min () const
{
return tree_lower_bound (0);
}
inline tree
irange::max () const
{
if (m_num_ranges)
return tree_upper_bound (m_num_ranges - 1);
return NULL;
}
inline bool
irange::varying_p () const
{
if (simple_ranges_p ())
return m_kind == VR_VARYING;
return (m_num_ranges == 1
&& vrp_val_is_min (m_base[0])
&& vrp_val_is_max (m_base[1]));
}
inline bool
irange::undefined_p () const
{
if (simple_ranges_p ())
{
gcc_checking_assert (m_kind != VR_UNDEFINED || m_num_ranges == 0);
return m_kind == VR_UNDEFINED;
}
return m_num_ranges == 0;
}
inline bool
irange::zero_p () const
{
if (m_num_ranges == 1
&& integer_zerop (tree_lower_bound (0))
&& integer_zerop (tree_upper_bound (0)))
{
gcc_checking_assert (!simple_ranges_p () || m_kind == VR_RANGE);
return true;
}
return false;
}
inline bool
irange::nonzero_p () const
{
if (undefined_p ())
return false;
tree zero = build_zero_cst (type ());
return *this == int_range<1> (zero, zero, VR_ANTI_RANGE);
}
inline bool
irange::supports_type_p (tree type)
{
if (type && (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)))
return type;
return false;
}
inline bool
range_includes_zero_p (const irange *vr)
{
if (vr->undefined_p ())
return false;
if (vr->varying_p ())
return true;
return vr->may_contain_p (build_zero_cst (vr->type ()));
}
template
static inline void
gt_ggc_mx (int_range *x)
{
for (unsigned i = 0; i < N; ++i)
{
gt_ggc_mx (x->m_ranges[i * 2]);
gt_ggc_mx (x->m_ranges[i * 2 + 1]);
}
}
template
static inline void
gt_pch_nx (int_range *x)
{
for (unsigned i = 0; i < N; ++i)
{
gt_pch_nx (x->m_ranges[i * 2]);
gt_pch_nx (x->m_ranges[i * 2 + 1]);
}
}
template
static inline void
gt_pch_nx (int_range *x, gt_pointer_operator op, void *cookie)
{
for (unsigned i = 0; i < N; ++i)
{
op (&x->m_ranges[i * 2], cookie);
op (&x->m_ranges[i * 2 + 1], cookie);
}
}
#endif // GCC_VALUE_RANGE_H