]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
New modref/ipa_modref optimization passes
authorJan Hubicka <jh@suse.cz>
Sun, 20 Sep 2020 05:25:16 +0000 (07:25 +0200)
committerJan Hubicka <jh@suse.cz>
Sun, 20 Sep 2020 05:27:48 +0000 (07:27 +0200)
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.

18 files changed:
gcc/Makefile.in
gcc/alias.c
gcc/alias.h
gcc/common.opt
gcc/gengtype.c
gcc/ipa-modref-tree.c [new file with mode: 0644]
gcc/ipa-modref-tree.h [new file with mode: 0644]
gcc/ipa-modref.c [new file with mode: 0644]
gcc/ipa-modref.h [new file with mode: 0644]
gcc/lto-section-in.c
gcc/lto-streamer.h
gcc/opts.c
gcc/params.opt
gcc/passes.def
gcc/timevar.def
gcc/tree-pass.h
gcc/tree-ssa-alias.c
gcc/tree-ssa-alias.h

index 79e854aa938101d09e75a3fe56af346e72ad9ea5..c710bad27b1743c1d1127db05092b7bc0d6013e3 100644 (file)
@@ -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 \
index df85f07ee9acfd8fdfb3a65229afc4bc1bde38b0..1cb702be2ce3bc3a7c565b313bcd08217f11f761 100644 (file)
@@ -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;
index 4453d9723ce970eee444e1653f0074158c93f67d..807af957f02c863b89117600ff1081a693ec74e5 100644 (file)
@@ -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);
index dd68c61ae1d27a838c22eede2834c67d63677eff..b833b98dfb8d46550c25b4a1dcd243cd6c81f58e 100644 (file)
@@ -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.
index 981577481af64b34aebc2a8019f252c4934444df..a59a8823f82a40cbefe30fdc875bf791182113ce 100644 (file)
@@ -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;
diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
new file mode 100644 (file)
index 0000000..e37dee6
--- /dev/null
@@ -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 *) {}
+
+
diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
new file mode 100644 (file)
index 0000000..3bdd305
--- /dev/null
@@ -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
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
new file mode 100644 (file)
index 0000000..f982ce9
--- /dev/null
@@ -0,0 +1,1376 @@
+/* Search for references that a functions loads or stores.
+   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/>.  */
+
+/* Mod/ref pass records summary about loads and stores performed by the
+   function.  This is later used by alias analysis to disambiguate memory
+   accesses across function calls.  The summary has a form of decision tree and
+   contains:
+
+    - base alias set
+      and for each:
+      - ref alias set
+
+   In future more information will be tracked.
+
+   This file contains a tree pass and an IPA pass.  Both performs the same
+   analys however tree pass is executed during early and late optimization
+   passes to propagate info downwards in the compilation order.  IPA pass
+   propagates across the callgraph and is able to handle recursion and works on
+   whole program during link-time analysis.
+
+   LTO mode differs from the local mode by not recroding alias sets but types
+   that are translated to alias sets later.  This is necessary in order stream
+   the information becaue the alias sets are rebuild at stream-in time and may
+   not correspond to ones seen during analsis.  For this reason part of analysis
+   is duplicated.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "cgraph.h"
+#include "ipa-utils.h"
+#include "symbol-summary.h"
+#include "gimple-pretty-print.h"
+#include "gimple-walk.h"
+#include "print-tree.h"
+#include "tree-streamer.h"
+#include "alias.h"
+#include "calls.h"
+#include "ipa-modref-tree.h"
+#include "ipa-modref.h"
+
+/* Class (from which there is one global instance) that holds modref summaries
+   for all analyzed functions.  */
+class GTY((user)) modref_summaries
+  : public fast_function_summary <modref_summary *, va_gc>
+{
+public:
+  modref_summaries (symbol_table *symtab)
+      : fast_function_summary <modref_summary *, va_gc> (symtab) {}
+  virtual void insert (cgraph_node *, modref_summary *state);
+  virtual void duplicate (cgraph_node *src_node,
+                         cgraph_node *dst_node,
+                         modref_summary *src_data,
+                         modref_summary *dst_data);
+  /* This flag controls whether newly inserted functions should be analyzed
+     in IPA or normal mode.  Functions inserted betweehn IPA analysis and
+     ipa-modref pass execution needs to be analyzed in IPA mode while all
+     other insertions leads to normal analysis.  */
+  bool ipa;
+};
+
+/* Global variable holding all modref summaries.  */
+static GTY(()) fast_function_summary <modref_summary *, va_gc> *summaries;
+
+/* Summary for a single function which this pass produces.  */
+
+modref_summary::modref_summary ()
+  : loads (NULL), stores (NULL), loads_lto (NULL),
+    stores_lto (NULL), finished (0)
+{
+}
+
+modref_summary::~modref_summary ()
+{
+  if (loads)
+    ggc_delete (loads);
+  if (stores)
+    ggc_delete (stores);
+  if (loads_lto)
+    ggc_delete (loads_lto);
+  if (stores_lto)
+    ggc_delete (stores_lto);
+}
+
+/* Dump records TT to OUT.  */
+
+static void
+dump_records (modref_records *tt, FILE *out)
+{
+  fprintf (out, "    Limits: %i bases, %i refs\n",
+          (int)tt->max_bases, (int)tt->max_refs);
+  if (tt->every_base)
+    {
+      fprintf (out, "    Every base\n");
+      return;
+    }
+  size_t i;
+  modref_base_node <alias_set_type> *n;
+  FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
+    {
+      fprintf (out, "      Base %i: alias set %i\n", (int)i, n->base);
+      if (n->every_ref)
+       {
+         fprintf (out, "      Every ref\n");
+         continue;
+       }
+      size_t j;
+      modref_ref_node <alias_set_type> *r;
+      FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
+       {
+         fprintf (out, "        Ref %i: alias set %i\n", (int)j, r->ref);
+       }
+    }
+}
+
+/* Dump records TT to OUT.  */
+
+static void
+dump_lto_records (modref_records_lto *tt, FILE *out)
+{
+  fprintf (out, "    Limits: %i bases, %i refs\n",
+          (int)tt->max_bases, (int)tt->max_refs);
+  if (tt->every_base)
+    {
+      fprintf (out, "    Every base\n");
+      return;
+    }
+  size_t i;
+  modref_base_node <tree> *n;
+  FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
+    {
+      fprintf (out, "      Base %i:", (int)i);
+      print_generic_expr (dump_file, n->base);
+      fprintf (out, " (alias set %i)\n",
+              get_alias_set (n->base));
+      if (n->every_ref)
+       {
+         fprintf (out, "      Every ref\n");
+         continue;
+       }
+      size_t j;
+      modref_ref_node <tree> *r;
+      FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
+       {
+         fprintf (out, "        Ref %i:", (int)j);
+         print_generic_expr (dump_file, r->ref);
+         fprintf (out, " (alias set %i)\n",
+                  get_alias_set (r->ref));
+       }
+    }
+}
+
+/* Dump summary.  */
+
+void
+modref_summary::dump (FILE *out)
+{
+  if (loads)
+    {
+      fprintf (out, "  loads:\n");
+      dump_records (loads, out);
+    }
+  if (stores)
+    {
+      fprintf (out, "  stores:\n");
+      dump_records (stores, out);
+    }
+  if (loads_lto)
+    {
+      fprintf (out, "  LTO loads:\n");
+      dump_lto_records (loads_lto, out);
+    }
+  if (stores_lto)
+    {
+      fprintf (out, "  LTO stores:\n");
+      dump_lto_records (stores_lto, out);
+    }
+}
+
+
+/* Get function summary for FUNC if it exists, return NULL otherwise.  */
+
+modref_summary *
+get_modref_function_summary (cgraph_node *func)
+{
+  /* Avoid creation of the summary too early (e.g. when front-end calls us).  */
+  if (!summaries)
+    return NULL;
+
+  /* A single function body may be represented by multiple symbols with
+     different visibility.  For example, if FUNC is an interposable alias,
+     we don't want to return anything, even if we have summary for the target
+     function.  */
+  enum availability avail;
+  func = func->function_or_virtual_thunk_symbol
+            (&avail, cgraph_node::get (current_function_decl));
+  if (avail <= AVAIL_INTERPOSABLE)
+    return NULL;
+
+  /* Attempt to get summary for FUNC.  If analysis of FUNC hasn't finished yet,
+     don't return anything.  */
+  modref_summary *r = summaries->get (func);
+  if (r && r->finished)
+    return r;
+
+  return NULL;
+}
+
+/* Record access into the modref_records data structure.  */
+
+static void
+record_access (modref_records *tt, ao_ref *ref)
+{
+  alias_set_type base_set = !flag_strict_aliasing ? 0
+                           : ao_ref_base_alias_set (ref);
+  alias_set_type ref_set = !flag_strict_aliasing ? 0
+                           : (ao_ref_alias_set (ref));
+  if (dump_file)
+    {
+       fprintf (dump_file, "   - Recording base_set=%i ref_set=%i\n",
+               base_set, ref_set);
+    }
+  tt->insert (base_set, ref_set);
+}
+
+/* IPA version of record_access_tree.  */
+
+static void
+record_access_lto (modref_records_lto *tt, ao_ref *ref)
+{
+  /* get_alias_set sometimes use different type to compute the alias set
+     than TREE_TYPE (base).  Do same adjustments.  */
+  tree base_type = NULL_TREE, ref_type = NULL_TREE;
+  if (flag_strict_aliasing)
+    {
+      tree base;
+
+      base = ref->ref;
+      while (handled_component_p (base))
+       base = TREE_OPERAND (base, 0);
+
+      base_type = reference_alias_ptr_type_1 (&base);
+
+      if (!base_type)
+       base_type = TREE_TYPE (base);
+      else
+       base_type = TYPE_REF_CAN_ALIAS_ALL (base_type)
+                   ? NULL_TREE : TREE_TYPE (base_type);
+
+      tree ref_expr = ref->ref;
+      ref_type = reference_alias_ptr_type_1 (&ref_expr);
+
+      if (!ref_type)
+       ref_type = TREE_TYPE (ref_expr);
+      else
+       ref_type = TYPE_REF_CAN_ALIAS_ALL (ref_type)
+                  ? NULL_TREE : TREE_TYPE (ref_type);
+
+      /* Sanity check that we are in sync with what get_alias_set does.  */
+      gcc_checking_assert ((!base_type && !ao_ref_base_alias_set (ref))
+                          || get_alias_set (base_type)
+                             == ao_ref_base_alias_set (ref));
+      gcc_checking_assert ((!ref_type && !ao_ref_alias_set (ref))
+                          || get_alias_set (ref_type)
+                             == ao_ref_alias_set (ref));
+
+      /* Do not bother to record types that have no meaningful alias set.
+        Also skip variably modified types since these go to local streams.  */
+      if (base_type && (!get_alias_set (base_type)
+                       || variably_modified_type_p (base_type, NULL_TREE)))
+       base_type = NULL_TREE;
+      if (ref_type && (!get_alias_set (ref_type)
+                      || variably_modified_type_p (ref_type, NULL_TREE)))
+       ref_type = NULL_TREE;
+    }
+  if (dump_file)
+    {
+      fprintf (dump_file, "   - Recording base type:");
+      print_generic_expr (dump_file, base_type);
+      fprintf (dump_file, " (alias set %i) ref type:",
+              base_type ? get_alias_set (base_type) : 0);
+      print_generic_expr (dump_file, ref_type);
+      fprintf (dump_file, " (alias set %i)\n",
+              ref_type ? get_alias_set (ref_type) : 0);
+    }
+
+  tt->insert (base_type, ref_type);
+}
+
+/* Returns true if and only if we should store the access to EXPR.
+   Some accesses, e.g. loads from automatic variables, are not interesting.  */
+
+static bool
+record_access_p (tree expr)
+{
+  /* Non-escaping memory is fine  */
+  tree t = get_base_address (expr);
+  if (t && (INDIRECT_REF_P (t)
+           || TREE_CODE (t) == MEM_REF
+           || TREE_CODE (t) == TARGET_MEM_REF)
+       && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
+       && !ptr_deref_may_alias_global_p (TREE_OPERAND (t, 0)))
+    {
+      if (dump_file)
+       fprintf (dump_file, "   - Non-escaping memory, ignoring.\n");
+      return false;
+    }
+
+  /* Automatic variables are fine.  */
+  if (DECL_P (t)
+      && auto_var_in_fn_p (t, current_function_decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "   - Automatic variable, ignoring.\n");
+      return false;
+    }
+
+  /* Read-only variables are fine.  */
+  if (DECL_P (t) && TREE_READONLY (t))
+    {
+      if (dump_file)
+       fprintf (dump_file, "   - Read-only variable, ignoring.\n");
+      return false;
+    }
+
+  return true;
+}
+
+/* Return true if ECF flags says that stores can be ignored.  */
+
+static bool
+ignore_stores_p (tree caller, int flags)
+{
+  if (flags & ECF_PURE)
+    return true;
+  if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
+      || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
+    return true;
+  return false;
+}
+
+/* Analyze function call STMT in function F.  */
+
+static bool
+analyze_call (modref_summary *cur_summary,
+             gimple *stmt)
+{
+  /* Check flags on the function call.  In certain cases, analysis can be
+     simplified.  */
+  int flags = gimple_call_flags (stmt);
+  if (flags & (ECF_CONST | ECF_NOVOPS))
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                " - ECF_CONST | ECF_NOVOPS, ignoring all stores and all loads "
+                "except for args.\n");
+      return true;
+    }
+
+  /* Pure functions do not affect global memory.  Stores by functions which are
+     noreturn and do not throw can safely be ignored.  */
+  bool ignore_stores = ignore_stores_p (current_function_decl, flags);
+
+  /* Next, we try to get the callee's function declaration.  The goal is to
+     merge their summary with ours.  */
+  tree callee = gimple_call_fndecl (stmt);
+
+  /* Check if this is an indirect call.  */
+  if (!callee)
+    {
+      /* If the indirect call does not write memory, our store summary is
+        unaffected, but we have to discard our loads summary (we don't know
+        anything about the loads that the called function performs).  */
+      if (ignore_stores)
+       {
+         if (dump_file)
+           fprintf (dump_file, " - Indirect call which does not write memory, "
+                   "discarding loads.\n");
+         if (cur_summary->loads)
+           cur_summary->loads->collapse ();
+         if (cur_summary->loads_lto)
+           cur_summary->loads_lto->collapse ();
+         return true;
+       }
+      if (dump_file)
+       fprintf (dump_file, " - Indirect call.\n");
+      return false;
+    }
+
+  struct cgraph_node *callee_node = cgraph_node::get_create (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 which may not happen in the interposed
+     variant.  */
+  if (!callee_node->binds_to_current_def_p ())
+    {
+      if (dump_file)
+       fprintf (dump_file, " - May be interposed: collapsing loads.\n");
+      if (cur_summary->loads)
+       cur_summary->loads->collapse ();
+      if (cur_summary->loads_lto)
+       cur_summary->loads_lto->collapse ();
+    }
+
+  /* If this is a recursive call, the target summary is the same as ours, so
+     there's nothing to do.  */
+  if (recursive_call_p (current_function_decl, callee))
+    {
+      if (dump_file)
+       fprintf (dump_file, " - Skipping recursive call.\n");
+      return true;
+    }
+
+  gcc_assert (callee_node != NULL);
+
+  /* Get the function symbol and its availability.  */
+  enum availability avail;
+  callee_node = callee_node->function_symbol (&avail);
+  if (avail <= AVAIL_INTERPOSABLE)
+    {
+      /* Keep stores summary, but discard all loads for interposable function
+        symbols.  */
+      if (ignore_stores)
+       {
+         if (cur_summary->loads)
+           cur_summary->loads->collapse ();
+         if (cur_summary->loads_lto)
+           cur_summary->loads_lto->collapse ();
+         return true;
+       }
+      if (dump_file)
+       fprintf (dump_file, " - Function availability <= AVAIL_INTERPOSABLE.\n");
+      return false;
+    }
+
+  /* Get callee's modref summary.  As above, if there's no summary, we either
+     have to give up or, if stores are ignored, we can just purge loads.  */
+  modref_summary *callee_summary = summaries->get (callee_node);
+  if (!callee_summary)
+    {
+      if (ignore_stores)
+       {
+         if (cur_summary->loads)
+           cur_summary->loads->collapse ();
+         if (cur_summary->loads_lto)
+           cur_summary->loads_lto->collapse ();
+         return true;
+       }
+      if (dump_file)
+       fprintf (dump_file, " - No modref summary available for callee.\n");
+      return false;
+    }
+
+  /* Merge with callee's summary.  */
+  if (cur_summary->loads)
+    cur_summary->loads->merge (callee_summary->loads);
+  if (cur_summary->loads_lto)
+    cur_summary->loads_lto->merge (callee_summary->loads_lto);
+  if (!ignore_stores)
+    {
+      if (cur_summary->stores)
+       cur_summary->stores->merge (callee_summary->stores);
+      if (cur_summary->stores_lto)
+       cur_summary->stores_lto->merge (callee_summary->stores_lto);
+    }
+
+  return true;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_load (gimple *, tree, tree op, void *data)
+{
+  modref_summary *summary = (modref_summary *)data;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, " - Analyzing load: ");
+      print_generic_expr (dump_file, op);
+      fprintf (dump_file, "\n");
+    }
+
+  if (!record_access_p (op))
+    return false;
+
+  ao_ref r;
+  ao_ref_init (&r, op);
+
+  if (summary->loads)
+    record_access (summary->loads, &r);
+  if (summary->loads_lto)
+    record_access_lto (summary->loads_lto, &r);
+  return false;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_store (gimple *, tree, tree op, void *data)
+{
+  modref_summary *summary = (modref_summary *)data;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, " - Analyzing store: ");
+      print_generic_expr (dump_file, op);
+      fprintf (dump_file, "\n");
+    }
+
+  if (!record_access_p (op))
+    return false;
+
+  ao_ref r;
+  ao_ref_init (&r, op);
+
+  if (summary->stores)
+    record_access (((modref_summary *)data)->stores, &r);
+  if (summary->stores_lto)
+    record_access_lto (((modref_summary *)data)->stores_lto, &r);
+  return false;
+}
+
+/* Analyze statement STMT of function F.
+   If IPA is true do not merge in side effects of calls.  */
+
+static bool
+analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
+{
+  /* Analyze all loads and stores in STMT.  */
+  walk_stmt_load_store_ops (stmt, summary,
+                           analyze_load, analyze_store);
+  /* or call analyze_load_ipa, analyze_store_ipa */
+
+  switch (gimple_code (stmt))
+   {
+   case GIMPLE_ASM:
+     /* If the ASM statement does not read nor write memory, there's nothing
+       to do.  Otherwise just give up.  */
+     if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (stmt)))
+       return true;
+     if (dump_file)
+       fprintf (dump_file, " - Function contains GIMPLE_ASM statement "
+              "which clobbers memory.\n");
+     return false;
+   case GIMPLE_CALL:
+     if (!ipa)
+       return analyze_call (summary, stmt);
+     return true;
+   default:
+     /* Nothing to do for other types of statements.  */
+     return true;
+   }
+}
+
+/* Analyze function F.  IPA indicates whether we're running in tree mode (false)
+   or the IPA mode (true).  */
+
+static void
+analyze_function (function *f, bool ipa)
+{
+  if (dump_file)
+    fprintf (dump_file, "modref analyzing '%s' (ipa=%i)...\n",
+            function_name (f), ipa);
+
+  /* Don't analyze this function if it's compiled with -fno-strict-aliasing.  */
+  if (!flag_ipa_modref)
+    return;
+
+  /* Initialize the summary.  */
+  if (!summaries)
+    summaries = new (ggc_alloc <modref_summaries> ())
+                    modref_summaries (symtab);
+  else /* Remove existing summary if we are re-running the pass.  */
+    summaries->remove (cgraph_node::get (f->decl));
+
+  ((modref_summaries *)summaries)->ipa = ipa;
+
+  modref_summary *summary = summaries->get_create (cgraph_node::get (f->decl));
+
+  /* Compute no-LTO summaries when local optimization is going to happen.  */
+  bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p)
+               || (in_lto_p && !flag_wpa
+                   && flag_incremental_link != INCREMENTAL_LINK_LTO));
+
+  /* Compute LTO when LTO streaming is going to happen.  */
+  bool lto = ipa && ((flag_lto && !in_lto_p)
+                    || flag_wpa
+                    || flag_incremental_link == INCREMENTAL_LINK_LTO);
+
+  /* Create and initialize summary for F.
+     Note that summaries may be already allocated from previous
+     run of the pass.  */
+  if (nolto)
+    {
+      gcc_assert (!summary->loads);
+      summary->loads
+        = new (ggc_alloc <modref_tree<alias_set_type> > ())
+               modref_records (param_modref_max_bases,
+                               param_modref_max_refs);
+      gcc_assert (!summary->stores);
+      summary->stores
+        = new (ggc_alloc <modref_tree<alias_set_type> > ())
+               modref_records (param_modref_max_bases,
+                               param_modref_max_refs);
+    }
+  if (lto)
+    {
+      gcc_assert (!summary->loads_lto);
+      summary->loads_lto
+        = new (ggc_alloc <modref_tree<tree> > ())
+               modref_records_lto (param_modref_max_bases,
+                                   param_modref_max_refs);
+      gcc_assert (!summary->stores_lto);
+      summary->stores_lto
+        = new (ggc_alloc <modref_tree<tree> > ())
+               modref_records_lto (param_modref_max_bases,
+                                   param_modref_max_refs);
+    }
+  summary->finished = false;
+
+  /* Analyze each statement in each basic block of the function.  If the
+     statement cannot be analyzed (for any reason), the entire function cannot
+     be analyzed by modref.  */
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, f)
+    {
+      gimple_stmt_iterator si;
+      for (si = gsi_after_labels (bb); !gsi_end_p (si); gsi_next (&si))
+       {
+         if (!analyze_stmt (summary, gsi_stmt (si), ipa))
+           {
+             cgraph_node *fnode = cgraph_node::get (current_function_decl);
+             summaries->remove (fnode);
+             if (dump_file)
+               fprintf (dump_file,
+                        " - modref done with result: not tracked.\n");
+             return;
+           }
+       }
+    }
+
+  if (!ipa)
+    summary->finished = true;
+
+  if (dump_file)
+    {
+      fprintf (dump_file, " - modref done with result: tracked.\n");
+      summary->dump (dump_file);
+    }
+}
+
+/* Callback for generate_summary.  */
+
+static void
+modref_generate (void)
+{
+  struct cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    {
+      function *f = DECL_STRUCT_FUNCTION (node->decl);
+      if (!f)
+       continue;
+      push_cfun (f);
+      analyze_function (f, true);
+      pop_cfun ();
+    }
+}
+
+/* Called when a new function is inserted to callgraph late.  */
+
+void
+modref_summaries::insert (struct cgraph_node *node, modref_summary *)
+{
+  if (!DECL_STRUCT_FUNCTION (node->decl))
+    return;
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+  analyze_function (DECL_STRUCT_FUNCTION (node->decl), ipa);
+  pop_cfun ();
+}
+
+/* Called when new clone is inserted to callgraph late.  */
+
+void
+modref_summaries::duplicate (cgraph_node *, cgraph_node *,
+                            modref_summary *src_data,
+                            modref_summary *dst_data)
+{
+  dst_data->finished = src_data->finished;
+  if (src_data->stores)
+    {
+      dst_data->stores = new (ggc_alloc <modref_tree<alias_set_type> > ())
+                             modref_records
+                                (src_data->stores->max_bases,
+                                 src_data->stores->max_refs);
+      dst_data->stores->merge (src_data->stores);
+    }
+  if (src_data->loads)
+    {
+      dst_data->loads = new (ggc_alloc <modref_tree<alias_set_type> > ())
+                            modref_records
+                               (src_data->loads->max_bases,
+                                src_data->loads->max_refs);
+      dst_data->loads->merge (src_data->loads);
+    }
+  if (src_data->stores_lto)
+    {
+      dst_data->stores_lto = new (ggc_alloc <modref_tree<tree> > ())
+                                 modref_records_lto
+                                   (src_data->stores_lto->max_bases,
+                                    src_data->stores_lto->max_refs);
+      dst_data->stores_lto->merge (src_data->stores_lto);
+    }
+  if (src_data->loads_lto)
+    {
+      dst_data->loads_lto = new (ggc_alloc <modref_tree<tree> > ())
+                                 modref_records_lto
+                                   (src_data->stores_lto->max_bases,
+                                    src_data->stores_lto->max_refs);
+      dst_data->loads_lto->merge (src_data->loads_lto);
+    }
+}
+
+namespace
+{
+/* Definition of the modref pass on GIMPLE.  */
+const pass_data pass_data_modref = {
+  GIMPLE_PASS,
+  "modref",
+  OPTGROUP_IPA,
+  TV_TREE_MODREF,
+  (PROP_cfg | PROP_ssa),
+  0,
+  0,
+  0,
+  0,
+};
+
+class pass_modref : public gimple_opt_pass
+{
+  public:
+    pass_modref (gcc::context *ctxt)
+       : gimple_opt_pass (pass_data_modref, ctxt) {}
+
+    ~pass_modref ()
+      {
+       ggc_delete (summaries);
+       summaries = NULL;
+      }
+
+    /* opt_pass methods: */
+    opt_pass *clone ()
+    {
+      return new pass_modref (m_ctxt);
+    }
+    virtual bool gate (function *)
+    {
+      return flag_ipa_modref;
+    }
+    virtual unsigned int execute (function *);
+};
+
+/* Encode TT to the output block OB using the summary streaming API.  */
+
+static void
+write_modref_records (modref_records_lto *tt, struct output_block *ob)
+{
+  streamer_write_uhwi (ob, tt->max_bases);
+  streamer_write_uhwi (ob, tt->max_refs);
+
+  streamer_write_uhwi (ob, tt->every_base);
+  streamer_write_uhwi (ob, vec_safe_length (tt->bases));
+  size_t i;
+  modref_base_node <tree> *base_node;
+  FOR_EACH_VEC_SAFE_ELT (tt->bases, i, base_node)
+    {
+      stream_write_tree (ob, base_node->base, true);
+
+      streamer_write_uhwi (ob, base_node->every_ref);
+      streamer_write_uhwi (ob, vec_safe_length (base_node->refs));
+      size_t j;
+      modref_ref_node <tree> *ref_node;
+      FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
+       {
+         stream_write_tree (ob, ref_node->ref, true);
+       }
+    }
+}
+
+/* Read a modref_tree from the input block IB using the data from DATA_IN.
+   This assumes that the tree was encoded using write_modref_tree.
+   Either nolto_ret or lto_ret is initialized by the tree depending whether
+   LTO streaming is expcted or not.  */
+
+void
+read_modref_records (lto_input_block *ib, struct data_in *data_in,
+                    modref_records **nolto_ret,
+                    modref_records_lto **lto_ret)
+{
+  size_t max_bases = streamer_read_uhwi (ib);
+  size_t max_refs = streamer_read_uhwi (ib);
+
+  /* Decide whether we want to turn LTO data types to non-LTO (i.e. when
+     LTO re-streaming is not going to happen).  */
+  if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO)
+    *lto_ret = new (ggc_alloc <modref_records_lto> ()) modref_records_lto
+                             (max_bases, max_refs);
+  else
+    *nolto_ret = new (ggc_alloc <modref_records> ()) modref_records
+                             (max_bases, max_refs);
+
+  size_t every_base = streamer_read_uhwi (ib);
+  size_t nbase = streamer_read_uhwi (ib);
+
+  gcc_assert (!every_base || nbase == 0);
+  if (every_base)
+    {
+      if (*nolto_ret)
+       (*nolto_ret)->collapse ();
+      if (*lto_ret)
+       (*lto_ret)->collapse ();
+    }
+  for (size_t i = 0; i < nbase; i++)
+    {
+      tree base_tree = stream_read_tree (ib, data_in);
+      modref_base_node <alias_set_type> *nolto_base_node = NULL;
+      modref_base_node <tree> *lto_base_node = NULL;
+
+      /* At stream in time we have LTO alias info.  Check if we streamed in
+        something obviously unnecessary.  Do not glob types by alias sets;
+        it is not 100% clear that ltrans types will get merged same way.
+        Types may get refined based on ODR type conflicts.  */
+      if (base_tree && !get_alias_set (base_tree))
+       {
+         if (dump_file)
+           {
+             fprintf (dump_file, "Streamed in alias set 0 type ");
+             print_generic_expr (dump_file, base_tree);
+             fprintf (dump_file, "\n");
+           }
+         base_tree = NULL;
+       }
+
+      if (*nolto_ret)
+       nolto_base_node = (*nolto_ret)->insert_base (base_tree
+                                                    ? get_alias_set (base_tree)
+                                                    : 0);
+      if (*lto_ret)
+       lto_base_node = (*lto_ret)->insert_base (base_tree);
+      size_t every_ref = streamer_read_uhwi (ib);
+      size_t nref = streamer_read_uhwi (ib);
+
+      gcc_assert (!every_ref || nref == 0);
+      if (every_ref)
+       {
+         if (nolto_base_node)
+           nolto_base_node->collapse ();
+         if (lto_base_node)
+           lto_base_node->collapse ();
+       }
+      for (size_t j = 0; j < nref; j++)
+       {
+         tree ref_tree = stream_read_tree (ib, data_in);
+
+         if (ref_tree && !get_alias_set (ref_tree))
+           {
+             if (dump_file)
+               {
+                 fprintf (dump_file, "Streamed in alias set 0 type ");
+                 print_generic_expr (dump_file, ref_tree);
+                 fprintf (dump_file, "\n");
+               }
+             base_tree = NULL;
+           }
+
+         if (nolto_base_node)
+           nolto_base_node->insert_ref (ref_tree ? get_alias_set (ref_tree)
+                                        : 0, max_refs);
+         if (lto_base_node)
+           lto_base_node->insert_ref (ref_tree, max_refs);
+       }
+    }
+}
+
+/* Callback for write_summary.  */
+
+static void
+modref_write ()
+{
+  struct output_block *ob = create_output_block (LTO_section_ipa_modref);
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  unsigned int count = 0;
+  int i;
+
+  if (!summaries)
+    {
+      streamer_write_uhwi (ob, 0);
+      streamer_write_char_stream (ob->main_stream, 0);
+      produce_asm (ob, NULL);
+      destroy_output_block (ob);
+      return;
+    }
+
+  for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
+    {
+      symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
+      cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
+
+      if (cnode && cnode->definition && !cnode->alias
+         && summaries->get (cnode))
+       count++;
+    }
+  streamer_write_uhwi (ob, count);
+
+  for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
+    {
+      symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
+      cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
+
+      if (cnode && cnode->definition && !cnode->alias)
+       {
+
+         modref_summary *r = summaries->get (cnode);
+
+         if (!r)
+           continue;
+
+         streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, cnode));
+
+         streamer_write_uhwi (ob, r->loads_lto ? 1 : 0);
+         streamer_write_uhwi (ob, r->stores_lto ? 1 : 0);
+         if (r->loads_lto)
+           write_modref_records (r->loads_lto, ob);
+         if (r->stores_lto)
+           write_modref_records (r->stores_lto, ob);
+       }
+    }
+  streamer_write_char_stream (ob->main_stream, 0);
+  produce_asm (ob, NULL);
+  destroy_output_block (ob);
+}
+
+static void
+read_section (struct lto_file_decl_data *file_data, const char *data,
+             size_t len)
+{
+  const struct lto_function_header *header
+    = (const struct lto_function_header *) data;
+  const int cfg_offset = sizeof (struct lto_function_header);
+  const int main_offset = cfg_offset + header->cfg_size;
+  const int string_offset = main_offset + header->main_size;
+  struct data_in *data_in;
+  unsigned int i;
+  unsigned int f_count;
+
+  lto_input_block ib ((const char *) data + main_offset, header->main_size,
+                     file_data->mode_table);
+
+  data_in
+    = lto_data_in_create (file_data, (const char *) data + string_offset,
+                         header->string_size, vNULL);
+  f_count = streamer_read_uhwi (&ib);
+  for (i = 0; i < f_count; i++)
+    {
+      struct cgraph_node *node;
+      lto_symtab_encoder_t encoder;
+
+      unsigned int index = streamer_read_uhwi (&ib);
+      encoder = file_data->symtab_node_encoder;
+      node = dyn_cast <cgraph_node *> (lto_symtab_encoder_deref (encoder,
+                                                               index));
+
+      modref_summary *modref_sum = summaries->get_create (node);
+      modref_sum->finished = false;
+      int have_loads = streamer_read_uhwi (&ib);
+      int have_stores = streamer_read_uhwi (&ib);
+      gcc_assert (!modref_sum->loads_lto
+                 && !modref_sum->stores_lto
+                 && !modref_sum->loads
+                 && !modref_sum->stores);
+      if (have_loads)
+        read_modref_records (&ib, data_in,
+                             &modref_sum->loads,
+                             &modref_sum->loads_lto);
+      if (have_stores)
+        read_modref_records (&ib, data_in,
+                             &modref_sum->stores,
+                             &modref_sum->stores_lto);
+      if (dump_file)
+       {
+         fprintf (dump_file, "Read modref for %s\n",
+                  node->dump_name ());
+         modref_sum->dump (dump_file);
+       }
+      if (flag_ltrans)
+       modref_sum->finished = true;
+    }
+
+  lto_free_section_data (file_data, LTO_section_ipa_modref, NULL, data,
+                        len);
+  lto_data_in_delete (data_in);
+}
+
+/* Callback for read_summary.  */
+
+static void
+modref_read (void)
+{
+  struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
+  struct lto_file_decl_data *file_data;
+  unsigned int j = 0;
+
+  if (!summaries)
+    summaries = new (ggc_alloc <modref_summaries> ())
+                    modref_summaries (symtab);
+  ((modref_summaries *)summaries)->ipa = true;
+
+  while ((file_data = file_data_vec[j++]))
+    {
+      size_t len;
+      const char *data = lto_get_summary_section_data (file_data,
+                                                      LTO_section_ipa_modref,
+                                                      &len);
+      if (data)
+       read_section (file_data, data, len);
+      else
+       /* Fatal error here.  We do not want to support compiling ltrans units
+          with different version of compiler or different flags than the WPA
+          unit, so this should never happen.  */
+       fatal_error (input_location,
+                    "IPA modref summary is missing in input file");
+    }
+}
+
+/* Definition of the modref IPA pass.  */
+const pass_data pass_data_ipa_modref =
+{
+  IPA_PASS,           /* type */
+  "modref",       /* name */
+  OPTGROUP_IPA,       /* optinfo_flags */
+  TV_IPA_MODREF, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_dump_symtab ), /* todo_flags_finish */
+};
+
+class pass_ipa_modref : public ipa_opt_pass_d
+{
+public:
+  pass_ipa_modref (gcc::context *ctxt)
+    : ipa_opt_pass_d (pass_data_ipa_modref, ctxt,
+                     modref_generate, /* generate_summary */
+                     modref_write,    /* write_summary */
+                     modref_read,     /* read_summary */
+                     modref_write,    /* write_optimization_summary */
+                     modref_read,     /* read_optimization_summary */
+                     NULL,            /* stmt_fixup */
+                     0,               /* function_transform_todo_flags_start */
+                     NULL,            /* function_transform */
+                     NULL)            /* variable_transform */
+  {}
+
+  /* opt_pass methods: */
+  opt_pass *clone () { return new pass_ipa_modref (m_ctxt); }
+  virtual bool gate (function *)
+  {
+    return true;
+  }
+  virtual unsigned int execute (function *);
+
+};
+
+}
+
+unsigned int pass_modref::execute (function *f)
+{
+  /* If new function is being added during IPA, we can skip analysis.  */
+  if (summaries && ((modref_summaries *)summaries)->ipa)
+    return 0;
+  analyze_function (f, false);
+  return 0;
+}
+
+gimple_opt_pass *
+make_pass_modref (gcc::context *ctxt)
+{
+  return new pass_modref (ctxt);
+}
+
+ipa_opt_pass_d *
+make_pass_ipa_modref (gcc::context *ctxt)
+{
+  return new pass_ipa_modref (ctxt);
+}
+
+/* Skip edges from and to nodes without ipa_pure_const enabled.
+   Ignore not available symbols.  */
+
+static bool
+ignore_edge (struct cgraph_edge *e)
+{
+  enum availability avail;
+  cgraph_node *callee = e->callee->function_or_virtual_thunk_symbol
+                         (&avail, e->caller);
+
+  return (avail <= AVAIL_INTERPOSABLE
+         || !summaries->get (callee)
+         || flags_from_decl_or_type (e->callee->decl)
+            & (ECF_CONST | ECF_NOVOPS));
+}
+
+/* Run the IPA pass.  This will take a function's summaries and calls and
+   construct new summaries which represent a transitive closure.  So that
+   summary of an analyzed function contains information about the loads and
+   stores that the function or any function that it calls does.  */
+
+unsigned int pass_ipa_modref::execute (function *)
+{
+  if (!summaries)
+    return 0;
+
+  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
+                                        symtab->cgraph_count);
+  int order_pos;
+  order_pos = ipa_reduced_postorder (order, true, ignore_edge);
+  int i;
+
+  /* Iterate over all strongly connected components in post-order.  */
+  for (i = 0; i < order_pos; i++)
+    {
+      bool its_hopeless = false;
+      modref_records *loads = NULL;
+      modref_records *stores = NULL;
+      modref_records_lto *loads_lto = NULL;
+      modref_records_lto *stores_lto = NULL;
+
+      /* Get the component's representative.  That's just any node in the
+        component from which we can traverse the entire component.  */
+      struct cgraph_node *component_node = order[i];
+      cgraph_node *first = NULL;
+
+      if (dump_file)
+       fprintf (dump_file, "Start of SCC component\n");
+
+      /* Walk the component.  CUR is the current node of the component that's
+        being processed.  */
+      for (struct cgraph_node *cur = component_node; cur && !its_hopeless;
+          cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
+       {
+         /* Merge in summaries from CUR.  */
+         modref_summary *cur_summary = summaries->get (cur);
+
+         if (dump_file)
+           fprintf (dump_file, "  Processing %s\n",
+                    cur->dump_name ());
+
+         /* We don't know anything about CUR, hence we cannot tell anything
+            about the entire component.  */
+         if (!cur_summary)
+           {
+             if (dump_file)
+               fprintf (dump_file, "    No summary\n");
+             its_hopeless = true;
+             break;
+           }
+
+         /* Summaries are all going to be same, pick first ones and merge
+            everything in.  */
+         if (!first)
+           {
+             first = cur;
+             loads = cur_summary->loads;
+             stores = cur_summary->stores;
+             loads_lto = cur_summary->loads_lto;
+             stores_lto = cur_summary->stores_lto;
+           }
+         for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
+           {
+             if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS))
+               continue;
+             if (ignore_stores_p (cur->decl, e->indirect_info->ecf_flags))
+               {
+                 if (dump_file)
+                   fprintf (dump_file, "    Indirect call: "
+                            "collapsing loads\n");
+                 if (loads)
+                   loads->collapse ();
+                 if (loads_lto)
+                   loads_lto->collapse ();
+               }
+             else
+               {
+                 if (dump_file)
+                   fprintf (dump_file, "    Indirect call: giving up\n");
+                 its_hopeless = true;
+               }
+           }
+
+         /* Walk every function that CUR calls and merge its summary.  */
+         for (cgraph_edge *callee_edge = cur->callees; callee_edge;
+              callee_edge = callee_edge->next_callee)
+           {
+             int flags = flags_from_decl_or_type (callee_edge->callee->decl);
+             modref_summary *callee_summary;
+             struct cgraph_node *callee;
+
+             if (flags & (ECF_CONST | ECF_NOVOPS))
+               continue;
+
+             if (dump_file)
+               fprintf (dump_file, "    Call to %s\n",
+                        cur->dump_name ());
+
+             /* 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 which may not happen in
+                the interposed variant.  */
+             if (!callee_edge->binds_to_current_def_p ())
+               {
+                 if (loads)
+                   loads->collapse ();
+                 if (loads_lto)
+                   loads_lto->collapse ();
+                 if (dump_file)
+                   fprintf (dump_file, "      May not bind local;"
+                            " collapsing loads\n");
+               }
+
+             /* Get the callee and its summary.  */
+             enum availability avail;
+             callee = callee_edge->callee->function_or_virtual_thunk_symbol
+                        (&avail, cur);
+
+             /* See if we can derive something from ECF flags.  Be careful on
+                not skipping calls within the SCC component:  we must merge
+                all their summaries.
+                If we switch to iterative dataflow that may be necessary
+                for future improvements this may go away.  */
+             if (callee->aux
+                 && ((struct ipa_dfs_info *)cur->aux)->scc_no
+                    == ((struct ipa_dfs_info *)callee->aux)->scc_no)
+               flags = 0;
+
+             bool ignore_stores = ignore_stores_p (cur->decl, flags);
+
+             /* We don't know anything about CALLEE, hence we cannot tell
+                anything about the entire component.  */
+
+             if (avail <= AVAIL_INTERPOSABLE
+                 || !(callee_summary = summaries->get (callee)))
+               {
+                 if (!ignore_stores)
+                   {
+                     its_hopeless = true;
+                     if (dump_file && avail <= AVAIL_INTERPOSABLE)
+                       fprintf (dump_file, "      Call target interposable"
+                                "or not available\n");
+                     else if (dump_file)
+                       fprintf (dump_file, "      No call target summary\n");
+                     break;
+                   }
+                 else
+                   {
+                     if (loads)
+                       loads->collapse ();
+                     if (loads_lto)
+                       loads_lto->collapse ();
+                     if (dump_file && avail <= AVAIL_INTERPOSABLE)
+                       fprintf (dump_file, "      Call target interposable"
+                                "or not available; collapsing loads\n");
+                     else if (dump_file)
+                       fprintf (dump_file, "      No call target summary;"
+                                " collapsing loads\n");
+                     continue;
+                   }
+               }
+
+             /* Merge in callee's information.  */
+             if (callee_summary->loads
+                 && callee_summary->loads != loads)
+               loads->merge (callee_summary->loads);
+             if (callee_summary->stores
+                 && callee_summary->stores != stores)
+               stores->merge (callee_summary->stores);
+             if (callee_summary->loads_lto
+                 && callee_summary->loads_lto != loads_lto)
+               loads_lto->merge (callee_summary->loads_lto);
+             if (callee_summary->stores_lto
+                 && callee_summary->stores_lto != stores_lto)
+               stores_lto->merge (callee_summary->stores_lto);
+           }
+       }
+
+       /* At this time, ipa_loads and ipa_stores contain information
+          about all loads and stores done by any of the component's nodes and
+          all functions that any of the nodes calls.  We will now propagate
+          this information to all nodes in the component.  Therefore, we will
+          walk the component one more time to do it.  */
+       for (struct cgraph_node *cur = component_node; cur;
+          cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
+       {
+         modref_summary *cur_summary = summaries->get (cur);
+         if (!cur_summary)
+           {
+             /* The function doesn't have a summary.  We must have noticed
+                that during the first pass and the hopeless flag must
+                therefore be set.  Skip the function.  */
+             gcc_assert (its_hopeless);
+           }
+         else if (its_hopeless)
+           {
+             if (dump_file)
+               fprintf (dump_file, "Cleared modref info for %s\n",
+                        cur->dump_name ());
+             summaries->remove (cur);
+           }
+         else
+           {
+             if (cur == first)
+               ;
+             else
+               {
+                 if (loads)
+                   cur_summary->loads->merge (loads);
+                 if (stores)
+                   cur_summary->stores->merge (stores);
+                 if (loads_lto)
+                   cur_summary->loads_lto->merge (loads_lto);
+                 if (stores_lto)
+                   cur_summary->stores_lto->merge (stores_lto);
+               }
+             cur_summary->finished = true;
+             if (dump_file)
+               {
+                 fprintf (dump_file, "Propagated modref for %s%s%s\n",
+                          cur->dump_name (),
+                          TREE_READONLY (cur->decl) ? " (const)" : "",
+                          DECL_PURE_P (cur->decl) ? " (pure)" : "");
+                 cur_summary->dump (dump_file);
+               }
+           }
+       }
+    }
+  ((modref_summaries *)summaries)->ipa = false;
+  ipa_free_postorder_info ();
+  return 0;
+}
+
+#include "gt-ipa-modref.h"
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
new file mode 100644 (file)
index 0000000..6f97920
--- /dev/null
@@ -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
index 8a38fa27d7c721a45c64180554bc908f5b47a93d..c12bc8a74b3ab0eeb421f8fb922cabbadffc6c87 100644 (file)
@@ -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
index 8c98c96e3682c4a8d2d7749f7c7b4de0070cf76c..b465a5e9c18ca3f5fe46841d0495d482a58ba4e2 100644 (file)
@@ -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.  */
 };
 
index c5c60581f70220a89289a59e5f4a6b1a282fd8b9..3c4a0b540b490abd99d0c00287bb970e355cd9e1 100644 (file)
@@ -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 },
index f39e5d1a0127ff8b3df34a7deef9eceb780a87cd..1d864047ad2c6c179d59892b64e1d05a522ba61d 100644 (file)
@@ -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.
index c0098d755bf083fa2ef891762c0c7bcc49c483b7..f865bdc19acd0191c6e1aba4ec0d3ec4e84ac446 100644 (file)
@@ -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)
index 7dd1e2685a4e98b3367c3fa93b839e5921a193a8..08c21c04009eb7f5720b9bd32d1c5e4e930e3298 100644 (file)
@@ -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")
index f01e811917da8eeba2fd478952eaf06d81392847..62e5b696cab7490621fb0a78833b8816554deaf2 100644 (file)
@@ -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);
 
index ede4f198342db99e58c6d7febac1d957b9e18df8..be4d4462fc50395d3eb26b832ebf1d15571d665e 100644 (file)
@@ -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))
     {
index d6e92d8dc121806f343237761df7662a55e5255a..1dd02c0ea620fd18f9936a8094329b2daf0e5ac4 100644 (file)
@@ -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