diff mbox series

New modref/ipa_modref optimization passes

Message ID 157394261677.27454.2367573047582814412@a285.localdomain
State New
Headers show
Series New modref/ipa_modref optimization passes | expand

Commit Message

David Čepelík Nov. 16, 2019, 10:16 p.m. UTC
Dear GCC developers,

Below you may find a patch which implements two new optimization
passes, called "modref" and "ipa_modref", which operate on GIMPLE and
during WPO, respectively.

What the passes do is fairly simple: for each analyzed function, they
collect information about loads and stores performed by that function.
The GIMPLE pass collects alias sets. The IPA pass collects sequences of
types that make up each load/store. The IPA pass furthermore constructs
a transitive closure from the data (so that we have information about
loads/stores each function, and any function that it calls, performs).

The data is collected in a data structure called a "modref tree". That's
a really simple tree-like structure which is three levels deep; the
first level is indexed by the base of the access, the second level is
indexed by the ref of the access (using what seems to be standard GCC
terminology for the innermost and outermost type of a reference) and the
last is indexed by the range of the access (currently unused). The data
structure's primary function is to organize the data and be able to
quickly answer, given a load/store, whether any other load/store in the
tree may alias it.

The data structure has configurable limits on the size of each level and
attempts to degrade gracefully when the limit is reached (i.e., it tries
to keep as much information as possible without exceeding the limits).

There's a lot of work to be done, but I believe it should mostly be
refactoring and enhancing existing alias analysis by using the
information which these new analyses collect.

On the refactoring part: I need to rewrite the data structure to get rid
of the C++ template. Since the data structure is used for both the
GIMPLE pass and the IPA pass, which are indexed by alias sets and GIMPLE
trees, respectively, a C++ template seemed appropriate. Wrong.
Especially getting GC to work with a custom template was a nightmare,
not to mention it brought lots of funky C++ peculiarities which I prefer
to avoid.

In the end, I couldn't avoid code duplication anyway, so some functions
have a _tree and an _ipa version; I will address that as well.

The integration with the alias oracle will be done incrementally over a
series of patches which I will prepare with Honza Hubička. These
shouldn't require extensive changes, so I hope they're fine as part of
early stage3.

No doubt, there will be some coding style issues and other violations of
common sense which I have not yet learned.

Last, I need to apologize. The patch worked with bootstrapping and
passed all tests (except what was already broken in master). However,
I've rebased at the last minute which was a poor judgement. With current
master, I'm getting loads of warnings in stage2 with regard to unused
variables and a few other diagnostics. I won't be able to fix that
immediately as I need to discuss it with Honza first. It's therefore
possible that I broke something with my the rebase and clean-ups. Please
bear with me, I will fix this ASAP and send an updated version of the
patch, which will bootstrap and pass all tests again with current
master.


To summarize, this patch implements a new analysis to collect
information about loads/stores in functions. That can be used (and is
used already, for the GIMPLE part) in the alias oracle. In theory, this
should substantially improve alias analysis capabilities of GCC, leading
to many more disambiguations in the alias oracle (because there will be
much more information available about function's load/store refs). The
patch needs a good deal of cleaning up and some further plumbing is
required to get it to do some real work in the alias oracle. I think
this is doable as part of stage3. I would very much like to see this in
the upcoming release as I've spent quite some time with it.

I'd like to thank Honza Hubička (my advisor) who was instrumental in
putting this together. (But all bugs are mine.) Thanks go also to other
guys from SUSE and others who were helping me to debug some peculiar
issues with the garbage collector.

                                             Regards, David

From 7d63fa301c12455aa6e9ba1b68f72e2d93fc2d2d Mon Sep 17 00:00:00 2001
From: David Cepelik <dcepelik@kam.mff.cuni.cz>
Date: Fri, 11 Oct 2019 21:15:23 +0200
Subject: [PATCH] Modref pass

---
 ChangeLog            |   20 +
 gcc/ChangeLog        |   22 ++
 gcc/Makefile.in      |    4 +
 gcc/gengtype.c       |    2 +-
 gcc/ipa-modref.c     | 1012 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/ipa-modref.h     |   43 +++
 gcc/lto-section-in.c |    3 +-
 gcc/lto-streamer.h   |    1 +
 gcc/modref-tree.c    |  342 +++++++++++++++++
 gcc/modref-tree.h    |  355 ++++++++++++++++++
 gcc/params.opt       |   12 +
 gcc/passes.def       |    5 +
 gcc/timevar.def      |    2 +
 gcc/tree-pass.h      |    2 +
 gcc/tree-ssa-alias.c |  105 +++++-
 15 files changed, 1924 insertions(+), 6 deletions(-)
 create mode 100644 gcc/ipa-modref.c
 create mode 100644 gcc/ipa-modref.h
 create mode 100644 gcc/modref-tree.c
 create mode 100644 gcc/modref-tree.h

Comments

Jan Hubicka Sept. 19, 2020, 10:32 p.m. UTC | #1
Hi,
this is cleaned up version of the patch.  I removed unfinished bits, fixed
propagation, cleaned it up and fixed fallout.

At present modref does bare minimum of what it can do and only collects
base/ref alias sets of accesses to disambiguate uding them.  I have followup
patches adding more features.

Like pure-const pass, modref is both local and IPA pass (so early optimizations
benefits from it). It works with both LTO and non-LTO. LTO tracking is bit
harder since instead of alias sets one needs to track types alias sets are
derived from (because alias sets change from compile to link time). For that
reason I had to exprt reference_alias_ptr_type_1 so I can lookup same type as
get_alias_set does.  There is an assert checking that both functions are in
sync.

Building cc1plus with LTO I get:

Alias oracle query stats:
  refs_may_alias_p: 59342803 disambiguations, 69597406 queries
  ref_maybe_used_by_call_p: 139290 disambiguations, 60227579 queries
  call_may_clobber_ref_p: 17604 disambiguations, 23290 queries
  nonoverlapping_component_refs_p: 0 disambiguations, 37088 queries
  nonoverlapping_refs_since_match_p: 19439 disambiguations, 55079 must overlaps, 76966 queries
  aliasing_component_refs_p: 54891 disambiguations, 756375 queries
  TBAA oracle: 22475946 disambiguations 51373119 queries
               15188806 are in alias set 0
               8345975 queries asked about the same object
               124 queries asked about the same alias set
               0 access volatile
               3811042 are dependent in the DAG
               1551226 are aritificially in conflict with void *

Modref stats:
  modref use: 6315 disambiguations, 60861 queries
  modref clobber: 1788334 disambiguations, 2706342 queries
  1223123 tbaa querries (0.451947 per modref querry)

PTA query stats:
  pt_solution_includes: 922059 disambiguations, 13392569 queries
  pt_solutions_intersect: 1018189 disambiguations, 11428599 queries

This ignores the disambiguations enabled by the local modref done during early
optimization.

On cc1plus it seems that modref querries are cheap (usually converging after
checking 0.45 alias sets :) and quite effective for stores with similar number
of disambiguations as the PTA oracle (that is surprisingly high). Load
disambiguations are quite low. Parlty this is becuase we only track lods that
binds locally, since otherwise we risk that there is optimized out load that
will be still present in prevailing version of the function and may lead to
memory trap.

Incrementally it may be worth to start tracking info about ealry optimized out
loads, but that needs more work.

Other problem I noticed while looking to dumps is that gimple, tree and rtl
datastrutures are unions and we drop all accesses to them to alias set 0 that
makes the anaysis not very effetive on GCC itself. This analysis of all those
uninlined tree/rtl/gimple predicates quite ineffective that is bit sad.

For tramp3d I get:
Alias oracle query stats:
  refs_may_alias_p: 2261286 disambiguations, 2525487 queries
  ref_maybe_used_by_call_p: 7402 disambiguations, 2298244 queries
  call_may_clobber_ref_p: 232 disambiguations, 232 queries
  nonoverlapping_component_refs_p: 0 disambiguations, 4145 queries
  nonoverlapping_refs_since_match_p: 329 disambiguations, 10232 must overlaps, 10648 queries
  aliasing_component_refs_p: 858 disambiguations, 34731 queries
  TBAA oracle: 972943 disambiguations 1808294 queries
               130565 are in alias set 0
               497083 queries asked about the same object
               0 queries asked about the same alias set
               0 access volatile
               207388 are dependent in the DAG
               315 are aritificially in conflict with void *

Modref stats:
  modref use: 950 disambiguations, 5474 queries
  modref clobber: 30035 disambiguations, 81948 queries
  41671 tbaa querries (0.508505 per modref querry)

PTA query stats:
  pt_solution_includes: 383095 disambiguations, 593201 queries
  pt_solutions_intersect: 147774 disambiguations, 434255 queries

So again the loads are disambiguated less.

There is a new testsuite failure:
./testsuite/gfortran/gfortran.sum:FAIL: gfortran.dg/assumed_type_9.f90   -O2  execution test                                                                                              ./testsuite/gfortran/gfortran.sum:FAIL: gfortran.dg/assumed_type_9.f90   -Os  execution test

This is an pre-existing issue where fortran frontend builds array descriptors
in type that contains pointer to actual data while mixes them up with
data type describing array of unknown type. The two structures are not TBAA
compatible without LTO and thus leads to misoptimization.

Same wrong code can be triggered by forcing inlining to happen and disabling
ccp pass (that otherwise hides the problem), so I think we could handle this
incrementally as independent bug - I would like to get basic code in so we can
test additional patches in foreseeable future.

While there are several areas for improvements but I think it is not in shape
for mainline and rest can be dealt with incrementally.

Bootstrapped/regtested x86_64-linux including ada, d and go.  I plan to commit
it after bit more testing tomorrow.

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.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 79e854aa938..c710bad27b1 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -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 \
diff --git a/gcc/alias.c b/gcc/alias.c
index df85f07ee9a..1cb702be2ce 100644
--- a/gcc/alias.c
+++ b/gcc/alias.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;
diff --git a/gcc/alias.h b/gcc/alias.h
index 4453d9723ce..807af957f02 100644
--- a/gcc/alias.h
+++ b/gcc/alias.h
@@ -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);
diff --git a/gcc/common.opt b/gcc/common.opt
index dd68c61ae1d..b833b98dfb8 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -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.
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index 981577481af..a59a8823f82 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -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.c b/gcc/ipa-modref.c
new file mode 100644
index 00000000000..f982ce94a75
--- /dev/null
+++ b/gcc/ipa-modref.c
@@ -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
index 00000000000..6f979200cc2
--- /dev/null
+++ b/gcc/ipa-modref.h
@@ -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
diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
new file mode 100644
index 00000000000..e37dee67fa3
--- /dev/null
+++ b/gcc/ipa-modref-tree.c
@@ -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
index 00000000000..3bdd3058aa1
--- /dev/null
+++ b/gcc/ipa-modref-tree.h
@@ -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/lto-section-in.c b/gcc/lto-section-in.c
index 8a38fa27d7c..c12bc8a74b3 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -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
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 8c98c96e368..b465a5e9c18 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -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.  */
 };
 
diff --git a/gcc/opts.c b/gcc/opts.c
index c5c60581f70..3c4a0b540b4 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -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 },
diff --git a/gcc/params.opt b/gcc/params.opt
index f39e5d1a012..1d864047ad2 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -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.
diff --git a/gcc/passes.def b/gcc/passes.def
index c0098d755bf..f865bdc19ac 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -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)
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 7dd1e2685a4..08c21c04009 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -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")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index f01e811917d..62e5b696cab 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -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);
 
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index ede4f198342..2bd26cff478 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -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,66 @@ 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 (!flag_strict_aliasing)
+    return true;
+
+  if (tt->every_base)
+    return true;
+
+  base_set = ao_ref_base_alias_set (ref);
+  if (!base_set)
+    return true;
+
+  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)
+	{
+	  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 (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 +2494,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 +2547,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 +2900,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 +2927,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 +2992,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 +3233,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 +3260,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))
     {
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index d6e92d8dc12..1dd02c0ea62 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -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
Jan Hubicka Sept. 20, 2020, 6:15 a.m. UTC | #2
Hi,
this is patch I am using to fix the assumed_alias_type.f90 failure by
simply arranging alias set 0 for the problematic array descriptor.

I am not sure this is the best option, but I suppose it is better than
setting all array descritors to have same canonical type (as done by
LTO)?

Honza

	* trans-types.c (gfc_get_array_type_bounds): Set alias set to 0 for
	arrays of unknown element type.
diff --git a/gcc/fortran/trans-types.c b/gcc/fortran/trans-types.c
index 26fdb2803a7..bef3d270c06 100644
--- a/gcc/fortran/trans-types.c
+++ b/gcc/fortran/trans-types.c
@@ -1903,6 +1903,12 @@ gfc_get_array_type_bounds (tree etype, int dimen, int codimen, tree * lbound,
   base_type = gfc_get_array_descriptor_base (dimen, codimen, false);
   TYPE_CANONICAL (fat_type) = base_type;
   TYPE_STUB_DECL (fat_type) = TYPE_STUB_DECL (base_type);
+  /* Arrays of unknown type must alias with all array descriptors.  */
+  if (etype == ptr_type_node)
+    {
+      TYPE_ALIAS_SET (base_type) = 0;
+      TYPE_ALIAS_SET (fat_type) = 0;
+    }
 
   tmp = TYPE_NAME (etype);
   if (tmp && TREE_CODE (tmp) == TYPE_DECL)
David Malcolm Sept. 20, 2020, 2:33 p.m. UTC | #3
On Sun, 2020-09-20 at 00:32 +0200, Jan Hubicka wrote:
> Hi,
> this is cleaned up version of the patch.  I removed unfinished bits,
> fixed
> propagation, cleaned it up and fixed fallout.

[...]

> While there are several areas for improvements but I think it is not
> in shape
> for mainline and rest can be dealt with incrementally.

FWIW I think you typoed:
  "not in shape for mainline"
when you meant:
  "now in shape for mainline"
given...

> 
> Bootstrapped/regtested x86_64-linux including ada, d and go.  I plan
> to commit
> it after bit more testing tomorrow.

...this, which suggests the opposite meaning.


I didn't look at the guts of the patch, but I spotted some nits.

> 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.

Should new C++ source files have a .cc suffix, rather than .c?

[...]

> +  $(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \

...which would affect this            ^^^^^^^^^^^^^

[...]

> diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
> new file mode 100644
> index 00000000000..e37dee67fa3
> --- /dev/null
> +++ b/gcc/ipa-modref-tree.c

[...]

> +#if CHECKING_P
> +
> +
> +static void
> +test_insert_search_collapse ()
> +{
[...]
> +}
> +
> +static void
> +test_merge ()
> +{
[...]
> +}
> +
> +
> +void
> +modref_tree_c_tests ()
> +{
> +  test_insert_search_collapse ();
> +  test_merge ();
> +}
> +
> +#endif

It looks like these tests aren't being called anywhere; should they be?

The convention is to put such tests inside namespace selftest and to
call the "run all the tests in this file" function from
selftest::run_tests.

If you change this to be a .cc file, then the _c_ in the function name
should become a _cc_.

[...]

> diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
> new file mode 100644
> index 00000000000..3bdd3058aa1
> --- /dev/null
> +++ b/gcc/ipa-modref-tree.h

[...]

> +void modref_c_tests ();

Likewise here; the convention is to declare these within
selftest.h

[...]

Hope this is constructive
Dave
Jan Hubicka Sept. 20, 2020, 5:30 p.m. UTC | #4
> On Sun, 2020-09-20 at 00:32 +0200, Jan Hubicka wrote:
> > Hi,
> > this is cleaned up version of the patch.  I removed unfinished bits,
> > fixed
> > propagation, cleaned it up and fixed fallout.
> 
> [...]
> 
> > While there are several areas for improvements but I think it is not
> > in shape
> > for mainline and rest can be dealt with incrementally.
> 
> FWIW I think you typoed:
>   "not in shape for mainline"
> when you meant:
>   "now in shape for mainline"
> given...

Yep, sorry for that :)
> 
> Should new C++ source files have a .cc suffix, rather than .c?
> 
> [...]
> 
> > +  $(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \
> 
> ...which would affect this            ^^^^^^^^^^^^^

I was wondering about that and decided to stay with .c since it is what
other ipa passes use.  I can rename the files. We have some sources with
.c extension and others with .cc while they are now all C++. Is there
some plan to clean it up?
> > +void
> > +modref_tree_c_tests ()
> > +{
> > +  test_insert_search_collapse ();
> > +  test_merge ();
> > +}
> > +
> > +#endif
> 
> It looks like these tests aren't being called anywhere; should they be?
> 
> The convention is to put such tests inside namespace selftest and to
> call the "run all the tests in this file" function from
> selftest::run_tests.
> 
> If you change this to be a .cc file, then the _c_ in the function name
> should become a _cc_.
> 
> [...]
> 
> > diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
> > new file mode 100644
> > index 00000000000..3bdd3058aa1
> > --- /dev/null
> > +++ b/gcc/ipa-modref-tree.h
> 
> [...]
> 
> > +void modref_c_tests ();
> 
> Likewise here; the convention is to declare these within
> selftest.h
> 
> [...]

Thanks, indeed is seems that the self tests code was dropped at some
point which I did not noticed.  I will fix that up.
> 
> Hope this is constructive
It is :)

Honza
> Dave
>
David Malcolm Sept. 20, 2020, 11:27 p.m. UTC | #5
On Sun, 2020-09-20 at 19:30 +0200, Jan Hubicka wrote:
> > 

[...]

> > Should new C++ source files have a .cc suffix, rather than .c?
> > 
> > [...]
> > 
> > > +  $(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \
> > 
> > ...which would affect this            ^^^^^^^^^^^^^
> 
> I was wondering about that and decided to stay with .c since it is
> what
> other ipa passes use.  I can rename the files. 

Given that they're in the source tree now, maybe better to wait until
some mass renaming in the future?

> We have some sources with
> .c extension and others with .cc while they are now all C++. Is there
> some plan to clean it up?

I think we've been avoiding it, partly out of a concern of making
backports harder, and also because someone has to do the work.

That said, it's yet another unfinished transition, and is technical
debt for the project.  It's confusing to newcomers.

It's been bugging me for a while, so I might take a look at doing it in
this cycle.

Dave
Richard Biener Sept. 21, 2020, 7:13 a.m. UTC | #6
On Sun, 20 Sep 2020, Jan Hubicka wrote:

> Hi,
> this is patch I am using to fix the assumed_alias_type.f90 failure by
> simply arranging alias set 0 for the problematic array descriptor.

There's no such named testcase on trunk.  Can you be more specific
as to the problem at hand?  It looks like gfortran.dg/assumed_type_9.f90
execute FAILs at the moment.

In particular how's this not an issue w/o IPA modref?

For TYPE(*) I think the object itself cannot be accessed but for
arrays the meta-info in the array descriptor can.  Now my question
would be why the Fortran FE at the call site does not build an
appropriately typed array descriptor?

CCing the fortran list.

> I am not sure this is the best option, but I suppose it is better than
> setting all array descritors to have same canonical type (as done by
> LTO)?
> 
> Honza
> 
> 	* trans-types.c (gfc_get_array_type_bounds): Set alias set to 0 for
> 	arrays of unknown element type.
> diff --git a/gcc/fortran/trans-types.c b/gcc/fortran/trans-types.c
> index 26fdb2803a7..bef3d270c06 100644
> --- a/gcc/fortran/trans-types.c
> +++ b/gcc/fortran/trans-types.c
> @@ -1903,6 +1903,12 @@ gfc_get_array_type_bounds (tree etype, int dimen, int codimen, tree * lbound,
>    base_type = gfc_get_array_descriptor_base (dimen, codimen, false);
>    TYPE_CANONICAL (fat_type) = base_type;
>    TYPE_STUB_DECL (fat_type) = TYPE_STUB_DECL (base_type);
> +  /* Arrays of unknown type must alias with all array descriptors.  */
> +  if (etype == ptr_type_node)
> +    {
> +      TYPE_ALIAS_SET (base_type) = 0;
> +      TYPE_ALIAS_SET (fat_type) = 0;
> +    }
>  
>    tmp = TYPE_NAME (etype);
>    if (tmp && TREE_CODE (tmp) == TYPE_DECL)
>
Jan Hubicka Sept. 21, 2020, 7:47 a.m. UTC | #7
> On Sun, 20 Sep 2020, Jan Hubicka wrote:
> 
> > Hi,
> > this is patch I am using to fix the assumed_alias_type.f90 failure by
> > simply arranging alias set 0 for the problematic array descriptor.
> 
> There's no such named testcase on trunk.  Can you be more specific
> as to the problem at hand?  It looks like gfortran.dg/assumed_type_9.f90
> execute FAILs at the moment.
> 
> In particular how's this not an issue w/o IPA modref?

> 
> For TYPE(*) I think the object itself cannot be accessed but for
> arrays the meta-info in the array descriptor can.  Now my question
> would be why the Fortran FE at the call site does not build an
> appropriately typed array descriptor?
> 
> CCing the fortran list.

The problem is:

alsize (struct array15_unknown & restrict a)
{
...
  _2 = *a_13(D).dtype.rank;
  _3 = (integer(kind=8)) _2;
...
}
}
and in main:

  struct array02_integer(kind=4) am;
  <bb 6> :
  MEM <c_char[8]> [(struct dtype_type *)&am + 24B] = {};
  am.dtype.elem_len = 4;
  am.dtype.rank = 2;
  am.dtype.type = 1;
...
  _52 = alsize (&am);

Here array15_unknown and array02_integer are different structures with
different canonical types and thus we end up disambiguating the accesses
via base alias sets.

My understanding is that this _unknown array descriptor is supposed to
be universal and work with all kinds of arrays.

Wihtout modref this works because alsize is not inlined (we think code
size would grow). Forcing inliner to inline stil leads to working code
because we first constant propagate the pointer and then we see accesses
from same base DECL thus bypass the TBAA checks.  Disabling the
constant propagation leads to wrong code as wel.

Honza
Richard Biener Sept. 21, 2020, 7:55 a.m. UTC | #8
On Mon, 21 Sep 2020, Jan Hubicka wrote:

> > On Sun, 20 Sep 2020, Jan Hubicka wrote:
> > 
> > > Hi,
> > > this is patch I am using to fix the assumed_alias_type.f90 failure by
> > > simply arranging alias set 0 for the problematic array descriptor.
> > 
> > There's no such named testcase on trunk.  Can you be more specific
> > as to the problem at hand?  It looks like gfortran.dg/assumed_type_9.f90
> > execute FAILs at the moment.
> > 
> > In particular how's this not an issue w/o IPA modref?
> 
> > 
> > For TYPE(*) I think the object itself cannot be accessed but for
> > arrays the meta-info in the array descriptor can.  Now my question
> > would be why the Fortran FE at the call site does not build an
> > appropriately typed array descriptor?
> > 
> > CCing the fortran list.
> 
> The problem is:
> 
> alsize (struct array15_unknown & restrict a)
> {
> ...
>   _2 = *a_13(D).dtype.rank;
>   _3 = (integer(kind=8)) _2;
> ...
> }
> }
> and in main:
> 
>   struct array02_integer(kind=4) am;
>   <bb 6> :
>   MEM <c_char[8]> [(struct dtype_type *)&am + 24B] = {};
>   am.dtype.elem_len = 4;
>   am.dtype.rank = 2;
>   am.dtype.type = 1;
> ...
>   _52 = alsize (&am);
> 
> Here array15_unknown and array02_integer are different structures with
> different canonical types and thus we end up disambiguating the accesses
> via base alias sets.
> 
> My understanding is that this _unknown array descriptor is supposed to
> be universal and work with all kinds of arrays.

But the FE builds a new descriptor for each individual call and thus
should build a universal descriptor for a call to an universal
descriptor argument.

Richard.

> Wihtout modref this works because alsize is not inlined (we think code
> size would grow). Forcing inliner to inline stil leads to working code
> because we first constant propagate the pointer and then we see accesses
> from same base DECL thus bypass the TBAA checks.  Disabling the
> constant propagation leads to wrong code as wel.
> 
> Honza
>
Jan Hubicka Sept. 21, 2020, 8:04 a.m. UTC | #9
> > 
> > The problem is:
> > 
> > alsize (struct array15_unknown & restrict a)
> > {
> > ...
> >   _2 = *a_13(D).dtype.rank;
> >   _3 = (integer(kind=8)) _2;
> > ...
> > }
> > }
> > and in main:
> > 
> >   struct array02_integer(kind=4) am;
> >   <bb 6> :
> >   MEM <c_char[8]> [(struct dtype_type *)&am + 24B] = {};
> >   am.dtype.elem_len = 4;
> >   am.dtype.rank = 2;
> >   am.dtype.type = 1;
> > ...
> >   _52 = alsize (&am);
> > 
> > Here array15_unknown and array02_integer are different structures with
> > different canonical types and thus we end up disambiguating the accesses
> > via base alias sets.
> > 
> > My understanding is that this _unknown array descriptor is supposed to
> > be universal and work with all kinds of arrays.
> 
> But the FE builds a new descriptor for each individual call and thus
> should build a universal descriptor for a call to an universal
> descriptor argument.

I see, so you would expect call to alsize to initialize things in
array15_unkonwn type?  That would work too.

Honza
Richard Biener Sept. 21, 2020, 8:10 a.m. UTC | #10
On Mon, 21 Sep 2020, Jan Hubicka wrote:

> > > 
> > > The problem is:
> > > 
> > > alsize (struct array15_unknown & restrict a)
> > > {
> > > ...
> > >   _2 = *a_13(D).dtype.rank;
> > >   _3 = (integer(kind=8)) _2;
> > > ...
> > > }
> > > }
> > > and in main:
> > > 
> > >   struct array02_integer(kind=4) am;
> > >   <bb 6> :
> > >   MEM <c_char[8]> [(struct dtype_type *)&am + 24B] = {};
> > >   am.dtype.elem_len = 4;
> > >   am.dtype.rank = 2;
> > >   am.dtype.type = 1;
> > > ...
> > >   _52 = alsize (&am);
> > > 
> > > Here array15_unknown and array02_integer are different structures with
> > > different canonical types and thus we end up disambiguating the accesses
> > > via base alias sets.
> > > 
> > > My understanding is that this _unknown array descriptor is supposed to
> > > be universal and work with all kinds of arrays.
> > 
> > But the FE builds a new descriptor for each individual call and thus
> > should build a universal descriptor for a call to an universal
> > descriptor argument.
> 
> I see, so you would expect call to alsize to initialize things in
> array15_unkonwn type?  That would work too.

Yes, that's my expectation.  But let's see what fortran folks say.

Richard.
David Malcolm Sept. 21, 2020, 11:14 p.m. UTC | #11
On Sun, 2020-09-20 at 19:30 +0200, Jan Hubicka wrote:
> > On Sun, 2020-09-20 at 00:32 +0200, Jan Hubicka wrote:
> > > Hi,
> > > this is cleaned up version of the patch.  I removed unfinished
> > > bits,
> > > fixed
> > > propagation, cleaned it up and fixed fallout.
> > 
> > [...]
> > 
> > > While there are several areas for improvements but I think it is
> > > not
> > > in shape
> > > for mainline and rest can be dealt with incrementally.
> > 
> > FWIW I think you typoed:
> >   "not in shape for mainline"
> > when you meant:
> >   "now in shape for mainline"
> > given...
> 
> Yep, sorry for that :)

I've started seeing crashes in the jit testsuite even with trivial
inputs, which are happening at pass_modref::~pass_modref at:

772		ggc_delete (summaries);

on the first in-process iteration of the code, with:

(gdb) p summaries
$3 = (fast_function_summary<modref_summary*, va_gc> *) 0x0

I'm still investigating (but may have to call halt for the night), but
this could be an underlying issue with the new passes; the jit
testsuite runs with the equivalent of:

--param=ggc-min-expand=0 --param=ggc-min-heapsize=0

throughout to shake out GC issues (to do a full collection at each GC
opportunity).

Was this code tested with the jit?  Do you see issues in cc1 if you set
those params?  Anyone else seeing "random" crashes?

Thanks
Dave
Jan Hubicka Sept. 22, 2020, 6:45 a.m. UTC | #12
> On Sun, 2020-09-20 at 19:30 +0200, Jan Hubicka wrote:
> > > On Sun, 2020-09-20 at 00:32 +0200, Jan Hubicka wrote:
> > > > Hi,
> > > > this is cleaned up version of the patch.  I removed unfinished
> > > > bits,
> > > > fixed
> > > > propagation, cleaned it up and fixed fallout.
> > > 
> > > [...]
> > > 
> > > > While there are several areas for improvements but I think it is
> > > > not
> > > > in shape
> > > > for mainline and rest can be dealt with incrementally.
> > > 
> > > FWIW I think you typoed:
> > >   "not in shape for mainline"
> > > when you meant:
> > >   "now in shape for mainline"
> > > given...
> > 
> > Yep, sorry for that :)
> 
> I've started seeing crashes in the jit testsuite even with trivial
> inputs, which are happening at pass_modref::~pass_modref at:
> 
> 772		ggc_delete (summaries);
> 
> on the first in-process iteration of the code, with:
> 
> (gdb) p summaries
> $3 = (fast_function_summary<modref_summary*, va_gc> *) 0x0
> 
> I'm still investigating (but may have to call halt for the night), but
> this could be an underlying issue with the new passes; the jit
> testsuite runs with the equivalent of:
> 
> --param=ggc-min-expand=0 --param=ggc-min-heapsize=0
> 
> throughout to shake out GC issues (to do a full collection at each GC
> opportunity).
> 
> Was this code tested with the jit?  Do you see issues in cc1 if you set
> those params?  Anyone else seeing "random" crashes?

I suppose this happes when pass gets constructed but no summary is
computed.  Dos the NULL pointer guard here help?

Honza
> 
> Thanks
> Dave
> 
>
Jan Hubicka Sept. 22, 2020, 7:07 a.m. UTC | #13
> > (gdb) p summaries
> > $3 = (fast_function_summary<modref_summary*, va_gc> *) 0x0
> > 
> > I'm still investigating (but may have to call halt for the night), but
> > this could be an underlying issue with the new passes; the jit
> > testsuite runs with the equivalent of:
> > 
> > --param=ggc-min-expand=0 --param=ggc-min-heapsize=0
> > 
> > throughout to shake out GC issues (to do a full collection at each GC
> > opportunity).
> > 
> > Was this code tested with the jit?  Do you see issues in cc1 if you set
> > those params?  Anyone else seeing "random" crashes?
> 
> I suppose this happes when pass gets constructed but no summary is
> computed.  Dos the NULL pointer guard here help?

Hi,
I am currently in train and can not test the patch easilly, but this
should help.  If you run the pass on empty input then the destruction
happens with NULL summaries pointer.

My apologizes for that.
Honza

diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index af0b710333e..cd92b5a81d3 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -769,7 +885,8 @@ class pass_modref : public gimple_opt_pass
 
     ~pass_modref ()
       {
-	ggc_delete (summaries);
+	if (summaries)
+	  ggc_delete (summaries);
 	summaries = NULL;
       }
 
> 
> Honza
> > 
> > Thanks
> > Dave
> > 
> >
Tobias Burnus Sept. 22, 2020, 10:15 a.m. UTC | #14
On 9/21/20 10:10 AM, Richard Biener wrote:

>> I see, so you would expect call to alsize to initialize things in
>> array15_unkonwn type?  That would work too.
> Yes, that's my expectation.  But let's see what fortran folks say.

RFC patch attached; I think the following should work, but I am not
sure whether I missed something.

I wonder what to do about
   '!GCC$ NO_ARG_CHECK :: x
but that seems to work fine (creates void* type) and as it only
permits assumed size or scalar variables, the descriptor issue
does not occur.

Thoughts?

Tobias
David Malcolm Sept. 22, 2020, 11:21 a.m. UTC | #15
On Tue, 2020-09-22 at 09:07 +0200, Jan Hubicka wrote:
> > > (gdb) p summaries
> > > $3 = (fast_function_summary<modref_summary*, va_gc> *) 0x0
> > > 
> > > I'm still investigating (but may have to call halt for the
> > > night), but
> > > this could be an underlying issue with the new passes; the jit
> > > testsuite runs with the equivalent of:
> > > 
> > > --param=ggc-min-expand=0 --param=ggc-min-heapsize=0
> > > 
> > > throughout to shake out GC issues (to do a full collection at
> > > each GC
> > > opportunity).
> > > 
> > > Was this code tested with the jit?  Do you see issues in cc1 if
> > > you set
> > > those params?  Anyone else seeing "random" crashes?
> > 
> > I suppose this happes when pass gets constructed but no summary is
> > computed.  Dos the NULL pointer guard here help?
> 
> Hi,
> I am currently in train and can not test the patch easilly, but this
> should help.  If you run the pass on empty input then the destruction
> happens with NULL summaries pointer.
> 
> My apologizes for that.
> Honza
> 
> diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
> index af0b710333e..cd92b5a81d3 100644
> --- a/gcc/ipa-modref.c
> +++ b/gcc/ipa-modref.c
> @@ -769,7 +885,8 @@ class pass_modref : public gimple_opt_pass
>  
>      ~pass_modref ()
>        {
> -	ggc_delete (summaries);
> +	if (summaries)
> +	  ggc_delete (summaries);
>  	summaries = NULL;
>        }

Thanks; with that it survives the first in-process iteration, but then
dies inside the 3rd in-process iteration, on a different finalizer. 
I'm beginning to suspect a pre-existing bad interaction between
finalizers and jit which perhaps this patch has exposed.

I'll continue to investigate it.

Dave
Jan Hubicka Sept. 22, 2020, 12:29 p.m. UTC | #16
> 
> Thanks; with that it survives the first in-process iteration, but then
> dies inside the 3rd in-process iteration, on a different finalizer. 
> I'm beginning to suspect a pre-existing bad interaction between
> finalizers and jit which perhaps this patch has exposed.
> 
> I'll continue to investigate it.

I will look into it tonight (I have meeting now about remote teaching),
I think the pass should not have destructor but there should be fini
function as other passes do, so I will clean this up.  Those dtors make
no sense and there also seems to be some memory leaks.

Honza
> 
> Dave
>
Jan Hubicka Sept. 22, 2020, 6:39 p.m. UTC | #17
David,
with jit I get the following:
/usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed: nonrepresentable section on output
collect2: error: ld returned 1 exit status
make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1] Error

Is there a fix/workaround?
Patch I am trying to test/debug is attached, it fixes the selftest issue
and the destructor.

Honza

diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
index e37dee67fa3..a84508a2268 100644
--- a/gcc/ipa-modref-tree.c
+++ b/gcc/ipa-modref-tree.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #if CHECKING_P
 
+namespace selftest {
 
 static void
 test_insert_search_collapse ()
@@ -156,12 +157,14 @@ test_merge ()
 
 
 void
-modref_tree_c_tests ()
+ipa_modref_tree_c_tests ()
 {
   test_insert_search_collapse ();
   test_merge ();
 }
 
+} // namespace selftest
+
 #endif
 
 void
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index af0b710333e..ac7579a9e75 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -767,12 +770,6 @@ class pass_modref : public gimple_opt_pass
     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 ()
     {
@@ -1373,4 +1370,14 @@ unsigned int pass_ipa_modref::execute (function *)
   return 0;
 }
 
+/* Summaries must stay alive until end of compilation.  */
+
+void
+ipa_modref_c_finalize ()
+{
+  if (summaries)
+    ggc_delete (summaries);
+  summaries = NULL;
+}
+
 #include "gt-ipa-modref.h"
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 6f979200cc2..6cccdfe7af3 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -44,5 +44,6 @@ struct GTY(()) modref_summary
 };
 
 modref_summary *get_modref_function_summary (cgraph_node *func);
+void ipa_modref_c_finalize ();
 
 #endif
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index f0a81d43fd6..7a89b2df5bd 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -90,6 +90,7 @@ selftest::run_tests ()
   read_rtl_function_c_tests ();
   digraph_cc_tests ();
   tristate_cc_tests ();
+  ipa_modref_tree_c_tests ();
 
   /* Higher-level tests, or for components that other selftests don't
      rely on.  */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 5cffa13aedd..6c6c7f28675 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -268,6 +268,7 @@ extern void vec_perm_indices_c_tests ();
 extern void wide_int_cc_tests ();
 extern void opt_proposer_c_tests ();
 extern void dbgcnt_c_tests ();
+extern void ipa_modref_tree_c_tests ();
 
 extern int num_passes;
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index cdd4b5b4f92..a4cb8bb262e 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -84,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "dump-context.h"
 #include "print-tree.h"
 #include "optinfo-emit-json.h"
+#include "ipa-modref-tree.h"
+#include "ipa-modref.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -2497,6 +2499,7 @@ toplev::finalize (void)
   /* Needs to be called before cgraph_c_finalize since it uses symtab.  */
   ipa_reference_c_finalize ();
   ipa_fnsummary_c_finalize ();
+  ipa_modref_c_finalize ();
 
   cgraph_c_finalize ();
   cgraphunit_c_finalize ();
Jan Hubicka Sept. 22, 2020, 6:47 p.m. UTC | #18
> On Sun, 2020-09-20 at 19:30 +0200, Jan Hubicka wrote:
> > > 
> 
> [...]
> 
> > > Should new C++ source files have a .cc suffix, rather than .c?
> > > 
> > > [...]
> > > 
> > > > +  $(srcdir)/ipa-modref.h $(srcdir)/ipa-modref.c \
> > > 
> > > ...which would affect this            ^^^^^^^^^^^^^
> > 
> > I was wondering about that and decided to stay with .c since it is
> > what
> > other ipa passes use.  I can rename the files. 
> 
> Given that they're in the source tree now, maybe better to wait until
> some mass renaming in the future?

At the same time, I am only having patches against it, and I have no
problem update the name.
> 
> > We have some sources with
> > .c extension and others with .cc while they are now all C++. Is there
> > some plan to clean it up?
> 
> I think we've been avoiding it, partly out of a concern of making
> backports harder, and also because someone has to do the work.
> 
> That said, it's yet another unfinished transition, and is technical
> debt for the project.  It's confusing to newcomers.
> 
> It's been bugging me for a while, so I might take a look at doing it in
> this cycle.

Agreed. It would be nice to do the mass rename and at the same time make
a sane directory structure so newcomers can locate optimization passes
and other components.

David Cepelik was my student so he may have some feedback about what he
found hard.  I think main stoppers was the garbage collector (since he
decided to do the template for modref tree) and the flat includes that
breaks if you do not do them in right order and resolve all
dependencies.

Honza
> 
> Dave
>
David Malcolm Sept. 22, 2020, 8:13 p.m. UTC | #19
On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> David,
> with jit I get the following:
> /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> nonrepresentable section on output
> collect2: error: ld returned 1 exit status
> make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1]
> Error
> 
> Is there a fix/workaround?

I don't recognize that specific error, but googling suggests it may
relate to position-independent code.

Are you configuring with --enable-host-shared ?  This is needed when
enabling "jit" in --enable-languages (but slows down the compiler by a
few percent, which is why "jit" isn't in "all").


> Patch I am trying to test/debug is attached, it fixes the selftest
> issue
> and the destructor.

Thanks.

Sadly it doesn't fix the jit crashes, which are now in bugzilla (as PR
jit/97169).

Without the patch, the jit testcases crash at the end of the 1st in-
process iteration, in the dtor for the the new pass.

With the patch the jit testcases crash inside the 3rd in-process
iteration, invoking a modref_summaries finalizer at whichever GC-
collection point happens first, I think, where the modref_summaries *
seems to be pointing at corrupt data:

(gdb) p *(modref_summaries *)p
$2 = {<fast_function_summary<modref_summary*, va_gc>> =
{<function_summary_base<modref_summary>> = {
      _vptr.function_summary_base = 0x200000001,
m_symtab_insertion_hook = 0x1, m_symtab_removal_hook = 0x100000004, 
      m_symtab_duplication_hook = 0x0, m_symtab = 0x644210,
m_insertion_enabled = 112, m_allocator = {m_allocator = {
          m_name = 0x0, m_id = 0, m_elts_per_block = 1,
m_returned_free_list = 0x7afafaf01, 
          m_virgin_free_list = 0xafafafafafaf0001 <error: Cannot access
memory at address 0xafafafafafaf0001>, 
          m_virgin_elts_remaining = 0, m_elts_allocated =
140737080343888, m_elts_free = 0, m_blocks_allocated = 0, 
          m_block_list = 0x0, m_elt_size = 6517120, m_size = 13,
m_initialized = false, m_location = {
            m_filename = 0x0, m_function = 0x0, m_line = 1, m_origin =
2947481856, m_ggc = false}}}}, 
    m_vector = 0x0}, ipa = false}

I think this latter crash may be a pre-existing bug in how the jit
interacts with gc finalizers.  I think the finalizers are accumulating
from in-process run to run, leading to chaos, but I need to debug it
some more to be sure.  Alternatively, is there a way that a finalizer
is being registered, and then the object is somehow clobbered without
the finalizer being unregistered from the vec of finalizers?

Dave
Jan Hubicka Sept. 22, 2020, 8:19 p.m. UTC | #20
Hello,
this patch fixes the selftests and destructor issue noticed by David
(Malcolm).  According to David jit still crashes at different but I am
hitting different build failure in libjit that I will need to analyze
tomorrow.

Bootstrapped/regtested x86_64-linux, comitted.

	* ipa-modref-tree.c: Add namespace selftest.
	(modref_tree_c_tests): Rename to ...
	(ipa_modref_tree_c_tests): ... this.
	* ipa-modref.c (pass_modref): Remove destructor.
	(ipa_modref_c_finalize): New function.
	* ipa-modref.h (ipa_modref_c_finalize): Declare.
	* selftest-run-tests.c (selftest::run_tests): Call
	ipa_modref_c_finalize.
	* selftest.h (ipa_modref_tree_c_tests): Declare.
	* toplev.c: Include ipa-modref-tree.h and ipa-modref.h
	(toplev::finalize): Call ipa_modref_c_finalize.

diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c
index e37dee67fa3..a84508a2268 100644
--- a/gcc/ipa-modref-tree.c
+++ b/gcc/ipa-modref-tree.c
@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #if CHECKING_P
 
+namespace selftest {
 
 static void
 test_insert_search_collapse ()
@@ -156,12 +157,14 @@ test_merge ()
 
 
 void
-modref_tree_c_tests ()
+ipa_modref_tree_c_tests ()
 {
   test_insert_search_collapse ();
   test_merge ();
 }
 
+} // namespace selftest
+
 #endif
 
 void
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index af0b710333e..ac7579a9e75 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -767,12 +770,6 @@ class pass_modref : public gimple_opt_pass
     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 ()
     {
@@ -1373,4 +1370,14 @@ unsigned int pass_ipa_modref::execute (function *)
   return 0;
 }
 
+/* Summaries must stay alive until end of compilation.  */
+
+void
+ipa_modref_c_finalize ()
+{
+  if (summaries)
+    ggc_delete (summaries);
+  summaries = NULL;
+}
+
 #include "gt-ipa-modref.h"
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 6f979200cc2..6cccdfe7af3 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -44,5 +44,6 @@ struct GTY(()) modref_summary
 };
 
 modref_summary *get_modref_function_summary (cgraph_node *func);
+void ipa_modref_c_finalize ();
 
 #endif
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index f0a81d43fd6..7a89b2df5bd 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -90,6 +90,7 @@ selftest::run_tests ()
   read_rtl_function_c_tests ();
   digraph_cc_tests ();
   tristate_cc_tests ();
+  ipa_modref_tree_c_tests ();
 
   /* Higher-level tests, or for components that other selftests don't
      rely on.  */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 5cffa13aedd..6c6c7f28675 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -268,6 +268,7 @@ extern void vec_perm_indices_c_tests ();
 extern void wide_int_cc_tests ();
 extern void opt_proposer_c_tests ();
 extern void dbgcnt_c_tests ();
+extern void ipa_modref_tree_c_tests ();
 
 extern int num_passes;
 
diff --git a/gcc/toplev.c b/gcc/toplev.c
index cdd4b5b4f92..a4cb8bb262e 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -84,6 +84,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "dump-context.h"
 #include "print-tree.h"
 #include "optinfo-emit-json.h"
+#include "ipa-modref-tree.h"
+#include "ipa-modref.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -2497,6 +2499,7 @@ toplev::finalize (void)
   /* Needs to be called before cgraph_c_finalize since it uses symtab.  */
   ipa_reference_c_finalize ();
   ipa_fnsummary_c_finalize ();
+  ipa_modref_c_finalize ();
 
   cgraph_c_finalize ();
   cgraphunit_c_finalize ();
David Malcolm Sept. 22, 2020, 8:21 p.m. UTC | #21
On Tue, 2020-09-22 at 16:13 -0400, David Malcolm wrote:
> On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> > David,
> > with jit I get the following:
> > /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> > nonrepresentable section on output
> > collect2: error: ld returned 1 exit status
> > make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1]
> > Error
> > 
> > Is there a fix/workaround?
> 
> I don't recognize that specific error, but googling suggests it may
> relate to position-independent code.
> 
> Are you configuring with --enable-host-shared ?  This is needed when
> enabling "jit" in --enable-languages (but slows down the compiler by
> a
> few percent, which is why "jit" isn't in "all").
> 
> 
> > Patch I am trying to test/debug is attached, it fixes the selftest
> > issue
> > and the destructor.
> 
> Thanks.
> 
> Sadly it doesn't fix the jit crashes, which are now in bugzilla (as
> PR
> jit/97169).
> 
> Without the patch, the jit testcases crash at the end of the 1st in-
> process iteration, in the dtor for the the new pass.
> 
> With the patch the jit testcases crash inside the 3rd in-process
> iteration, invoking a modref_summaries finalizer at whichever GC-
> collection point happens first, I think, where the modref_summaries *
> seems to be pointing at corrupt data:
> 
> (gdb) p *(modref_summaries *)p
> $2 = {<fast_function_summary<modref_summary*, va_gc>> =
> {<function_summary_base<modref_summary>> = {
>       _vptr.function_summary_base = 0x200000001,
> m_symtab_insertion_hook = 0x1, m_symtab_removal_hook = 0x100000004, 
>       m_symtab_duplication_hook = 0x0, m_symtab = 0x644210,
> m_insertion_enabled = 112, m_allocator = {m_allocator = {
>           m_name = 0x0, m_id = 0, m_elts_per_block = 1,
> m_returned_free_list = 0x7afafaf01, 
>           m_virgin_free_list = 0xafafafafafaf0001 <error: Cannot
> access
> memory at address 0xafafafafafaf0001>, 
>           m_virgin_elts_remaining = 0, m_elts_allocated =
> 140737080343888, m_elts_free = 0, m_blocks_allocated = 0, 
>           m_block_list = 0x0, m_elt_size = 6517120, m_size = 13,
> m_initialized = false, m_location = {
>             m_filename = 0x0, m_function = 0x0, m_line = 1, m_origin
> =
> 2947481856, m_ggc = false}}}}, 
>     m_vector = 0x0}, ipa = false}
> 
> I think this latter crash may be a pre-existing bug in how the jit
> interacts with gc finalizers.  I think the finalizers are
> accumulating
> from in-process run to run, leading to chaos, but I need to debug it
> some more to be sure.  Alternatively, is there a way that a finalizer
> is being registered, and then the object is somehow clobbered without
> the finalizer being unregistered from the vec of finalizers?

Aha: this patch on top of yours seems to fix it, at least for the test
I've been debugging.

Calling gcc_delete on something seems to delete it without removing the
finalizer, leaving the finalizer around to run on whatever the memory
eventually gets reused for, leading to segfaults:

diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 4b9c4db4ee9..64d314321cb 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -1372,8 +1372,6 @@ unsigned int pass_ipa_modref::execute (function *)
 void
 ipa_modref_c_finalize ()
 {
-  if (summaries)
-    ggc_delete (summaries);
   summaries = NULL;
 }
Jan Hubicka Sept. 22, 2020, 8:23 p.m. UTC | #22
> On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> > David,
> > with jit I get the following:
> > /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> > nonrepresentable section on output
> > collect2: error: ld returned 1 exit status
> > make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1]
> > Error
> > 
> > Is there a fix/workaround?
> 
> I don't recognize that specific error, but googling suggests it may
> relate to position-independent code.
> 
> Are you configuring with --enable-host-shared ?  This is needed when
> enabling "jit" in --enable-languages (but slows down the compiler by a
> few percent, which is why "jit" isn't in "all").

Yes --enable-languages=all,jit --enable-host-shared.
I suppose my binutils may show the age, I will check that tomorrow. It
looks like weird error.
> 
> 
> > Patch I am trying to test/debug is attached, it fixes the selftest
> > issue
> > and the destructor.
> 
> Thanks.
> 
> Sadly it doesn't fix the jit crashes, which are now in bugzilla (as PR
> jit/97169).
> 
> Without the patch, the jit testcases crash at the end of the 1st in-
> process iteration, in the dtor for the the new pass.
> 
> With the patch the jit testcases crash inside the 3rd in-process
> iteration, invoking a modref_summaries finalizer at whichever GC-
> collection point happens first, I think, where the modref_summaries *
> seems to be pointing at corrupt data:
> 
> (gdb) p *(modref_summaries *)p
> $2 = {<fast_function_summary<modref_summary*, va_gc>> =
> {<function_summary_base<modref_summary>> = {
>       _vptr.function_summary_base = 0x200000001,
> m_symtab_insertion_hook = 0x1, m_symtab_removal_hook = 0x100000004, 
>       m_symtab_duplication_hook = 0x0, m_symtab = 0x644210,
> m_insertion_enabled = 112, m_allocator = {m_allocator = {
>           m_name = 0x0, m_id = 0, m_elts_per_block = 1,
> m_returned_free_list = 0x7afafaf01, 
>           m_virgin_free_list = 0xafafafafafaf0001 <error: Cannot access
> memory at address 0xafafafafafaf0001>, 
>           m_virgin_elts_remaining = 0, m_elts_allocated =
> 140737080343888, m_elts_free = 0, m_blocks_allocated = 0, 
>           m_block_list = 0x0, m_elt_size = 6517120, m_size = 13,
> m_initialized = false, m_location = {
>             m_filename = 0x0, m_function = 0x0, m_line = 1, m_origin =
> 2947481856, m_ggc = false}}}}, 
>     m_vector = 0x0}, ipa = false}
> 
> I think this latter crash may be a pre-existing bug in how the jit
> interacts with gc finalizers.  I think the finalizers are accumulating
> from in-process run to run, leading to chaos, but I need to debug it
> some more to be sure.  Alternatively, is there a way that a finalizer
> is being registered, and then the object is somehow clobbered without
> the finalizer being unregistered from the vec of finalizers?

It looks like released memory. I saw similar problem with ggc calling
finalizer in "wrong" order.  David's modref tree has two layers and
destructor of one was freeing the anohter that is good if you destroy
first the outer type, but not good if you do it in wrong order.
I will try to reproduce it.  I also plan to turn the classes to pods and
put them directly into the vectors.
I should not have allowed David to make a template at first place :)

Honza
> 
> Dave
>
Jan Hubicka Sept. 22, 2020, 8:24 p.m. UTC | #23
> On Tue, 2020-09-22 at 16:13 -0400, David Malcolm wrote:
> > On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> > > David,
> > > with jit I get the following:
> > > /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> > > nonrepresentable section on output
> > > collect2: error: ld returned 1 exit status
> > > make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1]
> > > Error
> > > 
> > > Is there a fix/workaround?
> > 
> > I don't recognize that specific error, but googling suggests it may
> > relate to position-independent code.
> > 
> > Are you configuring with --enable-host-shared ?  This is needed when
> > enabling "jit" in --enable-languages (but slows down the compiler by
> > a
> > few percent, which is why "jit" isn't in "all").
> > 
> > 
> > > Patch I am trying to test/debug is attached, it fixes the selftest
> > > issue
> > > and the destructor.
> > 
> > Thanks.
> > 
> > Sadly it doesn't fix the jit crashes, which are now in bugzilla (as
> > PR
> > jit/97169).
> > 
> > Without the patch, the jit testcases crash at the end of the 1st in-
> > process iteration, in the dtor for the the new pass.
> > 
> > With the patch the jit testcases crash inside the 3rd in-process
> > iteration, invoking a modref_summaries finalizer at whichever GC-
> > collection point happens first, I think, where the modref_summaries *
> > seems to be pointing at corrupt data:
> > 
> > (gdb) p *(modref_summaries *)p
> > $2 = {<fast_function_summary<modref_summary*, va_gc>> =
> > {<function_summary_base<modref_summary>> = {
> >       _vptr.function_summary_base = 0x200000001,
> > m_symtab_insertion_hook = 0x1, m_symtab_removal_hook = 0x100000004, 
> >       m_symtab_duplication_hook = 0x0, m_symtab = 0x644210,
> > m_insertion_enabled = 112, m_allocator = {m_allocator = {
> >           m_name = 0x0, m_id = 0, m_elts_per_block = 1,
> > m_returned_free_list = 0x7afafaf01, 
> >           m_virgin_free_list = 0xafafafafafaf0001 <error: Cannot
> > access
> > memory at address 0xafafafafafaf0001>, 
> >           m_virgin_elts_remaining = 0, m_elts_allocated =
> > 140737080343888, m_elts_free = 0, m_blocks_allocated = 0, 
> >           m_block_list = 0x0, m_elt_size = 6517120, m_size = 13,
> > m_initialized = false, m_location = {
> >             m_filename = 0x0, m_function = 0x0, m_line = 1, m_origin
> > =
> > 2947481856, m_ggc = false}}}}, 
> >     m_vector = 0x0}, ipa = false}
> > 
> > I think this latter crash may be a pre-existing bug in how the jit
> > interacts with gc finalizers.  I think the finalizers are
> > accumulating
> > from in-process run to run, leading to chaos, but I need to debug it
> > some more to be sure.  Alternatively, is there a way that a finalizer
> > is being registered, and then the object is somehow clobbered without
> > the finalizer being unregistered from the vec of finalizers?
> 
> Aha: this patch on top of yours seems to fix it, at least for the test
> I've been debugging.
> 
> Calling gcc_delete on something seems to delete it without removing the
> finalizer, leaving the finalizer around to run on whatever the memory
> eventually gets reused for, leading to segfaults:
> 
> diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
> index 4b9c4db4ee9..64d314321cb 100644
> --- a/gcc/ipa-modref.c
> +++ b/gcc/ipa-modref.c
> @@ -1372,8 +1372,6 @@ unsigned int pass_ipa_modref::execute (function *)
>  void
>  ipa_modref_c_finalize ()
>  {
> -  if (summaries)
> -    ggc_delete (summaries);
>    summaries = NULL;
>  }

Ah, thanks.  That is very odd behaviour of delete indeed.

Honza
>  
> 
> 
>
David Malcolm Sept. 22, 2020, 8:47 p.m. UTC | #24
On Tue, 2020-09-22 at 22:24 +0200, Jan Hubicka wrote:
> > On Tue, 2020-09-22 at 16:13 -0400, David Malcolm wrote:
> > > On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> > > > David,
> > > > with jit I get the following:
> > > > /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> > > > nonrepresentable section on output
> > > > collect2: error: ld returned 1 exit status
> > > > make[3]: *** [../../gcc/jit/Make-lang.in:121:
> > > > libgccjit.so.0.0.1]
> > > > Error
> > > > 
> > > > Is there a fix/workaround?
> > > 
> > > I don't recognize that specific error, but googling suggests it
> > > may
> > > relate to position-independent code.
> > > 
> > > Are you configuring with --enable-host-shared ?  This is needed
> > > when
> > > enabling "jit" in --enable-languages (but slows down the compiler
> > > by
> > > a
> > > few percent, which is why "jit" isn't in "all").
> > > 
> > > 
> > > > Patch I am trying to test/debug is attached, it fixes the
> > > > selftest
> > > > issue
> > > > and the destructor.
> > > 
> > > Thanks.
> > > 
> > > Sadly it doesn't fix the jit crashes, which are now in bugzilla
> > > (as
> > > PR
> > > jit/97169).
> > > 
> > > Without the patch, the jit testcases crash at the end of the 1st
> > > in-
> > > process iteration, in the dtor for the the new pass.
> > > 
> > > With the patch the jit testcases crash inside the 3rd in-process
> > > iteration, invoking a modref_summaries finalizer at whichever GC-
> > > collection point happens first, I think, where the
> > > modref_summaries *
> > > seems to be pointing at corrupt data:
> > > 
> > > (gdb) p *(modref_summaries *)p
> > > $2 = {<fast_function_summary<modref_summary*, va_gc>> =
> > > {<function_summary_base<modref_summary>> = {
> > >       _vptr.function_summary_base = 0x200000001,
> > > m_symtab_insertion_hook = 0x1, m_symtab_removal_hook =
> > > 0x100000004, 
> > >       m_symtab_duplication_hook = 0x0, m_symtab = 0x644210,
> > > m_insertion_enabled = 112, m_allocator = {m_allocator = {
> > >           m_name = 0x0, m_id = 0, m_elts_per_block = 1,
> > > m_returned_free_list = 0x7afafaf01, 
> > >           m_virgin_free_list = 0xafafafafafaf0001 <error: Cannot
> > > access
> > > memory at address 0xafafafafafaf0001>, 
> > >           m_virgin_elts_remaining = 0, m_elts_allocated =
> > > 140737080343888, m_elts_free = 0, m_blocks_allocated = 0, 
> > >           m_block_list = 0x0, m_elt_size = 6517120, m_size = 13,
> > > m_initialized = false, m_location = {
> > >             m_filename = 0x0, m_function = 0x0, m_line = 1,
> > > m_origin
> > > =
> > > 2947481856, m_ggc = false}}}}, 
> > >     m_vector = 0x0}, ipa = false}
> > > 
> > > I think this latter crash may be a pre-existing bug in how the
> > > jit
> > > interacts with gc finalizers.  I think the finalizers are
> > > accumulating
> > > from in-process run to run, leading to chaos, but I need to debug
> > > it
> > > some more to be sure.  Alternatively, is there a way that a
> > > finalizer
> > > is being registered, and then the object is somehow clobbered
> > > without
> > > the finalizer being unregistered from the vec of finalizers?
> > 
> > Aha: this patch on top of yours seems to fix it, at least for the
> > test
> > I've been debugging.
> > 
> > Calling gcc_delete on something seems to delete it without removing
> > the
> > finalizer, leaving the finalizer around to run on whatever the
> > memory
> > eventually gets reused for, leading to segfaults:
> > 
> > diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
> > index 4b9c4db4ee9..64d314321cb 100644
> > --- a/gcc/ipa-modref.c
> > +++ b/gcc/ipa-modref.c
> > @@ -1372,8 +1372,6 @@ unsigned int pass_ipa_modref::execute
> > (function *)
> >  void
> >  ipa_modref_c_finalize ()
> >  {
> > -  if (summaries)
> > -    ggc_delete (summaries);
> >    summaries = NULL;
> >  }
> 
> Ah, thanks.  That is very odd behaviour of delete indeed.

Summarizing what's going on:

We have a use-after-ggc_delete happening with the finalizers code.

analyze_function has:

    summaries = new (ggc_alloc <modref_summaries> ())
		     modref_summaries (symtab);

ggc_alloc (as opposed to ggc_alloc_no_dtor) uses need_finalization_p<T>
and "sees" that the class has a nontrivial dtor, and hence it passes
finalize<T> to ggc_internal_alloc as the "f" callback.

Within ggc_internal_alloc we have:

  if (f)
    add_finalizer (result, f, s, n);

and so that callback is registered within G.finalizers - but there's
nothing stored in the object itself to track that finalizer.

Later, in ipa_modref_c_finalize, gcc_delete is called on the
mod_summaries object, but the finalizer is still registered in
G.finalizers with its address.

Later, a GC happens, and if the bit for "marked" on that old
modref_summaries object happens to be cleared (with whatever that
memory is now being used for, if anything), the finalizer callback is
called, and ~modref_summaries is called with its "this" pointing at
random bits, and we have a segfault.

This seems like a big "gotcha" in ggc_delete: it doesn't remove any
finalizer for the object, instead leaving it as a timebomb to happen in
some future collection.

Should ggc_delete remove the finalizer?  It would have to scan the
G.finalizer vecs to find them - O(N).  Alternatively, perhaps we could
stash some kind of reference to the finalizer in memory near the object
(perhaps allocating a header).

Or we could put a big warning on ggc_delete saying not to use it on
ggc_alloc-ed objects with dtors.

I'm not sure what the best approach here is.

There were only 4 places in the source tree where ggc_delete were used
before the patch, which added 4 places; maybe we should just remove
those new ggc_deletes and set the pointers to NULL and let them be
cleared as regular garbage?

Dave
David Malcolm Sept. 22, 2020, 9:17 p.m. UTC | #25
On Tue, 2020-09-22 at 22:23 +0200, Jan Hubicka wrote:
> > On Tue, 2020-09-22 at 20:39 +0200, Jan Hubicka wrote:
> > > David,
> > > with jit I get the following:
> > > /usr/local/x86_64-pc-linux-gnu/bin/ld: final link failed:
> > > nonrepresentable section on output
> > > collect2: error: ld returned 1 exit status
> > > make[3]: *** [../../gcc/jit/Make-lang.in:121: libgccjit.so.0.0.1]
> > > Error
> > > 
> > > Is there a fix/workaround?
> > 
> > I don't recognize that specific error, but googling suggests it may
> > relate to position-independent code.
> > 
> > Are you configuring with --enable-host-shared ?  This is needed
> > when
> > enabling "jit" in --enable-languages (but slows down the compiler
> > by a
> > few percent, which is why "jit" isn't in "all").
> 
> Yes --enable-languages=all,jit --enable-host-shared.
> I suppose my binutils may show the age, I will check that tomorrow.
> It
> looks like weird error.

FWIW if you do get it to build, you can reproduce the crash via running
this in builddir/gcc:

[gcc] $ PRESERVE_EXECUTABLES= \
          make check-jit \
            RUNTESTFLAGS="-v -v -v jit.exp=test-factorial.c"

[gcc] $ PATH=.:$PATH \
        LD_LIBRARY_PATH=. \
        LIBRARY_PATH=. \
          gdb --args \
            testsuite/jit/test-factorial.c.exe

(taken from 
https://gcc.gnu.org/onlinedocs/jit/internals/index.html#running-the-test-suite
)
Jan Hubicka Sept. 23, 2020, 8:31 a.m. UTC | #26
> 
> Summarizing what's going on:
> 
> We have a use-after-ggc_delete happening with the finalizers code.
> 
> analyze_function has:
> 
>     summaries = new (ggc_alloc <modref_summaries> ())
> 		     modref_summaries (symtab);
> 
> ggc_alloc (as opposed to ggc_alloc_no_dtor) uses need_finalization_p<T>
> and "sees" that the class has a nontrivial dtor, and hence it passes
> finalize<T> to ggc_internal_alloc as the "f" callback.
> 
> Within ggc_internal_alloc we have:
> 
>   if (f)
>     add_finalizer (result, f, s, n);
> 
> and so that callback is registered within G.finalizers - but there's
> nothing stored in the object itself to track that finalizer.
> 
> Later, in ipa_modref_c_finalize, gcc_delete is called on the
> mod_summaries object, but the finalizer is still registered in
> G.finalizers with its address.
> 
> Later, a GC happens, and if the bit for "marked" on that old
> modref_summaries object happens to be cleared (with whatever that
> memory is now being used for, if anything), the finalizer callback is
> called, and ~modref_summaries is called with its "this" pointing at
> random bits, and we have a segfault.
> 
> This seems like a big "gotcha" in ggc_delete: it doesn't remove any
> finalizer for the object, instead leaving it as a timebomb to happen in
> some future collection.
> 
> Should ggc_delete remove the finalizer?  It would have to scan the
> G.finalizer vecs to find them - O(N).  Alternatively, perhaps we could
> stash some kind of reference to the finalizer in memory near the object
> (perhaps allocating a header).

I think the difference between ggc_free and ggc_delete should be just
like the difference between free and delete, that is, ggc_delete should
call finalizer.

I think the mistake is that ggc_delete would work well with
ggc_alloc_no_dtor, but the patch uses ggc_alloc.  I guess we do not see
the problem in normal compilation since ggc_delete is used in the patch
3 times for objects with no destructor and once for object with a
destructor but only at the end of compilation when ggc is not run w/o
libjit.
> 
> Or we could put a big warning on ggc_delete saying not to use it on
> ggc_alloc-ed objects with dtors.

I do not think it is reasonable for ggc_delete to walk all finalizers,
so perhaps we just want a sanity check with --enable-checking that
things are not mixed up?
That is, maintain on-side hash of finalizers introduced by ggc_alloc and
look it up in ggc_delete, assert on a spot with a comment saying how
things are intended to work?
> 
> I'm not sure what the best approach here is.
> 
> There were only 4 places in the source tree where ggc_delete were used
> before the patch, which added 4 places; maybe we should just remove
> those new ggc_deletes and set the pointers to NULL and let them be
> cleared as regular garbage?

For modref we really need to release things explicitly since it runs at
WPA time and I am trying to maintain the fact that WPA do not need
ggc_collect runs: they have large memory footprint and it is easy to
explicitly manage all memory used by symtab/optimization summaries.

Of course I can reorganize the code to not have a destructor as well
(which is not very ++-sy).

In fact since the templates have hand written markers anyway, I was
htinking of moving them off ggc memory and just walk to discover the
tree pointers in them.

Honza
> 
> Dave
>
Jan Hubicka Sept. 24, 2020, 6:30 a.m. UTC | #27
Hi,
This patch makes ggc_delete to be paired with ggc_alloc_no_dtor.
I copy same scheme as used by Martin in ipa-fnsummary, that is creating a
static member function create_ggc hidding the ugly bits and using it in
ipa-modref.c.

I also noticed that modref-tree leaks memory on destruction/collapse method and
fixed that.

Bootstrapped/regtested x86_64-linux.

gcc/ChangeLog:

2020-09-24  Jan Hubicka  <hubicka@ucw.cz>

	* ipa-modref-tree.h (modref_base::collapse): Release memory.
	(modref_tree::create_ggc): New member function.
	(modref_tree::colapse): Release memory.
	(modref_tree::~modref_tree): New destructor.
	* ipa-modref.c (modref_summaries::create_ggc): New function.
	(analyze_function): Use create_ggc.
	(modref_summaries::duplicate): Likewise.
	(read_modref_records): Likewise.
	(modref_read): Likewise.

diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
index 3bdd3058aa1..82e959a7d46 100644
--- a/gcc/ipa-modref-tree.h
+++ b/gcc/ipa-modref-tree.h
@@ -95,7 +95,15 @@ struct GTY((user)) modref_base_node
 
   void collapse ()
   {
-    vec_free (refs);
+    size_t i;
+    modref_ref_node <T> *r;
+
+    if (refs)
+      {
+	FOR_EACH_VEC_SAFE_ELT (refs, i, r)
+	  ggc_free (r);
+	vec_free (refs);
+      }
     refs = NULL;
     every_ref = true;
   }
@@ -214,12 +222,36 @@ struct GTY((user)) modref_tree
     return NULL;
   }
 
+  /* Return ggc allocated instance.  We explicitly call destructors via
+     ggc_delete and do not want finalizers to be registered and
+     called at the garbage collection time.  */
+  static modref_tree<T> *create_ggc (size_t max_bases, size_t max_refs)
+  {
+    return new (ggc_alloc_no_dtor<modref_tree<T>> ())
+	 modref_tree<T> (max_bases, max_refs);
+  }
+
   void collapse ()
   {
-    vec_free (bases);
+    size_t i;
+    modref_base_node <T> *n;
+
+    if (bases)
+      {
+	FOR_EACH_VEC_SAFE_ELT (bases, i, n)
+	  {
+	    n->collapse ();
+	    ggc_free (n);
+	  }
+	vec_free (bases);
+      }
     bases = NULL;
     every_base = true;
   }
+  ~modref_tree ()
+  {
+    collapse ();
+  }
 };
 
 void modref_c_tests ();
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 9cc90565891..43545c1fb09 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -84,6 +84,11 @@ public:
      ipa-modref pass execution needs to be analyzed in IPA mode while all
      other insertions leads to normal analysis.  */
   bool ipa;
+  static modref_summaries *create_ggc (symbol_table *symtab)
+  {
+    return new (ggc_alloc_no_dtor<modref_summaries> ())
+	     modref_summaries (symtab);
+  }
 };
 
 /* Global variable holding all modref summaries.  */
@@ -608,8 +613,7 @@ analyze_function (function *f, bool ipa)
 
   /* Initialize the summary.  */
   if (!summaries)
-    summaries = new (ggc_alloc <modref_summaries> ())
-		     modref_summaries (symtab);
+    summaries = modref_summaries::create_ggc (symtab);
   else /* Remove existing summary if we are re-running the pass.  */
     summaries->remove (cgraph_node::get (f->decl));
 
@@ -633,28 +637,22 @@ analyze_function (function *f, bool ipa)
   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);
+      summary->loads = modref_records::create_ggc (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);
+      summary->stores = modref_records::create_ggc (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);
+      summary->loads_lto = modref_records_lto::create_ggc
+				 (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->stores_lto = modref_records_lto::create_ggc
+				 (param_modref_max_bases,
+				  param_modref_max_refs);
     }
   summary->finished = false;
   int ecf_flags = flags_from_decl_or_type (current_function_decl);
@@ -730,34 +728,30 @@ modref_summaries::duplicate (cgraph_node *, cgraph_node *,
   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 = modref_records::create_ggc
+			    (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 = modref_records::create_ggc
+			    (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 = modref_records_lto::create_ggc
+			    (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 = modref_records_lto::create_ggc
+			    (src_data->loads_lto->max_bases,
+			     src_data->loads_lto->max_refs);
       dst_data->loads_lto->merge (src_data->loads_lto);
     }
 }
@@ -838,11 +832,9 @@ read_modref_records (lto_input_block *ib, struct data_in *data_in,
   /* 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);
+    *lto_ret = modref_records_lto::create_ggc (max_bases, max_refs);
   else
-    *nolto_ret = new (ggc_alloc <modref_records> ()) modref_records
-			      (max_bases, max_refs);
+    *nolto_ret = modref_records::create_ggc (max_bases, max_refs);
 
   size_t every_base = streamer_read_uhwi (ib);
   size_t nbase = streamer_read_uhwi (ib);
@@ -1048,8 +1040,7 @@ modref_read (void)
   unsigned int j = 0;
 
   if (!summaries)
-    summaries = new (ggc_alloc <modref_summaries> ())
-		     modref_summaries (symtab);
+    summaries = modref_summaries::create_ggc (symtab);
   ((modref_summaries *)summaries)->ipa = true;
 
   while ((file_data = file_data_vec[j++]))
David Malcolm Sept. 25, 2020, 2:42 p.m. UTC | #28
On Thu, 2020-09-24 at 08:30 +0200, Jan Hubicka wrote:
> Hi,
> This patch makes ggc_delete to be paired with ggc_alloc_no_dtor.
> I copy same scheme as used by Martin in ipa-fnsummary, that is
> creating a
> static member function create_ggc hidding the ugly bits and using it
> in
> ipa-modref.c.
> 
> I also noticed that modref-tree leaks memory on destruction/collapse
> method and
> fixed that.
> 
> Bootstrapped/regtested x86_64-linux.

It looks like you committed this as
c9da53d6987af5f8ff68b58dd76a9fbc900a6a21.

This appears to fix the issues seen with the GC with jit
(PR jit/97169).

With the previous commit, jit.sum had:

    # of expected passes            5751
    # of unexpected failures        64
    # of unresolved testcases       1

with a number of SIGSEGV showing up in the FAIL reports, whereas with
c9da53d6987af5f8ff68b58dd76a9fbc900a6a21, jit.sum is restored to:

    # of expected passes            10854

Thanks!
Dave
Jan Hubicka Sept. 27, 2020, 9:37 p.m. UTC | #29
> On 9/21/20 10:10 AM, Richard Biener wrote:
> 
> > > I see, so you would expect call to alsize to initialize things in
> > > array15_unkonwn type?  That would work too.
> > Yes, that's my expectation.  But let's see what fortran folks say.
> 
> RFC patch attached; I think the following should work, but I am not
> sure whether I missed something.
> 
> I wonder what to do about
>   '!GCC$ NO_ARG_CHECK :: x
> but that seems to work fine (creates void* type) and as it only
> permits assumed size or scalar variables, the descriptor issue
> does not occur.
> 
> Thoughts?

Hi,
with somewhat improved ipa-modref and your patch i get following
failures:
FAIL: gfortran.dg/assumed_type_2.f90   -O   scan-tree-dump-times original "sub_array_assumed \\\\(\\\\(struct t1.0:. .\\\\) parm" 1
FAIL: gfortran.dg/assumed_type_9.f90   -O2  execution test
FAIL: gfortran.dg/assumed_type_9.f90   -Os  execution test
FAIL: gfortran.dg/class_allocate_20.f90   -O2  execution test
FAIL: gfortran.dg/class_allocate_20.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
FAIL: gfortran.dg/class_allocate_20.f90   -O3 -g  execution test
FAIL: gfortran.dg/class_allocate_20.f90   -Os  execution test
FAIL: gfortran.dg/finalize_25.f90   -O2  execution test
FAIL: gfortran.dg/finalize_25.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
FAIL: gfortran.dg/finalize_25.f90   -O3 -g  execution test
FAIL: gfortran.dg/finalize_25.f90   -Os  execution test
FAIL: gfortran.dg/no_arg_check_2.f90   -O   scan-tree-dump-times original "sub_array_assumed \\\\(\\\\(struct t1.0:. .\\\\) parm" 1
WARNING: gfortran.dg/pdt_14.f03   -O2  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O2  execution test
WARNING: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
WARNING: gfortran.dg/pdt_14.f03   -O3 -g  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O3 -g  execution test
WARNING: gfortran.dg/pdt_14.f03   -Os  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -Os  execution test
FAIL: gfortran.dg/sizeof_4.f90   -O0  execution test
FAIL: gfortran.dg/sizeof_4.f90   -O1  execution test
FAIL: gfortran.dg/sizeof_4.f90   -O2  execution test
FAIL: gfortran.dg/sizeof_4.f90   -O3 -fomit-frame-pointer -funroll-loops
-fpeel-loops -ftracer -finline-functions  execution test
FAIL: gfortran.dg/sizeof_4.f90   -O3 -g  execution test
FAIL: gfortran.dg/sizeof_4.f90   -Os  execution test

With asumed_type_9.f90 we get:
__final_test_T4 (struct array15_t4 & restrict array, integer(kind=8) byte_stride, logical(kind=1) fini_coarray)

called as:

struct array00_t1 decl_uidesc.19
....
__final_test_T4 (&desc.19, 24, 0);

and we optimize out initializer of desc.19 since it is TBAA
incompatible (so same problem as with assumed type but this time the
consumer descriptor is not universal; just different).


With finalize_25 I see:

__final_gn_Sl (struct array15_sl & restrict array, integer(kind=8) byte_stride, logical(kind=1) fini_coarray)

called as:

struct array00_sl desc.20
...
__final_gn_Sl (&desc.20, 64, 0);


With pdf14_f03 I get disambiguation 
ipa-modref: in main/8, call to push_8/6 does not clobber __vtab_link_module_Pdtlink_8._deallocate 14->13
so this seems different and I am not quite sure what is wrong here.

FAIL: gfortran.dg/sizeof_4.f90   -O1  execution test

actually goes away with reverting your patch.

Honza
> 
> Tobias
> 

> gcc/fortran/ChangeLog:
> 
> 	* trans-array.c (gfc_conv_expr_descriptor):
> 	(gfc_conv_array_parameter):
> 	* trans-array.h (gfc_conv_expr_descriptor):
> 
>  gcc/fortran/trans-array.c | 15 +++++++++------
>  gcc/fortran/trans-array.h |  3 ++-
>  2 files changed, 11 insertions(+), 7 deletions(-)
> 
> diff --git a/gcc/fortran/trans-array.c b/gcc/fortran/trans-array.c
> index 6566c47d4ae..a5d1b477a0a 100644
> --- a/gcc/fortran/trans-array.c
> +++ b/gcc/fortran/trans-array.c
> @@ -7216,7 +7216,7 @@ walk_coarray (gfc_expr *e)
>     function call.  */
>  
>  void
> -gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr)
> +gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr, bool want_assumed_type)
>  {
>    gfc_ss *ss;
>    gfc_ss_type ss_type;
> @@ -7611,7 +7611,9 @@ gfc_conv_expr_descriptor (gfc_se *se, gfc_expr *expr)
>        else
>  	{
>  	  /* Otherwise make a new one.  */
> -	  if (expr->ts.type == BT_CHARACTER && expr->ts.deferred)
> +	  if (want_assumed_type)
> +	    parmtype = ptr_type_node;
> +	  else if (expr->ts.type == BT_CHARACTER && expr->ts.deferred)
>  	    parmtype = gfc_typenode_for_spec (&expr->ts);
>  	  else
>  	    parmtype = gfc_get_element_type (TREE_TYPE (desc));
> @@ -7950,7 +7952,8 @@ gfc_conv_array_parameter (gfc_se * se, gfc_expr * expr, bool g77,
>          {
>  	  if (sym->attr.dummy || sym->attr.result)
>  	    {
> -	      gfc_conv_expr_descriptor (se, expr);
> +	      gfc_conv_expr_descriptor (se, expr,
> +					fsym && fsym->ts.type == BT_ASSUMED);
>  	      tmp = se->expr;
>  	    }
>  	  if (size)
> @@ -8014,7 +8017,7 @@ gfc_conv_array_parameter (gfc_se * se, gfc_expr * expr, bool g77,
>  
>    if (no_pack || array_constructor || good_allocatable || ultimate_alloc_comp)
>      {
> -      gfc_conv_expr_descriptor (se, expr);
> +      gfc_conv_expr_descriptor (se, expr, fsym && fsym->ts.type == BT_ASSUMED);
>        /* Deallocate the allocatable components of structures that are
>  	 not variable.  */
>        if ((expr->ts.type == BT_DERIVED || expr->ts.type == BT_CLASS)
> @@ -8037,7 +8040,7 @@ gfc_conv_array_parameter (gfc_se * se, gfc_expr * expr, bool g77,
>    if (this_array_result)
>      {
>        /* Result of the enclosing function.  */
> -      gfc_conv_expr_descriptor (se, expr);
> +      gfc_conv_expr_descriptor (se, expr, fsym && fsym->ts.type == BT_ASSUMED);
>        if (size)
>  	array_parameter_size (se->expr, expr, size);
>        se->expr = gfc_build_addr_expr (NULL_TREE, se->expr);
> @@ -8053,7 +8056,7 @@ gfc_conv_array_parameter (gfc_se * se, gfc_expr * expr, bool g77,
>      {
>        /* Every other type of array.  */
>        se->want_pointer = 1;
> -      gfc_conv_expr_descriptor (se, expr);
> +      gfc_conv_expr_descriptor (se, expr, fsym && fsym->ts.type == BT_ASSUMED);
>  
>        if (size)
>  	array_parameter_size (build_fold_indirect_ref_loc (input_location,
> diff --git a/gcc/fortran/trans-array.h b/gcc/fortran/trans-array.h
> index e561605aaed..be3b1b79860 100644
> --- a/gcc/fortran/trans-array.h
> +++ b/gcc/fortran/trans-array.h
> @@ -143,7 +143,8 @@ void gfc_get_dataptr_offset (stmtblock_t*, tree, tree, tree, bool, gfc_expr*);
>  /* Obtain the span of an array.  */
>  tree gfc_get_array_span (tree, gfc_expr *);
>  /* Evaluate an array expression.  */
> -void gfc_conv_expr_descriptor (gfc_se *, gfc_expr *);
> +void gfc_conv_expr_descriptor (gfc_se *, gfc_expr *,
> +			       bool want_assumed_type = false);
>  /* Convert an array for passing as an actual function parameter.  */
>  void gfc_conv_array_parameter (gfc_se *, gfc_expr *, bool,
>  			       const gfc_symbol *, const char *, tree *);
Jan Hubicka Oct. 16, 2020, 7:56 a.m. UTC | #30
Hi,
I am slowly getting finished with the fn spec changes on trunk and then
would like to proceed with modref.  Sadly  still get the assumed_type
failuere and in addition to that:
FAIL: gfortran.dg/finalize_25.f90   -O2  execution test
FAIL: gfortran.dg/finalize_25.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
FAIL: gfortran.dg/finalize_25.f90   -O3 -g  execution test
FAIL: gfortran.dg/finalize_25.f90   -Os  execution test
WARNING: gfortran.dg/pdt_14.f03   -O2  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O2  execution test
WARNING: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
WARNING: gfortran.dg/pdt_14.f03   -O3 -g  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -O3 -g  execution test
WARNING: gfortran.dg/pdt_14.f03   -Os  execution test program timed out.
FAIL: gfortran.dg/pdt_14.f03   -Os  execution test

I wonder if there is any chance to get Fortran FE fixed here?

Honza
Richard Biener Oct. 16, 2020, 8:05 a.m. UTC | #31
On Fri, 16 Oct 2020, Jan Hubicka wrote:

> Hi,
> I am slowly getting finished with the fn spec changes on trunk and then
> would like to proceed with modref.  Sadly  still get the assumed_type
> failuere and in addition to that:
> FAIL: gfortran.dg/finalize_25.f90   -O2  execution test
> FAIL: gfortran.dg/finalize_25.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> FAIL: gfortran.dg/finalize_25.f90   -O3 -g  execution test
> FAIL: gfortran.dg/finalize_25.f90   -Os  execution test
> WARNING: gfortran.dg/pdt_14.f03   -O2  execution test program timed out.
> FAIL: gfortran.dg/pdt_14.f03   -O2  execution test
> WARNING: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> program timed out.
> FAIL: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> WARNING: gfortran.dg/pdt_14.f03   -O3 -g  execution test program timed out.
> FAIL: gfortran.dg/pdt_14.f03   -O3 -g  execution test
> WARNING: gfortran.dg/pdt_14.f03   -Os  execution test program timed out.
> FAIL: gfortran.dg/pdt_14.f03   -Os  execution test
> 
> I wonder if there is any chance to get Fortran FE fixed here?

OK, I'll try doing a little surgery in the FE today, coming up with
a little refactoring and a fix along your original one that allows
for a better one by FE folks.

Richard.
Richard Biener Oct. 16, 2020, 9:20 a.m. UTC | #32
On Fri, 16 Oct 2020, Richard Biener wrote:

> On Fri, 16 Oct 2020, Jan Hubicka wrote:
> 
> > Hi,
> > I am slowly getting finished with the fn spec changes on trunk and then
> > would like to proceed with modref.  Sadly  still get the assumed_type
> > failuere and in addition to that:
> > FAIL: gfortran.dg/finalize_25.f90   -O2  execution test
> > FAIL: gfortran.dg/finalize_25.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > FAIL: gfortran.dg/finalize_25.f90   -O3 -g  execution test
> > FAIL: gfortran.dg/finalize_25.f90   -Os  execution test
> > WARNING: gfortran.dg/pdt_14.f03   -O2  execution test program timed out.
> > FAIL: gfortran.dg/pdt_14.f03   -O2  execution test
> > WARNING: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > program timed out.
> > FAIL: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > WARNING: gfortran.dg/pdt_14.f03   -O3 -g  execution test program timed out.
> > FAIL: gfortran.dg/pdt_14.f03   -O3 -g  execution test
> > WARNING: gfortran.dg/pdt_14.f03   -Os  execution test program timed out.
> > FAIL: gfortran.dg/pdt_14.f03   -Os  execution test
> > 
> > I wonder if there is any chance to get Fortran FE fixed here?
> 
> OK, I'll try doing a little surgery in the FE today, coming up with
> a little refactoring and a fix along your original one that allows
> for a better one by FE folks.

So I've sent a refactoring patch improving the tree building code.

But now trying to fix the actual issue with the idea to perform
accesses indirectly via a descriptor with dim[] type I see that
the coarray 'token' field is appended to descriptors conditional
on -fcoarray and that this field makes the dim[] array no longer
trailing - which means the offset of the 'token' field depends
on the rank of the array.

The dim[] field is even optional when dim + codimen == 0 and that
case indeed happens (ah, via get_scalar_to_descriptor_type).
So much for re-using this combo ;)

I suppose we can compensate for this by dynamically computing the
offset of the 'token' field but then it's not obvious to me
where the total 'rank' is stored inside the descriptor or how
the 'token' field is accessed for assumed-shape arrays - the
current method by simple field chaining definitely won't work.

Anyway, I'll try to deal with all this by just adjusting the TBAA
type but not the access type leaving that alone.

IMHO the cleanest way would be to swap the CAF token field and
the dim[] field (which is an ABI change for -fcoarray)

Richard.
Richard Biener Oct. 16, 2020, 10:42 a.m. UTC | #33
On Fri, 16 Oct 2020, Richard Biener wrote:

> On Fri, 16 Oct 2020, Richard Biener wrote:
> 
> > On Fri, 16 Oct 2020, Jan Hubicka wrote:
> > 
> > > Hi,
> > > I am slowly getting finished with the fn spec changes on trunk and then
> > > would like to proceed with modref.  Sadly  still get the assumed_type
> > > failuere and in addition to that:
> > > FAIL: gfortran.dg/finalize_25.f90   -O2  execution test
> > > FAIL: gfortran.dg/finalize_25.f90   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > > FAIL: gfortran.dg/finalize_25.f90   -O3 -g  execution test
> > > FAIL: gfortran.dg/finalize_25.f90   -Os  execution test
> > > WARNING: gfortran.dg/pdt_14.f03   -O2  execution test program timed out.
> > > FAIL: gfortran.dg/pdt_14.f03   -O2  execution test
> > > WARNING: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > > program timed out.
> > > FAIL: gfortran.dg/pdt_14.f03   -O3 -fomit-frame-pointer -funroll-loops -fpeel-loops -ftracer -finline-functions  execution test
> > > WARNING: gfortran.dg/pdt_14.f03   -O3 -g  execution test program timed out.
> > > FAIL: gfortran.dg/pdt_14.f03   -O3 -g  execution test
> > > WARNING: gfortran.dg/pdt_14.f03   -Os  execution test program timed out.
> > > FAIL: gfortran.dg/pdt_14.f03   -Os  execution test
> > > 
> > > I wonder if there is any chance to get Fortran FE fixed here?
> > 
> > OK, I'll try doing a little surgery in the FE today, coming up with
> > a little refactoring and a fix along your original one that allows
> > for a better one by FE folks.
> 
> So I've sent a refactoring patch improving the tree building code.
> 
> But now trying to fix the actual issue with the idea to perform
> accesses indirectly via a descriptor with dim[] type I see that
> the coarray 'token' field is appended to descriptors conditional
> on -fcoarray and that this field makes the dim[] array no longer
> trailing - which means the offset of the 'token' field depends
> on the rank of the array.
> 
> The dim[] field is even optional when dim + codimen == 0 and that
> case indeed happens (ah, via get_scalar_to_descriptor_type).
> So much for re-using this combo ;)
> 
> I suppose we can compensate for this by dynamically computing the
> offset of the 'token' field but then it's not obvious to me
> where the total 'rank' is stored inside the descriptor or how
> the 'token' field is accessed for assumed-shape arrays - the
> current method by simple field chaining definitely won't work.
> 
> Anyway, I'll try to deal with all this by just adjusting the TBAA
> type but not the access type leaving that alone.
> 
> IMHO the cleanest way would be to swap the CAF token field and
> the dim[] field (which is an ABI change for -fcoarray)

OK, so I tried

diff --git a/gcc/fortran/trans-array.c b/gcc/fortran/trans-array.c
index f30a2f75701..29381f6756e 100644
--- a/gcc/fortran/trans-array.c
+++ b/gcc/fortran/trans-array.c
@@ -142,6 +142,17 @@ gfc_get_descriptor_field (tree desc, unsigned 
field_idx)
   tree field = gfc_advance_chain (TYPE_FIELDS (type), field_idx);
   gcc_assert (field != NULL_TREE);
 
+  /* We need to use a consistent descriptor type across all accesses
+     which should be possible by indirectly accessing the descriptor
+     via a type with a trailing flexible array dim[] member if there
+     were not the CAF token field after it.  So for now ensure correct
+     TBAA behavior by explicitely specifying a common TBAA type - any
+     descriptor-like type is OK here.  */
+  tree tbaa_type
+    = build_pointer_type (gfc_get_array_descriptor_base (4, 2, false));
+  desc = fold_build2_loc (input_location, MEM_REF, type,
+                         build_fold_addr_expr_loc (input_location, desc),
+                         build_int_cst (tbaa_type, 0));
   return fold_build3_loc (input_location, COMPONENT_REF, TREE_TYPE 
(field),
                          desc, field, NULL_TREE);
 }

which first reveals two spots missed by the sent refactoring and
second exposes the fact that we use aggregate copies to copy
array descritors or aggregates with array descriptor typed fields.
There's currently no way to assign a custom TBAA type to aggregate
fields which means that the only choice is to fix things at the
above central place is using alias-set zero (and hoping for
the embedded/aggregate copied places to match up).  In the end
this means that the optimal approach is to adjust only those
accesses where we do not know the actual descriptor type but
I expect those to be spread out?  Eventually those cases
could be identified above?

Meanwhile the refactoring patch still applies of course,
amended by adjustments to gfc_conv_descriptor_data_addr
and gfc_conv_descriptor_data_set.

Unfortunately punning the descriptor like above causes
numerous testsuite FAILs due to orignal dump scannings no
longer matching :/  So I'm hoping for a hint as to how to
identify problematical descriptor types to reduce that
noise ...

Richard.
Bernhard Reutner-Fischer Oct. 23, 2020, 9:54 a.m. UTC | #34
On 16 October 2020 11:20:23 CEST, Richard Biener <rguenther@suse.de> wrote:

>IMHO the cleanest way would be to swap the CAF token field and
>the dim[] field (which is an ABI change for -fcoarray)

I think coarrays are new anyway so I suppose an ABI break is fine?
Andre Vehreschild Oct. 23, 2020, 10:05 a.m. UTC | #35
On Fri, 23 Oct 2020 11:54:08 +0200
Bernhard Reutner-Fischer via Fortran <fortran@gcc.gnu.org> wrote:

> On 16 October 2020 11:20:23 CEST, Richard Biener <rguenther@suse.de> wrote:
>
> >IMHO the cleanest way would be to swap the CAF token field and
> >the dim[] field (which is an ABI change for -fcoarray)
>
> I think coarrays are new anyway so I suppose an ABI break is fine?

Coarrays are in the standard since Fortran 2008. So what I'd rather not call
them new being there for more than 10 years...

The descriptor is used in the opencoarrays library, too. And has to be kept in
sync. So when the ABI break is reasonable it's fine.

--
Andre Vehreschild * Email: vehre ad gmx dot de
Jan Hubicka Oct. 29, 2020, 3:14 p.m. UTC | #36
> Hi,
> this is patch I am using to fix the assumed_alias_type.f90 failure by
> simply arranging alias set 0 for the problematic array descriptor.
> 
> I am not sure this is the best option, but I suppose it is better than
> setting all array descritors to have same canonical type (as done by
> LTO)?
> 
Hi,
here is updated patch which used TYPELESS_STORAGE instead of alias set
0, so it is LTO safe.  Unforunately I also had to enable it for all
array descriptors otherwise I still get misopitmizations with modref
extended to handle bulitins, for example:

FAIL: gfortran.dg/class_array_20.f03   -Os  execution test
FAIL: gfortran.dg/coindexed_1.f90   -O2  execution test
FAIL: gfortran.dg/coindexed_1.f90   -O3 -fomit-frame-pointer
FAIL: gfortran.dg/coindexed_1.f90   -O3 -g  execution test

This is not a perfect solution (we really want to track array
descriptors), but it fixes wrong code and would let me to move forward.
Is it OK for mainline?

With extended modref I still get infinite loop on pdt_14 testcase.
ipa-modref only performs disambiguation on
__vtab_link_module_Pdtlink_8._deallocate this global variable is
readonly (and is detected as such with LTO) so it must be just
uncovering some latent problem there.  I am however not familiar enough
with Fortran to tell what is wrong there.

The testcase fail different way with -flto for me.

Bootstrapped/regtested x86_64-linux, OK?

Honza

	* trans-types.c: Include alias.h
	(gfc_get_array_type_bounds): Set typeless storage.
diff --git a/gcc/fortran/trans-types.c b/gcc/fortran/trans-types.c
index b15ea667411..b7129dcbe6d 100644
--- a/gcc/fortran/trans-types.c
+++ b/gcc/fortran/trans-types.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "trans-array.h"
 #include "dwarf2out.h"	/* For struct array_descr_info.  */
 #include "attribs.h"
+#include "alias.h"
 
 
 #if (GFC_MAX_DIMENSIONS < 10)
@@ -1903,6 +1904,10 @@ gfc_get_array_type_bounds (tree etype, int dimen, int codimen, tree * lbound,
   base_type = gfc_get_array_descriptor_base (dimen, codimen, false);
   TYPE_CANONICAL (fat_type) = base_type;
   TYPE_STUB_DECL (fat_type) = TYPE_STUB_DECL (base_type);
+  /* Arrays of unknown type must alias with all array descriptors.  */
+  TYPE_TYPELESS_STORAGE (base_type) = 1;
+  TYPE_TYPELESS_STORAGE (fat_type) = 1;
+  gcc_checking_assert (!get_alias_set (base_type) && !get_alias_set (fat_type));
 
   tmp = TYPE_NAME (etype);
   if (tmp && TREE_CODE (tmp) == TYPE_DECL)
Richard Biener Oct. 30, 2020, 8:16 a.m. UTC | #37
On Thu, 29 Oct 2020, Jan Hubicka wrote:

> > Hi,
> > this is patch I am using to fix the assumed_alias_type.f90 failure by
> > simply arranging alias set 0 for the problematic array descriptor.
> > 
> > I am not sure this is the best option, but I suppose it is better than
> > setting all array descritors to have same canonical type (as done by
> > LTO)?
> > 
> Hi,
> here is updated patch which used TYPELESS_STORAGE instead of alias set
> 0, so it is LTO safe.  Unforunately I also had to enable it for all
> array descriptors otherwise I still get misopitmizations with modref
> extended to handle bulitins, for example:
> 
> FAIL: gfortran.dg/class_array_20.f03   -Os  execution test
> FAIL: gfortran.dg/coindexed_1.f90   -O2  execution test
> FAIL: gfortran.dg/coindexed_1.f90   -O3 -fomit-frame-pointer
> FAIL: gfortran.dg/coindexed_1.f90   -O3 -g  execution test
> 
> This is not a perfect solution (we really want to track array
> descriptors), but it fixes wrong code and would let me to move forward.
> Is it OK for mainline?
> 
> With extended modref I still get infinite loop on pdt_14 testcase.
> ipa-modref only performs disambiguation on
> __vtab_link_module_Pdtlink_8._deallocate this global variable is
> readonly (and is detected as such with LTO) so it must be just
> uncovering some latent problem there.  I am however not familiar enough
> with Fortran to tell what is wrong there.
> 
> The testcase fail different way with -flto for me.
> 
> Bootstrapped/regtested x86_64-linux, OK?

OK.

Thanks,
Richard.

> Honza
> 
> 	* trans-types.c: Include alias.h
> 	(gfc_get_array_type_bounds): Set typeless storage.
> diff --git a/gcc/fortran/trans-types.c b/gcc/fortran/trans-types.c
> index b15ea667411..b7129dcbe6d 100644
> --- a/gcc/fortran/trans-types.c
> +++ b/gcc/fortran/trans-types.c
> @@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "trans-array.h"
>  #include "dwarf2out.h"	/* For struct array_descr_info.  */
>  #include "attribs.h"
> +#include "alias.h"
>  
>  
>  #if (GFC_MAX_DIMENSIONS < 10)
> @@ -1903,6 +1904,10 @@ gfc_get_array_type_bounds (tree etype, int dimen, int codimen, tree * lbound,
>    base_type = gfc_get_array_descriptor_base (dimen, codimen, false);
>    TYPE_CANONICAL (fat_type) = base_type;
>    TYPE_STUB_DECL (fat_type) = TYPE_STUB_DECL (base_type);
> +  /* Arrays of unknown type must alias with all array descriptors.  */
> +  TYPE_TYPELESS_STORAGE (base_type) = 1;
> +  TYPE_TYPELESS_STORAGE (fat_type) = 1;
> +  gcc_checking_assert (!get_alias_set (base_type) && !get_alias_set (fat_type));
>  
>    tmp = TYPE_NAME (etype);
>    if (tmp && TREE_CODE (tmp) == TYPE_DECL)
>
diff mbox series

Patch

diff --git a/ChangeLog b/ChangeLog
index 664c018b13c..58a7bbbe643 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@ 
+2019-11-16  David Čepelík  <d@dcepelik.cz>
+
+        * Makefile.in: Add targets for new source modules.
+        * gengtype.c (open_base_files): Register new source files.
+        * ipa-modref.c: New file.
+        * ipa-modref.h: New file.
+        * lto-section-in.c: Add LTO section for the modref pass.
+        * lto-streamer.h (enum lto_section_type): Likewise.
+        * modref-tree.c: New file.
+        * modref-tree.h: New file.
+        * params.opt: Add params (unused so far) related to the modref pass.
+        * passes.def: Register new passes.
+        * timevar.def (TV_IPA_MODREF): Add TVs for new pass.
+        (TV_TREE_MODREF): Likewise.
+        * tree-pass.h (make_pass_modref): New helper (GIMPLE pass).
+        (make_pass_ipa_modref): New helper (IPA pass).
+        * tree-ssa-alias.c (modref_may_conflict): Use collected information.
+        (ref_maybe_used_by_call_p_1): Likewise.
+        (call_may_clobber_ref_p_1): Likewise.
+
 2019-11-15  Kelvin Nilsen  <kelvin@gcc.gnu.org>
 
 	* MAINTAINERS: Change my email address as maintainer.
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index ef62b5dee44..168a8a59fa6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,25 @@ 
+2019-11-16  David Čepelík  <d@dcepelik.cz>
+
+        * Makefile.in: Add targets for new source files.
+        * gengtype.c (open_base_files):
+        * ipa-modref.c: New file.
+        * ipa-modref.h: New file.
+        * ipa-pure-const.c (check_op):
+        * lto-section-in.c:
+        * lto-streamer.h (enum lto_section_type):
+        * modref-tree.c: New file.
+        * modref-tree.h: New file.
+        * params.opt:
+        * passes.def:
+        * timevar.def (TV_IPA_MODREF):
+        (TV_TREE_MODREF):
+        * trans-mem.c (ipa_tm_create_version):
+        * tree-pass.h (make_pass_modref):
+        (make_pass_ipa_modref):
+        * tree-ssa-alias.c (modref_may_conflict):
+        (ref_maybe_used_by_call_p_1):
+        (call_may_clobber_ref_p_1):
+
 2019-11-15  Szabolcs Nagy  <szabolcs.nagy@arm.com>
 
 	* config/m68k/linux.h (MUSL_DYNAMIC_LINKER): Define.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7d3c13230e4..14c97361be5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1388,6 +1388,7 @@  OBJS = \
 	ipa-icf-gimple.o \
 	ipa-reference.o \
 	ipa-hsa.o \
+	ipa-modref.o \
 	ipa-ref.o \
 	ipa-utils.o \
 	ipa.o \
@@ -1426,6 +1427,7 @@  OBJS = \
 	lto-compress.o \
 	mcf.o \
 	mode-switching.o \
+	modref-tree.o \
 	modulo-sched.o \
 	multiple_target.o \
 	omp-offload.o \
@@ -2547,6 +2549,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)/modref-tree.h \
   $(srcdir)/signop.h \
   $(srcdir)/dwarf2out.h \
   $(srcdir)/dwarf2asm.c \
diff --git a/gcc/gengtype.c b/gcc/gengtype.c
index fa95776876d..a5532b3cc9f 100644
--- a/gcc/gengtype.c
+++ b/gcc/gengtype.c
@@ -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.h", "modref-tree.h", NULL
     };
     const char *const *ifp;
     outf_p gtype_desc_c;
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
new file mode 100644
index 00000000000..03d91fe15d8
--- /dev/null
+++ b/gcc/ipa-modref.c
@@ -0,0 +1,1012 @@ 
+/* 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/>.  */
+
+/* This file contains a tree pass and an IPA pass.
+
+The tree pass stores, for each load/store in every analyzed function, the
+sequence of types that make up the reference (the source of the load or the
+destination of the store).  For each function, it stores a list of accesses and
+their type sequences in a function summary.
+
+The IPA pass is similar: it also constructs a load/store summary for each
+analyzed function.  However, later in the IPA pass, a transitive closure is
+calculated, so that every function's summary contains not only information
+about the loads/stores that the function does, but also about every load and
+store that any of the functions that the function calls does (recursively).
+
+The information about loads/stores and the structure of their source and
+destination references can later be used to disambiguate accesses, which is the
+point of this optimization pass.
+
+Using the information obtained in this pass, other passes can remove useless
+stores and loads from the program.  This in turn makes other optimizations
+possible.  */
+
+#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 "ipa-modref.h"
+#include "gimple-pretty-print.h"
+#include "gimple-walk.h"
+#include "print-tree.h"
+#include "modref-tree.h"
+#include "tree-streamer.h"
+
+/* Summary for a single function which this pass produces.  */
+modref_summary::modref_summary ()
+{
+
+  loads_tree = new (ggc_alloc <modref_tree<alias_set_type> > ())
+               modref_tree <alias_set_type> (200, 200, 200);
+  stores_tree = new (ggc_alloc <modref_tree<alias_set_type> > ())
+                modref_tree <alias_set_type> (200, 200, 200);
+  stores_tree_ipa = new (ggc_alloc <modref_tree<tree> > ())
+                    modref_tree <tree> (200, 200, 200);
+  loads_tree_ipa = new (ggc_alloc <modref_tree<tree> > ())
+                   modref_tree <tree> (200, 200, 200);
+}
+
+/* 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);
+};
+
+/* Global variable holding all modref summaries.  */
+static GTY(()) fast_function_summary <modref_summary *, va_gc> *summaries;
+
+/* Return the global variable holding summaries; make sure it's initialized.  */
+static fast_function_summary <modref_summary *, va_gc> *
+get_global_summaries ()
+{
+  if (!summaries)
+    summaries = new (ggc_alloc <modref_summaries> ())
+                    modref_summaries (symtab);
+  return summaries;
+}
+
+
+/* Get function summary for FUNC if it exists, return NULL otherwise.  */
+
+modref_summary *
+get_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);
+  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 = get_global_summaries ()->get (func);
+  if (r && r->finished)
+    return r;
+
+  return NULL;
+}
+
+/* Store access into the modref_tree data structure.  */
+
+static void
+store_access_tree (modref_tree <alias_set_type> *tt,
+              alias_set_type base_set,
+              alias_set_type ref_set)
+{
+    if (!base_set && !ref_set)
+      tt->collapse ();
+    else
+      {
+        /* TODO The goal is to also store the range of the access (offset, size
+           and max_size) here.  That will make it possible to do finer access
+           disambiguation.  For the time being, we're only using base/ref alias
+           sets.  Including more infromation will make the analysis useful in
+           many more cases, but it does not change it fundamentally.  Hence
+           I believe collecting more data and tuning the alias oracle to use it
+           can be done in stage3.  */
+        tt->insert (base_set, ref_set, NULL, 0, 0);
+      }
+}
+
+/* IPA version of store_access_tree.  */
+
+static void
+store_access_ipa (modref_tree <tree> *tt, tree access)
+{
+  tree ref = TREE_TYPE(access);
+  tree base = access;
+  ipa_access *x = new (ggc_alloc <ipa_access> ()) ipa_access ();
+  while (handled_component_p (base))
+    {
+      ipa_access_component c;
+      c.type = TREE_TYPE(base);
+      c.offset = 0;
+      vec_safe_push (x->crumbs, c);
+      base = TREE_OPERAND (base, 0);
+    }
+  /* TODO Same as above in store_access_tree -- the goal here is to collect
+     more information about the access range.  */
+  tt->insert(TREE_TYPE(base), ref, x, 0, 0);
+}
+
+/* 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
+store_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)
+      /* TODO: It is possible to track what part of parameters are used.  */
+      && TREE_CODE (t) != PARM_DECL
+      && 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) && TREE_CODE (t) != PARM_DECL)
+    {
+      if (dump_file)
+        fprintf(dump_file, "   - Read-only variable, ignoring.\n");
+      return false;
+    }
+
+  return true;
+}
+
+/* Analyze access EXPR.  If it's intersting, store it into TT.  */
+
+static void
+analyze_access_tree (modref_tree <alias_set_type> *tt, tree expr)
+{
+  if (dump_file)
+    {
+      fprintf(dump_file, " - Analyzing expression: ");
+      print_generic_expr (dump_file, expr);
+      fprintf(dump_file, "\n");
+    }
+
+  if (!store_access_p (expr))
+    return;
+
+  ao_ref r;
+  ao_ref_init (&r, expr);
+  if (dump_file)
+    {
+       fprintf(dump_file, "   - Storing base_set=%i ref_set=%i\n",
+               ao_ref_base_alias_set (&r),
+               ao_ref_alias_set (&r));
+    }
+  store_access_tree (tt, ao_ref_base_alias_set (&r), ao_ref_alias_set (&r));
+}
+
+/* IPA version of analyze_access_tree.  */
+
+static void
+analyze_access_ipa (modref_tree <tree> *tt, tree expr)
+{
+  if (dump_file)
+    {
+      fprintf(dump_file, " - Analyzing expression (IPA): ");
+      print_generic_expr (dump_file, expr);
+      fprintf(dump_file, "\n");
+    }
+
+  if (!store_access_p (expr))
+    return;
+
+  ao_ref r;
+  ao_ref_init (&r, expr);
+  if (dump_file)
+    {
+      fprintf(dump_file, "   - Storing base_set=%i ref_set=%i\n",
+              ao_ref_base_alias_set (&r),
+              ao_ref_alias_set (&r));
+    }
+  store_access_ipa (tt, expr);
+}
+
+/* Encode TT to the output block OB using the summary streaming API.  */
+
+static void
+write_modref_tree (modref_tree <tree> *tt, struct output_block *ob)
+{
+  streamer_write_uhwi (ob, tt->max_bases);
+  streamer_write_uhwi (ob, tt->max_refs);
+  streamer_write_uhwi (ob, tt->max_accesses);
+
+  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);
+
+          streamer_write_uhwi (ob, ref_node->every_access);
+          streamer_write_uhwi (ob, vec_safe_length (ref_node->accesses));
+          size_t k;
+          modref_access_node *access_node;
+          FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
+            {
+              streamer_write_uhwi (ob, vec_safe_length (access_node->x->crumbs));
+              size_t l;
+              ipa_access_component *c;
+              FOR_EACH_VEC_SAFE_ELT (access_node->x->crumbs, l, c)
+                {
+                  stream_write_tree (ob, c->type, true);
+                }
+              // TODO Stream (and use) access sizes.
+              streamer_write_uhwi (ob, 0);
+              streamer_write_uhwi (ob, 0);
+            }
+        }
+    }
+}
+
+/* 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.  */
+
+static modref_tree <tree> *
+read_modref_tree (lto_input_block *ib, struct data_in *data_in)
+{
+  size_t max_bases = streamer_read_uhwi (ib);
+  size_t max_refs = streamer_read_uhwi (ib);
+  size_t max_accesses = streamer_read_uhwi (ib);
+  modref_tree <tree> *tt = new (ggc_alloc <modref_tree<tree> > ()) modref_tree<tree>
+                            (max_bases, max_refs, max_accesses);
+
+  size_t every_base = streamer_read_uhwi (ib);
+  size_t nbase = streamer_read_uhwi (ib);
+
+  gcc_assert (!every_base || nbase == 0);
+  if (every_base)
+    tt->collapse ();
+  for (size_t i = 0; i < nbase; i++)
+    {
+      tree base_tree = stream_read_tree (ib, data_in);
+      modref_base_node <tree> *base_node = tt->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)
+        base_node->collapse ();
+      for (size_t j = 0; j < nref; j++)
+        {
+          tree ref_tree = stream_read_tree (ib, data_in);
+          modref_ref_node <tree> *ref_node = base_node->insert_ref (ref_tree, max_refs);
+          size_t every_access = streamer_read_uhwi (ib);
+          size_t naccess = streamer_read_uhwi (ib);
+
+          gcc_assert (!every_access || naccess == 0);
+          if (every_access)
+            ref_node->collapse ();
+          for (size_t k = 0; k < naccess; k++)
+            {
+              size_t ncrumb = streamer_read_uhwi(ib);
+              ipa_access *x = new (ggc_alloc <ipa_access> ()) ipa_access ();
+              ipa_access_component c;
+
+              for (size_t l = 0; l < ncrumb; l++)
+                {
+                  c.type = stream_read_tree (ib, data_in);
+                  c.offset = 0;
+                  vec_safe_push (x->crumbs, c);
+                }
+              size_t a = streamer_read_uhwi (ib);
+              size_t b = streamer_read_uhwi (ib);
+              ref_node->insert_access (x, a, b, max_accesses);
+            }
+        }
+    }
+  return tt;
+}
+
+/* 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;
+
+  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)
+        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)
+        {
+          streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, cnode));
+
+          modref_summary *r = get_global_summaries ()->get (cnode);
+          if (!r)
+            {
+              streamer_write_uhwi (ob, 0);
+              streamer_write_uhwi (ob, 0);
+              continue;
+            }
+
+          streamer_write_uhwi (ob, r->loads_tree_ipa ? 1 : 0);
+          streamer_write_uhwi (ob, r->stores_tree_ipa ? 1 : 0);
+          if (r->loads_tree_ipa)
+            write_modref_tree (r->loads_tree_ipa, ob);
+          if (r->stores_tree_ipa)
+            write_modref_tree (r->stores_tree_ipa, 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 = get_global_summaries ()->get_create (node);
+      modref_sum->finished = false;
+      int have_loads_tree = streamer_read_uhwi (&ib);
+      int have_stores_tree = streamer_read_uhwi (&ib);
+      modref_sum->loads_tree_ipa = modref_sum->stores_tree_ipa = NULL;
+      if (have_loads_tree)
+        modref_sum->loads_tree_ipa = read_modref_tree (&ib, data_in);
+      if (have_stores_tree)
+        modref_sum->stores_tree_ipa = read_modref_tree (&ib, data_in);
+    }
+
+  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;
+
+  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");
+    }
+}
+
+/* TODO This helper can be removed if get (fnode) is used carefully everywhere,
+   and the `finished' can be dropped.  I will do it as part of cleaning up.  */
+modref_summary *
+get_cur (void)
+{
+  cgraph_node *fnode = cgraph_node::get (current_function_decl);
+  bool had = (get_global_summaries ()->get (fnode) != NULL);
+  modref_summary *r = get_global_summaries ()->get_create (fnode);
+
+  if (!had)
+    r->finished = false;
+  return r;
+}
+
+static void
+remove_cur (void)
+{
+  cgraph_node *fnode = cgraph_node::get (current_function_decl);
+  get_global_summaries ()->remove (fnode);
+}
+
+/* Analyze function call STMT in function F.  */
+
+static bool
+analyze_call (function *f, gimple *stmt, bool ipa)
+{
+  (void) f;
+
+  modref_summary *cur_summary = get_cur();
+
+  /* Check flags on the function call. In certain cases, analysis can be
+     simplified.  */
+  int flags = gimple_call_flags (stmt);
+  if (flags & ECF_CONST)
+    {
+      if (dump_file)
+        fprintf(dump_file, " - ECF_CONST, ignoring all stores and all loads except for args.\n");
+      return true;
+    }
+
+  /* TODO Further improvement: noreturn && no except region && no setjmp =>
+     discard all stores.  */
+
+  /* Pure functions do not affect global memory.  Stores by functions which are
+     noreturn and do not throw can safely be ignored.  */
+  bool ignore_stores = flags & ECF_PURE;
+  if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
+      || (!flag_exceptions && (flags & ECF_NORETURN)))
+    ignore_stores = true;
+
+  /* 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 (ipa)
+            cur_summary->loads_tree_ipa->collapse ();
+          else
+            cur_summary->loads_tree->collapse ();
+          return true;
+        }
+      if (dump_file)
+        fprintf(dump_file, " - Indirect call.\n");
+      return false;
+    }
+
+  /* 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;
+    }
+
+  struct cgraph_node *callee_node = cgraph_node::get (callee);
+  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 (ipa)
+            cur_summary->loads_tree_ipa->collapse ();
+          else
+            cur_summary->loads_tree->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 = get_global_summaries ()->get (callee_node);
+  if (!callee_summary)
+    {
+      if (ignore_stores)
+        {
+          if (ipa)
+            cur_summary->loads_tree_ipa->collapse ();
+          else
+            cur_summary->loads_tree->collapse ();
+          return true;
+        }
+      if (dump_file)
+        fprintf(dump_file, " - No modref summary available for callee.\n");
+      return false;
+    }
+
+  /* Merge with callee's summary.  */
+  cur_summary->loads_tree->merge (callee_summary->loads_tree);
+  if (!ignore_stores)
+    cur_summary->stores_tree->merge (callee_summary->stores_tree);
+
+  return true;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_load_tree (gimple *, tree, tree op, void *)
+{
+  analyze_access_tree (get_cur ()->loads_tree, op);
+  return false;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_load_ipa (gimple *, tree, tree op, void *)
+{
+  analyze_access_ipa (get_cur ()->stores_tree_ipa, op);
+  return false;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_store_tree (gimple *, tree, tree op, void *)
+{
+  analyze_access_tree (get_cur ()->stores_tree, op);
+  return false;
+}
+
+/* Helper for analyze_stmt.  */
+
+static bool
+analyze_store_ipa (gimple *, tree, tree op, void *)
+{
+  analyze_access_ipa (get_cur ()->stores_tree_ipa, op);
+  return false;
+}
+
+/* Analyze statement STMT of function F.  */
+
+static bool
+analyze_stmt (function *f, gimple *stmt, bool ipa)
+{
+  /* Analyze all loads and stores in STMT.  */
+  if (ipa)
+    walk_stmt_load_store_ops (stmt, NULL, analyze_load_ipa, analyze_store_ipa);
+  else
+    walk_stmt_load_store_ops (stmt, NULL, analyze_load_tree, analyze_store_tree);
+  /* 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:
+     return analyze_call (f, stmt, ipa);
+   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_strict_aliasing)
+    return;
+
+  /* Create and initialize summary for F.  */
+  if (ipa)
+    {
+      if (get_cur ()->stores_tree_ipa->bases)
+        get_cur ()->stores_tree_ipa->bases->truncate(0);
+      if (get_cur ()->loads_tree_ipa->bases)
+        get_cur ()->loads_tree_ipa->bases->truncate(0);
+      get_cur ()->stores_tree_ipa->every_base = false;
+      get_cur ()->loads_tree_ipa->every_base = false;
+    }
+  else
+    {
+      if (get_cur ()->stores_tree->bases)
+        get_cur ()->stores_tree->bases->truncate(0);
+      if (get_cur ()->loads_tree->bases)
+        get_cur ()->loads_tree->bases->truncate(0);
+      get_cur ()->stores_tree->every_base = false;
+      get_cur ()->loads_tree->every_base = false;
+    }
+  get_cur ()->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 (f, gsi_stmt (si), ipa))
+            {
+              remove_cur ();
+              if (dump_file)
+                fprintf (dump_file, " - modref done with result: not tracked.\n");
+              return;
+            }
+        }
+    }
+
+  get_cur ()->finished = true;
+
+  if (dump_file)
+    fprintf (dump_file, " - modref done with result: tracked.\n");
+}
+
+/* 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 *)
+{
+  push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+  analyze_function (DECL_STRUCT_FUNCTION (node->decl), false);
+  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;
+  /* TODO Add test: tree merged with empty tree the same as original tree.  */
+  /* TODO Un-hardcode the constants, use run-time params.  */
+  dst_data->stores_tree = new (ggc_alloc <modref_tree<alias_set_type> > ())
+                          modref_tree <alias_set_type> (200, 200, 200);
+  dst_data->stores_tree->merge (src_data->stores_tree);
+  dst_data->loads_tree = new (ggc_alloc <modref_tree<alias_set_type> > ())
+                         modref_tree <alias_set_type> (200, 200, 200);
+  dst_data->loads_tree->merge (src_data->loads_tree);
+
+  dst_data->stores_tree_ipa = new (ggc_alloc <modref_tree<tree> > ())
+                              modref_tree <tree> (200, 200, 200);
+  dst_data->stores_tree_ipa->merge (src_data->stores_tree_ipa);
+  dst_data->loads_tree_ipa = new (ggc_alloc <modref_tree<tree> > ())
+                             modref_tree <tree> (200, 200, 200);
+  dst_data->loads_tree_ipa->merge (src_data->loads_tree_ipa);
+}
+
+namespace
+{
+
+/*********************** GIMPLE pass specific stuff **********************/
+
+/* 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 ()
+          {
+            /*summaries = NULL;*/
+            // TODO free
+          }
+
+        /* opt_pass methods: */
+        opt_pass *clone ()
+        {
+                return new pass_modref (m_ctxt);
+        }
+        virtual bool gate (function *) { return flag_strict_aliasing; }
+        virtual unsigned int execute (function *);
+}; // class pass_modref
+
+/*********************** IPA pass specific stuff **********************/
+
+/* Definition of the modref IPA pass.  */
+const pass_data pass_data_ipa_modref =
+{
+  IPA_PASS,           /* type */
+  "ipa_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 unsigned int execute (function *);
+
+}; // class pass_ipa_modref
+
+}
+
+unsigned int pass_modref::execute (function *f)
+{
+  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);
+}
+
+/* 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 *)
+{
+  struct cgraph_node **order = XCNEWVEC (struct cgraph_node *, symtab->cgraph_count);
+  int order_pos;
+  order_pos = ipa_reduced_postorder (order, true, NULL);
+  struct cgraph_node *component_node;
+  struct cgraph_edge *callee_edge;
+  struct cgraph_node *cur;
+  struct cgraph_node *callee;
+  modref_summary *cur_summary;
+  modref_summary *callee_summary;
+  struct modref_tree <tree> *ipa_loads_tree;
+  struct modref_tree <tree> *ipa_stores_tree;
+  bool its_hopeless;
+  int i;
+
+  /* Iterate over all strongly connected components in post-order.  */
+  for (i = 0; i < order_pos; i++)
+    {
+      its_hopeless = false;
+
+      /* Get the component's representative.  That's just any node in the
+         component from which we can traverse the entire component.  */
+      component_node = order[i];
+
+      /* Create loads and stores trees.  These trees will be the same for the
+         entire component.  */
+      ipa_loads_tree = new (ggc_alloc <modref_tree<tree> > ())
+                       modref_tree <tree> (200, 200, 200);
+      ipa_stores_tree = new (ggc_alloc <modref_tree<tree> > ())
+                        modref_tree <tree> (200, 200, 200);;
+
+      /* Walk the component.  CUR is the current node of the component that's
+         being processed.  */
+      for (cur = component_node; cur; cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
+        {
+          /* Merge in summaries from CUR. */
+          cur_summary = get_function_summary (cur);
+
+          /* We don't know anything about CUR, hence we cannot tell anything
+             about the entire component.  */
+          if (!cur_summary)
+            {
+              its_hopeless = true;
+              goto propagate;
+            }
+
+          /* Walk every function that CUR calls and merge its summary.  */
+          for (callee_edge = cur->callees; callee_edge; callee_edge = callee_edge->next_callee)
+            {
+              /* Get the callee and its summary.  */
+              callee = callee_edge->callee;
+              callee_summary = get_function_summary (callee);
+
+              /* We don't know anything about CALLEE, hence we cannot tell
+                 anything about the entire component.  */
+              if (!callee_summary)
+                {
+                  its_hopeless = true;
+                  goto propagate;
+                }
+
+              /* Merge in callee's information.  */
+              ipa_loads_tree->merge (callee_summary->loads_tree_ipa);
+              ipa_stores_tree->merge (callee_summary->stores_tree_ipa);
+            }
+        }
+
+propagate:
+      /* If we couldn't find a modref summary for any function in the cycle, or
+         for any function that any function in the cycle calls, its hopeless
+         and we must assume that everything aliases.  Therefore, we will
+         collapse both the loads and the stores trees.  */
+      if (its_hopeless)
+        {
+          ipa_loads_tree->collapse ();
+          ipa_stores_tree->collapse ();
+        }
+
+      /* At this time, ipa_loads_tree and ipa_stores_tree 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 (cur = component_node; cur; cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
+        {
+          //fprintf(stderr, ">> HERE %p <<\n", (void *)cur_summary);
+          cur_summary = get_function_summary (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);
+              continue;
+            }
+          cur_summary->loads_tree_ipa = ipa_loads_tree;
+          cur_summary->stores_tree_ipa = ipa_stores_tree;
+        }
+    }
+  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
index 00000000000..d770965fae1
--- /dev/null
+++ b/gcc/ipa-modref.h
@@ -0,0 +1,43 @@ 
+/* 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
+
+#include "modref-tree.h"
+#include "alias.h"
+
+/* Single function summary.  */
+
+struct GTY(()) modref_summary
+{
+  const char *fname;
+  modref_tree <alias_set_type> *loads_tree;
+  modref_tree <alias_set_type> *stores_tree;
+  modref_tree <tree> *loads_tree_ipa;
+  modref_tree <tree> *stores_tree_ipa;
+  bool finished;
+
+  modref_summary();
+};
+
+void analyze_modref (struct cgraph_node *node);
+modref_summary *get_function_summary (cgraph_node *func);
+
+#endif
diff --git a/gcc/lto-section-in.c b/gcc/lto-section-in.c
index 67d998472e0..0c109835fe7 100644
--- a/gcc/lto-section-in.c
+++ b/gcc/lto-section-in.c
@@ -54,7 +54,8 @@  const char *lto_section_name[LTO_N_SECTION_TYPES] =
   "mode_table",
   "hsa",
   "lto",
-  "ipa_sra"
+  "ipa_sra",
+  "ipa_modref",
 };
 
 /* Hooks so that the ipa passes can call into the lto front end to get
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index 067a6660d2f..cd0f6da6587 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -236,6 +236,7 @@  enum lto_section_type
   LTO_section_ipa_hsa,
   LTO_section_lto,
   LTO_section_ipa_sra,
+  LTO_section_ipa_modref,
   LTO_N_SECTION_TYPES		/* Must be last.  */
 };
 
diff --git a/gcc/modref-tree.c b/gcc/modref-tree.c
new file mode 100644
index 00000000000..d77adc8aa05
--- /dev/null
+++ b/gcc/modref-tree.c
@@ -0,0 +1,342 @@ 
+/* Data structure for the modref pass.
+   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/>.  */
+
+#include "modref-tree.h"
+#include "selftest.h"
+
+#if CHECKING_P
+
+/*
+ * Returns true if and only if the access (T1, A1, B1) is dominated by the
+ * access (T2, A2, B2).  An access is said to be dominated by another access
+ * if and only if their types are the same and the former access is "included"
+ * in the latter access, i.e. the memory the former access touches is a subset
+ * of the memory the seconds access touches.
+ */
+
+bool
+access_covered_by (tree t1, poly_int64 a1, poly_int64 b1,
+                   tree t2, poly_int64 a2, poly_int64 b2)
+{
+  if (t1 != t2)
+    return false;
+  return known_le (a2, a1) && known_le (a1, b2) && known_le (b1, b2);
+}
+
+
+static void
+test_insert_search_collapse ()
+{
+  modref_base_node<alias_set_type> *base_node;
+  modref_ref_node<alias_set_type> *ref_node;
+  modref_access_node *access_node;
+
+  modref_tree<alias_set_type> *t = new modref_tree<alias_set_type>(1, 2, 2);
+  ASSERT_FALSE (t->every_base);
+
+  /* Insert into an empty tree.  */
+  t->insert (1, 2, NULL, 16, 32);
+  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);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 1);
+  
+  access_node = (*ref_node->accesses)[0];
+  ASSERT_TRUE (known_eq (access_node->a, 16));
+  ASSERT_TRUE (known_eq (access_node->b, 32));
+  ASSERT_FALSE (ref_node->every_access);
+
+  /* Insert when base exists but ref does not.  */
+  t->insert (1, 3, NULL, 2, 4);
+  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);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 1);
+  ASSERT_FALSE (ref_node->every_access);
+  
+  access_node = (*ref_node->accesses)[0];
+  ASSERT_TRUE (known_eq (access_node->a, 2));
+  ASSERT_TRUE (known_eq (access_node->b, 4));
+
+  /* Insert when base and ref exist, but access is not dominated by nor
+   * dominates other accesses.  */
+  t->insert (1, 2, NULL, 15, 17);
+  ASSERT_EQ (t->bases->length (), 1);
+  ASSERT_EQ (t->search (1), base_node);
+
+  ref_node = base_node->search (2);
+  ASSERT_NE (ref_node, NULL);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 2);
+  ASSERT_FALSE (ref_node->every_access);
+  
+  access_node = (*ref_node->accesses)[0];
+  ASSERT_TRUE (known_eq (access_node->a, 16));
+  ASSERT_TRUE (known_eq (access_node->b, 32));
+
+  access_node = (*ref_node->accesses)[1];
+  ASSERT_TRUE (known_eq (access_node->a, 15));
+  ASSERT_TRUE (known_eq (access_node->b, 17));
+
+  /* Insert when base and ref exist and access is dominated.  */
+  t->insert (1, 2, NULL, 18, 20);
+  ASSERT_EQ (t->search (1), base_node);
+  ASSERT_EQ (base_node->search (2), ref_node);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 2);
+  ASSERT_FALSE (ref_node->every_access);
+  
+  access_node = (*ref_node->accesses)[0];
+  ASSERT_TRUE (known_eq (access_node->a, 16));
+  ASSERT_TRUE (known_eq (access_node->b, 32));
+
+  access_node = (*ref_node->accesses)[1];
+  ASSERT_TRUE (known_eq (access_node->a, 15));
+  ASSERT_TRUE (known_eq (access_node->b, 17));
+
+  /* Insert when base and ref exists and access dominates.  */
+  t->insert (1, 2, NULL, 0, 64);
+  ASSERT_EQ (t->search (1), base_node);
+  ASSERT_EQ (base_node->search (2), ref_node);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 1);
+  ASSERT_FALSE (ref_node->every_access);
+  
+  access_node = (*ref_node->accesses)[0];
+  ASSERT_TRUE (known_eq (access_node->a, 0));
+  ASSERT_TRUE (known_eq (access_node->b, 64));
+
+  /* Insert accesses to trigger access list collapse.  */
+  t->insert (1, 2, NULL, 64, 68);
+  ASSERT_EQ (t->search (1), base_node);
+  ASSERT_EQ (base_node->search (2), ref_node);
+  ASSERT_NE (ref_node->accesses, NULL);
+  ASSERT_EQ (ref_node->accesses->length (), 2);
+  ASSERT_FALSE (ref_node->every_access);
+
+  t->insert (1, 2, NULL, 68, 72);
+  ASSERT_EQ (t->search (1), base_node);
+  ASSERT_EQ (base_node->search (2), ref_node);
+  ASSERT_EQ (ref_node->accesses, NULL);
+  ASSERT_TRUE (ref_node->every_access);
+
+  /* Further inserts to collapsed access list are ignored.  */
+  t->insert (1, 2, NULL, 100, 108);
+  ASSERT_EQ (t->search (1), base_node);
+  ASSERT_EQ (base_node->search (2), ref_node);
+  ASSERT_EQ (ref_node->accesses, NULL);
+  ASSERT_TRUE (ref_node->every_access);
+
+  /* Insert ref to trigger ref list collapse for base 1.  */
+  t->insert (1, 4, NULL, 0, 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, NULL, 2, 2);
+  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, NULL, 0, 4);
+  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, NULL, 0, 1);
+  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;
+  modref_ref_node<alias_set_type> *ref_node;
+
+  t1 = new modref_tree<alias_set_type>(3, 4, 1);
+  t1->insert(1, 1, NULL, 0, 1);
+  t1->insert(1, 2, NULL, 1, 2);
+  t1->insert(1, 3, NULL, 2, 4);
+  t1->insert(2, 1, NULL, 4, 8);
+  t1->insert(3, 1, NULL, 4, 8);
+
+  t2 = new modref_tree<alias_set_type>(10, 10, 10);
+  t2->insert(1, 2, NULL, 8, 16);
+  t2->insert(1, 3, NULL, 2, 4);
+  t2->insert(1, 4, NULL, 4, 8);
+  t2->insert(3, 2, NULL, 0, 1);
+  t2->insert(3, 3, NULL, 0, 1);
+  t2->insert(3, 4, NULL, 0, 1);
+  t2->insert(3, 5, NULL, 0, 1);
+
+  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);
+
+  ref_node = base_node->search (2);
+  ASSERT_EQ (ref_node->accesses, NULL);
+  ASSERT_TRUE (ref_node->every_access);
+
+  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);
+  if (r->accesses)
+    {
+      ggc_test_and_set_mark (r->accesses);
+      gt_ggc_mx (r->accesses);
+    }
+}
+
+void gt_ggc_mx (modref_ref_node<tree_node*>* &r)
+{
+  ggc_test_and_set_mark (r);
+  if (r->accesses)
+    {
+      ggc_test_and_set_mark (r->accesses);
+      gt_ggc_mx (r->accesses);
+    }
+  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 *) {}
+
+
+void gt_ggc_mx (modref_access_node* &a)
+{
+  ggc_test_and_set_mark (a);
+  //size_t l;
+  //xxx_crumb *c;
+  //if (a->x)
+  //  FOR_EACH_VEC_SAFE_ELT (a->x->crumbs, l, c)
+  //    gt_ggc_mx(c)
+}
+
+//void gt_pch_nx (modref_access_node*&) {}
+//void gt_pch_nx (modref_access_node*&, gt_pointer_operator, void *) {}
diff --git a/gcc/modref-tree.h b/gcc/modref-tree.h
new file mode 100644
index 00000000000..a5630c8df62
--- /dev/null
+++ b/gcc/modref-tree.h
@@ -0,0 +1,355 @@ 
+/* Data structure for the modref pass.
+   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 GCC_MODREF_TREE_H
+#define GCC_MODREF_TREE_H
+
+#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 "vec.h"
+
+struct ipa_modref_summary;
+
+struct GTY(()) ipa_access_component
+{
+  tree type;
+  poly_int64 offset;
+};
+
+struct GTY(()) ipa_access
+{
+  vec <ipa_access_component, va_gc> *crumbs;
+  ipa_access() : crumbs(NULL) {}
+};
+
+struct GTY(()) modref_access_node
+{
+  ipa_access *x;
+  poly_int64 a;
+  poly_int64 b;
+
+  modref_access_node (ipa_access *x, poly_int64 a, poly_int64 b):
+    x(x),
+    a(a),
+    b(b) {}
+};
+
+bool access_covered_by (tree t1, poly_int64 a1, poly_int64 b1,
+                        tree t2, poly_int64 a2, poly_int64 b2);
+
+
+template <typename T>
+struct GTY((user)) modref_ref_node
+{
+  T ref;
+  vec <modref_access_node *, va_gc> *accesses;
+  bool every_access;
+
+  modref_ref_node (T ref):
+    ref(ref),
+    accesses (NULL),
+    every_access(false)
+  {}
+
+  void insert_access (ipa_access *x, poly_int64 a, poly_int64 b, size_t max_accesses)
+  {
+    /* If this base->ref pair has no access information, bail out.  */
+    if (every_access)
+      return;
+
+    size_t i;
+    modref_access_node *n;
+    
+    /* Otherwise, try to search an access which dominates the access we would
+     * like to store.  */
+    FOR_EACH_VEC_SAFE_ELT (accesses, i, n)
+      {
+        /* Inserted access already covered by larger access stored earlier.  */
+        //if (access_covered_by (type, a, b, n->type, n->a, n->b))
+        //  return;
+      }
+
+    /* If there's no access that would already dominate the inserted access,
+     * try it the other way around: look for accesses which would be dominated
+     * by the inserted access.  */
+    bool dominated = false;
+    //FOR_EACH_VEC_SAFE_ELT (accesses, i, n)
+    //  {
+    //    //if (!access_covered_by (n->type, n->a, n->b, type, a, b))
+    //    //  continue;
+
+    //    if (!dominated)
+    //      {
+    //        /* First time we find an access that's dominated by the inserted
+    //         * access, we will "extend" the already present access.  */
+    //        n->type = type;
+    //        n->a = a;
+    //        n->b = b;
+    //        dominated = true;
+    //      }
+    //    else
+    //      {
+    //        /* Other accesses inserted earlier which are dominated by the
+    //         * inserted access will be removed.  */
+    //        accesses->ordered_remove (i);
+    //      }
+    //  }
+
+    /* If inserted access was neither dominated by any already present access,
+     * nor did it dominate any already inserted access, we have to insert a new
+     * node for it.  */
+    if (!dominated)
+      {
+        /* If this base->ref pair has too many accesses stored, we will clear
+         * all accesses and bail out.  */
+        if (accesses && accesses->length () >= max_accesses)
+          {
+            collapse ();
+            return;
+          }
+        modref_access_node *access_node = new (ggc_alloc <modref_access_node> ())
+                                          modref_access_node (x, a, b);
+        vec_safe_push (accesses, access_node);
+      }
+  }
+
+  void collapse ()
+  {
+    accesses = NULL; /* TODO cleanup */
+    every_access = true;
+  }
+};
+
+
+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) {}
+
+  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;
+  }
+
+  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;
+
+    /* 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)
+      {
+        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 ()
+  {
+    refs = NULL; /* TODO cleanup */
+    every_ref = true;
+  }
+};
+
+/*
+ * TODO Collect types and [a, b) ranges of memory access components.
+ * TODO Use flag_modref_max_{bases,refs,accesses}.
+ */
+template <typename T>
+struct GTY((user)) modref_tree
+{
+  vec <modref_base_node <T> *, va_gc> *bases;
+  size_t max_bases;
+  size_t max_refs;
+  size_t max_accesses;
+  bool every_base;
+
+  modref_tree (size_t max_bases, size_t max_refs, size_t max_accesses):
+    bases (NULL),
+    max_bases (max_bases),
+    max_refs (max_refs),
+    max_accesses (max_accesses),
+    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)
+      {
+        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;
+  }
+
+  void insert (T base, T ref, ipa_access *x, poly_int64 a, poly_int64 b)
+  {
+    modref_base_node <T> *base_node;
+    modref_ref_node <T> *ref_node;
+
+    base_node = insert_base (base);
+    if (!base_node)
+      return;
+    gcc_assert (search (base) != NULL);
+
+    ref_node = base_node->insert_ref (ref, max_refs);
+    if (!ref_node)
+      return;
+
+    ref_node->insert_access (x, a, b, max_accesses);
+  }
+
+  void merge (modref_tree <T> *other)
+  {
+    if (!other)
+      return;
+    if (other->every_base)
+      {
+        collapse ();
+        return;
+      }
+
+    size_t i, j, k;
+    modref_base_node <T> *base_node, *my_base_node;
+    modref_ref_node <T> *ref_node, *my_ref_node;
+    modref_access_node *access_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;
+
+            if (ref_node->every_access)
+              {
+                my_ref_node->collapse ();
+                continue;
+              }
+
+            FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
+              my_ref_node->insert_access (access_node->x, access_node->a, access_node->b, max_accesses);
+          }
+      }
+  }
+
+  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 ()
+  {
+    bases = NULL; /* TODO cleanup */
+    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/params.opt b/gcc/params.opt
index d60db5825cc..75070c8f0af 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -876,6 +876,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
 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(200)
+Maximum number of bases stored in each modref tree.
+
+-param=modref-max-refs=
+Common Joined UInteger Var(param_modref_max_refs)
+Maximum number of refs stored in each modref tree.
+
+-param=modref-max-accesses=
+Common Joined UInteger Var(param_modref_max_accesses)
+Maximum number of accesses stored in each modref tree.
+
 -param=tm-max-aggregate-size=
 Common Joined UInteger Var(param_tm_max_aggregate_size) Init(9) Param
 Size in bytes after which thread-local aggregates should be instrumented with the logging functions instead of save/restore pairs.
diff --git a/gcc/passes.def b/gcc/passes.def
index 798a391bd35..a71ae62b690 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -51,6 +51,7 @@  along with GCC; see the file COPYING3.  If not see
   INSERT_PASSES_AFTER (all_small_ipa_passes)
   NEXT_PASS (pass_ipa_free_lang_data);
   NEXT_PASS (pass_ipa_function_and_variable_visibility);
+  NEXT_PASS (pass_ipa_modref);
   NEXT_PASS (pass_build_ssa_passes);
   PUSH_INSERT_PASSES_WITHIN (pass_build_ssa_passes)
       NEXT_PASS (pass_fixup_cfg);
@@ -89,6 +90,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);
@@ -143,6 +145,7 @@  along with GCC; see the file COPYING3.  If not see
 
   INSERT_PASSES_AFTER (all_regular_ipa_passes)
   NEXT_PASS (pass_ipa_whole_program_visibility);
+  NEXT_PASS (pass_ipa_modref);
   NEXT_PASS (pass_ipa_profile);
   NEXT_PASS (pass_ipa_icf);
   NEXT_PASS (pass_ipa_devirt);
@@ -342,6 +345,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)
@@ -374,6 +378,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)
diff --git a/gcc/timevar.def b/gcc/timevar.def
index 357fcfd65c5..61de26caeed 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -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")
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a987661530e..de16cd7197b 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -478,6 +478,7 @@  extern gimple_opt_pass *make_pass_gen_hsail (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);
 
 /* IPA Passes */
 extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
@@ -514,6 +515,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);
 
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 8c63e3bf5a9..7070e0e1298 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -38,6 +38,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "ipa-reference.h"
 #include "varasm.h"
+#include "ipa-modref.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -2273,6 +2274,51 @@  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.  */
+
+static bool
+modref_may_conflict (modref_tree <alias_set_type> *tt, ao_ref *ref)
+{
+  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 (!flag_strict_aliasing)
+    return true;
+
+  if (tt->every_base)
+    return true;
+
+  base_set = ao_ref_base_alias_set (ref);
+  if (!base_set)
+    return true;
+
+  ref_set = ao_ref_alias_set (ref);
+
+  FOR_EACH_VEC_SAFE_ELT (tt->bases, i, base_node)
+    {
+      if (base_node->every_ref)
+	return true;
+
+      if (!base_node->base)
+	return true;
+
+      if (!alias_sets_conflict_p (base_set, base_node->base))
+	continue;
+
+      FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
+	{
+	  if (!alias_sets_conflict_p (ref_set, ref_node->ref))
+	    continue;
+
+	  /* TODO use more access information.  */
+	  return true;
+	}
+    }
+  return false;
+}
+
 /* If the call CALL may use the memory reference REF return true,
    otherwise return false.  */
 
@@ -2288,10 +2334,35 @@  ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p)
       && (flags & (ECF_CONST|ECF_NOVOPS)))
     goto process_args;
 
+  callee = gimple_call_fndecl (call);
+
   base = ao_ref_base (ref);
   if (!base)
     return true;
 
+  if (callee != NULL_TREE)
+    {
+      struct cgraph_node *node = cgraph_node::get (callee);
+      if (node)
+        {
+          modref_summary *summary = get_function_summary (node);
+	  if (summary) {
+	    if (!modref_may_conflict(summary->loads_tree, ref))
+	      {
+		if (dump_file)
+		  {
+		    fprintf(dump_file, "modref said: in %s, call to %s does not use ",
+			    current_function_name (), "????");
+		    fprintf(dump_file, " %i->%i\n",
+			    ao_ref_base_alias_set (ref),
+			    ao_ref_alias_set (ref));
+		  }
+		return false;
+	      }
+	  }
+       }
+    }
+
   /* A call that is not without side-effects might involve volatile
      accesses and thus conflicts with all other volatile accesses.  */
   if (ref->volatile_p)
@@ -2305,8 +2376,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.  */
@@ -2691,6 +2760,36 @@  call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref)
   if (!base)
     return true;
 
+  callee = gimple_call_fndecl (call);
+  if (callee != NULL_TREE)
+    {
+      struct cgraph_node *node = cgraph_node::get (callee);
+      if (node)
+	{
+          enum availability avail;
+          node = node->function_symbol(&avail);
+	  if (avail > AVAIL_INTERPOSABLE) {
+	    modref_summary *summary = get_function_summary (node);
+	    if (summary)
+	      {
+		if (!modref_may_conflict(summary->stores_tree, ref))
+		  {
+		    if (dump_file)
+		      {
+			fprintf(dump_file, "modref said: in %s, call to %s does not clobber ",
+				current_function_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;
+		  }
+	      }
+	   }
+	}
+    }
+
   if (TREE_CODE (base) == SSA_NAME
       || CONSTANT_CLASS_P (base))
     return false;
@@ -2719,8 +2818,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.  */