mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-03 23:36:21 +08:00
New modref/ipa_modref optimization passes
2020-09-19 David Cepelik <d@dcepelik.cz> Jan Hubicka <hubicka@ucw.cz> * Makefile.in: Add ipa-modref.c and ipa-modref-tree.c. * alias.c: (reference_alias_ptr_type_1): Export. * alias.h (reference_alias_ptr_type_1): Declare. * common.opt (fipa-modref): New. * gengtype.c (open_base_files): Add ipa-modref-tree.h and ipa-modref.h * ipa-modref-tree.c: New file. * ipa-modref-tree.h: New file. * ipa-modref.c: New file. * ipa-modref.h: New file. * lto-section-in.c (lto_section_name): Add ipa_modref. * lto-streamer.h (enum lto_section_type): Add LTO_section_ipa_modref. * opts.c (default_options_table): Enable ipa-modref at -O1+. * params.opt (-param=modref-max-bases, -param=modref-max-refs, -param=modref-max-tests): New params. * passes.def: Schedule pass_modref and pass_ipa_modref. * timevar.def (TV_IPA_MODREF): New timevar. (TV_TREE_MODREF): New timevar. * tree-pass.h (make_pass_modref): Declare. (make_pass_ipa_modref): Declare. * tree-ssa-alias.c (dump_alias_stats): Include ipa-modref-tree.h and ipa-modref.h (alias_stats): Add modref_use_may_alias, modref_use_no_alias, modref_clobber_may_alias, modref_clobber_no_alias, modref_tests. (dump_alias_stats): Dump new stats. (nonoverlapping_array_refs_p): Fix formating. (modref_may_conflict): New function. (ref_maybe_used_by_call_p_1): Use it. (call_may_clobber_ref_p_1): Use it. (call_may_clobber_ref_p): Update. (stmt_may_clobber_ref_p_1): Update. * tree-ssa-alias.h (call_may_clobber_ref_p_1): Update.
This commit is contained in:
parent
2fe5b7d1f6
commit
d119f34c95
@ -1419,6 +1419,8 @@ OBJS = \
|
||||
ipa-visibility.o \
|
||||
ipa-inline-analysis.o \
|
||||
ipa-inline-transform.o \
|
||||
ipa-modref.o \
|
||||
ipa-modref-tree.o \
|
||||
ipa-predicate.o \
|
||||
ipa-profile.o \
|
||||
ipa-prop.o \
|
||||
@ -2587,6 +2589,8 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
|
||||
$(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
|
||||
$(srcdir)/ipa-prop.c $(srcdir)/ipa-cp.c $(srcdir)/ipa-utils.h \
|
||||
$(srcdir)/ipa-param-manipulation.h $(srcdir)/ipa-sra.c $(srcdir)/dbxout.c \
|
||||
$(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \
|
||||
$(srcdir)/ipa-modref-tree.h \
|
||||
$(srcdir)/signop.h \
|
||||
$(srcdir)/dwarf2out.h \
|
||||
$(srcdir)/dwarf2asm.c \
|
||||
|
@ -737,7 +737,7 @@ get_deref_alias_set (tree t)
|
||||
adjusted to point to the outermost component reference that
|
||||
can be used for assigning an alias set. */
|
||||
|
||||
static tree
|
||||
tree
|
||||
reference_alias_ptr_type_1 (tree *t)
|
||||
{
|
||||
tree inner;
|
||||
|
@ -36,6 +36,7 @@ extern int objects_must_conflict_p (tree, tree);
|
||||
extern int nonoverlapping_memrefs_p (const_rtx, const_rtx, bool);
|
||||
extern void dump_alias_stats_in_alias_c (FILE *s);
|
||||
tree reference_alias_ptr_type (tree);
|
||||
tree reference_alias_ptr_type_1 (tree *);
|
||||
bool alias_ptr_types_compatible_p (tree, tree);
|
||||
int compare_base_decls (tree, tree);
|
||||
bool refs_same_for_tbaa_p (tree, tree);
|
||||
|
@ -1825,6 +1825,10 @@ fipa-bit-cp
|
||||
Common Report Var(flag_ipa_bit_cp) Optimization
|
||||
Perform interprocedural bitwise constant propagation.
|
||||
|
||||
fipa-modref
|
||||
Common Report Var(flag_ipa_modref) Optimization
|
||||
Perform interprocedural modref analysis
|
||||
|
||||
fipa-profile
|
||||
Common Report Var(flag_ipa_profile) Init(0) Optimization
|
||||
Perform interprocedural profile propagation.
|
||||
|
@ -1726,7 +1726,7 @@ open_base_files (void)
|
||||
"except.h", "output.h", "cfgloop.h", "target.h", "lto-streamer.h",
|
||||
"target-globals.h", "ipa-ref.h", "cgraph.h", "symbol-summary.h",
|
||||
"ipa-prop.h", "ipa-fnsummary.h", "dwarf2out.h", "omp-general.h",
|
||||
"omp-offload.h", NULL
|
||||
"omp-offload.h", "ipa-modref-tree.h", "ipa-modref.h", NULL
|
||||
};
|
||||
const char *const *ifp;
|
||||
outf_p gtype_desc_c;
|
||||
|
236
gcc/ipa-modref-tree.c
Normal file
236
gcc/ipa-modref-tree.c
Normal file
@ -0,0 +1,236 @@
|
||||
/* Data structure for the modref pass.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
Contributed by David Cepelik and Jan Hubicka
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "backend.h"
|
||||
#include "tree.h"
|
||||
#include "ipa-modref-tree.h"
|
||||
#include "selftest.h"
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
|
||||
static void
|
||||
test_insert_search_collapse ()
|
||||
{
|
||||
modref_base_node<alias_set_type> *base_node;
|
||||
modref_ref_node<alias_set_type> *ref_node;
|
||||
|
||||
modref_tree<alias_set_type> *t = new modref_tree<alias_set_type>(1, 2);
|
||||
ASSERT_FALSE (t->every_base);
|
||||
|
||||
/* Insert into an empty tree. */
|
||||
t->insert (1, 2);
|
||||
ASSERT_NE (t->bases, NULL);
|
||||
ASSERT_EQ (t->bases->length (), 1);
|
||||
ASSERT_FALSE (t->every_base);
|
||||
ASSERT_EQ (t->search (2), NULL);
|
||||
|
||||
base_node = t->search (1);
|
||||
ASSERT_NE (base_node, NULL);
|
||||
ASSERT_EQ (base_node->base, 1);
|
||||
ASSERT_NE (base_node->refs, NULL);
|
||||
ASSERT_EQ (base_node->refs->length (), 1);
|
||||
ASSERT_EQ (base_node->search (1), NULL);
|
||||
|
||||
ref_node = base_node->search (2);
|
||||
ASSERT_NE (ref_node, NULL);
|
||||
ASSERT_EQ (ref_node->ref, 2);
|
||||
|
||||
/* Insert when base exists but ref does not. */
|
||||
t->insert (1, 3);
|
||||
ASSERT_NE (t->bases, NULL);
|
||||
ASSERT_EQ (t->bases->length (), 1);
|
||||
ASSERT_EQ (t->search (1), base_node);
|
||||
ASSERT_EQ (t->search (2), NULL);
|
||||
ASSERT_NE (base_node->refs, NULL);
|
||||
ASSERT_EQ (base_node->refs->length (), 2);
|
||||
|
||||
ref_node = base_node->search (3);
|
||||
ASSERT_NE (ref_node, NULL);
|
||||
|
||||
/* Insert when base and ref exist, but access is not dominated by nor
|
||||
dominates other accesses. */
|
||||
t->insert (1, 2);
|
||||
ASSERT_EQ (t->bases->length (), 1);
|
||||
ASSERT_EQ (t->search (1), base_node);
|
||||
|
||||
ref_node = base_node->search (2);
|
||||
ASSERT_NE (ref_node, NULL);
|
||||
|
||||
/* Insert when base and ref exist and access is dominated. */
|
||||
t->insert (1, 2);
|
||||
ASSERT_EQ (t->search (1), base_node);
|
||||
ASSERT_EQ (base_node->search (2), ref_node);
|
||||
|
||||
/* Insert ref to trigger ref list collapse for base 1. */
|
||||
t->insert (1, 4);
|
||||
ASSERT_EQ (t->search (1), base_node);
|
||||
ASSERT_EQ (base_node->refs, NULL);
|
||||
ASSERT_EQ (base_node->search (2), NULL);
|
||||
ASSERT_EQ (base_node->search (3), NULL);
|
||||
ASSERT_TRUE (base_node->every_ref);
|
||||
|
||||
/* Further inserts to collapsed ref list are ignored. */
|
||||
t->insert (1, 5);
|
||||
ASSERT_EQ (t->search (1), base_node);
|
||||
ASSERT_EQ (base_node->refs, NULL);
|
||||
ASSERT_EQ (base_node->search (2), NULL);
|
||||
ASSERT_EQ (base_node->search (3), NULL);
|
||||
ASSERT_TRUE (base_node->every_ref);
|
||||
|
||||
/* Insert base to trigger base list collapse. */
|
||||
t->insert (5, 6);
|
||||
ASSERT_TRUE (t->every_base);
|
||||
ASSERT_EQ (t->bases, NULL);
|
||||
ASSERT_EQ (t->search (1), NULL);
|
||||
|
||||
/* Further inserts to collapsed base list are ignored. */
|
||||
t->insert (7, 8);
|
||||
ASSERT_TRUE (t->every_base);
|
||||
ASSERT_EQ (t->bases, NULL);
|
||||
ASSERT_EQ (t->search (1), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
test_merge ()
|
||||
{
|
||||
modref_tree<alias_set_type> *t1, *t2;
|
||||
modref_base_node<alias_set_type> *base_node;
|
||||
|
||||
t1 = new modref_tree<alias_set_type>(3, 4);
|
||||
t1->insert (1, 1);
|
||||
t1->insert (1, 2);
|
||||
t1->insert (1, 3);
|
||||
t1->insert (2, 1);
|
||||
t1->insert (3, 1);
|
||||
|
||||
t2 = new modref_tree<alias_set_type>(10, 10);
|
||||
t2->insert (1, 2);
|
||||
t2->insert (1, 3);
|
||||
t2->insert (1, 4);
|
||||
t2->insert (3, 2);
|
||||
t2->insert (3, 3);
|
||||
t2->insert (3, 4);
|
||||
t2->insert (3, 5);
|
||||
|
||||
t1->merge (t2);
|
||||
|
||||
ASSERT_FALSE (t1->every_base);
|
||||
ASSERT_NE (t1->bases, NULL);
|
||||
ASSERT_EQ (t1->bases->length (), 3);
|
||||
|
||||
base_node = t1->search (1);
|
||||
ASSERT_NE (base_node->refs, NULL);
|
||||
ASSERT_FALSE (base_node->every_ref);
|
||||
ASSERT_EQ (base_node->refs->length (), 4);
|
||||
|
||||
base_node = t1->search (2);
|
||||
ASSERT_NE (base_node->refs, NULL);
|
||||
ASSERT_FALSE (base_node->every_ref);
|
||||
ASSERT_EQ (base_node->refs->length (), 1);
|
||||
|
||||
base_node = t1->search (3);
|
||||
ASSERT_EQ (base_node->refs, NULL);
|
||||
ASSERT_TRUE (base_node->every_ref);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
modref_tree_c_tests ()
|
||||
{
|
||||
test_insert_search_collapse ();
|
||||
test_merge ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
gt_ggc_mx (modref_tree < int >*const &tt)
|
||||
{
|
||||
if (tt->bases)
|
||||
{
|
||||
ggc_test_and_set_mark (tt->bases);
|
||||
gt_ggc_mx (tt->bases);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gt_ggc_mx (modref_tree < tree_node * >*const &tt)
|
||||
{
|
||||
if (tt->bases)
|
||||
{
|
||||
ggc_test_and_set_mark (tt->bases);
|
||||
gt_ggc_mx (tt->bases);
|
||||
}
|
||||
}
|
||||
|
||||
void gt_pch_nx (modref_tree<int>* const&) {}
|
||||
void gt_pch_nx (modref_tree<tree_node*>* const&) {}
|
||||
void gt_pch_nx (modref_tree<int>* const&, gt_pointer_operator, void *) {}
|
||||
void gt_pch_nx (modref_tree<tree_node*>* const&, gt_pointer_operator, void *) {}
|
||||
|
||||
void gt_ggc_mx (modref_base_node<int>* &b)
|
||||
{
|
||||
ggc_test_and_set_mark (b);
|
||||
if (b->refs)
|
||||
{
|
||||
ggc_test_and_set_mark (b->refs);
|
||||
gt_ggc_mx (b->refs);
|
||||
}
|
||||
}
|
||||
|
||||
void gt_ggc_mx (modref_base_node<tree_node*>* &b)
|
||||
{
|
||||
ggc_test_and_set_mark (b);
|
||||
if (b->refs)
|
||||
{
|
||||
ggc_test_and_set_mark (b->refs);
|
||||
gt_ggc_mx (b->refs);
|
||||
}
|
||||
if (b->base)
|
||||
gt_ggc_mx (b->base);
|
||||
}
|
||||
|
||||
void gt_pch_nx (modref_base_node<int>*) {}
|
||||
void gt_pch_nx (modref_base_node<tree_node*>*) {}
|
||||
void gt_pch_nx (modref_base_node<int>*, gt_pointer_operator, void *) {}
|
||||
void gt_pch_nx (modref_base_node<tree_node*>*, gt_pointer_operator, void *) {}
|
||||
|
||||
void gt_ggc_mx (modref_ref_node<int>* &r)
|
||||
{
|
||||
ggc_test_and_set_mark (r);
|
||||
}
|
||||
|
||||
void gt_ggc_mx (modref_ref_node<tree_node*>* &r)
|
||||
{
|
||||
ggc_test_and_set_mark (r);
|
||||
if (r->ref)
|
||||
gt_ggc_mx (r->ref);
|
||||
}
|
||||
|
||||
void gt_pch_nx (modref_ref_node<int>* ) {}
|
||||
void gt_pch_nx (modref_ref_node<tree_node*>*) {}
|
||||
void gt_pch_nx (modref_ref_node<int>*, gt_pointer_operator, void *) {}
|
||||
void gt_pch_nx (modref_ref_node<tree_node*>*, gt_pointer_operator, void *) {}
|
||||
|
||||
|
253
gcc/ipa-modref-tree.h
Normal file
253
gcc/ipa-modref-tree.h
Normal file
@ -0,0 +1,253 @@
|
||||
/* Data structure for the modref pass.
|
||||
Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
Contributed by David Cepelik and Jan Hubicka
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_MODREF_TREE_H
|
||||
#define GCC_MODREF_TREE_H
|
||||
|
||||
struct ipa_modref_summary;
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct GTY((user)) modref_ref_node
|
||||
{
|
||||
T ref;
|
||||
|
||||
modref_ref_node (T ref):
|
||||
ref (ref)
|
||||
{}
|
||||
};
|
||||
|
||||
/* Base of an access. */
|
||||
template <typename T>
|
||||
struct GTY((user)) modref_base_node
|
||||
{
|
||||
T base;
|
||||
vec <modref_ref_node <T> *, va_gc> *refs;
|
||||
bool every_ref;
|
||||
|
||||
modref_base_node (T base):
|
||||
base (base),
|
||||
refs (NULL),
|
||||
every_ref (false) {}
|
||||
|
||||
/* Search REF; return NULL if failed. */
|
||||
modref_ref_node <T> *search (T ref)
|
||||
{
|
||||
size_t i;
|
||||
modref_ref_node <T> *n;
|
||||
FOR_EACH_VEC_SAFE_ELT (refs, i, n)
|
||||
if (n->ref == ref)
|
||||
return n;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Insert REF; collapse tree if there are more than MAX_REFS. */
|
||||
modref_ref_node <T> *insert_ref (T ref, size_t max_refs)
|
||||
{
|
||||
modref_ref_node <T> *ref_node;
|
||||
|
||||
/* If the node is collapsed, don't do anything. */
|
||||
if (every_ref)
|
||||
return NULL;
|
||||
|
||||
if (!ref)
|
||||
{
|
||||
collapse ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Otherwise, insert a node for the ref of the access under the base. */
|
||||
ref_node = search (ref);
|
||||
if (ref_node)
|
||||
return ref_node;
|
||||
|
||||
/* Collapse the node if too full already. */
|
||||
if (refs && refs->length () >= max_refs)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "--param param=modref-max-refs limit reached\n");
|
||||
collapse ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref_node = new (ggc_alloc <modref_ref_node <T> > ())modref_ref_node <T>
|
||||
(ref);
|
||||
vec_safe_push (refs, ref_node);
|
||||
return ref_node;
|
||||
}
|
||||
|
||||
void collapse ()
|
||||
{
|
||||
vec_free (refs);
|
||||
refs = NULL;
|
||||
every_ref = true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Access tree for a single function. */
|
||||
template <typename T>
|
||||
struct GTY((user)) modref_tree
|
||||
{
|
||||
vec <modref_base_node <T> *, va_gc> *bases;
|
||||
size_t max_bases;
|
||||
size_t max_refs;
|
||||
bool every_base;
|
||||
|
||||
modref_tree (size_t max_bases, size_t max_refs):
|
||||
bases (NULL),
|
||||
max_bases (max_bases),
|
||||
max_refs (max_refs),
|
||||
every_base (false) {}
|
||||
|
||||
modref_base_node <T> *insert_base (T base)
|
||||
{
|
||||
modref_base_node <T> *base_node;
|
||||
|
||||
/* If the node is collapsed, don't do anything. */
|
||||
if (every_base)
|
||||
return NULL;
|
||||
|
||||
/* Otherwise, insert a node for the base of the access into the tree. */
|
||||
base_node = search (base);
|
||||
if (base_node)
|
||||
return base_node;
|
||||
|
||||
/* Collapse the node if too full already. */
|
||||
if (bases && bases->length () >= max_bases)
|
||||
{
|
||||
if (dump_file)
|
||||
fprintf (dump_file, "--param param=modref-max-bases limit reached\n");
|
||||
collapse ();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base_node = new (ggc_alloc <modref_base_node <T> > ())
|
||||
modref_base_node <T> (base);
|
||||
vec_safe_push (bases, base_node);
|
||||
return base_node;
|
||||
}
|
||||
|
||||
/* Insert memory access to the tree. */
|
||||
void insert (T base, T ref)
|
||||
{
|
||||
modref_base_node <T> *base_node;
|
||||
|
||||
base_node = insert_base (base);
|
||||
|
||||
if (!base && !ref)
|
||||
{
|
||||
collapse ();
|
||||
return;
|
||||
}
|
||||
if (!base_node)
|
||||
return;
|
||||
gcc_assert (search (base) != NULL);
|
||||
|
||||
base_node->insert_ref (ref, max_refs);
|
||||
if (!base && base_node->every_ref)
|
||||
{
|
||||
collapse ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge OTHER into the tree. */
|
||||
void merge (modref_tree <T> *other)
|
||||
{
|
||||
if (!other)
|
||||
return;
|
||||
if (other->every_base)
|
||||
{
|
||||
collapse ();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i, j;
|
||||
modref_base_node <T> *base_node, *my_base_node;
|
||||
modref_ref_node <T> *ref_node, *my_ref_node;
|
||||
FOR_EACH_VEC_SAFE_ELT (other->bases, i, base_node)
|
||||
{
|
||||
my_base_node = insert_base (base_node->base);
|
||||
if (!my_base_node)
|
||||
continue;
|
||||
|
||||
if (base_node->every_ref)
|
||||
{
|
||||
my_base_node->collapse ();
|
||||
continue;
|
||||
}
|
||||
|
||||
FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
|
||||
{
|
||||
my_ref_node = my_base_node->insert_ref (ref_node->ref, max_refs);
|
||||
if (!my_ref_node)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Search BASE in tree; return NULL if failed. */
|
||||
modref_base_node <T> *search (T base)
|
||||
{
|
||||
size_t i;
|
||||
modref_base_node <T> *n;
|
||||
FOR_EACH_VEC_SAFE_ELT (bases, i, n)
|
||||
if (n->base == base)
|
||||
return n;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void collapse ()
|
||||
{
|
||||
vec_free (bases);
|
||||
bases = NULL;
|
||||
every_base = true;
|
||||
}
|
||||
};
|
||||
|
||||
void modref_c_tests ();
|
||||
|
||||
void gt_ggc_mx (modref_tree <int>* const&);
|
||||
void gt_ggc_mx (modref_tree <tree_node*>* const&);
|
||||
void gt_pch_nx (modref_tree <int>* const&);
|
||||
void gt_pch_nx (modref_tree <tree_node*>* const&);
|
||||
void gt_pch_nx (modref_tree <int>* const&, gt_pointer_operator op, void *cookie);
|
||||
void gt_pch_nx (modref_tree <tree_node*>* const&, gt_pointer_operator op,
|
||||
void *cookie);
|
||||
|
||||
void gt_ggc_mx (modref_base_node <int>*);
|
||||
void gt_ggc_mx (modref_base_node <tree_node*>* &);
|
||||
void gt_pch_nx (modref_base_node <int>* const&);
|
||||
void gt_pch_nx (modref_base_node <tree_node*>* const&);
|
||||
void gt_pch_nx (modref_base_node <int>* const&, gt_pointer_operator op,
|
||||
void *cookie);
|
||||
void gt_pch_nx (modref_base_node <tree_node*>* const&, gt_pointer_operator op,
|
||||
void *cookie);
|
||||
|
||||
void gt_ggc_mx (modref_ref_node <int>*);
|
||||
void gt_ggc_mx (modref_ref_node <tree_node*>* &);
|
||||
void gt_pch_nx (modref_ref_node <int>* const&);
|
||||
void gt_pch_nx (modref_ref_node <tree_node*>* const&);
|
||||
void gt_pch_nx (modref_ref_node <int>* const&, gt_pointer_operator op,
|
||||
void *cookie);
|
||||
void gt_pch_nx (modref_ref_node <tree_node*>* const&, gt_pointer_operator op,
|
||||
void *cookie);
|
||||
|
||||
#endif
|
1376
gcc/ipa-modref.c
Normal file
1376
gcc/ipa-modref.c
Normal file
File diff suppressed because it is too large
Load Diff
48
gcc/ipa-modref.h
Normal file
48
gcc/ipa-modref.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* Search for references that a functions loads or stores.
|
||||
Copyright (C) 2019 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef IPA_MODREF_H
|
||||
#define IPA_MODREF_H
|
||||
|
||||
typedef modref_tree <alias_set_type> modref_records;
|
||||
typedef modref_tree <tree> modref_records_lto;
|
||||
|
||||
/* Single function summary. */
|
||||
|
||||
struct GTY(()) modref_summary
|
||||
{
|
||||
/* Load and stores in function (transitively closed to all callees) */
|
||||
modref_records *loads;
|
||||
modref_records *stores;
|
||||
|
||||
/* The same but using tree types rather than alias sets. This is necessary
|
||||
to make the information streamable for LTO but is also more verbose
|
||||
and thus more likely to hit the limits. */
|
||||
modref_records_lto *loads_lto;
|
||||
modref_records_lto *stores_lto;
|
||||
bool finished;
|
||||
|
||||
modref_summary ();
|
||||
~modref_summary ();
|
||||
void dump (FILE *);
|
||||
};
|
||||
|
||||
modref_summary *get_modref_function_summary (cgraph_node *func);
|
||||
|
||||
#endif
|
@ -56,6 +56,7 @@ const char *lto_section_name[LTO_N_SECTION_TYPES] =
|
||||
"lto",
|
||||
"ipa_sra",
|
||||
"odr_types",
|
||||
"ipa_modref",
|
||||
};
|
||||
|
||||
/* Hooks so that the ipa passes can call into the lto front end to get
|
||||
|
@ -227,6 +227,7 @@ enum lto_section_type
|
||||
LTO_section_lto,
|
||||
LTO_section_ipa_sra,
|
||||
LTO_section_odr_types,
|
||||
LTO_section_ipa_modref,
|
||||
LTO_N_SECTION_TYPES /* Must be last. */
|
||||
};
|
||||
|
||||
|
@ -466,6 +466,7 @@ static const struct default_options default_options_table[] =
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_finline_functions_called_once, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_fmove_loop_invariants, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_fssa_phiopt, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_fipa_modref, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_ftree_bit_ccp, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_ftree_dse, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS_NOT_DEBUG, OPT_ftree_pta, NULL, 1 },
|
||||
|
@ -872,6 +872,18 @@ Maximum size of a single store merging region in bytes.
|
||||
Common Joined UInteger Var(param_switch_conversion_branch_ratio) Init(8) IntegerRange(1, 65536) Param Optimization
|
||||
The maximum ratio between array size and switch branches for a switch conversion to take place.
|
||||
|
||||
-param=modref-max-bases=
|
||||
Common Joined UInteger Var(param_modref_max_bases) Init(32)
|
||||
Maximum number of bases stored in each modref tree.
|
||||
|
||||
-param=modref-max-refs=
|
||||
Common Joined UInteger Var(param_modref_max_refs) Init(16)
|
||||
Maximum number of refs stored in each modref tree.
|
||||
|
||||
-param=modref-max-tests=
|
||||
Common Joined UInteger Var(param_modref_max_tests) Init(64)
|
||||
Maximum number of tests perofmed by modref query
|
||||
|
||||
-param=tm-max-aggregate-size=
|
||||
Common Joined UInteger Var(param_tm_max_aggregate_size) Init(9) Param Optimization
|
||||
Size in bytes after which thread-local aggregates should be instrumented with the logging functions instead of save/restore pairs.
|
||||
|
@ -91,6 +91,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_dse);
|
||||
NEXT_PASS (pass_cd_dce);
|
||||
NEXT_PASS (pass_phiopt, true /* early_p */);
|
||||
NEXT_PASS (pass_modref);
|
||||
NEXT_PASS (pass_tail_recursion);
|
||||
NEXT_PASS (pass_convert_switch);
|
||||
NEXT_PASS (pass_cleanup_eh);
|
||||
@ -156,6 +157,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_ipa_fn_summary);
|
||||
NEXT_PASS (pass_ipa_inline);
|
||||
NEXT_PASS (pass_ipa_pure_const);
|
||||
NEXT_PASS (pass_ipa_modref);
|
||||
NEXT_PASS (pass_ipa_free_fn_summary, false /* small_p */);
|
||||
NEXT_PASS (pass_ipa_reference);
|
||||
/* This pass needs to be scheduled after any IP code duplication. */
|
||||
@ -346,6 +348,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_late_warn_uninitialized);
|
||||
NEXT_PASS (pass_uncprop);
|
||||
NEXT_PASS (pass_local_pure_const);
|
||||
NEXT_PASS (pass_modref);
|
||||
POP_INSERT_PASSES ()
|
||||
NEXT_PASS (pass_all_optimizations_g);
|
||||
PUSH_INSERT_PASSES_WITHIN (pass_all_optimizations_g)
|
||||
@ -378,6 +381,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
NEXT_PASS (pass_late_warn_uninitialized);
|
||||
NEXT_PASS (pass_uncprop);
|
||||
NEXT_PASS (pass_local_pure_const);
|
||||
NEXT_PASS (pass_modref);
|
||||
POP_INSERT_PASSES ()
|
||||
NEXT_PASS (pass_tm_init);
|
||||
PUSH_INSERT_PASSES_WITHIN (pass_tm_init)
|
||||
|
@ -107,6 +107,7 @@ DEFTIMEVAR (TV_IPA_PTA , "ipa points-to")
|
||||
DEFTIMEVAR (TV_IPA_SRA , "ipa SRA")
|
||||
DEFTIMEVAR (TV_IPA_FREE_LANG_DATA , "ipa free lang data")
|
||||
DEFTIMEVAR (TV_IPA_FREE_INLINE_SUMMARY, "ipa free inline summary")
|
||||
DEFTIMEVAR (TV_IPA_MODREF , "ipa modref")
|
||||
/* Time spent by constructing CFG. */
|
||||
DEFTIMEVAR (TV_CFG , "cfg construction")
|
||||
/* Time spent by cleaning up CFG. */
|
||||
@ -218,6 +219,7 @@ DEFTIMEVAR (TV_TREE_SINCOS , "gimple CSE sin/cos")
|
||||
DEFTIMEVAR (TV_TREE_WIDEN_MUL , "gimple widening/fma detection")
|
||||
DEFTIMEVAR (TV_TRANS_MEM , "transactional memory")
|
||||
DEFTIMEVAR (TV_TREE_STRLEN , "tree strlen optimization")
|
||||
DEFTIMEVAR (TV_TREE_MODREF , "tree modref")
|
||||
DEFTIMEVAR (TV_CGRAPH_VERIFY , "callgraph verifier")
|
||||
DEFTIMEVAR (TV_DOM_FRONTIERS , "dominance frontiers")
|
||||
DEFTIMEVAR (TV_DOMINANCE , "dominance computation")
|
||||
|
@ -477,6 +477,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_oacc_kernels (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_warn_nonnull_compare (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_sprintf_length (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_walloca (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_modref (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_coroutine_lower_builtins (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_coroutine_early_expand_ifns (gcc::context *ctxt);
|
||||
extern gimple_opt_pass *make_pass_adjust_alignment (gcc::context *ctxt);
|
||||
@ -517,6 +518,7 @@ extern ipa_opt_pass_d *make_pass_ipa_profile (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_cdtor_merge (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_single_use (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_comdats (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_modref (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_materialize_all_clones (gcc::context *
|
||||
ctxt);
|
||||
|
||||
|
@ -38,6 +38,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "tree-dfa.h"
|
||||
#include "ipa-reference.h"
|
||||
#include "varasm.h"
|
||||
#include "ipa-modref-tree.h"
|
||||
#include "ipa-modref.h"
|
||||
|
||||
/* Broad overview of how alias analysis on gimple works:
|
||||
|
||||
@ -107,6 +109,11 @@ static struct {
|
||||
unsigned HOST_WIDE_INT nonoverlapping_refs_since_match_p_may_alias;
|
||||
unsigned HOST_WIDE_INT nonoverlapping_refs_since_match_p_must_overlap;
|
||||
unsigned HOST_WIDE_INT nonoverlapping_refs_since_match_p_no_alias;
|
||||
unsigned HOST_WIDE_INT modref_use_may_alias;
|
||||
unsigned HOST_WIDE_INT modref_use_no_alias;
|
||||
unsigned HOST_WIDE_INT modref_clobber_may_alias;
|
||||
unsigned HOST_WIDE_INT modref_clobber_no_alias;
|
||||
unsigned HOST_WIDE_INT modref_tests;
|
||||
} alias_stats;
|
||||
|
||||
void
|
||||
@ -153,6 +160,24 @@ dump_alias_stats (FILE *s)
|
||||
alias_stats.aliasing_component_refs_p_no_alias
|
||||
+ alias_stats.aliasing_component_refs_p_may_alias);
|
||||
dump_alias_stats_in_alias_c (s);
|
||||
fprintf (s, "\nModref stats:\n");
|
||||
fprintf (s, " modref use: "
|
||||
HOST_WIDE_INT_PRINT_DEC" disambiguations, "
|
||||
HOST_WIDE_INT_PRINT_DEC" queries\n",
|
||||
alias_stats.modref_use_no_alias,
|
||||
alias_stats.modref_use_no_alias
|
||||
+ alias_stats.modref_use_may_alias);
|
||||
fprintf (s, " modref clobber: "
|
||||
HOST_WIDE_INT_PRINT_DEC" disambiguations, "
|
||||
HOST_WIDE_INT_PRINT_DEC" queries\n "
|
||||
HOST_WIDE_INT_PRINT_DEC" tbaa querries (%f per modref querry)\n",
|
||||
alias_stats.modref_clobber_no_alias,
|
||||
alias_stats.modref_clobber_no_alias
|
||||
+ alias_stats.modref_clobber_may_alias,
|
||||
alias_stats.modref_tests,
|
||||
((double)alias_stats.modref_tests)
|
||||
/ (alias_stats.modref_clobber_no_alias
|
||||
+ alias_stats.modref_clobber_may_alias));
|
||||
}
|
||||
|
||||
|
||||
@ -1341,8 +1366,8 @@ nonoverlapping_array_refs_p (tree ref1, tree ref2)
|
||||
{
|
||||
tree index1 = TREE_OPERAND (ref1, 1);
|
||||
tree index2 = TREE_OPERAND (ref2, 1);
|
||||
tree low_bound1 = cheap_array_ref_low_bound(ref1);
|
||||
tree low_bound2 = cheap_array_ref_low_bound(ref2);
|
||||
tree low_bound1 = cheap_array_ref_low_bound (ref1);
|
||||
tree low_bound2 = cheap_array_ref_low_bound (ref2);
|
||||
|
||||
/* Handle zero offsets first: we do not need to match type size in this
|
||||
case. */
|
||||
@ -2394,6 +2419,63 @@ refs_output_dependent_p (tree store1, tree store2)
|
||||
return refs_may_alias_p_1 (&r1, &r2, false);
|
||||
}
|
||||
|
||||
/* Returns true if and only if REF may alias any access stored in TT.
|
||||
IF TBAA_P is true, use TBAA oracle. */
|
||||
|
||||
static bool
|
||||
modref_may_conflict (modref_tree <alias_set_type> *tt, ao_ref *ref, bool tbaa_p)
|
||||
{
|
||||
alias_set_type base_set, ref_set;
|
||||
modref_base_node <alias_set_type> *base_node;
|
||||
modref_ref_node <alias_set_type> *ref_node;
|
||||
size_t i, j;
|
||||
|
||||
if (tt->every_base)
|
||||
return true;
|
||||
|
||||
base_set = ao_ref_base_alias_set (ref);
|
||||
|
||||
ref_set = ao_ref_alias_set (ref);
|
||||
|
||||
int num_tests = 0, max_tests = param_modref_max_tests;
|
||||
FOR_EACH_VEC_SAFE_ELT (tt->bases, i, base_node)
|
||||
{
|
||||
if (base_node->every_ref)
|
||||
return true;
|
||||
|
||||
if (!base_node->base)
|
||||
return true;
|
||||
|
||||
if (tbaa_p && flag_strict_aliasing)
|
||||
{
|
||||
if (!alias_sets_conflict_p (base_set, base_node->base))
|
||||
continue;
|
||||
alias_stats.modref_tests++;
|
||||
num_tests++;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
if (num_tests >= max_tests)
|
||||
return true;
|
||||
|
||||
FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
|
||||
{
|
||||
/* Do not repeat same test as before. */
|
||||
if (ref_set == base_set && base_node->base == ref_node->ref)
|
||||
return true;
|
||||
if (!flag_strict_aliasing)
|
||||
return true;
|
||||
if (alias_sets_conflict_p (ref_set, ref_node->ref))
|
||||
return true;
|
||||
alias_stats.modref_tests++;
|
||||
num_tests++;
|
||||
if (num_tests >= max_tests)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the call CALL may use the memory reference REF return true,
|
||||
otherwise return false. */
|
||||
|
||||
@ -2409,15 +2491,51 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p)
|
||||
&& (flags & (ECF_CONST|ECF_NOVOPS)))
|
||||
goto process_args;
|
||||
|
||||
base = ao_ref_base (ref);
|
||||
if (!base)
|
||||
return true;
|
||||
|
||||
/* A call that is not without side-effects might involve volatile
|
||||
accesses and thus conflicts with all other volatile accesses. */
|
||||
if (ref->volatile_p)
|
||||
return true;
|
||||
|
||||
callee = gimple_call_fndecl (call);
|
||||
|
||||
if (!gimple_call_chain (call) && callee != NULL_TREE)
|
||||
{
|
||||
struct cgraph_node *node = cgraph_node::get (callee);
|
||||
/* We can not safely optimize based on summary of calle if it does
|
||||
not always bind to current def: it is possible that memory load
|
||||
was optimized out earlier and the interposed variant may not be
|
||||
optimized this way. */
|
||||
if (node && node->binds_to_current_def_p ())
|
||||
{
|
||||
modref_summary *summary = get_modref_function_summary (node);
|
||||
if (summary)
|
||||
{
|
||||
if (!modref_may_conflict (summary->loads, ref, tbaa_p))
|
||||
{
|
||||
alias_stats.modref_use_no_alias++;
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file, "ipa-modref: in %s,"
|
||||
" call to %s does not use ",
|
||||
cgraph_node::get
|
||||
(current_function_decl)->dump_name (),
|
||||
node->dump_name ());
|
||||
print_generic_expr (dump_file, ref->ref);
|
||||
fprintf (dump_file, " %i->%i\n",
|
||||
ao_ref_base_alias_set (ref),
|
||||
ao_ref_alias_set (ref));
|
||||
}
|
||||
goto process_args;
|
||||
}
|
||||
alias_stats.modref_use_may_alias++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base = ao_ref_base (ref);
|
||||
if (!base)
|
||||
return true;
|
||||
|
||||
/* If the reference is based on a decl that is not aliased the call
|
||||
cannot possibly use it. */
|
||||
if (DECL_P (base)
|
||||
@ -2426,8 +2544,6 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p)
|
||||
&& !is_global_var (base))
|
||||
goto process_args;
|
||||
|
||||
callee = gimple_call_fndecl (call);
|
||||
|
||||
/* Handle those builtin functions explicitly that do not act as
|
||||
escape points. See tree-ssa-structalias.c:find_func_aliases
|
||||
for the list of builtins we might need to handle here. */
|
||||
@ -2781,7 +2897,7 @@ ref_maybe_used_by_stmt_p (gimple *stmt, tree ref, bool tbaa_p)
|
||||
return true, otherwise return false. */
|
||||
|
||||
bool
|
||||
call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
|
||||
call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, bool tbaa_p)
|
||||
{
|
||||
tree base;
|
||||
tree callee;
|
||||
@ -2808,6 +2924,39 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
|
||||
break;
|
||||
}
|
||||
|
||||
callee = gimple_call_fndecl (call);
|
||||
|
||||
if (callee != NULL_TREE && !ref->volatile_p)
|
||||
{
|
||||
struct cgraph_node *node = cgraph_node::get (callee);
|
||||
if (node)
|
||||
{
|
||||
modref_summary *summary = get_modref_function_summary (node);
|
||||
if (summary)
|
||||
{
|
||||
if (!modref_may_conflict (summary->stores, ref, tbaa_p))
|
||||
{
|
||||
alias_stats.modref_clobber_no_alias++;
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
fprintf (dump_file,
|
||||
"ipa-modref: in %s, "
|
||||
"call to %s does not clobber ",
|
||||
cgraph_node::get
|
||||
(current_function_decl)->dump_name (),
|
||||
node->dump_name ());
|
||||
print_generic_expr (dump_file, ref->ref);
|
||||
fprintf (dump_file, " %i->%i\n",
|
||||
ao_ref_base_alias_set (ref),
|
||||
ao_ref_alias_set (ref));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
alias_stats.modref_clobber_may_alias++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base = ao_ref_base (ref);
|
||||
if (!base)
|
||||
return true;
|
||||
@ -2840,8 +2989,6 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
|
||||
&& SSA_NAME_POINTS_TO_READONLY_MEMORY (TREE_OPERAND (base, 0)))
|
||||
return false;
|
||||
|
||||
callee = gimple_call_fndecl (call);
|
||||
|
||||
/* Handle those builtin functions explicitly that do not act as
|
||||
escape points. See tree-ssa-structalias.c:find_func_aliases
|
||||
for the list of builtins we might need to handle here. */
|
||||
@ -3083,7 +3230,7 @@ call_may_clobber_ref_p (gcall *call, tree ref)
|
||||
bool res;
|
||||
ao_ref r;
|
||||
ao_ref_init (&r, ref);
|
||||
res = call_may_clobber_ref_p_1 (call, &r);
|
||||
res = call_may_clobber_ref_p_1 (call, &r, true);
|
||||
if (res)
|
||||
++alias_stats.call_may_clobber_ref_p_may_alias;
|
||||
else
|
||||
@ -3110,7 +3257,7 @@ stmt_may_clobber_ref_p_1 (gimple *stmt, ao_ref *ref, bool tbaa_p)
|
||||
return true;
|
||||
}
|
||||
|
||||
return call_may_clobber_ref_p_1 (as_a <gcall *> (stmt), ref);
|
||||
return call_may_clobber_ref_p_1 (as_a <gcall *> (stmt), ref, tbaa_p);
|
||||
}
|
||||
else if (gimple_assign_single_p (stmt))
|
||||
{
|
||||
|
@ -129,7 +129,7 @@ extern bool stmt_may_clobber_global_p (gimple *);
|
||||
extern bool stmt_may_clobber_ref_p (gimple *, tree, bool = true);
|
||||
extern bool stmt_may_clobber_ref_p_1 (gimple *, ao_ref *, bool = true);
|
||||
extern bool call_may_clobber_ref_p (gcall *, tree);
|
||||
extern bool call_may_clobber_ref_p_1 (gcall *, ao_ref *);
|
||||
extern bool call_may_clobber_ref_p_1 (gcall *, ao_ref *, bool = true);
|
||||
extern bool stmt_kills_ref_p (gimple *, tree);
|
||||
extern bool stmt_kills_ref_p (gimple *, ao_ref *);
|
||||
enum translate_flags
|
||||
|
Loading…
Reference in New Issue
Block a user