Implement class irange, a generic multi-range implementation for value ranges. This class is API compatible with value_range, and is meant to seamlessly coexist with it. gcc/ChangeLog: * Makefile.in (GTFILES): Move value-range.h up. * gengtype-lex.l: Set yylval to handle GTY markers on templates. * ipa-cp.c (initialize_node_lattices): Call value_range constructor. (ipcp_propagate_stage): Use in-place new so value_range construct is called. * ipa-fnsummary.c (evaluate_conditions_for_known_args): Use std vec instead of GCC's vec<>. (evaluate_properties_for_edge): Adjust for std vec. (ipa_fn_summary_t::duplicate): Same. (estimate_ipcp_clone_size_and_time): Same. * ipa-prop.c (ipa_get_value_range): Use in-place new for value_range. * ipa-prop.h (struct GTY): Remove class keyword for m_vr. * range-op.cc (empty_range_check): Rename to... (empty_range_varying): ...this and adjust for varying. (undefined_shift_range_check): Adjust for irange. (range_operator::wi_fold): Same. (range_operator::fold_range): Adjust for irange. Special case single pairs for performance. (range_operator::op1_range): Adjust for irange. (range_operator::op2_range): Same. (value_range_from_overflowed_bounds): Same. (value_range_with_overflow): Same. (create_possibly_reversed_range): Same. (range_true): Same. (range_false): Same. (range_true_and_false): Same. (get_bool_state): Adjust for irange and tweak for performance. (operator_equal::fold_range): Adjust for irange. (operator_equal::op1_range): Same. (operator_equal::op2_range): Same. (operator_not_equal::fold_range): Same. (operator_not_equal::op1_range): Same. (operator_not_equal::op2_range): Same. (build_lt): Same. (build_le): Same. (build_gt): Same. (build_ge): Same. (operator_lt::fold_range): Same. (operator_lt::op1_range): Same. (operator_lt::op2_range): Same. (operator_le::fold_range): Same. (operator_le::op1_range): Same. (operator_le::op2_range): Same. (operator_gt::fold_range): Same. (operator_gt::op1_range): Same. (operator_gt::op2_range): Same. (operator_ge::fold_range): Same. (operator_ge::op1_range): Same. (operator_ge::op2_range): Same. (operator_plus::wi_fold): Same. (operator_plus::op1_range): Same. (operator_plus::op2_range): Same. (operator_minus::wi_fold): Same. (operator_minus::op1_range): Same. (operator_minus::op2_range): Same. (operator_min::wi_fold): Same. (operator_max::wi_fold): Same. (cross_product_operator::wi_cross_product): Same. (operator_mult::op1_range): New. (operator_mult::op2_range): New. (operator_mult::wi_fold): Adjust for irange. (operator_div::wi_fold): Same. (operator_exact_divide::op1_range): Same. (operator_lshift::fold_range): Same. (operator_lshift::wi_fold): Same. (operator_lshift::op1_range): New. (operator_rshift::op1_range): New. (operator_rshift::fold_range): Adjust for irange. (operator_rshift::wi_fold): Same. (operator_cast::truncating_cast_p): Abstract out from operator_cast::fold_range. (operator_cast::fold_range): Adjust for irange and tweak for performance. (operator_cast::inside_domain_p): Abstract out from fold_range. (operator_cast::fold_pair): Same. (operator_cast::op1_range): Use abstracted methods above. Adjust for irange and tweak for performance. (operator_logical_and::fold_range): Adjust for irange. (operator_logical_and::op1_range): Same. (operator_logical_and::op2_range): Same. (unsigned_singleton_p): New. (operator_bitwise_and::remove_impossible_ranges): New. (operator_bitwise_and::fold_range): New. (wi_optimize_and_or): Adjust for irange. (operator_bitwise_and::wi_fold): Same. (set_nonzero_range_from_mask): New. (operator_bitwise_and::simple_op1_range_solver): New. (operator_bitwise_and::op1_range): Adjust for irange. (operator_bitwise_and::op2_range): Same. (operator_logical_or::fold_range): Same. (operator_logical_or::op1_range): Same. (operator_logical_or::op2_range): Same. (operator_bitwise_or::wi_fold): Same. (operator_bitwise_or::op1_range): Same. (operator_bitwise_or::op2_range): Same. (operator_bitwise_xor::wi_fold): Same. (operator_bitwise_xor::op1_range): New. (operator_bitwise_xor::op2_range): New. (operator_trunc_mod::wi_fold): Adjust for irange. (operator_logical_not::fold_range): Same. (operator_logical_not::op1_range): Same. (operator_bitwise_not::fold_range): Same. (operator_bitwise_not::op1_range): Same. (operator_cst::fold_range): Same. (operator_identity::fold_range): Same. (operator_identity::op1_range): Same. (class operator_unknown): New. (operator_unknown::fold_range): New. (class operator_abs): Adjust for irange. (operator_abs::wi_fold): Same. (operator_abs::op1_range): Same. (operator_absu::wi_fold): Same. (class operator_negate): Same. (operator_negate::fold_range): Same. (operator_negate::op1_range): Same. (operator_addr_expr::fold_range): Same. (operator_addr_expr::op1_range): Same. (pointer_plus_operator::wi_fold): Same. (pointer_min_max_operator::wi_fold): Same. (pointer_and_operator::wi_fold): Same. (pointer_or_operator::op1_range): New. (pointer_or_operator::op2_range): New. (pointer_or_operator::wi_fold): Adjust for irange. (integral_table::integral_table): Add entries for IMAGPART_EXPR and POINTER_DIFF_EXPR. (range_cast): Adjust for irange. (build_range3): New. (range3_tests): New. (widest_irange_tests): New. (multi_precision_range_tests): New. (operator_tests): New. (range_tests): New. * range-op.h (class range_operator): Adjust for irange. (range_cast): Same. * tree-vrp.c (range_fold_binary_symbolics_p): Adjust for irange and tweak for performance. (range_fold_binary_expr): Same. (masked_increment): Change to extern. * tree-vrp.h (masked_increment): New. * tree.c (cache_wide_int_in_type_cache): New function abstracted out from wide_int_to_tree_1. (wide_int_to_tree_1): Cache 0, 1, and MAX for pointers. * value-range-equiv.cc (value_range_equiv::deep_copy): Use kind method. (value_range_equiv::move): Same. (value_range_equiv::check): Adjust for irange. (value_range_equiv::intersect): Same. (value_range_equiv::union_): Same. (value_range_equiv::dump): Same. * value-range.cc (irange::operator=): Same. (irange::maybe_anti_range): New. (irange::copy_legacy_range): New. (irange::set_undefined): Adjust for irange. (irange::swap_out_of_order_endpoints): Abstract out from set(). (irange::set_varying): Adjust for irange. (irange::irange_set): New. (irange::irange_set_anti_range): New. (irange::set): Adjust for irange. (value_range::set_nonzero): Move to header file. (value_range::set_zero): Move to header file. (value_range::check): Rename to... (irange::verify_range): ...this. (value_range::num_pairs): Rename to... (irange::legacy_num_pairs): ...this, and adjust for irange. (value_range::lower_bound): Rename to... (irange::legacy_lower_bound): ...this, and adjust for irange. (value_range::upper_bound): Rename to... (irange::legacy_upper_bound): ...this, and adjust for irange. (value_range::equal_p): Rename to... (irange::legacy_equal_p): ...this. (value_range::operator==): Move to header file. (irange::equal_p): New. (irange::symbolic_p): Adjust for irange. (irange::constant_p): Same. (irange::singleton_p): Same. (irange::value_inside_range): Same. (irange::may_contain_p): Same. (irange::contains_p): Same. (irange::normalize_addresses): Same. (irange::normalize_symbolics): Same. (irange::legacy_intersect): Same. (irange::legacy_union): Same. (irange::union_): Same. (irange::intersect): Same. (irange::irange_union): New. (irange::irange_intersect): New. (subtract_one): New. (irange::invert): Adjust for irange. (dump_bound_with_infinite_markers): New. (irange::dump): Adjust for irange. (debug): Add irange versions. (range_has_numeric_bounds_p): Adjust for irange. (vrp_val_max): Move to header file. (vrp_val_min): Move to header file. (DEFINE_INT_RANGE_GC_STUBS): New. (DEFINE_INT_RANGE_INSTANCE): New. * value-range.h (class irange): New. (class int_range): New. (class value_range): Rename to a instantiation of int_range. (irange::legacy_mode_p): New. (value_range::value_range): Remove. (irange::kind): New. (irange::num_pairs): Adjust for irange. (irange::type): Adjust for irange. (irange::tree_lower_bound): New. (irange::tree_upper_bound): New. (irange::type): Adjust for irange. (irange::min): Same. (irange::max): Same. (irange::varying_p): Same. (irange::undefined_p): Same. (irange::zero_p): Same. (irange::nonzero_p): Same. (irange::supports_type_p): Same. (range_includes_zero_p): Same. (gt_ggc_mx): New. (gt_pch_nx): New. (irange::irange): New. (int_range::int_range): New. (int_range::operator=): New. (irange::set): Moved from value-range.cc and adjusted for irange. (irange::set_undefined): Same. (irange::set_varying): Same. (irange::operator==): Same. (irange::lower_bound): Same. (irange::upper_bound): Same. (irange::union_): Same. (irange::intersect): Same. (irange::set_nonzero): Same. (irange::set_zero): Same. (irange::normalize_min_max): New. (vrp_val_max): Move from value-range.cc. (vrp_val_min): Same. * vr-values.c (vr_values::get_lattice_entry): Call value_range constructor.
325 lines
8.0 KiB
C++
325 lines
8.0 KiB
C++
/* Support routines for value ranges with equivalences.
|
|
Copyright (C) 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
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "backend.h"
|
|
#include "tree.h"
|
|
#include "gimple.h"
|
|
#include "ssa.h"
|
|
#include "tree-pretty-print.h"
|
|
#include "value-range-equiv.h"
|
|
|
|
value_range_equiv::value_range_equiv (tree min, tree max, bitmap equiv,
|
|
value_range_kind kind)
|
|
{
|
|
m_equiv = NULL;
|
|
set (min, max, equiv, kind);
|
|
}
|
|
|
|
value_range_equiv::value_range_equiv (const value_range &other)
|
|
{
|
|
m_equiv = NULL;
|
|
set (other.min(), other.max (), NULL, other.kind ());
|
|
}
|
|
|
|
void
|
|
value_range_equiv::set (tree min, tree max, bitmap equiv,
|
|
value_range_kind kind)
|
|
{
|
|
value_range::set (min, max, kind);
|
|
set_equiv (equiv);
|
|
if (flag_checking)
|
|
check ();
|
|
}
|
|
|
|
void
|
|
value_range_equiv::set (tree val)
|
|
{
|
|
gcc_assert (TREE_CODE (val) == SSA_NAME || is_gimple_min_invariant (val));
|
|
if (TREE_OVERFLOW_P (val))
|
|
val = drop_tree_overflow (val);
|
|
set (val, val);
|
|
}
|
|
|
|
void
|
|
value_range_equiv::set_undefined ()
|
|
{
|
|
set (NULL, NULL, NULL, VR_UNDEFINED);
|
|
}
|
|
|
|
void
|
|
value_range_equiv::set_varying (tree type)
|
|
{
|
|
value_range::set_varying (type);
|
|
equiv_clear ();
|
|
}
|
|
|
|
/* Like set, but keep the equivalences in place. */
|
|
|
|
void
|
|
value_range_equiv::update (tree min, tree max, value_range_kind kind)
|
|
{
|
|
set (min, max,
|
|
(kind != VR_UNDEFINED && kind != VR_VARYING) ? m_equiv : NULL, kind);
|
|
}
|
|
|
|
/* Copy value_range in FROM into THIS while avoiding bitmap sharing.
|
|
|
|
Note: The code that avoids the bitmap sharing looks at the existing
|
|
this->m_equiv, so this function cannot be used to initalize an
|
|
object. Use the constructors for initialization. */
|
|
|
|
void
|
|
value_range_equiv::deep_copy (const value_range_equiv *from)
|
|
{
|
|
set (from->min (), from->max (), from->m_equiv, from->kind ());
|
|
}
|
|
|
|
void
|
|
value_range_equiv::move (value_range_equiv *from)
|
|
{
|
|
set (from->min (), from->max (), NULL, from->kind ());
|
|
m_equiv = from->m_equiv;
|
|
from->m_equiv = NULL;
|
|
}
|
|
|
|
void
|
|
value_range_equiv::set_equiv (bitmap equiv)
|
|
{
|
|
if (undefined_p () || varying_p ())
|
|
equiv = NULL;
|
|
/* Since updating the equivalence set involves deep copying the
|
|
bitmaps, only do it if absolutely necessary.
|
|
|
|
All equivalence bitmaps are allocated from the same obstack. So
|
|
we can use the obstack associated with EQUIV to allocate vr->equiv. */
|
|
if (m_equiv == NULL
|
|
&& equiv != NULL)
|
|
m_equiv = BITMAP_ALLOC (equiv->obstack);
|
|
|
|
if (equiv != m_equiv)
|
|
{
|
|
if (equiv && !bitmap_empty_p (equiv))
|
|
bitmap_copy (m_equiv, equiv);
|
|
else
|
|
bitmap_clear (m_equiv);
|
|
}
|
|
}
|
|
|
|
void
|
|
value_range_equiv::check ()
|
|
{
|
|
value_range::verify_range ();
|
|
switch (kind ())
|
|
{
|
|
case VR_UNDEFINED:
|
|
case VR_VARYING:
|
|
gcc_assert (!m_equiv || bitmap_empty_p (m_equiv));
|
|
default:;
|
|
}
|
|
}
|
|
|
|
/* Return true if the bitmaps B1 and B2 are equal. */
|
|
|
|
static bool
|
|
vr_bitmap_equal_p (const_bitmap b1, const_bitmap b2)
|
|
{
|
|
return (b1 == b2
|
|
|| ((!b1 || bitmap_empty_p (b1))
|
|
&& (!b2 || bitmap_empty_p (b2)))
|
|
|| (b1 && b2
|
|
&& bitmap_equal_p (b1, b2)));
|
|
}
|
|
|
|
/* Returns TRUE if THIS == OTHER. Ignores the equivalence bitmap if
|
|
IGNORE_EQUIVS is TRUE. */
|
|
|
|
bool
|
|
value_range_equiv::equal_p (const value_range_equiv &other,
|
|
bool ignore_equivs) const
|
|
{
|
|
return (value_range::equal_p (other)
|
|
&& (ignore_equivs || vr_bitmap_equal_p (m_equiv, other.m_equiv)));
|
|
}
|
|
|
|
void
|
|
value_range_equiv::equiv_clear ()
|
|
{
|
|
if (m_equiv)
|
|
bitmap_clear (m_equiv);
|
|
}
|
|
|
|
/* Add VAR and VAR's equivalence set (VAR_VR) to the equivalence
|
|
bitmap. If no equivalence table has been created, OBSTACK is the
|
|
obstack to use (NULL for the default obstack).
|
|
|
|
This is the central point where equivalence processing can be
|
|
turned on/off. */
|
|
|
|
void
|
|
value_range_equiv::equiv_add (const_tree var,
|
|
const value_range_equiv *var_vr,
|
|
bitmap_obstack *obstack)
|
|
{
|
|
if (!m_equiv)
|
|
m_equiv = BITMAP_ALLOC (obstack);
|
|
unsigned ver = SSA_NAME_VERSION (var);
|
|
bitmap_set_bit (m_equiv, ver);
|
|
if (var_vr && var_vr->m_equiv)
|
|
bitmap_ior_into (m_equiv, var_vr->m_equiv);
|
|
}
|
|
|
|
void
|
|
value_range_equiv::intersect (const value_range_equiv *other)
|
|
{
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Intersecting\n ");
|
|
dump_value_range (dump_file, this);
|
|
fprintf (dump_file, "\nand\n ");
|
|
dump_value_range (dump_file, other);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
/* If THIS is varying we want to pick up equivalences from OTHER.
|
|
Just special-case this here rather than trying to fixup after the
|
|
fact. */
|
|
if (this->varying_p ())
|
|
this->deep_copy (other);
|
|
else
|
|
{
|
|
legacy_intersect (this, other);
|
|
if (varying_p () || undefined_p ())
|
|
equiv_clear ();
|
|
|
|
/* If the result is VR_UNDEFINED there is no need to mess with
|
|
equivalencies. */
|
|
if (!undefined_p ())
|
|
{
|
|
/* The resulting set of equivalences for range intersection
|
|
is the union of the two sets. */
|
|
if (m_equiv && other->m_equiv && m_equiv != other->m_equiv)
|
|
bitmap_ior_into (m_equiv, other->m_equiv);
|
|
else if (other->m_equiv && !m_equiv)
|
|
{
|
|
/* All equivalence bitmaps are allocated from the same
|
|
obstack. So we can use the obstack associated with
|
|
VR to allocate this->m_equiv. */
|
|
m_equiv = BITMAP_ALLOC (other->m_equiv->obstack);
|
|
bitmap_copy (m_equiv, other->m_equiv);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "to\n ");
|
|
dump_value_range (dump_file, this);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
value_range_equiv::union_ (const value_range_equiv *other)
|
|
{
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "Meeting\n ");
|
|
dump_value_range (dump_file, this);
|
|
fprintf (dump_file, "\nand\n ");
|
|
dump_value_range (dump_file, other);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
|
|
/* If THIS is undefined we want to pick up equivalences from OTHER.
|
|
Just special-case this here rather than trying to fixup after the fact. */
|
|
if (this->undefined_p ())
|
|
this->deep_copy (other);
|
|
else
|
|
{
|
|
legacy_union (this, other);
|
|
if (varying_p () || undefined_p ())
|
|
equiv_clear ();
|
|
|
|
/* The resulting set of equivalences is always the intersection of
|
|
the two sets. */
|
|
if (this->m_equiv && other->m_equiv && this->m_equiv != other->m_equiv)
|
|
bitmap_and_into (this->m_equiv, other->m_equiv);
|
|
else if (this->m_equiv && !other->m_equiv)
|
|
bitmap_clear (this->m_equiv);
|
|
}
|
|
|
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
|
{
|
|
fprintf (dump_file, "to\n ");
|
|
dump_value_range (dump_file, this);
|
|
fprintf (dump_file, "\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
value_range_equiv::dump (FILE *file) const
|
|
{
|
|
value_range::dump (file);
|
|
if ((kind () == VR_RANGE || kind () == VR_ANTI_RANGE)
|
|
&& m_equiv)
|
|
{
|
|
bitmap_iterator bi;
|
|
unsigned i, c = 0;
|
|
|
|
fprintf (file, " EQUIVALENCES: { ");
|
|
EXECUTE_IF_SET_IN_BITMAP (m_equiv, 0, i, bi)
|
|
{
|
|
print_generic_expr (file, ssa_name (i));
|
|
fprintf (file, " ");
|
|
c++;
|
|
}
|
|
fprintf (file, "} (%u elements)", c);
|
|
}
|
|
}
|
|
|
|
void
|
|
value_range_equiv::dump () const
|
|
{
|
|
dump (stderr);
|
|
}
|
|
|
|
void
|
|
dump_value_range (FILE *file, const value_range_equiv *vr)
|
|
{
|
|
if (!vr)
|
|
fprintf (file, "[]");
|
|
else
|
|
vr->dump (file);
|
|
}
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (const value_range_equiv *vr)
|
|
{
|
|
dump_value_range (stderr, vr);
|
|
}
|
|
|
|
DEBUG_FUNCTION void
|
|
debug (const value_range_equiv &vr)
|
|
{
|
|
dump_value_range (stderr, &vr);
|
|
}
|