This patch provides the initial implementation to track points-to
information in prange.
It currently only tracks values which are is_gimple_min_invariant_p (),
and can be access via a fairly simple API:
expr will be either NULL_TREE is there is no points to info, or the
expression.
// unknown_p () is true if no object is pointed to.
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;
This patch provides the implementation and adjusts a couple of testcases
which fail due to IPA have better info immediately.
Bootstrapped on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
From 06e52fc94ee44cea8787ebd6bb4a9aae06198084 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Wed, 10 Jun 2026 13:42:27 -0400
Subject: [PATCH 1/4] 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.
---
gcc/gimple-range-fold.cc | 70 ++++++++-
gcc/ipa-prop.cc | 2 +-
gcc/range-op-ptr.cc | 43 +++++-
.../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 9dea0f64836..bae94dfa44b 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 330c8a39e5b..79b59a6a76f 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 6461530a9a3..a96e53bf141 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 8904df14aee..a110b4c4bef 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 d24f911e9cd..f6259c2a92a 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 e7a75832cb4..decf4e4d0ac 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 cd9a57d54cb..c3e62b0d73a 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 e5156b2e654..b6e6a488782 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 b2b3e445590..6a7ce4ceed3 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 280aa65fe2d..13d097666d6 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 fa17dd276af..488feb5ca0f 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 35421914a71..0ab67303009 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 ()
--
2.45.0