https://gcc.gnu.org/g:8b45cd81fc3509c5e8edf098818e3aed28e11d51
commit r17-1601-g8b45cd81fc3509c5e8edf098818e3aed28e11d51 Author: Andrew MacLeod <[email protected]> Date: Wed Jun 10 13:42:27 2026 -0400 Points-to functionality for prange. Add basic invariant points-to functionality to prange. Use tree affine for base and offset info Adjust various range-ops to support points-to. /gcc * gimple-range-fold.cc (x_fold_context): New. (pta_valueize): New. (fold_using_range::fold_stmt): Set PTA fields. (fold_using_range::range_of_phi): Ditto. * ipa-prop.cc (ipa_vr::ipa_vr): Allocate unshared range. * range-op-ptr.cc (pointer_plus_operator::fold_range): Handle PTA. (pointer_plus_operator::op2_range): Ditto. (operator_pointer_diff::fold_range): Ditto. (operator_cast::fold_range): Ditto. (operator_cast::lhs_op1_relation): Ditto. (operator_equal::fold_range): Ditto. (operator_not_equal::fold_range): Ditto. * value-query.cc (range_query::get_tree_range): Ditto. * value-range-pretty-print.cc (vrange_printer::print_pt): New. (vrange_printer::visit): Add print support. * value-range-pretty-print.h (vrange_printer::print_pt): New prototype. * value-range-storage.cc (vrange_allocator::clone): Allow unshared expression allocation. (vrange_storage::alloc): Likewise. (prange_storage::alloc): Likewise. (prange_storage::prange_format): Copy PTA. (prange_storage::set_prange): Ditto. (prange_storage::get_prange): Ditto. (prange_storage::equal_p): Compare points-to data. (ggc_alloc_vrange_storage): Allow unshared expression. * value-range-storage.h (clone): Adjust param list. (prange_storage::m_pt, m_points_to_p): New. * value-range.cc (inchash::add_vrange): Add points-to to hash value. (prange::set_pt): New. (prange::pt_base): New. (prange::pt_offset): New. (prange::pt_size): New. (prange::set): Initialize PTA field. (prange::union_): Handle PTA cases. (prange::intersect): Ditto. (prange::operator=): Ditto. (prange::operator==): Ditto. (prange::invert): Handle PTA cases. (prange::verify_range): Ditto. * value-range.h (set_pt, set_pt_unknown, pt_unknown_p, pt_invariant, pt_invariant_away, pt_invariant_p, pt_invariant_away_p, pt_invert, pt_inverted_p, pt_base, pt_offset, pt_size, pt_equal_p): New prange prototypes. (prange::m_pt, prange::m_points_to_p): New members. (prange::set_undefined): Clear PTA fields. (prange::set_varying): Ditto. (prange::set_nonzero): Ditto. (prange::set_zero): Ditto. (prange::zero_p): Check PTA fields. (varying_compatible_p): Ditto. (prange::set_pt): New. (prange::set_pt_unknown): New. (prange::pt_unknown_p): New. (prange::pt_equal_p): New prange prototypes. (prange::pt_inverted_p): New. (prange::pt_invert): New. (prange::pt_invariant): New. (prange::pt_invariant_away): New. (prange::pt_invariant_p): New. (prange::pt_invariant_away_p): New. gcc/testsuite/ * gcc.dg/Wreturn-local-addr-9.c: Remove xfail. * gcc.c-torture/execute/builtins/strncat-chk.c: Adjust for new prange points_to capabilities. Diff: --- gcc/gimple-range-fold.cc | 70 +++++++++- gcc/ipa-prop.cc | 2 +- gcc/range-op-ptr.cc | 43 +++++- .../gcc.c-torture/execute/builtins/strncat-chk.c | 14 -- gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c | 2 +- gcc/value-query.cc | 15 ++- gcc/value-range-pretty-print.cc | 45 +++++++ gcc/value-range-pretty-print.h | 1 + gcc/value-range-storage.cc | 33 +++-- gcc/value-range-storage.h | 13 +- gcc/value-range.cc | 144 +++++++++++++++++++- gcc/value-range.h | 145 ++++++++++++++++++++- 12 files changed, 478 insertions(+), 49 deletions(-) diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 9dea0f64836a..bae94dfa44ba 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -651,6 +651,25 @@ gimple_range_adjustment (vrange &res, const gimple *stmt) } } +// Provide context to the gimple fold callback. + +static struct +{ + gimple *m_stmt; + range_query *m_query; +} x_fold_context; + +// Gimple fold callback. + +static tree +pta_valueize (tree name) +{ + tree ret + = x_fold_context.m_query->value_of_expr (name, x_fold_context.m_stmt); + + return ret ? ret : name; +} + // Calculate a range for statement S and return it in R. If NAME is provided it // represents the SSA_NAME on the LHS of the statement. It is only required // if there is more than one lhs/output. If a range cannot @@ -721,6 +740,34 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name) gcc_checking_assert (range_compatible_p (r.type (), TREE_TYPE (name))); range_cast (r, TREE_TYPE (name)); } + + // IF this is not a prange, we are done. + if (!is_a <prange> (r)) + return true; + + prange &p = as_a <prange> (r); + // Check to see if points_to should be set. + if (p.pt_unknown_p () && name && gimple_code (s) == GIMPLE_ASSIGN) + { + tree rhs = gimple_assign_rhs1 (s); + tree_code code = gimple_assign_rhs_code (s); + // If code is SSA_NAME, any points to would already be copied. + if (code != SSA_NAME && get_gimple_rhs_class (code) == GIMPLE_SINGLE_RHS + && TREE_CODE (rhs) == ADDR_EXPR) + { + p.set_pt (rhs, true); + } + else + { + // If we couldn't find anything, try fold. + x_fold_context = { s, src.query () }; + rhs = gimple_fold_stmt_to_constant_1 (s, pta_valueize, pta_valueize); + if (rhs && TREE_CODE (rhs) == ADDR_EXPR) + { + p.set_pt (rhs, true); + } + } + } return true; } @@ -1172,7 +1219,7 @@ fold_using_range::range_of_phi (vrange &r, gphi *phi, fur_source &src) seen_arg = true; single_arg = arg; } - else if (single_arg != arg) + else if (!vrp_operand_equal_p (single_arg, arg)) single_arg = NULL_TREE; // Once the value reaches varying, stop looking. @@ -1210,14 +1257,27 @@ fold_using_range::range_of_phi (vrange &r, gphi *phi, fur_source &src) if (single_arg) src.register_relation (phi, VREL_EQ, phi_def, single_arg); } - else if (src.get_operand (arg_range, single_arg) - && arg_range.singleton_p ()) + else if (src.get_operand (arg_range, single_arg)) { + // Check if the single argument points to a specific object. + if (is_a <prange> (arg_range)) + { + prange &ptr = as_a <prange> (arg_range); + // If it doesn't already point at something, set points to. + if (!ptr.pt_unknown_p () + && TREE_CODE (single_arg) == ADDR_EXPR) + ptr.set_pt (single_arg, true); + r = ptr; + return true; + } // Numerical arguments that are a constant can be returned as // the constant. This can help fold later cases where even this // constant might have been UNDEFINED via an unreachable edge. - r = arg_range; - return true; + if (arg_range.singleton_p ()) + { + r = arg_range; + return true; + } } } diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index 330c8a39e5bd..79b59a6a76f9 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -148,7 +148,7 @@ ipa_vr::ipa_vr () } ipa_vr::ipa_vr (const vrange &r) - : m_storage (ggc_alloc_vrange_storage (r)), + : m_storage (ggc_alloc_vrange_storage (r, false /* shared_p */)), m_type (r.type ()) { } diff --git a/gcc/range-op-ptr.cc b/gcc/range-op-ptr.cc index 6461530a9a31..a96e53bf1412 100644 --- a/gcc/range-op-ptr.cc +++ b/gcc/range-op-ptr.cc @@ -370,24 +370,29 @@ pointer_plus_operator::fold_range (prange &r, tree type, else r.set_varying (type); + // If op1 refers to an object, op1 + 0 will also refer to the object. + if (rh_lb == rh_ub && rh_lb == 0) + r.set_pt (op1); + update_known_bitmask (r, POINTER_PLUS_EXPR, op1, op2); return true; } bool pointer_plus_operator::op2_range (irange &r, tree type, - const prange &lhs ATTRIBUTE_UNUSED, - const prange &op1 ATTRIBUTE_UNUSED, + const prange &lhs, + const prange &op1, relation_trio trio) const { relation_kind rel = trio.lhs_op1 (); r.set_varying (type); // If the LHS and OP1 are equal, the op2 must be zero. - if (rel == VREL_EQ) + if (rel == VREL_EQ || lhs.pt_invariant_p (op1)) r.set_zero (type); // If the LHS and OP1 are not equal, the offset must be non-zero. - else if (rel == VREL_NE) + // AWM: Check if aliasing may mean we can't do the not_equal check. + else if (rel == VREL_NE || lhs.pt_inverted_p (op1)) r.set_nonzero (type); else return false; @@ -490,6 +495,9 @@ operator_pointer_diff::fold_range (irange &r, tree type, r.set_varying (type); relation_kind rel = trio.op1_op2 (); op1_op2_relation_effect (r, type, op1, op2, rel); + // if op1 and op2 point to the same object, the diff is 0. + if (op1.pt_invariant_p (op2)) + r.set_zero (type); update_bitmask (r, op1, op2); return true; } @@ -564,6 +572,10 @@ operator_cast::fold_range (prange &r, tree type, return true; r.set (type, inner.lower_bound (), inner.upper_bound ()); + + // The resulting pointer still points to the same object. + r.set_pt (inner); + r.update_bitmask (inner.get_bitmask ()); return true; } @@ -725,6 +737,15 @@ operator_cast::lhs_op1_relation (const prange &lhs, return VREL_VARYING; } + // If the pointer precisions are the same, check for equality and + // inequality in the points to fields. + if (lhs_prec == op1_prec) + { + if (lhs.pt_invariant_p (op1)) + return VREL_EQ; + if (lhs.pt_inverted_p (op1)) + return VREL_NE; + } unsigned prec = MIN (lhs_prec, op1_prec); return bits_to_pe (prec); } @@ -878,7 +899,12 @@ operator_equal::fold_range (irange &r, tree type, // consist of a single value, and then compare them. bool op1_const = wi::eq_p (op1.lower_bound (), op1.upper_bound ()); bool op2_const = wi::eq_p (op2.lower_bound (), op2.upper_bound ()); - if (op1_const && op2_const) + // Check for points to equality and inequality first. + if (op1.pt_invariant_p (op2)) + r = range_true (type); + else if (op1.pt_inverted_p (op2)) + r = range_false (type); + else if (op1_const && op2_const) { if (wi::eq_p (op1.lower_bound (), op2.upper_bound())) r = range_true (type); @@ -977,7 +1003,12 @@ operator_not_equal::fold_range (irange &r, tree type, // consist of a single value, and then compare them. bool op1_const = wi::eq_p (op1.lower_bound (), op1.upper_bound ()); bool op2_const = wi::eq_p (op2.lower_bound (), op2.upper_bound ()); - if (op1_const && op2_const) + // Check for points to equality and inequality first. + if (op1.pt_inverted_p (op2)) + r = range_true (type); + else if (op1.pt_invariant_p (op2)) + r = range_false (type); + else if (op1_const && op2_const) { if (wi::ne_p (op1.lower_bound (), op2.upper_bound())) r = range_true (type); diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c index 8904df14aee5..a110b4c4befc 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strncat-chk.c @@ -67,20 +67,6 @@ test1 (void) abort (); strcat_disallowed = 0; - /* These __strncat_chk calls should be optimized into __strcat_chk, - as strlen (src) <= len. */ - strcpy (dst, s1); - if (strncat (dst, "foo", 3) != dst || strcmp (dst, "hello worldfoo")) - abort (); - strcpy (dst, s1); - if (strncat (dst, "foo", 100) != dst || strcmp (dst, "hello worldfoo")) - abort (); - strcpy (dst, s1); - if (strncat (dst, s1, 100) != dst || strcmp (dst, "hello worldhello world")) - abort (); - if (chk_calls != 3) - abort (); - chk_calls = 0; /* The following calls have side-effects in dest, so are not checked. */ strcpy (dst, s1); d2 = dst; diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c index d24f911e9cd1..f6259c2a92a0 100644 --- a/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c @@ -44,7 +44,7 @@ struct S* g (int i) } which leads to: */ - return p; /* { dg-bogus "may return address of local variable" "" { xfail *-*-* } } */ + return p; /* Whereas as -O2 the pass sees: diff --git a/gcc/value-query.cc b/gcc/value-query.cc index e7a75832cb4b..decf4e4d0ac0 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -388,13 +388,16 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt, case ADDR_EXPR: { - // Handle &var which can show up in phi arguments. + // Handle &expr, and set points to. if (tree_single_nonzero_p (expr)) - { - r.set_nonzero (type); - return true; - } - break; + r.set_nonzero (type); + else + r.set_varying (type); + // Set points to field. + gcc_checking_assert (is_a <prange> (r)); + prange &ptr = as_a <prange> (r); + ptr.set_pt (expr, true); + return true; } default: diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc index cd9a57d54cb0..c3e62b0d73a1 100644 --- a/gcc/value-range-pretty-print.cc +++ b/gcc/value-range-pretty-print.cc @@ -112,6 +112,49 @@ vrange_printer::visit (const irange &r) const print_irange_bitmasks (pp, r.m_bitmask); } +void +vrange_printer::print_pt (const prange &r) const +{ + bool points_to_p = false; + tree pt = r.pt_invariant (); + if (pt) + points_to_p = true; + else + pt = r.pt_invariant_away (); + + if (pt) + { + if (points_to_p) + pp_string (pp, " -> "); + else + pp_string (pp, " ! -> "); + dump_generic_node (pp, pt, 0, TDF_NONE | TDF_NOUID, false); + if (points_to_p) + { + int_range_max sz; + int_range_max off; + tree b; + b = r.pt_base (); + r.pt_offset(off); + r.pt_size (sz); + pp_string (pp, " { Base: "); + dump_generic_node (pp, b, 0, TDF_NONE | TDF_NOUID, false); + if (!off.varying_p () && !off.undefined_p () && !off.zero_p ()) + { + pp_string (pp, " ; Off: "); + visit (off); + } + if (!sz.varying_p () && !sz.undefined_p ()) + { + pp_string (pp, " ; Size: "); + visit (sz); + } + pp_string (pp, " }"); + } + + } +} + void vrange_printer::visit (const prange &r) const { @@ -135,6 +178,8 @@ vrange_printer::visit (const prange &r) const print_int_bound (pp, r.upper_bound (), r.type ()); pp_character (pp, ']'); print_irange_bitmasks (pp, r.m_bitmask); + // Dump the points to field if there is one. + print_pt (r); } void diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h index e5156b2e6548..b6e6a4887827 100644 --- a/gcc/value-range-pretty-print.h +++ b/gcc/value-range-pretty-print.h @@ -30,6 +30,7 @@ public: void visit (const prange &) const override; void visit (const frange &) const override; private: + void print_pt (const prange &) const; void print_frange_nan (const frange &) const; void print_real_value (tree type, const REAL_VALUE_TYPE &r) const; diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index b2b3e4455905..6a7ce4ceed3c 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "backend.h" #include "tree.h" #include "gimple.h" +#include "gimplify.h" #include "ssa.h" #include "tree-pretty-print.h" #include "fold-const.h" @@ -108,9 +109,9 @@ vrange_allocator::free (void *p) // it. vrange_storage * -vrange_allocator::clone (const vrange &r) +vrange_allocator::clone (const vrange &r, bool shared_p) { - return vrange_storage::alloc (*m_alloc, r); + return vrange_storage::alloc (*m_alloc, r, shared_p); } vrange_storage * @@ -141,12 +142,13 @@ vrange_allocator::clone_undefined (tree type) // it. Return NULL if R is unsupported. vrange_storage * -vrange_storage::alloc (vrange_internal_alloc &allocator, const vrange &r) +vrange_storage::alloc (vrange_internal_alloc &allocator, const vrange &r, + bool shared_p) { if (is_a <irange> (r)) return irange_storage::alloc (allocator, as_a <irange> (r)); if (is_a <prange> (r)) - return prange_storage::alloc (allocator, as_a <prange> (r)); + return prange_storage::alloc (allocator, as_a <prange> (r), shared_p); if (is_a <frange> (r)) return frange_storage::alloc (allocator, as_a <frange> (r)); return NULL; @@ -591,7 +593,8 @@ frange_storage::fits_p (const frange &) const //============================================================================ prange_storage * -prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r) +prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r, + bool shared_p) { unsigned num_words; prange_format (r, num_words); @@ -606,6 +609,9 @@ prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r) size_t size = sizeof (prange_storage) + extra_size; prange_storage *p = static_cast <prange_storage *> (allocator.alloc (size)); new (p) prange_storage (r); + if (p->m_pt && !shared_p) + p->m_pt = unshare_expr_without_location (p->m_pt); + return p; } @@ -657,7 +663,8 @@ prange_storage::prange_format (const prange &r, unsigned &num_words) num_words += 2; // PR_FULL must have a bitmask or points to, or it should be PR_VARYING. - gcc_checking_assert (kind != PR_FULL || !r.get_bitmask ().unknown_p ()); + gcc_checking_assert (kind != PR_FULL || !r.get_bitmask ().unknown_p () + || r.m_pt != NULL_TREE); return kind; } @@ -667,6 +674,8 @@ prange_storage::set_prange (const prange &r) unsigned num_words; m_kind = prange_format (r, num_words); m_has_bitmask = !r.get_bitmask ().unknown_p (); + m_pt = r.m_pt; + m_points_to_p = r.m_points_to_p; unsigned index = 0; @@ -751,6 +760,9 @@ prange_storage::get_prange (prange &r, tree type) const else r.m_bitmask.set_unknown (TYPE_PRECISION (type)); + r.m_points_to_p = m_points_to_p; + r.m_pt = m_pt; + if (flag_checking) r.verify_range (); } @@ -803,6 +815,11 @@ prange_storage::equal_p (const prange &r) const if (!r.m_bitmask.unknown_p ()) return false; + if (m_pt != r.m_pt) + return false; + if (m_points_to_p != r.m_points_to_p) + return false; + return true; } @@ -827,7 +844,7 @@ vrange_storage *ggc_alloc_vrange_storage (tree type) return ggc_vrange_allocator.clone_varying (type); } -vrange_storage *ggc_alloc_vrange_storage (const vrange &r) +vrange_storage *ggc_alloc_vrange_storage (const vrange &r, bool shared_p) { - return ggc_vrange_allocator.clone (r); + return ggc_vrange_allocator.clone (r, shared_p); } diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h index 280aa65fe2dc..13d097666d6d 100644 --- a/gcc/value-range-storage.h +++ b/gcc/value-range-storage.h @@ -30,7 +30,7 @@ public: // Use GC memory when GC is true, otherwise use obstacks. vrange_allocator (bool gc = false); ~vrange_allocator (); - class vrange_storage *clone (const vrange &r); + class vrange_storage *clone (const vrange &r, bool shared_p = true); vrange_storage *clone_varying (tree type); vrange_storage *clone_undefined (tree type); void *alloc (size_t size); @@ -49,7 +49,8 @@ private: class GTY((desc ("%h.m_discriminator"), tag("VR_UNKNOWN"))) vrange_storage { public: - static vrange_storage *alloc (vrange_internal_alloc &, const vrange &); + static vrange_storage *alloc (vrange_internal_alloc &, const vrange &, + bool shared_p = true); void get_vrange (vrange &r, tree type) const; void set_vrange (const vrange &r); bool fits_p (const vrange &r) const; @@ -121,7 +122,8 @@ public: friend void gt_pch_nx_vrange_storage(void *); friend void gt_pch_p_14vrange_storage(void *, void *, gt_pointer_operator, void *); - static prange_storage *alloc (vrange_internal_alloc &, const prange &); + static prange_storage *alloc (vrange_internal_alloc &, const prange &, + bool shared_p = true); void set_prange (const prange &r); void get_prange (prange &r, tree type) const; bool equal_p (const prange &r) const; @@ -135,6 +137,8 @@ private: enum prange_kind m_kind; bool m_has_bitmask; + bool m_points_to_p; + tree m_pt; // We don't use TRAILING_WIDE_INT_ACCESSOR because the getters here // must be const. Perhaps TRAILING_WIDE_INT_ACCESSOR could be made @@ -170,6 +174,7 @@ class GTY((tag ("VR_FRANGE"))) frange_storage : public vrange_storage }; extern vrange_storage *ggc_alloc_vrange_storage (tree type); -extern vrange_storage *ggc_alloc_vrange_storage (const vrange &); +extern vrange_storage *ggc_alloc_vrange_storage (const vrange &, + bool shared_p = true); #endif // GCC_VALUE_RANGE_STORAGE_H diff --git a/gcc/value-range.cc b/gcc/value-range.cc index fa17dd276afb..488feb5ca0ff 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -30,6 +30,8 @@ along with GCC; see the file COPYING3. If not see #include "value-range-pretty-print.h" #include "fold-const.h" #include "gimple-range.h" +#include "tree-dfa.h" +#include "tree-affine.h" // Return the bitmask inherent in a range : TYPE [MIN, MAX]. // This used to be get_bitmask_from_range (). @@ -458,6 +460,14 @@ add_vrange (const vrange &v, inchash::hash &hstate, irange_bitmask bm = r.get_bitmask (); hstate.add_wide_int (bm.value ()); hstate.add_wide_int (bm.mask ()); + bool flag = false; + tree tmp = r.pt_invariant (); + if (tmp) + flag = true; + else + tmp = r.pt_invariant_away (); + hstate.add_ptr (tmp); + hstate.add_flag (flag); } return; } @@ -516,6 +526,101 @@ irange::set_nonnegative (tree type) wi::to_wide (TYPE_MAX_VALUE (type))); } + +// Set the points to info for EXPR if possible. POINTS_TO_P is true if it +// points to EXPR, and FALSE if it points away. + +void +prange::set_pt (tree expr, bool points_to_p) +{ + gcc_checking_assert (m_kind != VR_UNDEFINED); + gcc_checking_assert (!expr || TREE_CODE (expr) != SSA_NAME); + + m_pt = NULL_TREE; + m_points_to_p = false; + + // No points to initially may make this VARYING. + if (varying_compatible_p ()) + set_varying (type ()); + else + m_kind = VR_RANGE; + + if (!expr) + return; + + gcc_checking_assert (TREE_CODE (expr) == ADDR_EXPR); + + // Ensure only constants get through for now. + if (!is_gimple_min_invariant (expr)) + return; + + aff_tree offset; + poly_widest_int size; + tree obj = TREE_OPERAND (expr, 0); + tree base = get_inner_reference_aff (obj, &offset, &size); + + if (!base) + return; + if (!offset.offset.is_constant ()) + return; + if (!size.is_constant ()) + return; + + m_pt = expr; + m_points_to_p = points_to_p; + m_kind = VR_RANGE; +} + +// Return object/allocation the pointer refers into, otherwise NULL_TREE. + +tree +prange::pt_base () const +{ + if (!m_pt) + return NULL_TREE; + + aff_tree off; + poly_widest_int sz; + + gcc_checking_assert (m_pt); + return get_inner_reference_aff (m_pt, &off, &sz); +} + +// Return possible byte offset range from BASE. + +void +prange::pt_offset (irange &r) const +{ + aff_tree off; + poly_widest_int sz; + + gcc_checking_assert (m_pt); + + get_inner_reference_aff (m_pt, &off, &sz); + gcc_checking_assert (off.offset.is_constant ()); + + widest_int w = off.offset.coeffs[0]; + wide_int w2 = wi::to_wide (wide_int_to_tree (sizetype, w)); + r.set (sizetype, w2, w2); +} + +// Return possible size range of the referenced object. + +void +prange::pt_size (irange &r) const +{ + aff_tree off; + poly_widest_int sz; + + gcc_checking_assert (m_pt); + + get_inner_reference_aff (m_pt, &off, &sz); + gcc_checking_assert (sz.is_constant ()); + + widest_int w = sz.coeffs[0]; + wide_int w2 = wi::to_wide (wide_int_to_tree (sizetype, w)); + r.set (sizetype, w2, w2); +} // Prange implementation. void @@ -561,6 +666,8 @@ prange::set (tree type, const wide_int &min, const wide_int &max, m_type = type; m_min = min; m_max = max; + set_pt_unknown (); + if (m_min == 0 && m_max == -1) { m_kind = VR_VARYING; @@ -640,6 +747,12 @@ prange::union_ (const vrange &v) prange new_range (type (), new_lb, new_ub); new_range.m_bitmask.union_ (m_bitmask); new_range.m_bitmask.union_ (r.m_bitmask); + + // Keep it simple, either both point to the same thing or both + // do not point to the same thing, or we drop the points to info. + if (pt_equal_p (r)) + new_range.set_pt (*this); + if (new_range.varying_compatible_p ()) { set_varying (type ()); @@ -690,6 +803,21 @@ prange::intersect (const vrange &v) set_undefined (); else if (!m_bitmask.intersect (r.m_bitmask)) set_undefined (); + // If only one object points to something, that is the intersection. + else if (pt_unknown_p () && !r.pt_unknown_p ()) + set_pt (r); + else if (!pt_unknown_p () && !r.pt_unknown_p ()) + { + // If both point to something, we want to be careful. Without aliasing + // 2 different values can point to the same thing, so UNDEFINED is + // not appropriate, but we want to keep the rule that intersection + // never becomes larger. + // If the other object points to something specific, and this one does + // not, use the specific one. Otherwise leave the range as is. + if (pt_invariant_away () && r.pt_invariant ()) + set_pt (r); + } + if (varying_compatible_p ()) { set_varying (type ()); @@ -711,6 +839,7 @@ prange::operator= (const prange &src) m_min = src.m_min; m_max = src.m_max; m_bitmask = src.m_bitmask; + set_pt (src); if (flag_checking) verify_range (); return *this; @@ -727,17 +856,27 @@ prange::operator== (const prange &src) const if (varying_p ()) return types_compatible_p (type (), src.type ()); + if (!pt_equal_p (src)) + return false; + return (m_min == src.m_min && m_max == src.m_max && m_bitmask == src.m_bitmask); } return false; } + void prange::invert () { gcc_checking_assert (!undefined_p () && !varying_p ()); + // Invert the points_to object. If that worked, this is done. + if (pt_invert ()) + return; + else + set_pt_unknown (); + wide_int new_lb, new_ub; unsigned prec = TYPE_PRECISION (type ()); wide_int type_min = wi::zero (prec); @@ -771,7 +910,10 @@ prange::verify_range () const gcc_checking_assert (m_discriminator == VR_PRANGE); if (m_kind == VR_UNDEFINED) - return; + { + gcc_checking_assert (pt_unknown_p ()); + return; + } gcc_checking_assert (supports_p (type ())); diff --git a/gcc/value-range.h b/gcc/value-range.h index 35421914a717..0ab673030092 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -440,6 +440,41 @@ public: 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 present and identical for THIS and R. + bool pt_invariant_p (const prange &r) const; + bool pt_invariant_away_p (const prange &r) const; + + bool pt_invert (); + bool pt_inverted_p (const prange &r) const; + + // 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; @@ -447,6 +482,12 @@ protected: 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 @@ -1329,6 +1370,7 @@ inline void prange::set_undefined () { m_kind = VR_UNDEFINED; + set_pt_unknown (); } inline void @@ -1339,6 +1381,7 @@ prange::set_varying (tree 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 (); @@ -1352,6 +1395,7 @@ prange::set_nonzero (tree 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 (); @@ -1365,6 +1409,7 @@ prange::set_zero (tree 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 (); @@ -1379,7 +1424,7 @@ prange::contains_p (tree cst) const inline bool prange::zero_p () const { - return m_kind == VR_RANGE && m_min == 0 && m_max == 0; + return m_kind == VR_RANGE && m_min == 0 && m_max == 0 && pt_unknown_p (); } inline bool @@ -1412,8 +1457,8 @@ prange::upper_bound () const inline bool prange::varying_compatible_p () const { - return (!undefined_p () - && m_min == 0 && m_max == -1 && get_bitmask ().unknown_p ()); + return (!undefined_p () && m_min == 0 && m_max == -1 + && get_bitmask ().unknown_p () && pt_unknown_p ()); } inline irange_bitmask @@ -1428,6 +1473,100 @@ 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) +{ + 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 +{ + return (m_points_to_p == p.m_points_to_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 m_pt; + return NULL_TREE; +} + +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 m_pt; + return NULL_TREE; +} + + +// ----------------------------------------------------------------------- inline frange::frange ()
