/* Alias analysis for GNU C
- Copyright (C) 1997-2015 Free Software Foundation, Inc.
+ Copyright (C) 1997-2020 Free Software Foundation, Inc.
Contributed by John Carr (jfc@mit.edu).
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
-#include "alias.h"
-#include "symtab.h"
-#include "wide-int.h"
-#include "inchash.h"
#include "tree.h"
+#include "gimple.h"
+#include "df.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "gimple-ssa.h"
+#include "emit-rtl.h"
+#include "alias.h"
#include "fold-const.h"
#include "varasm.h"
-#include "hashtab.h"
-#include "hard-reg-set.h"
-#include "function.h"
-#include "flags.h"
-#include "statistics.h"
-#include "real.h"
-#include "fixed-value.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "calls.h"
-#include "emit-rtl.h"
-#include "stmt.h"
-#include "expr.h"
-#include "tm_p.h"
-#include "regs.h"
-#include "diagnostic-core.h"
-#include "alloc-pool.h"
#include "cselib.h"
-#include "hash-map.h"
#include "langhooks.h"
-#include "timevar.h"
-#include "dumpfile.h"
-#include "target.h"
-#include "dominance.h"
-#include "cfg.h"
#include "cfganal.h"
-#include "predict.h"
-#include "basic-block.h"
-#include "df.h"
-#include "tree-ssa-alias.h"
-#include "internal-fn.h"
-#include "gimple-expr.h"
-#include "is-a.h"
-#include "gimple.h"
-#include "gimple-ssa.h"
#include "rtl-iter.h"
+#include "cgraph.h"
+#include "ipa-utils.h"
/* The aliasing API provided here solves related but different problems:
However, this is no actual entry for alias set zero. It is an
error to attempt to explicitly construct a subset of zero. */
-struct alias_set_traits : default_hashmap_traits
-{
- template<typename T>
- static bool
- is_empty (T &e)
- {
- return e.m_key == INT_MIN;
- }
-
- template<typename T>
- static bool
- is_deleted (T &e)
- {
- return e.m_key == (INT_MIN + 1);
- }
-
- template<typename T> static void mark_empty (T &e) { e.m_key = INT_MIN; }
-
- template<typename T>
- static void
- mark_deleted (T &e)
- {
- e.m_key = INT_MIN + 1;
- }
-};
+struct alias_set_hash : int_hash <int, INT_MIN, INT_MIN + 1> {};
-struct GTY(()) alias_set_entry_d {
+struct GTY(()) alias_set_entry {
/* The alias set number, as stored in MEM_ALIAS_SET. */
alias_set_type alias_set;
- /* The children of the alias set. These are not just the immediate
- children, but, in fact, all descendants. So, if we have:
-
- struct T { struct S s; float f; }
-
- continuing our example above, the children here will be all of
- `int', `double', `float', and `struct S'. */
- hash_map<int, int, alias_set_traits> *children;
-
/* Nonzero if would have a child of zero: this effectively makes this
alias set the same as alias set zero. */
bool has_zero_child;
bool is_pointer;
/* Nonzero if is_pointer or if one of childs have has_pointer set. */
bool has_pointer;
+
+ /* The children of the alias set. These are not just the immediate
+ children, but, in fact, all descendants. So, if we have:
+
+ struct T { struct S s; float f; }
+
+ continuing our example above, the children here will be all of
+ `int', `double', `float', and `struct S'. */
+ hash_map<alias_set_hash, int> *children;
};
-typedef struct alias_set_entry_d *alias_set_entry;
static int rtx_equal_for_memref_p (const_rtx, const_rtx);
-static int memrefs_conflict_p (int, rtx, int, rtx, HOST_WIDE_INT);
static void record_set (rtx, const_rtx, void *);
static int base_alias_check (rtx, rtx, rtx, rtx, machine_mode,
machine_mode);
static rtx find_base_value (rtx);
static int mems_in_disjoint_alias_sets_p (const_rtx, const_rtx);
-static alias_set_entry get_alias_set_entry (alias_set_type);
+static alias_set_entry *get_alias_set_entry (alias_set_type);
static tree decl_for_component_ref (tree);
static int write_dependence_p (const_rtx,
const_rtx, machine_mode, rtx,
bool, bool, bool);
+static int compare_base_symbol_refs (const_rtx, const_rtx);
static void memory_modified_1 (rtx, const_rtx, void *);
/* The splay-tree used to store the various alias set entries. */
-static GTY (()) vec<alias_set_entry, va_gc> *alias_sets;
+static GTY (()) vec<alias_set_entry *, va_gc> *alias_sets;
\f
/* Build a decomposed reference object for querying the alias-oracle
from the MEM rtx and store it in *REF.
&& TREE_CODE (TMR_BASE (base)) == SSA_NAME)))
return false;
- /* If this is a reference based on a partitioned decl replace the
- base with a MEM_REF of the pointer representative we
- created during stack slot partitioning. */
- if (TREE_CODE (base) == VAR_DECL
- && ! is_global_var (base)
- && cfun->gimple_df->decls_to_pointers != NULL)
- {
- tree *namep = cfun->gimple_df->decls_to_pointers->get (base);
- if (namep)
- ref->base = build_simple_mem_ref (*namep);
- }
-
ref->ref_alias_set = MEM_ALIAS_SET (mem);
/* If MEM_OFFSET or MEM_SIZE are unknown what we got from MEM_EXPR
|| !MEM_SIZE_KNOWN_P (mem))
return true;
- /* If the base decl is a parameter we can have negative MEM_OFFSET in
- case of promoted subregs on bigendian targets. Trust the MEM_EXPR
- here. */
- if (MEM_OFFSET (mem) < 0
- && (MEM_SIZE (mem) + MEM_OFFSET (mem)) * BITS_PER_UNIT == ref->size)
- return true;
+ /* If MEM_OFFSET/MEM_SIZE get us outside of ref->offset/ref->max_size
+ drop ref->ref. */
+ if (maybe_lt (MEM_OFFSET (mem), 0)
+ || (ref->max_size_known_p ()
+ && maybe_gt ((MEM_OFFSET (mem) + MEM_SIZE (mem)) * BITS_PER_UNIT,
+ ref->max_size)))
+ ref->ref = NULL_TREE;
- /* Otherwise continue and refine size and offset we got from analyzing
- MEM_EXPR by using MEM_SIZE and MEM_OFFSET. */
+ /* Refine size and offset we got from analyzing MEM_EXPR by using
+ MEM_SIZE and MEM_OFFSET. */
ref->offset += MEM_OFFSET (mem) * BITS_PER_UNIT;
ref->size = MEM_SIZE (mem) * BITS_PER_UNIT;
/* The MEM may extend into adjacent fields, so adjust max_size if
necessary. */
- if (ref->max_size != -1
- && ref->size > ref->max_size)
- ref->max_size = ref->size;
+ if (ref->max_size_known_p ())
+ ref->max_size = upper_bound (ref->max_size, ref->size);
- /* If MEM_OFFSET and MEM_SIZE get us outside of the base object of
+ /* If MEM_OFFSET and MEM_SIZE might get us outside of the base object of
the MEM_EXPR punt. This happens for STRICT_ALIGNMENT targets a lot. */
if (MEM_EXPR (mem) != get_spill_slot_decl (false)
- && (ref->offset < 0
+ && (maybe_lt (ref->offset, 0)
|| (DECL_P (ref->base)
&& (DECL_SIZE (ref->base) == NULL_TREE
- || TREE_CODE (DECL_SIZE (ref->base)) != INTEGER_CST
- || wi::ltu_p (wi::to_offset (DECL_SIZE (ref->base)),
- ref->offset + ref->size)))))
+ || !poly_int_tree_p (DECL_SIZE (ref->base))
+ || maybe_lt (wi::to_poly_offset (DECL_SIZE (ref->base)),
+ ref->offset + ref->size)))))
return false;
return true;
&& MEM_ALIAS_SET (mem) != 0);
}
+/* Return true if the ref EARLIER behaves the same as LATER with respect
+ to TBAA for every memory reference that might follow LATER. */
+
+bool
+refs_same_for_tbaa_p (tree earlier, tree later)
+{
+ ao_ref earlier_ref, later_ref;
+ ao_ref_init (&earlier_ref, earlier);
+ ao_ref_init (&later_ref, later);
+ alias_set_type earlier_set = ao_ref_alias_set (&earlier_ref);
+ alias_set_type later_set = ao_ref_alias_set (&later_ref);
+ if (!(earlier_set == later_set
+ || alias_set_subset_of (later_set, earlier_set)))
+ return false;
+ alias_set_type later_base_set = ao_ref_base_alias_set (&later_ref);
+ alias_set_type earlier_base_set = ao_ref_base_alias_set (&earlier_ref);
+ return (earlier_base_set == later_base_set
+ || alias_set_subset_of (later_base_set, earlier_base_set));
+}
+
/* Returns a pointer to the alias set entry for ALIAS_SET, if there is
such an entry, or NULL otherwise. */
-static inline alias_set_entry
+static inline alias_set_entry *
get_alias_set_entry (alias_set_type alias_set)
{
return (*alias_sets)[alias_set];
bool
alias_set_subset_of (alias_set_type set1, alias_set_type set2)
{
- alias_set_entry ase2;
+ alias_set_entry *ase2;
+
+ /* Disable TBAA oracle with !flag_strict_aliasing. */
+ if (!flag_strict_aliasing)
+ return true;
/* Everything is a subset of the "aliases everything" set. */
if (set2 == 0)
get_alias_set for more details. */
if (ase2 && ase2->has_pointer)
{
- alias_set_entry ase1 = get_alias_set_entry (set1);
+ alias_set_entry *ase1 = get_alias_set_entry (set1);
if (ase1 && ase1->is_pointer)
{
int
alias_sets_conflict_p (alias_set_type set1, alias_set_type set2)
{
- alias_set_entry ase1;
- alias_set_entry ase2;
+ alias_set_entry *ase1;
+ alias_set_entry *ase2;
/* The easy case. */
if (alias_sets_must_conflict_p (set1, set2))
int
alias_sets_must_conflict_p (alias_set_type set1, alias_set_type set2)
{
+ /* Disable TBAA oracle with !flag_strict_aliasing. */
+ if (!flag_strict_aliasing)
+ return 1;
if (set1 == 0 || set2 == 0)
{
++alias_stats.num_alias_zero;
return alias_sets_must_conflict_p (set1, set2);
}
\f
+/* Return true if T is an end of the access path which can be used
+ by type based alias oracle. */
+
+bool
+ends_tbaa_access_path_p (const_tree t)
+{
+ switch (TREE_CODE (t))
+ {
+ case COMPONENT_REF:
+ if (DECL_NONADDRESSABLE_P (TREE_OPERAND (t, 1)))
+ return true;
+ /* Permit type-punning when accessing a union, provided the access
+ is directly through the union. For example, this code does not
+ permit taking the address of a union member and then storing
+ through it. Even the type-punning allowed here is a GCC
+ extension, albeit a common and useful one; the C standard says
+ that such accesses have implementation-defined behavior. */
+ else if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
+ return true;
+ break;
+
+ case ARRAY_REF:
+ case ARRAY_RANGE_REF:
+ if (TYPE_NONALIASED_COMPONENT (TREE_TYPE (TREE_OPERAND (t, 0))))
+ return true;
+ break;
+
+ case REALPART_EXPR:
+ case IMAGPART_EXPR:
+ break;
+
+ case BIT_FIELD_REF:
+ case VIEW_CONVERT_EXPR:
+ /* Bitfields and casts are never addressable. */
+ return true;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ return false;
+}
+
/* Return the outermost parent of component present in the chain of
component references handled by get_inner_reference in T with the
following property:
- - the component is non-addressable, or
- - the parent has alias set zero,
+ - the component is non-addressable
or NULL_TREE if no such parent exists. In the former cases, the alias
set of this parent is the alias set that must be used for T itself. */
while (handled_component_p (t))
{
- switch (TREE_CODE (t))
- {
- case COMPONENT_REF:
- if (DECL_NONADDRESSABLE_P (TREE_OPERAND (t, 1)))
- found = t;
- break;
-
- case ARRAY_REF:
- case ARRAY_RANGE_REF:
- if (TYPE_NONALIASED_COMPONENT (TREE_TYPE (TREE_OPERAND (t, 0))))
- found = t;
- break;
-
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- break;
-
- case BIT_FIELD_REF:
- case VIEW_CONVERT_EXPR:
- /* Bitfields and casts are never addressable. */
- found = t;
- break;
-
- default:
- gcc_unreachable ();
- }
-
- if (get_alias_set (TREE_TYPE (TREE_OPERAND (t, 0))) == 0)
+ if (ends_tbaa_access_path_p (t))
found = t;
t = TREE_OPERAND (t, 0);
tree
reference_alias_ptr_type (tree t)
{
+ /* If the frontend assigns this alias-set zero, preserve that. */
+ if (lang_hooks.get_alias_set (t) == 0)
+ return ptr_type_node;
+
tree ptype = reference_alias_ptr_type_1 (&t);
/* If there is a given pointer type for aliasing purposes, return it. */
if (ptype != NULL_TREE)
|| ref_all_alias_ptr_type_p (t2))
return false;
- return (TYPE_MAIN_VARIANT (TREE_TYPE (t1))
- == TYPE_MAIN_VARIANT (TREE_TYPE (t2)));
+ /* This function originally abstracts from simply comparing
+ get_deref_alias_set so that we are sure this still computes
+ the same result after LTO type merging is applied.
+ When in LTO type merging is done we can actually do this compare.
+ */
+ if (in_lto_p)
+ return get_deref_alias_set (t1) == get_deref_alias_set (t2);
+ else
+ return (TYPE_MAIN_VARIANT (TREE_TYPE (t1))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (t2)));
}
/* Create emptry alias set entry. */
-alias_set_entry
+alias_set_entry *
init_alias_set_entry (alias_set_type set)
{
- alias_set_entry ase = ggc_alloc<alias_set_entry_d> ();
+ alias_set_entry *ase = ggc_alloc<alias_set_entry> ();
ase->alias_set = set;
ase->children = NULL;
ase->has_zero_child = false;
{
alias_set_type set;
- /* If we're not doing any alias analysis, just assume everything
- aliases everything else. Also return 0 if this or its type is
- an error. */
- if (! flag_strict_aliasing || t == error_mark_node
+ /* We cannot give up with -fno-strict-aliasing because we need to build
+ proper type representations for possible functions which are built with
+ -fstrict-aliasing. */
+
+ /* return 0 if this or its type is an error. */
+ if (t == error_mark_node
|| (! TYPE_P (t)
&& (TREE_TYPE (t) == 0 || TREE_TYPE (t) == error_mark_node)))
return 0;
/* If we've already determined the alias set for a decl, just return
it. This is necessary for C++ anonymous unions, whose component
variables don't look like union members (boo!). */
- if (TREE_CODE (t) == VAR_DECL
+ if (VAR_P (t)
&& DECL_RTL_SET_P (t) && MEM_P (DECL_RTL (t)))
return MEM_ALIAS_SET (DECL_RTL (t));
variant. */
t = TYPE_MAIN_VARIANT (t);
+ if (AGGREGATE_TYPE_P (t)
+ && TYPE_TYPELESS_STORAGE (t))
+ return 0;
+
/* Always use the canonical type as well. If this is a type that
requires structural comparisons to identify compatible types
use alias set zero. */
set = lang_hooks.get_alias_set (t);
if (set != -1)
return set;
- return 0;
+ /* Handle structure type equality for pointer types, arrays and vectors.
+ This is easy to do, because the code below ignores canonical types on
+ these anyway. This is important for LTO, where TYPE_CANONICAL for
+ pointers cannot be meaningfully computed by the frontend. */
+ if (canonical_type_used_p (t))
+ {
+ /* In LTO we set canonical types for all types where it makes
+ sense to do so. Double check we did not miss some type. */
+ gcc_checking_assert (!in_lto_p || !type_with_alias_set_p (t));
+ return 0;
+ }
+ }
+ else
+ {
+ t = TYPE_CANONICAL (t);
+ gcc_checking_assert (!TYPE_STRUCTURAL_EQUALITY_P (t));
}
-
- t = TYPE_CANONICAL (t);
-
- /* The canonical type should not require structural equality checks. */
- gcc_checking_assert (!TYPE_STRUCTURAL_EQUALITY_P (t));
/* If this is a type with a known alias set, return it. */
+ gcc_checking_assert (t == TYPE_MAIN_VARIANT (t));
if (TYPE_ALIAS_SET_KNOWN_P (t))
return TYPE_ALIAS_SET (t);
integer(kind=4)[4] the same alias set or not.
Just be pragmatic here and make sure the array and its element
type get the same alias set assigned. */
- else if (TREE_CODE (t) == ARRAY_TYPE && !TYPE_NONALIASED_COMPONENT (t))
+ else if (TREE_CODE (t) == ARRAY_TYPE
+ && (!TYPE_NONALIASED_COMPONENT (t)
+ || TYPE_STRUCTURAL_EQUALITY_P (t)))
set = get_alias_set (TREE_TYPE (t));
/* From the former common C and C++ langhook implementation:
ptr_type_node but that is a bad idea, because it prevents disabiguations
in between pointers. For Firefox this accounts about 20% of all
disambiguations in the program. */
- else if (POINTER_TYPE_P (t) && t != ptr_type_node && !in_lto_p)
+ else if (POINTER_TYPE_P (t) && t != ptr_type_node)
{
tree p;
auto_vec <bool, 8> reference;
/* Unnest all pointers and references.
- We also want to make pointer to array equivalent to pointer to its
- element. So skip all array types, too. */
+ We also want to make pointer to array/vector equivalent to pointer to
+ its element (see the reasoning above). Skip all those types, too. */
for (p = t; POINTER_TYPE_P (p)
- || (TREE_CODE (p) == ARRAY_TYPE && !TYPE_NONALIASED_COMPONENT (p));
+ || (TREE_CODE (p) == ARRAY_TYPE
+ && (!TYPE_NONALIASED_COMPONENT (p)
+ || !COMPLETE_TYPE_P (p)
+ || TYPE_STRUCTURAL_EQUALITY_P (p)))
+ || TREE_CODE (p) == VECTOR_TYPE;
p = TREE_TYPE (p))
{
+ /* Ada supports recursive pointers. Instead of doing recursion
+ check, just give up once the preallocated space of 8 elements
+ is up. In this case just punt to void * alias set. */
+ if (reference.length () == 8)
+ {
+ p = ptr_type_node;
+ break;
+ }
if (TREE_CODE (p) == REFERENCE_TYPE)
- reference.safe_push (true);
+ /* In LTO we want languages that use references to be compatible
+ with languages that use pointers. */
+ reference.safe_push (true && !in_lto_p);
if (TREE_CODE (p) == POINTER_TYPE)
reference.safe_push (false);
}
p = TYPE_MAIN_VARIANT (p);
+ /* In LTO for C++ programs we can turn incomplete types to complete
+ using ODR name lookup. */
+ if (in_lto_p && TYPE_STRUCTURAL_EQUALITY_P (p) && odr_type_p (p))
+ {
+ p = prevailing_odr_type (p);
+ gcc_checking_assert (TYPE_MAIN_VARIANT (p) == p);
+ }
+
/* Make void * compatible with char * and also void **.
Programs are commonly violating TBAA by this.
set = get_alias_set (ptr_type_node);
else
{
- /* Rebuild pointer type from starting from canonical types using
+ /* Rebuild pointer type starting from canonical types using
unqualified pointers and references only. This way all such
pointers will have the same alias set and will conflict with
each other.
p = build_reference_type (p);
else
p = build_pointer_type (p);
- p = TYPE_CANONICAL (TYPE_MAIN_VARIANT (p));
+ gcc_checking_assert (p == TYPE_MAIN_VARIANT (p));
+ /* build_pointer_type should always return the canonical type.
+ For LTO TYPE_CANOINCAL may be NULL, because we do not compute
+ them. Be sure that frontends do not glob canonical types of
+ pointers in unexpected way and that p == TYPE_CANONICAL (p)
+ in all other cases. */
+ gcc_checking_assert (!TYPE_CANONICAL (p)
+ || p == TYPE_CANONICAL (p));
}
- gcc_checking_assert (TYPE_CANONICAL (p) == p);
/* Assign the alias set to both p and t.
- We can not call get_alias_set (p) here as that would trigger
+ We cannot call get_alias_set (p) here as that would trigger
infinite recursion when p == t. In other cases it would just
trigger unnecesary legwork of rebuilding the pointer again. */
+ gcc_checking_assert (p == TYPE_MAIN_VARIANT (p));
if (TYPE_ALIAS_SET_KNOWN_P (p))
set = TYPE_ALIAS_SET (p);
else
}
}
}
- /* In LTO the rules above needs to be part of canonical type machinery.
- For now just punt. */
- else if (POINTER_TYPE_P (t) && t != ptr_type_node && in_lto_p)
- set = get_alias_set (ptr_type_node);
+ /* Alias set of ptr_type_node is special and serve as universal pointer which
+ is TBAA compatible with every other pointer type. Be sure we have the
+ alias set built even for LTO which otherwise keeps all TYPE_CANONICAL
+ of pointer types NULL. */
+ else if (t == ptr_type_node)
+ set = new_alias_set ();
/* Otherwise make a new alias set for this type. */
else
/* We treat pointer types specially in alias_set_subset_of. */
if (POINTER_TYPE_P (t) && set)
{
- alias_set_entry ase = get_alias_set_entry (set);
+ alias_set_entry *ase = get_alias_set_entry (set);
if (!ase)
ase = init_alias_set_entry (set);
ase->is_pointer = true;
alias_set_type
new_alias_set (void)
{
- if (flag_strict_aliasing)
- {
- if (alias_sets == 0)
- vec_safe_push (alias_sets, (alias_set_entry) 0);
- vec_safe_push (alias_sets, (alias_set_entry) 0);
- return alias_sets->length () - 1;
- }
- else
- return 0;
+ if (alias_sets == 0)
+ vec_safe_push (alias_sets, (alias_set_entry *) NULL);
+ vec_safe_push (alias_sets, (alias_set_entry *) NULL);
+ return alias_sets->length () - 1;
}
/* Indicate that things in SUBSET can alias things in SUPERSET, but that
void
record_alias_subset (alias_set_type superset, alias_set_type subset)
{
- alias_set_entry superset_entry;
- alias_set_entry subset_entry;
+ alias_set_entry *superset_entry;
+ alias_set_entry *subset_entry;
/* It is possible in complex type situations for both sets to be the same,
in which case we can ignore this operation. */
superset_entry->has_zero_child = 1;
else
{
- subset_entry = get_alias_set_entry (subset);
if (!superset_entry->children)
superset_entry->children
- = hash_map<int, int, alias_set_traits>::create_ggc (64);
+ = hash_map<alias_set_hash, int>::create_ggc (64);
+
+ /* Enter the SUBSET itself as a child of the SUPERSET. If it was
+ already there we're done. */
+ if (superset_entry->children->put (subset, 0))
+ return;
+
+ subset_entry = get_alias_set_entry (subset);
/* If there is an entry for the subset, enter all of its children
(if they are not already present) as children of the SUPERSET. */
if (subset_entry)
if (subset_entry->children)
{
- hash_map<int, int, alias_set_traits>::iterator iter
+ hash_map<alias_set_hash, int>::iterator iter
= subset_entry->children->begin ();
for (; iter != subset_entry->children->end (); ++iter)
superset_entry->children->put ((*iter).first, (*iter).second);
}
}
-
- /* Enter the SUBSET itself as a child of the SUPERSET. */
- superset_entry->children->put (subset, 0);
}
}
-/* Record that component types of TYPE, if any, are part of that type for
+/* Record that component types of TYPE, if any, are part of SUPERSET for
aliasing purposes. For record types, we only record component types
for fields that are not marked non-addressable. For array types, we
only record the component type if it is not marked non-aliased. */
void
-record_component_aliases (tree type)
+record_component_aliases (tree type, alias_set_type superset)
{
- alias_set_type superset = get_alias_set (type);
tree field;
if (superset == 0)
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
- for (field = TYPE_FIELDS (type); field != 0; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL && !DECL_NONADDRESSABLE_P (field))
- record_alias_subset (superset, get_alias_set (TREE_TYPE (field)));
+ {
+ /* LTO non-ODR type merging does not make any difference between
+ component pointer types. We may have
+
+ struct foo {int *a;};
+
+ as TYPE_CANONICAL of
+
+ struct bar {float *a;};
+
+ Because accesses to int * and float * do not alias, we would get
+ false negative when accessing the same memory location by
+ float ** and bar *. We thus record the canonical type as:
+
+ struct {void *a;};
+
+ void * is special cased and works as a universal pointer type.
+ Accesses to it conflicts with accesses to any other pointer
+ type. */
+ bool void_pointers = in_lto_p
+ && (!odr_type_p (type)
+ || !odr_based_tbaa_p (type));
+ for (field = TYPE_FIELDS (type); field != 0; field = DECL_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL && !DECL_NONADDRESSABLE_P (field))
+ {
+ tree t = TREE_TYPE (field);
+ if (void_pointers)
+ {
+ /* VECTOR_TYPE and ARRAY_TYPE share the alias set with their
+ element type and that type has to be normalized to void *,
+ too, in the case it is a pointer. */
+ while (!canonical_type_used_p (t) && !POINTER_TYPE_P (t))
+ {
+ gcc_checking_assert (TYPE_STRUCTURAL_EQUALITY_P (t));
+ t = TREE_TYPE (t);
+ }
+ if (POINTER_TYPE_P (t))
+ t = ptr_type_node;
+ else if (flag_checking)
+ gcc_checking_assert (get_alias_set (t)
+ == get_alias_set (TREE_TYPE (field)));
+ }
+
+ alias_set_type set = get_alias_set (t);
+ record_alias_subset (superset, set);
+ /* If the field has alias-set zero make sure to still record
+ any componets of it. This makes sure that for
+ struct A {
+ struct B {
+ int i;
+ char c[4];
+ } b;
+ };
+ in C++ even though 'B' has alias-set zero because
+ TYPE_TYPELESS_STORAGE is set, 'A' has the alias-set of
+ 'int' as subset. */
+ if (set == 0)
+ record_component_aliases (t, superset);
+ }
+ }
break;
case COMPLEX_TYPE:
}
}
+/* Record that component types of TYPE, if any, are part of that type for
+ aliasing purposes. For record types, we only record component types
+ for fields that are not marked non-addressable. For array types, we
+ only record the component type if it is not marked non-aliased. */
+
+void
+record_component_aliases (tree type)
+{
+ alias_set_type superset = get_alias_set (type);
+ record_component_aliases (type, superset);
+}
+
+
/* Allocate an alias set for use in storing and reading from the varargs
spill area. */
find_base_value (rtx src)
{
unsigned int regno;
+ scalar_int_mode int_mode;
#if defined (FIND_BASE_TERM)
/* Try machine-dependent ways to find the base term. */
if (GET_CODE (src) != PLUS && GET_CODE (src) != MINUS)
break;
- /* ... fall through ... */
+ /* fall through */
case PLUS:
case MINUS:
return find_base_value (XEXP (src, 1));
case AND:
- /* If the second operand is constant set the base
- address to the first operand. */
- if (CONST_INT_P (XEXP (src, 1)) && INTVAL (XEXP (src, 1)) != 0)
+ /* Look through aligning ANDs. And AND with zero or one with
+ the LSB set isn't one (see for example PR92462). */
+ if (CONST_INT_P (XEXP (src, 1))
+ && INTVAL (XEXP (src, 1)) != 0
+ && (INTVAL (XEXP (src, 1)) & 1) == 0)
return find_base_value (XEXP (src, 0));
return 0;
address modes depending on the address space. */
if (!target_default_pointer_address_modes_p ())
break;
- if (GET_MODE_SIZE (GET_MODE (src)) < GET_MODE_SIZE (Pmode))
+ if (!is_a <scalar_int_mode> (GET_MODE (src), &int_mode)
+ || GET_MODE_PRECISION (int_mode) < GET_MODE_PRECISION (Pmode))
break;
/* Fall through. */
case HIGH:
new_reg_base_value[regno] = 0;
return;
}
+
src = SET_SRC (set);
}
else
rtx x1 = canon_rtx (XEXP (x, 1));
if (x0 != XEXP (x, 0) || x1 != XEXP (x, 1))
- {
- if (CONST_INT_P (x0))
- return plus_constant (GET_MODE (x), x1, INTVAL (x0));
- else if (CONST_INT_P (x1))
- return plus_constant (GET_MODE (x), x0, INTVAL (x1));
- return gen_rtx_PLUS (GET_MODE (x), x0, x1);
- }
+ return simplify_gen_binary (PLUS, GET_MODE (x), x0, x1);
}
/* This gives us much better alias analysis when called from
return REGNO (x) == REGNO (y);
case LABEL_REF:
- return LABEL_REF_LABEL (x) == LABEL_REF_LABEL (y);
+ return label_ref_label (x) == label_ref_label (y);
case SYMBOL_REF:
- return XSTR (x, 0) == XSTR (y, 0);
+ return compare_base_symbol_refs (x, y) == 1;
case ENTRY_VALUE:
/* This is magic, don't go through canonicalization et al. */
return 0;
break;
+ case 'p':
+ if (maybe_ne (SUBREG_BYTE (x), SUBREG_BYTE (y)))
+ return 0;
+ break;
+
case 'E':
/* Two vectors must have the same length. */
if (XVECLEN (x, i) != XVECLEN (y, i))
}
static rtx
-find_base_term (rtx x)
+find_base_term (rtx x, vec<std::pair<cselib_val *,
+ struct elt_loc_list *> > &visited_vals)
{
cselib_val *val;
struct elt_loc_list *l, *f;
rtx ret;
+ scalar_int_mode int_mode;
#if defined (FIND_BASE_TERM)
/* Try machine-dependent ways to find the base term. */
address modes depending on the address space. */
if (!target_default_pointer_address_modes_p ())
return 0;
- if (GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (Pmode))
+ if (!is_a <scalar_int_mode> (GET_MODE (x), &int_mode)
+ || GET_MODE_PRECISION (int_mode) < GET_MODE_PRECISION (Pmode))
return 0;
/* Fall through. */
case HIGH:
case POST_DEC:
case PRE_MODIFY:
case POST_MODIFY:
- return find_base_term (XEXP (x, 0));
+ return find_base_term (XEXP (x, 0), visited_vals);
case ZERO_EXTEND:
case SIGN_EXTEND: /* Used for Alpha/NT pointers */
return 0;
{
- rtx temp = find_base_term (XEXP (x, 0));
+ rtx temp = find_base_term (XEXP (x, 0), visited_vals);
if (temp != 0 && CONSTANT_P (temp))
temp = convert_memory_address (Pmode, temp);
if (cselib_sp_based_value_p (val))
return static_reg_base_value[STACK_POINTER_REGNUM];
+ if (visited_vals.length () > (unsigned) param_max_find_base_term_values)
+ return ret;
+
f = val->locs;
- /* Temporarily reset val->locs to avoid infinite recursion. */
+ /* Reset val->locs to avoid infinite recursion. */
+ if (f)
+ visited_vals.safe_push (std::make_pair (val, f));
val->locs = NULL;
for (l = f; l; l = l->next)
&& !CSELIB_VAL_PTR (l->loc)->locs->next
&& CSELIB_VAL_PTR (l->loc)->locs->loc == x)
continue;
- else if ((ret = find_base_term (l->loc)) != 0)
+ else if ((ret = find_base_term (l->loc, visited_vals)) != 0)
break;
- val->locs = f;
return ret;
case LO_SUM:
/* The standard form is (lo_sum reg sym) so look only at the
second operand. */
- return find_base_term (XEXP (x, 1));
+ return find_base_term (XEXP (x, 1), visited_vals);
case CONST:
x = XEXP (x, 0);
other operand is the base register. */
if (tmp1 == pic_offset_table_rtx && CONSTANT_P (tmp2))
- return find_base_term (tmp2);
+ return find_base_term (tmp2, visited_vals);
/* If either operand is known to be a pointer, then prefer it
to determine the base term. */
term is from a pointer or is a named object or a special address
(like an argument or stack reference), then use it for the
base term. */
- rtx base = find_base_term (tmp1);
+ rtx base = find_base_term (tmp1, visited_vals);
if (base != NULL_RTX
&& ((REG_P (tmp1) && REG_POINTER (tmp1))
|| known_base_value_p (base)))
return base;
- base = find_base_term (tmp2);
+ base = find_base_term (tmp2, visited_vals);
if (base != NULL_RTX
&& ((REG_P (tmp2) && REG_POINTER (tmp2))
|| known_base_value_p (base)))
}
case AND:
- if (CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) != 0)
- return find_base_term (XEXP (x, 0));
+ /* Look through aligning ANDs. And AND with zero or one with
+ the LSB set isn't one (see for example PR92462). */
+ if (CONST_INT_P (XEXP (x, 1))
+ && INTVAL (XEXP (x, 1)) != 0
+ && (INTVAL (XEXP (x, 1)) & 1) == 0)
+ return find_base_term (XEXP (x, 0), visited_vals);
return 0;
case SYMBOL_REF:
}
}
+/* Wrapper around the worker above which removes locs from visited VALUEs
+ to avoid visiting them multiple times. We unwind that changes here. */
+
+static rtx
+find_base_term (rtx x)
+{
+ auto_vec<std::pair<cselib_val *, struct elt_loc_list *>, 32> visited_vals;
+ rtx res = find_base_term (x, visited_vals);
+ for (unsigned i = 0; i < visited_vals.length (); ++i)
+ visited_vals[i].first->locs = visited_vals[i].second;
+ return res;
+}
+
/* Return true if accesses to address X may alias accesses based
on the stack pointer. */
return !base || base == static_reg_base_value[STACK_POINTER_REGNUM];
}
+/* BASE1 and BASE2 are decls. Return 1 if they refer to same object, 0
+ if they refer to different objects and -1 if we cannot decide. */
+
+int
+compare_base_decls (tree base1, tree base2)
+{
+ int ret;
+ gcc_checking_assert (DECL_P (base1) && DECL_P (base2));
+ if (base1 == base2)
+ return 1;
+
+ /* If we have two register decls with register specification we
+ cannot decide unless their assembler names are the same. */
+ if (DECL_REGISTER (base1)
+ && DECL_REGISTER (base2)
+ && HAS_DECL_ASSEMBLER_NAME_P (base1)
+ && HAS_DECL_ASSEMBLER_NAME_P (base2)
+ && DECL_ASSEMBLER_NAME_SET_P (base1)
+ && DECL_ASSEMBLER_NAME_SET_P (base2))
+ {
+ if (DECL_ASSEMBLER_NAME_RAW (base1) == DECL_ASSEMBLER_NAME_RAW (base2))
+ return 1;
+ return -1;
+ }
+
+ /* Declarations of non-automatic variables may have aliases. All other
+ decls are unique. */
+ if (!decl_in_symtab_p (base1)
+ || !decl_in_symtab_p (base2))
+ return 0;
+
+ /* Don't cause symbols to be inserted by the act of checking. */
+ symtab_node *node1 = symtab_node::get (base1);
+ if (!node1)
+ return 0;
+ symtab_node *node2 = symtab_node::get (base2);
+ if (!node2)
+ return 0;
+
+ ret = node1->equal_address_to (node2, true);
+ return ret;
+}
+
+/* Same as compare_base_decls but for SYMBOL_REF. */
+
+static int
+compare_base_symbol_refs (const_rtx x_base, const_rtx y_base)
+{
+ tree x_decl = SYMBOL_REF_DECL (x_base);
+ tree y_decl = SYMBOL_REF_DECL (y_base);
+ bool binds_def = true;
+
+ if (XSTR (x_base, 0) == XSTR (y_base, 0))
+ return 1;
+ if (x_decl && y_decl)
+ return compare_base_decls (x_decl, y_decl);
+ if (x_decl || y_decl)
+ {
+ if (!x_decl)
+ {
+ std::swap (x_decl, y_decl);
+ std::swap (x_base, y_base);
+ }
+ /* We handle specially only section anchors and assume that other
+ labels may overlap with user variables in an arbitrary way. */
+ if (!SYMBOL_REF_HAS_BLOCK_INFO_P (y_base))
+ return -1;
+ /* Anchors contains static VAR_DECLs and CONST_DECLs. We are safe
+ to ignore CONST_DECLs because they are readonly. */
+ if (!VAR_P (x_decl)
+ || (!TREE_STATIC (x_decl) && !TREE_PUBLIC (x_decl)))
+ return 0;
+
+ symtab_node *x_node = symtab_node::get_create (x_decl)
+ ->ultimate_alias_target ();
+ /* External variable cannot be in section anchor. */
+ if (!x_node->definition)
+ return 0;
+ x_base = XEXP (DECL_RTL (x_node->decl), 0);
+ /* If not in anchor, we can disambiguate. */
+ if (!SYMBOL_REF_HAS_BLOCK_INFO_P (x_base))
+ return 0;
+
+ /* We have an alias of anchored variable. If it can be interposed;
+ we must assume it may or may not alias its anchor. */
+ binds_def = decl_binds_to_current_def_p (x_decl);
+ }
+ /* If we have variable in section anchor, we can compare by offset. */
+ if (SYMBOL_REF_HAS_BLOCK_INFO_P (x_base)
+ && SYMBOL_REF_HAS_BLOCK_INFO_P (y_base))
+ {
+ if (SYMBOL_REF_BLOCK (x_base) != SYMBOL_REF_BLOCK (y_base))
+ return 0;
+ if (SYMBOL_REF_BLOCK_OFFSET (x_base) == SYMBOL_REF_BLOCK_OFFSET (y_base))
+ return binds_def ? 1 : -1;
+ if (SYMBOL_REF_ANCHOR_P (x_base) != SYMBOL_REF_ANCHOR_P (y_base))
+ return -1;
+ return 0;
+ }
+ /* In general we assume that memory locations pointed to by different labels
+ may overlap in undefined ways. */
+ return -1;
+}
+
/* Return 0 if the addresses X and Y are known to point to different
objects, 1 if they might be pointers to the same object. */
/* The base addresses are different expressions. If they are not accessed
via AND, there is no conflict. We can bring knowledge of object
alignment into play here. For example, on alpha, "char a, b;" can
- alias one another, though "char a; long b;" cannot. AND addesses may
+ alias one another, though "char a; long b;" cannot. AND addresses may
implicitly alias surrounding objects; i.e. unaligned access in DImode
via AND address can alias all surrounding object types except those
with aligment 8 or higher. */
return 1;
/* Differing symbols not accessed via AND never alias. */
+ if (GET_CODE (x_base) == SYMBOL_REF && GET_CODE (y_base) == SYMBOL_REF)
+ return compare_base_symbol_refs (x_base, y_base) != 0;
+
if (GET_CODE (x_base) != ADDRESS && GET_CODE (y_base) != ADDRESS)
return 0;
}
/* Return TRUE if EXPR refers to a VALUE whose uid is greater than
- that of V. */
+ (or equal to) that of V. */
static bool
refs_newer_value_p (const_rtx expr, rtx v)
int minuid = CSELIB_VAL_PTR (v)->uid;
subrtx_iterator::array_type array;
FOR_EACH_SUBRTX (iter, array, expr, NONCONST)
- if (GET_CODE (*iter) == VALUE && CSELIB_VAL_PTR (*iter)->uid > minuid)
+ if (GET_CODE (*iter) == VALUE && CSELIB_VAL_PTR (*iter)->uid >= minuid)
return true;
return false;
}
/* Convert the address X into something we can use. This is done by returning
- it unchanged unless it is a value; in the latter case we call cselib to get
- a more useful rtx. */
+ it unchanged unless it is a VALUE or VALUE +/- constant; for VALUE
+ we call cselib to get a more useful rtx. */
rtx
get_addr (rtx x)
struct elt_loc_list *l;
if (GET_CODE (x) != VALUE)
- return x;
+ {
+ if ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS)
+ && GET_CODE (XEXP (x, 0)) == VALUE
+ && CONST_SCALAR_INT_P (XEXP (x, 1)))
+ {
+ rtx op0 = get_addr (XEXP (x, 0));
+ if (op0 != XEXP (x, 0))
+ {
+ poly_int64 c;
+ if (GET_CODE (x) == PLUS
+ && poly_int_rtx_p (XEXP (x, 1), &c))
+ return plus_constant (GET_MODE (x), op0, c);
+ return simplify_gen_binary (GET_CODE (x), GET_MODE (x),
+ op0, XEXP (x, 1));
+ }
+ }
+ return x;
+ }
v = CSELIB_VAL_PTR (x);
if (v)
{
is not modified by the memory reference then ADDR is returned. */
static rtx
-addr_side_effect_eval (rtx addr, int size, int n_refs)
+addr_side_effect_eval (rtx addr, poly_int64 size, int n_refs)
{
- int offset = 0;
+ poly_int64 offset = 0;
switch (GET_CODE (addr))
{
return addr;
}
- if (offset)
- addr = gen_rtx_PLUS (GET_MODE (addr), XEXP (addr, 0),
- gen_int_mode (offset, GET_MODE (addr)));
- else
- addr = XEXP (addr, 0);
+ addr = plus_constant (GET_MODE (addr), XEXP (addr, 0), offset);
addr = canon_rtx (addr);
return addr;
absolute value of the sizes as the actual sizes. */
static inline bool
-offset_overlap_p (HOST_WIDE_INT c, int xsize, int ysize)
+offset_overlap_p (poly_int64 c, poly_int64 xsize, poly_int64 ysize)
{
- return (xsize == 0 || ysize == 0
- || (c >= 0
- ? (abs (xsize) > c)
- : (abs (ysize) > -c)));
+ if (known_eq (xsize, 0) || known_eq (ysize, 0))
+ return true;
+
+ if (maybe_ge (c, 0))
+ return maybe_gt (maybe_lt (xsize, 0) ? -xsize : xsize, c);
+ else
+ return maybe_gt (maybe_lt (ysize, 0) ? -ysize : ysize, -c);
}
/* Return one if X and Y (memory addresses) reference the
If that is fixed the TBAA hack for union type-punning can be removed. */
static int
-memrefs_conflict_p (int xsize, rtx x, int ysize, rtx y, HOST_WIDE_INT c)
+memrefs_conflict_p (poly_int64 xsize, rtx x, poly_int64 ysize, rtx y,
+ poly_int64 c)
{
if (GET_CODE (x) == VALUE)
{
else if (GET_CODE (x) == LO_SUM)
x = XEXP (x, 1);
else
- x = addr_side_effect_eval (x, abs (xsize), 0);
+ x = addr_side_effect_eval (x, maybe_lt (xsize, 0) ? -xsize : xsize, 0);
if (GET_CODE (y) == HIGH)
y = XEXP (y, 0);
else if (GET_CODE (y) == LO_SUM)
y = XEXP (y, 1);
else
- y = addr_side_effect_eval (y, abs (ysize), 0);
+ y = addr_side_effect_eval (y, maybe_lt (ysize, 0) ? -ysize : ysize, 0);
- if (rtx_equal_for_memref_p (x, y))
+ if (GET_CODE (x) == SYMBOL_REF && GET_CODE (y) == SYMBOL_REF)
+ {
+ int cmp = compare_base_symbol_refs (x,y);
+
+ /* If both decls are the same, decide by offsets. */
+ if (cmp == 1)
+ return offset_overlap_p (c, xsize, ysize);
+ /* Assume a potential overlap for symbolic addresses that went
+ through alignment adjustments (i.e., that have negative
+ sizes), because we can't know how far they are from each
+ other. */
+ if (maybe_lt (xsize, 0) || maybe_lt (ysize, 0))
+ return -1;
+ /* If decls are different or we know by offsets that there is no overlap,
+ we win. */
+ if (!cmp || !offset_overlap_p (c, xsize, ysize))
+ return 0;
+ /* Decls may or may not be different and offsets overlap....*/
+ return -1;
+ }
+ else if (rtx_equal_for_memref_p (x, y))
{
return offset_overlap_p (c, xsize, ysize);
}
rtx x0 = XEXP (x, 0);
rtx x1 = XEXP (x, 1);
+ /* However, VALUEs might end up in different positions even in
+ canonical PLUSes. Comparing their addresses is enough. */
+ if (x0 == y)
+ return memrefs_conflict_p (xsize, x1, ysize, const0_rtx, c);
+ else if (x1 == y)
+ return memrefs_conflict_p (xsize, x0, ysize, const0_rtx, c);
+
+ poly_int64 cx1, cy1;
if (GET_CODE (y) == PLUS)
{
/* The fact that Y is canonicalized means that this
rtx y0 = XEXP (y, 0);
rtx y1 = XEXP (y, 1);
+ if (x0 == y1)
+ return memrefs_conflict_p (xsize, x1, ysize, y0, c);
+ if (x1 == y0)
+ return memrefs_conflict_p (xsize, x0, ysize, y1, c);
+
if (rtx_equal_for_memref_p (x1, y1))
return memrefs_conflict_p (xsize, x0, ysize, y0, c);
if (rtx_equal_for_memref_p (x0, y0))
return memrefs_conflict_p (xsize, x1, ysize, y1, c);
- if (CONST_INT_P (x1))
+ if (poly_int_rtx_p (x1, &cx1))
{
- if (CONST_INT_P (y1))
+ if (poly_int_rtx_p (y1, &cy1))
return memrefs_conflict_p (xsize, x0, ysize, y0,
- c - INTVAL (x1) + INTVAL (y1));
+ c - cx1 + cy1);
else
- return memrefs_conflict_p (xsize, x0, ysize, y,
- c - INTVAL (x1));
+ return memrefs_conflict_p (xsize, x0, ysize, y, c - cx1);
}
- else if (CONST_INT_P (y1))
- return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
+ else if (poly_int_rtx_p (y1, &cy1))
+ return memrefs_conflict_p (xsize, x, ysize, y0, c + cy1);
return -1;
}
- else if (CONST_INT_P (x1))
- return memrefs_conflict_p (xsize, x0, ysize, y, c - INTVAL (x1));
+ else if (poly_int_rtx_p (x1, &cx1))
+ return memrefs_conflict_p (xsize, x0, ysize, y, c - cx1);
}
else if (GET_CODE (y) == PLUS)
{
rtx y0 = XEXP (y, 0);
rtx y1 = XEXP (y, 1);
- if (CONST_INT_P (y1))
- return memrefs_conflict_p (xsize, x, ysize, y0, c + INTVAL (y1));
+ if (x == y0)
+ return memrefs_conflict_p (xsize, const0_rtx, ysize, y1, c);
+ if (x == y1)
+ return memrefs_conflict_p (xsize, const0_rtx, ysize, y0, c);
+
+ poly_int64 cy1;
+ if (poly_int_rtx_p (y1, &cy1))
+ return memrefs_conflict_p (xsize, x, ysize, y0, c + cy1);
else
return -1;
}
return offset_overlap_p (c, xsize, ysize);
/* Can't properly adjust our sizes. */
- if (!CONST_INT_P (x1))
+ poly_int64 c1;
+ if (!poly_int_rtx_p (x1, &c1)
+ || !can_div_trunc_p (xsize, c1, &xsize)
+ || !can_div_trunc_p (ysize, c1, &ysize)
+ || !can_div_trunc_p (c, c1, &c))
return -1;
- xsize /= INTVAL (x1);
- ysize /= INTVAL (x1);
- c /= INTVAL (x1);
return memrefs_conflict_p (xsize, x0, ysize, y0, c);
}
{
HOST_WIDE_INT sc = INTVAL (XEXP (x, 1));
unsigned HOST_WIDE_INT uc = sc;
- if (sc < 0 && -uc == (uc & -uc))
+ if (sc < 0 && pow2_or_zerop (-uc))
{
- if (xsize > 0)
+ if (maybe_gt (xsize, 0))
xsize = -xsize;
- if (xsize)
+ if (maybe_ne (xsize, 0))
xsize += sc + 1;
c -= sc + 1;
return memrefs_conflict_p (xsize, canon_rtx (XEXP (x, 0)),
{
HOST_WIDE_INT sc = INTVAL (XEXP (y, 1));
unsigned HOST_WIDE_INT uc = sc;
- if (sc < 0 && -uc == (uc & -uc))
+ if (sc < 0 && pow2_or_zerop (-uc))
{
- if (ysize > 0)
+ if (maybe_gt (ysize, 0))
ysize = -ysize;
- if (ysize)
+ if (maybe_ne (ysize, 0))
ysize += sc + 1;
c += sc + 1;
return memrefs_conflict_p (xsize, x,
if (CONSTANT_P (x))
{
- if (CONST_INT_P (x) && CONST_INT_P (y))
+ poly_int64 cx, cy;
+ if (poly_int_rtx_p (x, &cx) && poly_int_rtx_p (y, &cy))
{
- c += (INTVAL (y) - INTVAL (x));
+ c += cy - cx;
return offset_overlap_p (c, xsize, ysize);
}
sizes), because we can't know how far they are from each
other. */
if (CONSTANT_P (y))
- return (xsize < 0 || ysize < 0 || offset_overlap_p (c, xsize, ysize));
+ return (maybe_lt (xsize, 0)
+ || maybe_lt (ysize, 0)
+ || offset_overlap_p (c, xsize, ysize));
return -1;
}
ways.
If both memory references are volatile, then there must always be a
- dependence between the two references, since their order can not be
+ dependence between the two references, since their order cannot be
changed. A volatile and non-volatile reference can be interchanged
though.
static void
adjust_offset_for_component_ref (tree x, bool *known_p,
- HOST_WIDE_INT *offset)
+ poly_int64 *offset)
{
if (!*known_p)
return;
{
tree xoffset = component_ref_field_offset (x);
tree field = TREE_OPERAND (x, 1);
- if (TREE_CODE (xoffset) != INTEGER_CST)
+ if (!poly_int_tree_p (xoffset))
{
*known_p = false;
return;
}
- offset_int woffset
- = (wi::to_offset (xoffset)
- + wi::lrshift (wi::to_offset (DECL_FIELD_BIT_OFFSET (field)),
- LOG2_BITS_PER_UNIT));
- if (!wi::fits_uhwi_p (woffset))
+ poly_offset_int woffset
+ = (wi::to_poly_offset (xoffset)
+ + (wi::to_offset (DECL_FIELD_BIT_OFFSET (field))
+ >> LOG2_BITS_PER_UNIT)
+ + *offset);
+ if (!woffset.to_shwi (offset))
{
*known_p = false;
return;
}
- *offset += woffset.to_uhwi ();
x = TREE_OPERAND (x, 0);
}
rtx rtlx, rtly;
rtx basex, basey;
bool moffsetx_known_p, moffsety_known_p;
- HOST_WIDE_INT moffsetx = 0, moffsety = 0;
- HOST_WIDE_INT offsetx = 0, offsety = 0, sizex, sizey, tem;
+ poly_int64 moffsetx = 0, moffsety = 0;
+ poly_int64 offsetx = 0, offsety = 0, sizex, sizey;
/* Unless both have exprs, we can't tell anything. */
if (exprx == 0 || expry == 0)
if (! DECL_P (exprx) || ! DECL_P (expry))
return 0;
+ /* If we refer to different gimple registers, or one gimple register
+ and one non-gimple-register, we know they can't overlap. First,
+ gimple registers don't have their addresses taken. Now, there
+ could be more than one stack slot for (different versions of) the
+ same gimple register, but we can presumably tell they don't
+ overlap based on offsets from stack base addresses elsewhere.
+ It's important that we don't proceed to DECL_RTL, because gimple
+ registers may not pass DECL_RTL_SET_P, and make_decl_rtl won't be
+ able to do anything about them since no SSA information will have
+ remained to guide it. */
+ if (is_gimple_reg (exprx) || is_gimple_reg (expry))
+ return exprx != expry
+ || (moffsetx_known_p && moffsety_known_p
+ && MEM_SIZE_KNOWN_P (x) && MEM_SIZE_KNOWN_P (y)
+ && !offset_overlap_p (moffsety - moffsetx,
+ MEM_SIZE (x), MEM_SIZE (y)));
+
/* With invalid code we can end up storing into the constant pool.
Bail out to avoid ICEing when creating RTL for this.
See gfortran.dg/lto/20091028-2_0.f90. */
|| TREE_CODE (expry) == CONST_DECL)
return 1;
+ /* If one decl is known to be a function or label in a function and
+ the other is some kind of data, they can't overlap. */
+ if ((TREE_CODE (exprx) == FUNCTION_DECL
+ || TREE_CODE (exprx) == LABEL_DECL)
+ != (TREE_CODE (expry) == FUNCTION_DECL
+ || TREE_CODE (expry) == LABEL_DECL))
+ return 1;
+
+ /* If either of the decls doesn't have DECL_RTL set (e.g. marked as
+ living in multiple places), we can't tell anything. Exception
+ are FUNCTION_DECLs for which we can create DECL_RTL on demand. */
+ if ((!DECL_RTL_SET_P (exprx) && TREE_CODE (exprx) != FUNCTION_DECL)
+ || (!DECL_RTL_SET_P (expry) && TREE_CODE (expry) != FUNCTION_DECL))
+ return 0;
+
rtlx = DECL_RTL (exprx);
rtly = DECL_RTL (expry);
we can avoid overlap is if we can deduce that they are nonoverlapping
pieces of that decl, which is very rare. */
basex = MEM_P (rtlx) ? XEXP (rtlx, 0) : rtlx;
- if (GET_CODE (basex) == PLUS && CONST_INT_P (XEXP (basex, 1)))
- offsetx = INTVAL (XEXP (basex, 1)), basex = XEXP (basex, 0);
+ basex = strip_offset_and_add (basex, &offsetx);
basey = MEM_P (rtly) ? XEXP (rtly, 0) : rtly;
- if (GET_CODE (basey) == PLUS && CONST_INT_P (XEXP (basey, 1)))
- offsety = INTVAL (XEXP (basey, 1)), basey = XEXP (basey, 0);
+ basey = strip_offset_and_add (basey, &offsety);
/* If the bases are different, we know they do not overlap if both
are constants or if one is a constant and the other a pointer into the
stack frame. Otherwise a different base means we can't tell if they
overlap or not. */
- if (! rtx_equal_p (basex, basey))
+ if (compare_base_decls (exprx, expry) == 0)
return ((CONSTANT_P (basex) && CONSTANT_P (basey))
|| (CONSTANT_P (basex) && REG_P (basey)
&& REGNO_PTR_FRAME_P (REGNO (basey)))
/* Offset based disambiguation not appropriate for loop invariant */
if (loop_invariant)
- return 0;
+ return 0;
+
+ /* Offset based disambiguation is OK even if we do not know that the
+ declarations are necessarily different
+ (i.e. compare_base_decls (exprx, expry) == -1) */
- sizex = (!MEM_P (rtlx) ? (int) GET_MODE_SIZE (GET_MODE (rtlx))
+ sizex = (!MEM_P (rtlx) ? poly_int64 (GET_MODE_SIZE (GET_MODE (rtlx)))
: MEM_SIZE_KNOWN_P (rtlx) ? MEM_SIZE (rtlx)
: -1);
- sizey = (!MEM_P (rtly) ? (int) GET_MODE_SIZE (GET_MODE (rtly))
+ sizey = (!MEM_P (rtly) ? poly_int64 (GET_MODE_SIZE (GET_MODE (rtly)))
: MEM_SIZE_KNOWN_P (rtly) ? MEM_SIZE (rtly)
: -1);
if (MEM_SIZE_KNOWN_P (y) && moffsety_known_p)
sizey = MEM_SIZE (y);
- /* Put the values of the memref with the lower offset in X's values. */
- if (offsetx > offsety)
- {
- tem = offsetx, offsetx = offsety, offsety = tem;
- tem = sizex, sizex = sizey, sizey = tem;
- }
-
- /* If we don't know the size of the lower-offset value, we can't tell
- if they conflict. Otherwise, we do the test. */
- return sizex >= 0 && offsety >= offsetx + sizex;
+ return !ranges_maybe_overlap_p (offsetx, sizex, offsety, sizey);
}
/* Helper for true_dependence and canon_true_dependence.
int ret;
gcc_checking_assert (x_canonicalized
- ? (x_addr != NULL_RTX && x_mode != VOIDmode)
+ ? (x_addr != NULL_RTX
+ && (x_mode != VOIDmode || GET_MODE (x) == VOIDmode))
: (x_addr == NULL_RTX && x_mode == VOIDmode));
if (MEM_VOLATILE_P (x) && MEM_VOLATILE_P (mem))
/*mem_canonicalized=*/false,
/*x_canonicalized*/false, /*writep=*/true);
}
+
+/* Likewise, but we already have a canonicalized MEM, and X_ADDR for X.
+ Also, consider X in X_MODE (which might be from an enclosing
+ STRICT_LOW_PART / ZERO_EXTRACT).
+ If MEM_CANONICALIZED is true, MEM is canonicalized. */
+
+int
+canon_output_dependence (const_rtx mem, bool mem_canonicalized,
+ const_rtx x, machine_mode x_mode, rtx x_addr)
+{
+ return write_dependence_p (mem, x, x_mode, x_addr,
+ mem_canonicalized, /*x_canonicalized=*/true,
+ /*writep=*/true);
+}
\f
argument. FUNCTION_ARG_REGNO_P tests outgoing register
numbers, so translate if necessary due to register windows. */
if (FUNCTION_ARG_REGNO_P (OUTGOING_REGNO (i))
- && HARD_REGNO_MODE_OK (i, Pmode))
+ && targetm.hard_regno_mode_ok (i, Pmode))
static_reg_base_value[i] = arg_base_value;
+ /* RTL code is required to be consistent about whether it uses the
+ stack pointer, the frame pointer or the argument pointer to
+ access a given area of the frame. We can therefore use the
+ base address to distinguish between the different areas. */
static_reg_base_value[STACK_POINTER_REGNUM]
= unique_base_value (UNIQUE_BASE_VALUE_SP);
static_reg_base_value[ARG_POINTER_REGNUM]
= unique_base_value (UNIQUE_BASE_VALUE_ARGP);
static_reg_base_value[FRAME_POINTER_REGNUM]
= unique_base_value (UNIQUE_BASE_VALUE_FP);
+
+ /* The above rules extend post-reload, with eliminations applying
+ consistently to each of the three pointers. Cope with cases in
+ which the frame pointer is eliminated to the hard frame pointer
+ rather than the stack pointer. */
if (!HARD_FRAME_POINTER_IS_FRAME_POINTER)
static_reg_base_value[HARD_FRAME_POINTER_REGNUM]
= unique_base_value (UNIQUE_BASE_VALUE_HFP);
{
if (!INSN_P (insn))
return false;
+ /* Conservatively assume all non-readonly MEMs might be modified in
+ calls. */
+ if (CALL_P (insn))
+ return true;
memory_modified = false;
- note_stores (PATTERN (insn), memory_modified_1, CONST_CAST_RTX(mem));
+ note_stores (as_a<const rtx_insn *> (insn), memory_modified_1,
+ CONST_CAST_RTX(mem));
return memory_modified;
}
-/* Return TRUE if the destination of a set is rtx identical to
- ITEM. */
-static inline bool
-set_dest_equal_p (const_rtx set, const_rtx item)
-{
- rtx dest = SET_DEST (set);
- return rtx_equal_p (dest, item);
-}
-
-/* Like memory_modified_in_insn_p, but return TRUE if INSN will
- *DEFINITELY* modify the memory contents of MEM. */
-bool
-memory_must_be_modified_in_insn_p (const_rtx mem, const_rtx insn)
-{
- if (!INSN_P (insn))
- return false;
- insn = PATTERN (insn);
- if (GET_CODE (insn) == SET)
- return set_dest_equal_p (insn, mem);
- else if (GET_CODE (insn) == PARALLEL)
- {
- int i;
- for (i = 0; i < XVECLEN (insn, 0); i++)
- {
- rtx sub = XVECEXP (insn, 0, i);
- if (GET_CODE (sub) == SET
- && set_dest_equal_p (sub, mem))
- return true;
- }
- }
- return false;
-}
-
/* Initialize the aliasing machinery. Initialize the REG_KNOWN_VALUE
array. */
rpo = XNEWVEC (int, n_basic_blocks_for_fn (cfun));
rpo_cnt = pre_and_rev_post_order_compute (NULL, rpo, false);
+ /* The prologue/epilogue insns are not threaded onto the
+ insn chain until after reload has completed. Thus,
+ there is no sense wasting time checking if INSN is in
+ the prologue/epilogue until after reload has completed. */
+ bool could_be_prologue_epilogue = ((targetm.have_prologue ()
+ || targetm.have_epilogue ())
+ && reload_completed);
+
pass = 0;
do
{
/* Initialize the alias information for this pass. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (static_reg_base_value[i])
+ if (static_reg_base_value[i]
+ /* Don't treat the hard frame pointer as special if we
+ eliminated the frame pointer to the stack pointer instead. */
+ && !(i == HARD_FRAME_POINTER_REGNUM
+ && reload_completed
+ && !frame_pointer_needed
+ && targetm.can_eliminate (FRAME_POINTER_REGNUM,
+ STACK_POINTER_REGNUM)))
{
new_reg_base_value[i] = static_reg_base_value[i];
bitmap_set_bit (reg_seen, i);
{
rtx note, set;
-#if defined (HAVE_prologue)
- static const bool prologue = true;
-#else
- static const bool prologue = false;
-#endif
-
- /* The prologue/epilogue insns are not threaded onto the
- insn chain until after reload has completed. Thus,
- there is no sense wasting time checking if INSN is in
- the prologue/epilogue until after reload has completed. */
- if ((prologue || HAVE_epilogue) && reload_completed
+ if (could_be_prologue_epilogue
&& prologue_epilogue_contains (insn))
continue;
&& find_reg_note (insn, REG_NOALIAS, NULL_RTX))
record_set (SET_DEST (PATTERN (insn)), NULL_RTX, NULL);
else
- note_stores (PATTERN (insn), record_set, NULL);
+ note_stores (insn, record_set, NULL);
set = single_set (insn);
&& DF_REG_DEF_COUNT (regno) != 1)
note = NULL_RTX;
+ poly_int64 offset;
if (note != NULL_RTX
&& GET_CODE (XEXP (note, 0)) != EXPR_LIST
&& ! rtx_varies_p (XEXP (note, 0), 1)
&& GET_CODE (src) == PLUS
&& REG_P (XEXP (src, 0))
&& (t = get_reg_known_value (REGNO (XEXP (src, 0))))
- && CONST_INT_P (XEXP (src, 1)))
+ && poly_int_rtx_p (XEXP (src, 1), &offset))
{
- t = plus_constant (GET_MODE (src), t,
- INTVAL (XEXP (src, 1)));
+ t = plus_constant (GET_MODE (src), t, offset);
set_reg_known_value (regno, t);
set_reg_known_equiv_p (regno, false);
}