diff --git a/gcc/gimple.c b/gcc/gimple.c
index 9e65fa61c73..1e0fad92e15 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1575,11 +1575,12 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg)
       else
 	{
 	  if (fnspec.arg_direct_p (arg))
-	    flags |= EAF_DIRECT;
+	    flags |= EAF_NO_INDIRECT_READ | EAF_NO_INDIRECT_ESCAPE
+		     | EAF_NOT_RETURNED_INDIRECTLY | EAF_NO_INDIRECT_CLOBBER;
 	  if (fnspec.arg_noescape_p (arg))
-	    flags |= EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+	    flags |= EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE;
 	  if (fnspec.arg_readonly_p (arg))
-	    flags |= EAF_NOCLOBBER;
+	    flags |= EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER;
 	}
     }
   tree callee = gimple_call_fndecl (stmt);
@@ -1608,7 +1609,7 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg)
 int
 gimple_call_retslot_flags (const gcall *stmt)
 {
-  int flags = EAF_DIRECT | EAF_NOREAD;
+  int flags = implicit_retslot_eaf_flags;
 
   tree callee = gimple_call_fndecl (stmt);
   if (callee)
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 22efc06c583..f6b0bf3212b 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -148,22 +148,24 @@ struct escape_entry
 static void
 dump_eaf_flags (FILE *out, int flags, bool newline = true)
 {
-  if (flags & EAF_DIRECT)
-    fprintf (out, " direct");
-  if (flags & EAF_NOCLOBBER)
-    fprintf (out, " noclobber");
-  if (flags & EAF_NOESCAPE)
-    fprintf (out, " noescape");
-  if (flags & EAF_NODIRECTESCAPE)
-    fprintf (out, " nodirectescape");
   if (flags & EAF_UNUSED)
     fprintf (out, " unused");
-  if (flags & EAF_NOT_RETURNED)
-    fprintf (out, " not_returned");
+  if (flags & EAF_NO_DIRECT_CLOBBER)
+    fprintf (out, " no_direct_clobber");
+  if (flags & EAF_NO_INDIRECT_CLOBBER)
+    fprintf (out, " no_indirect_clobber");
+  if (flags & EAF_NO_DIRECT_ESCAPE)
+    fprintf (out, " no_direct_escape");
+  if (flags & EAF_NO_INDIRECT_ESCAPE)
+    fprintf (out, " no_indirect_escape");
   if (flags & EAF_NOT_RETURNED_DIRECTLY)
     fprintf (out, " not_returned_directly");
-  if (flags & EAF_NOREAD)
-    fprintf (out, " noread");
+  if (flags & EAF_NOT_RETURNED_INDIRECTLY)
+    fprintf (out, " not_returned_indirectly");
+  if (flags & EAF_NO_DIRECT_READ)
+    fprintf (out, " no_direct_read");
+  if (flags & EAF_NO_INDIRECT_READ)
+    fprintf (out, " no_indirect_read");
   if (newline)
   fprintf (out, "\n");
 }
@@ -296,7 +298,7 @@ remove_useless_eaf_flags (int eaf_flags, int ecf_flags, bool returns_void)
   else if (ecf_flags & ECF_PURE)
     eaf_flags &= ~implicit_pure_eaf_flags;
   else if ((ecf_flags & ECF_NORETURN) || returns_void)
-    eaf_flags &= ~(EAF_NOT_RETURNED | EAF_NOT_RETURNED_DIRECTLY);
+    eaf_flags &= ~(EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY);
   return eaf_flags;
 }
 
@@ -1412,35 +1414,32 @@ memory_access_to (tree op, tree ssa_name)
 static int
 deref_flags (int flags, bool ignore_stores)
 {
-  int ret = EAF_NODIRECTESCAPE | EAF_NOT_RETURNED_DIRECTLY;
+  /* Dereference is also a direct read but dereferenced value does not
+     yield any other direct use.  */
+  int ret = EAF_NO_DIRECT_CLOBBER | EAF_NO_DIRECT_ESCAPE
+	    | EAF_NOT_RETURNED_DIRECTLY;
   /* If argument is unused just account for
      the read involved in dereference.  */
   if (flags & EAF_UNUSED)
-    ret |= EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOT_RETURNED;
+    ret |= EAF_NO_INDIRECT_READ | EAF_NO_INDIRECT_CLOBBER
+	   | EAF_NO_INDIRECT_ESCAPE;
   else
     {
-      if ((flags & EAF_NOCLOBBER) || ignore_stores)
-	ret |= EAF_NOCLOBBER;
-      if ((flags & EAF_NOESCAPE) || ignore_stores)
-	ret |= EAF_NOESCAPE;
-      /* If the value dereferenced is not used for another load or store
-	 we can still consider ARG as used only directly.
-
-	 Consider
-
-	 int
-	 test (int *a)
-	   {
-	     return *a!=0;
-	   }
-
-	*/
-      if ((flags & (EAF_NOREAD | EAF_NOT_RETURNED | EAF_NOESCAPE | EAF_DIRECT))
-	  == (EAF_NOREAD | EAF_NOT_RETURNED | EAF_NOESCAPE | EAF_DIRECT)
-	  && ((flags & EAF_NOCLOBBER) || ignore_stores))
-	ret |= EAF_DIRECT;
-      if (flags & EAF_NOT_RETURNED)
-	ret |= EAF_NOT_RETURNED;
+      /* Direct or indirect accesses leads to indirect accesses.  */
+      if (((flags & EAF_NO_DIRECT_CLOBBER)
+	   && (flags & EAF_NO_INDIRECT_CLOBBER))
+	  || ignore_stores)
+	ret |= EAF_NO_INDIRECT_CLOBBER;
+      if (((flags & EAF_NO_DIRECT_ESCAPE)
+	   && (flags & EAF_NO_INDIRECT_ESCAPE))
+	  || ignore_stores)
+	ret |= EAF_NO_INDIRECT_ESCAPE;
+      if ((flags & EAF_NO_DIRECT_READ)
+	   && (flags & EAF_NO_INDIRECT_READ))
+	ret |= EAF_NO_INDIRECT_READ;
+      if ((flags & EAF_NOT_RETURNED_DIRECTLY)
+	  && (flags & EAF_NOT_RETURNED_INDIRECTLY))
+	ret |= EAF_NOT_RETURNED_INDIRECTLY;
     }
   return ret;
 }
@@ -1508,9 +1507,11 @@ void
 modref_lattice::init ()
 {
   /* All flags we track.  */
-  int f = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
-	  | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED |
-	  EAF_NOT_RETURNED_DIRECTLY | EAF_NOREAD;
+  int f = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
+	  | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE
+	  | EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ
+	  | EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY
+	  | EAF_UNUSED;
   flags = f;
   /* Check that eaf_flags_t is wide enough to hold all flags.  */
   gcc_checking_assert (f == flags);
@@ -1589,12 +1590,6 @@ modref_lattice::merge (int f)
 {
   if (f & EAF_UNUSED)
     return false;
-  /* Noescape implies that value also does not escape directly.
-     Fnspec machinery does set both so compensate for this.  */
-  if (f & EAF_NOESCAPE)
-    f |= EAF_NODIRECTESCAPE;
-  if (f & EAF_NOT_RETURNED)
-    f |= EAF_NOT_RETURNED_DIRECTLY;
   if ((flags & f) != flags)
     {
       flags &= f;
@@ -1664,7 +1659,7 @@ modref_lattice::merge_deref (const modref_lattice &with, bool ignore_stores)
 bool
 modref_lattice::merge_direct_load ()
 {
-  return merge (~(EAF_UNUSED | EAF_NOREAD));
+  return merge (~(EAF_UNUSED | EAF_NO_DIRECT_READ));
 }
 
 /* Merge in flags for direct store.  */
@@ -1672,7 +1667,7 @@ modref_lattice::merge_direct_load ()
 bool
 modref_lattice::merge_direct_store ()
 {
-  return merge (~(EAF_UNUSED | EAF_NOCLOBBER));
+  return merge (~(EAF_UNUSED | EAF_NO_DIRECT_CLOBBER));
 }
 
 /* Analyzer of EAF flags.
@@ -1729,22 +1724,30 @@ private:
   auto_vec<int> m_names_to_propagate;
 
   void merge_with_ssa_name (tree dest, tree src, bool deref);
-  void merge_call_lhs_flags (gcall *call, int arg, tree name, bool deref);
+  void merge_call_lhs_flags (gcall *call, int arg, tree name, bool direct,
+			     bool deref);
 };
 
 
-/* Call statements may return their parameters.  Consider argument number
+/* Call statements may return tgeir parameters.  Consider argument number
    ARG of USE_STMT and determine flags that can needs to be cleared
    in case pointer possibly indirectly references from ARG I is returned.
+   If DIRECT is true consider direct returns and if INDIRECT consider
+   indirect returns.
    LATTICE, DEPTH and ipa are same as in analyze_ssa_name.
    ARG is set to -1 for static chain.  */
 
 void
 modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
-					   tree name, bool deref)
+					   tree name, bool direct,
+					   bool indirect)
 {
   int index = SSA_NAME_VERSION (name);
 
+  /* If value is not returned at all, do nothing.  */
+  if (!direct && !indirect)
+    return;
+
   /* If there is no return value, no flags are affected.  */
   if (!gimple_call_lhs (call))
     return;
@@ -1763,10 +1766,13 @@ modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
   if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
     {
       tree lhs = gimple_call_lhs (call);
-      merge_with_ssa_name (name, lhs, deref);
+      if (direct)
+	merge_with_ssa_name (name, lhs, false);
+      if (indirect)
+	merge_with_ssa_name (name, lhs, true);
     }
   /* In the case of memory store we can do nothing.  */
-  else if (deref)
+  else if (!direct)
     m_lattice[index].merge (deref_flags (0, false));
   else
     m_lattice[index].merge (0);
@@ -1782,18 +1788,19 @@ callee_to_caller_flags (int call_flags, bool ignore_stores,
 {
   /* call_flags is about callee returning a value
      that is not the same as caller returning it.  */
-  call_flags |= EAF_NOT_RETURNED
-		| EAF_NOT_RETURNED_DIRECTLY;
+  call_flags |= EAF_NOT_RETURNED_DIRECTLY
+		| EAF_NOT_RETURNED_INDIRECTLY;
   /* TODO: We miss return value propagation.
      Be conservative and if value escapes to memory
      also mark it as escaping.  */
   if (!ignore_stores && !(call_flags & EAF_UNUSED))
     {
-      if (!(call_flags & EAF_NOESCAPE))
-	lattice.merge (~(EAF_NOT_RETURNED | EAF_UNUSED));
-      if (!(call_flags & (EAF_NODIRECTESCAPE | EAF_NOESCAPE)))
+      if (!(call_flags & EAF_NO_DIRECT_ESCAPE))
 	lattice.merge (~(EAF_NOT_RETURNED_DIRECTLY
-			 | EAF_NOT_RETURNED
+			 | EAF_NOT_RETURNED_INDIRECTLY
+			 | EAF_UNUSED));
+      if (!(call_flags & EAF_NO_INDIRECT_ESCAPE))
+	lattice.merge (~(EAF_NOT_RETURNED_INDIRECTLY
 			 | EAF_UNUSED));
     }
   else
@@ -1869,13 +1876,13 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 	      && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
 	    ;
 	  else if (gimple_return_retval (ret) == name)
-	    m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED
+	    m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED_DIRECTLY
 				      | EAF_NOT_RETURNED_DIRECTLY));
 	  else if (memory_access_to (gimple_return_retval (ret), name))
 	    {
 	      m_lattice[index].merge_direct_load ();
-	      m_lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED
-					| EAF_NOT_RETURNED_DIRECTLY));
+	      m_lattice[index].merge (~(EAF_UNUSED
+					| EAF_NOT_RETURNED_INDIRECTLY));
 	    }
 	}
       /* Account for LHS store, arg loads and flags from callee function.  */
@@ -1889,7 +1896,7 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 	     is on since that would allow propagation of this from -fno-ipa-pta
 	     to -fipa-pta functions.  */
 	  if (gimple_call_fn (use_stmt) == name)
-	    m_lattice[index].merge (~(EAF_NOCLOBBER | EAF_UNUSED));
+	    m_lattice[index].merge (~(EAF_NO_DIRECT_CLOBBER | EAF_UNUSED));
 
 	  /* Recursion would require bit of propagation; give up for now.  */
 	  if (callee && !m_ipa && recursive_call_p (current_function_decl,
@@ -1932,14 +1939,14 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 			 arg is written to itself which is an escape.  */
 		      if (!isretslot)
 			{
-			  if (!(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
-			    m_lattice[index].merge (~(EAF_NOESCAPE
-						      | EAF_UNUSED));
 			  if (!(call_flags & (EAF_NOT_RETURNED_DIRECTLY
-					      | EAF_UNUSED
-					      | EAF_NOT_RETURNED)))
-			    m_lattice[index].merge (~(EAF_NODIRECTESCAPE
-						      | EAF_NOESCAPE
+					      | EAF_UNUSED)))
+			    m_lattice[index].merge (~(EAF_NO_DIRECT_ESCAPE
+						      | EAF_NO_INDIRECT_ESCAPE
+						      | EAF_UNUSED));
+			  if (!(call_flags & (EAF_NOT_RETURNED_INDIRECTLY
+					      | EAF_UNUSED)))
+			    m_lattice[index].merge (~(EAF_NO_INDIRECT_ESCAPE
 						      | EAF_UNUSED));
 			  call_flags = callee_to_caller_flags
 					   (call_flags, false,
@@ -1953,9 +1960,11 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 		  && (gimple_call_chain (call) == name))
 		{
 		  int call_flags = gimple_call_static_chain_flags (call);
-		  if (!ignore_retval
-		       && !(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
-		    merge_call_lhs_flags (call, -1, name, false);
+		  if (!ignore_retval && !(call_flags & EAF_UNUSED))
+		    merge_call_lhs_flags
+			 (call, -1, name,
+			  !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
+			  !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
 		  call_flags = callee_to_caller_flags
 				   (call_flags, ignore_stores,
 				    m_lattice[index]);
@@ -1974,11 +1983,11 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 		if (gimple_call_arg (call, i) == name)
 		  {
 		    int call_flags = gimple_call_arg_flags (call, i);
-		    if (!ignore_retval && !(call_flags
-					    & (EAF_NOT_RETURNED | EAF_UNUSED)))
+		    if (!ignore_retval && !(call_flags & EAF_UNUSED))
 		      merge_call_lhs_flags
 			      (call, i, name,
-			       call_flags & EAF_NOT_RETURNED_DIRECTLY);
+			       !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
+			       !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
 		    if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
 		      {
 			call_flags = callee_to_caller_flags
@@ -1996,9 +2005,10 @@ modref_eaf_analysis::analyze_ssa_name (tree name)
 		  {
 		    int call_flags = deref_flags
 			    (gimple_call_arg_flags (call, i), ignore_stores);
-		    if (!ignore_retval
-			 && !(call_flags & (EAF_NOT_RETURNED | EAF_UNUSED)))
-		      merge_call_lhs_flags (call, i, name, true);
+		    if (!ignore_retval && !(call_flags & EAF_UNUSED)
+			&& !(call_flags & EAF_NOT_RETURNED_DIRECTLY)
+			&& !(call_flags & EAF_NOT_RETURNED_INDIRECTLY))
+		      merge_call_lhs_flags (call, i, name, false, true);
 		    if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
 		      m_lattice[index].merge_direct_load ();
 		    else
@@ -2819,6 +2829,14 @@ modref_generate (void)
 
 }  /* ANON namespace.  */
 
+/* Debugging helper.  */
+
+void
+debug_eaf_flags (int flags)
+{
+   dump_eaf_flags (stderr, flags, true);
+}
+
 /* Called when a new function is inserted to callgraph late.  */
 
 void
@@ -4231,7 +4249,8 @@ modref_merge_call_site_flags (escape_summary *sum,
       int flags = 0;
       int flags_lto = 0;
       /* Returning the value is already accounted to at local propagation.  */
-      int implicit_flags = EAF_NOT_RETURNED | EAF_NOT_RETURNED_DIRECTLY;
+      int implicit_flags = EAF_NOT_RETURNED_DIRECTLY
+			   | EAF_NOT_RETURNED_INDIRECTLY;
 
       if (summary && ee->arg < summary->arg_flags.length ())
 	flags = summary->arg_flags[ee->arg];
@@ -4262,11 +4281,15 @@ modref_merge_call_site_flags (escape_summary *sum,
 	      else
 		{
 		  if (fnspec.arg_direct_p (ee->arg))
-		    fnspec_flags |= EAF_DIRECT;
+		    fnspec_flags |= EAF_NO_INDIRECT_READ
+			     | EAF_NO_INDIRECT_ESCAPE
+			     | EAF_NOT_RETURNED_INDIRECTLY
+			     | EAF_NO_INDIRECT_CLOBBER;
 		  if (fnspec.arg_noescape_p (ee->arg))
-		    fnspec_flags |= EAF_NOESCAPE | EAF_NODIRECTESCAPE;
+		    fnspec_flags |= EAF_NO_DIRECT_ESCAPE
+				    | EAF_NO_INDIRECT_ESCAPE;
 		  if (fnspec.arg_readonly_p (ee->arg))
-		    fnspec_flags |= EAF_NOCLOBBER;
+		    flags |= EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER;
 		}
 	    }
 	  implicit_flags |= fnspec_flags;
@@ -4280,16 +4303,6 @@ modref_merge_call_site_flags (escape_summary *sum,
 	  flags = interposable_eaf_flags (flags, implicit_flags);
 	  flags_lto = interposable_eaf_flags (flags_lto, implicit_flags);
 	}
-      /* Noescape implies that value also does not escape directly.
-	 Fnspec machinery does set both so compensate for this.  */
-      if (flags & EAF_NOESCAPE)
-	flags |= EAF_NODIRECTESCAPE;
-      if (flags_lto & EAF_NOESCAPE)
-	flags_lto |= EAF_NODIRECTESCAPE;
-      if (flags & EAF_NOT_RETURNED)
-	flags |= EAF_NOT_RETURNED_DIRECTLY;
-      if (flags_lto & EAF_NOT_RETURNED)
-	flags_lto |= EAF_NOT_RETURNED_DIRECTLY;
       if (!(flags & EAF_UNUSED)
 	  && cur_summary && ee->parm_index < (int)cur_summary->arg_flags.length ())
 	{
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 20170a65ded..482c4e4633e 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -21,7 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #define IPA_MODREF_H
 
 typedef modref_tree <alias_set_type> modref_records;
-typedef unsigned char eaf_flags_t;
+typedef unsigned short eaf_flags_t;
 
 /* Single function summary.  */
 
@@ -48,15 +48,28 @@ void ipa_modref_c_finalize ();
 void ipa_merge_modref_summary_after_inlining (cgraph_edge *e);
 
 /* All flags that are implied by the ECF_CONST functions.  */
-static const int implicit_const_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
-				     | EAF_NODIRECTESCAPE | EAF_NOREAD;
+static const int implicit_const_eaf_flags
+   = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
+    | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE
+    | EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ
+    | EAF_NOT_RETURNED_INDIRECTLY;
+
 /* All flags that are implied by the ECF_PURE function.  */
-static const int implicit_pure_eaf_flags = EAF_NOCLOBBER | EAF_NOESCAPE
-				    | EAF_NODIRECTESCAPE;
+static const int implicit_pure_eaf_flags
+   = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
+    | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE;
+
 /* All flags implied when we know we can ignore stores (i.e. when handling
    call to noreturn).  */
-static const int ignore_stores_eaf_flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE
-				    | EAF_NODIRECTESCAPE;
+static const int ignore_stores_eaf_flags
+   = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
+    | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE;
+
+/* Return slot is write-only.  */
+static const int implicit_retslot_eaf_flags
+   = EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ
+     | EAF_NO_INDIRECT_ESCAPE | EAF_NO_INDIRECT_CLOBBER
+     | EAF_NOT_RETURNED_INDIRECTLY;
 
 /* If function does not bind to current def (i.e. it is inline in comdat
    section), the modref analysis may not match the behaviour of function
@@ -74,16 +87,15 @@ interposable_eaf_flags (int modref_flags, int flags)
   if ((modref_flags & EAF_UNUSED) && !(flags & EAF_UNUSED))
     {
       modref_flags &= ~EAF_UNUSED;
-      modref_flags |= EAF_NOESCAPE | EAF_NOT_RETURNED
-		      | EAF_NOT_RETURNED_DIRECTLY | EAF_NOCLOBBER;
+      modref_flags |= EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE
+		      | EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY
+		      | EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER;
     }
   /* We can not deterine that value is not read at all.  */
-  if ((modref_flags & EAF_NOREAD) && !(flags & EAF_NOREAD))
-    modref_flags &= ~EAF_NOREAD;
-  /* Clear direct flags so we also know that value is possibly read
-     indirectly.  */
-  if ((modref_flags & EAF_DIRECT) && !(flags & EAF_DIRECT))
-    modref_flags &= ~EAF_DIRECT;
+  if ((modref_flags & EAF_NO_DIRECT_READ) && !(flags & EAF_NO_DIRECT_READ))
+    modref_flags &= ~EAF_NO_DIRECT_READ;
+  if ((modref_flags & EAF_NO_INDIRECT_READ) && !(flags & EAF_NO_INDIRECT_READ))
+    modref_flags &= ~EAF_NO_INDIRECT_READ;
   return modref_flags;
 }
 
diff --git a/gcc/testsuite/g++.dg/ipa/modref-1.C b/gcc/testsuite/g++.dg/ipa/modref-1.C
index eaa14ea5c7f..c57aaca0230 100644
--- a/gcc/testsuite/g++.dg/ipa/modref-1.C
+++ b/gcc/testsuite/g++.dg/ipa/modref-1.C
@@ -31,5 +31,5 @@ int main()
 	return 0;
 }
 /* { dg-final { scan-tree-dump "Function found to be const: {anonymous}::B::genB" "local-pure-const1"  } } */
-/* { dg-final { scan-tree-dump "Retslot flags: direct noescape nodirectescape not_returned not_returned_directly noread" "modref1" } } */
+/* { dg-final { scan-tree-dump "Retslot flags: no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_direct_read no_indirect_read" "modref1" } } */
   
diff --git a/gcc/testsuite/gcc.dg/ipa/modref-3.c b/gcc/testsuite/gcc.dg/ipa/modref-3.c
index 84013541ce8..9a20e018ae1 100644
--- a/gcc/testsuite/gcc.dg/ipa/modref-3.c
+++ b/gcc/testsuite/gcc.dg/ipa/modref-3.c
@@ -17,4 +17,4 @@ main ()
     linker_error ();
   return 0;
 }
-/* { dg-final { scan-ipa-dump "Static chain flags: noclobber noescape nodirectescape" "modref" } } */
+/* { dg-final { scan-ipa-dump "Static chain flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly no_indirect_read" "modref" } } */
diff --git a/gcc/testsuite/gcc.dg/lto/modref-3_0.c b/gcc/testsuite/gcc.dg/lto/modref-3_0.c
index bd8f96f6ec4..0210d115111 100644
--- a/gcc/testsuite/gcc.dg/lto/modref-3_0.c
+++ b/gcc/testsuite/gcc.dg/lto/modref-3_0.c
@@ -14,4 +14,4 @@ main()
     __builtin_abort ();
   return 0;
 }
-/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: nodirectescape"  "modref"  } } */
+/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: no_direct_clobber no_direct_escape"  "modref"  } } */
diff --git a/gcc/testsuite/gcc.dg/lto/modref-4_0.c b/gcc/testsuite/gcc.dg/lto/modref-4_0.c
index db90b4f1f3d..94375851146 100644
--- a/gcc/testsuite/gcc.dg/lto/modref-4_0.c
+++ b/gcc/testsuite/gcc.dg/lto/modref-4_0.c
@@ -14,4 +14,4 @@ main()
     __builtin_abort ();
   return 0;
 }
-/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: nodirectescape"  "modref"  } } */
+/* { dg-final { scan-wpa-ipa-dump "parm 1 flags: no_direct_clobber no_direct_escape"  "modref"  } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c
index c608408809d..4a6d9e54c23 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-10.c
@@ -17,4 +17,4 @@ main()
 		linker_error ();
 	return 0;
 }
-/* { dg-final { scan-tree-dump "parm 0 flags: noclobber noescape nodirectescape not_returned_directly" "modref1"} } */
+/* { dg-final { scan-tree-dump "no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly no_indirect_read" "modref1"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
index de9ad16879f..cafb4f34894 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-11.c
@@ -10,4 +10,4 @@ find_last (struct linkedlist *l)
    l = l->next;
   return l;
 }
-/* { dg-final { scan-tree-dump "noclobber noescape nodirectescape" "modref1"} } */
+/* { dg-final { scan-tree-dump "parm 0 flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape" "modref1"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c
new file mode 100644
index 00000000000..5a5750425d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-13.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-release_ssa"  } */
+struct wrap {
+	void **array;
+};
+__attribute__ ((noinline))
+void
+write_array (struct wrap *ptr)
+{
+	ptr->array[0]=0;
+}
+int
+test ()
+{
+	void *arrayval;
+	struct wrap w = {&arrayval};
+	write_array (&w);
+	return w.array == &arrayval;
+}
+/* We should deterine that write_array writes to PTR only indirectly.  */
+/* { dg-final { scan-tree-dump "return 1" "releae_ssa"  } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c
index fde31772862..0bee79d769d 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-5.c
@@ -24,4 +24,4 @@ main()
     __builtin_abort ();
   return 0;
 }
-/* { dg-final { scan-tree-dump "parm 1 flags: nodirectescape" "modref1"  } } */
+/* { dg-final { scan-tree-dump "parm 1 flags: no_direct_clobber no_direct_escape" "modref1"  } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
index 2d97a4903ff..7146389a5b4 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
@@ -28,10 +28,10 @@ int test2()
    return a;
 }
 /* Flags for normal call.  */
-/* { dg-final { scan-tree-dump "parm 0 flags: direct noclobber noescape nodirectescape not_returned" "modref1"  } } */
+/* { dg-final { scan-tree-dump "parm 0 flags: no_direct_clobber no_indirect_clobber no_direct_escape no_indirect_escape not_returned_directly not_returned_indirectly no_indirect_read" "modref1"  } } */
 /* Flags for pure call.  */
-/* { dg-final { scan-tree-dump "parm 0 flags: direct not_returned" "modref1"  } } */
+/* { dg-final { scan-tree-dump "parm 0 flags: not_returned_directly not_returned_indirectly no_indirect_read" "modref1"  } } */
 /* Flags for const call.  */
-/* { dg-final { scan-tree-dump "parm 0 flags: not_returned" "modref1"  } } */
+/* { dg-final { scan-tree-dump "parm 0 flags: not_returned_directly" "modref1"  } } */
 /* Overall we want to make "int a" non escaping.  */
 /* { dg-final { scan-tree-dump "return 42" "optimized"  } } */
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index f0c65a25f07..8ab119dc9a2 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -97,32 +97,29 @@ struct die_struct;
 #define ECF_COLD		  (1 << 15)
 
 /* Call argument flags.  */
-/* Nonzero if the argument is not dereferenced recursively, thus only
-   directly reachable memory is read or written.  */
-#define EAF_DIRECT		(1 << 0)
 
-/* Nonzero if memory reached by the argument is not clobbered.  */
-#define EAF_NOCLOBBER		(1 << 1)
+/* Nonzero if the argument is not used by the function.  */
+#define EAF_UNUSED		(1 << 1)
 
-/* Nonzero if the argument does not escape.  */
-#define EAF_NOESCAPE		(1 << 2)
+/* Following flags come in pairs.  First one is about direct dereferences
+   from the parameter, while the second is about memory reachable by
+   recursive dereferences.  */
 
-/* Nonzero if the argument is not used by the function.  */
-#define EAF_UNUSED		(1 << 3)
+/* Nonzero if memory reached by the argument is not clobbered.  */
+#define EAF_NO_DIRECT_CLOBBER	(1 << 2)
+#define EAF_NO_INDIRECT_CLOBBER	(1 << 3)
 
-/* Nonzero if the argument itself does not escape but memory
-   referenced by it can escape.  */
-#define EAF_NODIRECTESCAPE	(1 << 4)
+/* Nonzero if the argument does not escape.  */
+#define EAF_NO_DIRECT_ESCAPE	(1 << 4)
+#define EAF_NO_INDIRECT_ESCAPE	(1 << 5)
 
 /* Nonzero if the argument does not escape to return value.  */
-#define EAF_NOT_RETURNED	(1 << 5)
-
-/* Nonzero if the argument itself does not escape
-   to return value but memory referenced by it may escape.  */
 #define EAF_NOT_RETURNED_DIRECTLY (1 << 6)
+#define EAF_NOT_RETURNED_INDIRECTLY (1 << 7)
 
 /* Nonzero if the argument is not read.  */
-#define EAF_NOREAD		(1 << 7)
+#define EAF_NO_DIRECT_READ	(1 << 8)
+#define EAF_NO_INDIRECT_READ	(1 << 9)
 
 /* Call return flags.  */
 /* Mask for the argument number that is returned.  Lower two bits of
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index eabf6805f2b..17ff6bb582c 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -2874,7 +2874,7 @@ process_args:
       tree op = gimple_call_arg (call, i);
       int flags = gimple_call_arg_flags (call, i);
 
-      if (flags & (EAF_UNUSED | EAF_NOREAD))
+      if (flags & (EAF_UNUSED | EAF_NO_DIRECT_READ))
 	continue;
 
       if (TREE_CODE (op) == WITH_SIZE_EXPR)
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index c70f5af8949..153ddf57a61 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4060,48 +4060,117 @@ static void
 handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
 		 int callescape_id, bool writes_global_memory)
 {
+  int relevant_indirect_flags = EAF_NO_INDIRECT_CLOBBER | EAF_NO_INDIRECT_READ
+				| EAF_NO_INDIRECT_ESCAPE;
+  int relevant_flags = relevant_indirect_flags
+		       | EAF_NO_DIRECT_CLOBBER
+		       | EAF_NO_DIRECT_READ
+		       | EAF_NO_DIRECT_ESCAPE;
+  if (gimple_call_lhs (stmt))
+    {
+      relevant_flags |= EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY;
+      relevant_indirect_flags |= EAF_NOT_RETURNED_INDIRECTLY;
+
+      /* If value is never read from it can not be returned indirectly
+	 (except through the escape solution).
+	 For all flags we get these implications right except for
+	 not_returned because we miss return functions in ipa-prop.  */
+	 
+      if (flags & EAF_NO_DIRECT_READ)
+	flags |= EAF_NOT_RETURNED_INDIRECTLY;
+    }
+
   /* If the argument is not used we can ignore it.
      Similarly argument is invisile for us if it not clobbered, does not
      escape, is not read and can not be returned.  */
-  if ((flags & EAF_UNUSED)
-      || ((flags & (EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOREAD
-		    | EAF_NOT_RETURNED))
-	  == (EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NOREAD
-	      | EAF_NOT_RETURNED)))
+  if ((flags & EAF_UNUSED) || ((flags & relevant_flags) == relevant_flags))
     return;
 
+  /* Produce varinfo for direct accesses to ARG.  */
   varinfo_t tem = new_var_info (NULL_TREE, "callarg", true);
   tem->is_reg_var = true;
   make_constraint_to (tem->id, arg);
   make_any_offset_constraints (tem);
 
-  if (!(flags & EAF_DIRECT))
-    make_transitive_closure_constraints (tem);
+  bool callarg_transitive = false;
+
+  /* As an compile time optimization if we make no difference between
+     direct and indirect accesses make arg transitively closed.
+     This avoids the need to build indir arg and do everything twice.  */
+  if (((flags & EAF_NO_INDIRECT_CLOBBER) != 0)
+      == ((flags & EAF_NO_DIRECT_CLOBBER) != 0)
+      && (((flags & EAF_NO_INDIRECT_READ) != 0)
+	  == ((flags & EAF_NO_DIRECT_READ) != 0))
+      && (((flags & EAF_NO_INDIRECT_ESCAPE) != 0)
+	  == ((flags & EAF_NO_DIRECT_ESCAPE) != 0))
+      && (((flags & EAF_NOT_RETURNED_INDIRECTLY) != 0)
+	  == ((flags & EAF_NOT_RETURNED_DIRECTLY) != 0)))
+    {
+      make_transitive_closure_constraints (tem);
+      callarg_transitive = true;
+      gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ));
+    }
+
+  /* If necessary, produce varinfo for indirect accesses to ARG.  */
+  varinfo_t indir_tem = NULL;
+  if (!callarg_transitive
+      && (flags & relevant_indirect_flags) != relevant_indirect_flags)
+    {
+      struct constraint_expr lhs, rhs;
+      indir_tem = new_var_info (NULL_TREE, "indircallarg", true);
+      indir_tem->is_reg_var = true;
+
+      /* indir_term = *tem.  */
+      lhs.type = SCALAR;
+      lhs.var = indir_tem->id;
+      lhs.offset = 0;
+
+      rhs.type = DEREF;
+      rhs.var = tem->id;
+      rhs.offset = UNKNOWN_OFFSET;
+      process_constraint (new_constraint (lhs, rhs));
+
+      make_any_offset_constraints (indir_tem);
 
-  if (!(flags & EAF_NOT_RETURNED))
+      /* If we do not read indirectly there is no need for transitive closure.
+	 We know there is only one level of indirection.  */
+      if (!(flags & EAF_NO_INDIRECT_READ))
+	make_transitive_closure_constraints (indir_tem);
+      gcc_checking_assert (!(flags & EAF_NO_DIRECT_READ));
+    }
+
+  if (gimple_call_lhs (stmt))
     {
-      struct constraint_expr cexpr;
-      cexpr.var = tem->id;
-      if (flags & EAF_NOT_RETURNED_DIRECTLY)
+      if (!(flags & EAF_NOT_RETURNED_DIRECTLY))
 	{
-	  cexpr.type = DEREF;
-	  cexpr.offset = UNKNOWN_OFFSET;
+	  struct constraint_expr cexpr;
+	  cexpr.var = tem->id;
+	  cexpr.type = SCALAR;
+	  cexpr.offset = 0;
+	  results->safe_push (cexpr);
 	}
-      else
+      if (!callarg_transitive & !(flags & EAF_NOT_RETURNED_INDIRECTLY))
 	{
+	  struct constraint_expr cexpr;
+	  cexpr.var = indir_tem->id;
 	  cexpr.type = SCALAR;
 	  cexpr.offset = 0;
+	  results->safe_push (cexpr);
 	}
-      results->safe_push (cexpr);
     }
 
-  if (!(flags & EAF_NOREAD))
+  if (!(flags & EAF_NO_DIRECT_READ))
     {
       varinfo_t uses = get_call_use_vi (stmt);
       make_copy_constraint (uses, tem->id);
+      if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_READ))
+	make_copy_constraint (uses, indir_tem->id);
     }
+  else
+    /* To read indirectly we need to read directly.  */
+    gcc_checking_assert (flags & EAF_NO_INDIRECT_READ);
 
-  if (!(flags & EAF_NOCLOBBER))
+  if (!(flags & EAF_NO_DIRECT_CLOBBER))
     {
       struct constraint_expr lhs, rhs;
 
@@ -4118,8 +4187,25 @@ handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
       /* callclobbered = arg.  */
       make_copy_constraint (get_call_clobber_vi (stmt), tem->id);
     }
+  if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_CLOBBER))
+    {
+      struct constraint_expr lhs, rhs;
 
-  if (!(flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
+      /* *indir_arg = callescape.  */
+      lhs.type = DEREF;
+      lhs.var = indir_tem->id;
+      lhs.offset = 0;
+
+      rhs.type = SCALAR;
+      rhs.var = callescape_id;
+      rhs.offset = 0;
+      process_constraint (new_constraint (lhs, rhs));
+
+      /* callclobbered = indir_arg.  */
+      make_copy_constraint (get_call_clobber_vi (stmt), indir_tem->id);
+    }
+
+  if (!(flags & (EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE)))
     {
       struct constraint_expr lhs, rhs;
 
@@ -4136,18 +4222,18 @@ handle_call_arg (gcall *stmt, tree arg, vec<ce_s> *results, int flags,
       if (writes_global_memory)
 	make_escape_constraint (arg);
     }
-  else if (!(flags & EAF_NOESCAPE))
+  else if (!callarg_transitive & !(flags & EAF_NO_INDIRECT_ESCAPE))
     {
       struct constraint_expr lhs, rhs;
 
-      /* callescape = *(arg + UNKNOWN);  */
+      /* callescape = *(indir_arg + UNKNOWN);  */
       lhs.var = callescape_id;
       lhs.offset = 0;
       lhs.type = SCALAR;
 
-      rhs.var = tem->id;
-      rhs.offset = UNKNOWN_OFFSET;
-      rhs.type = DEREF;
+      rhs.var = indir_tem->id;
+      rhs.offset = 0;
+      rhs.type = SCALAR;
       process_constraint (new_constraint (lhs, rhs));
 
       if (writes_global_memory)
@@ -4264,20 +4350,22 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results,
       && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (stmt))))
     {
       int flags = gimple_call_retslot_flags (stmt);
-      if ((flags & (EAF_NOESCAPE | EAF_NOT_RETURNED))
-	  != (EAF_NOESCAPE | EAF_NOT_RETURNED))
+      const int relevant_flags = EAF_NO_DIRECT_ESCAPE
+				 | EAF_NOT_RETURNED_DIRECTLY;
+
+      if (!(flags & EAF_UNUSED) && (flags & relevant_flags) != relevant_flags)
 	{
 	  auto_vec<ce_s> tmpc;
 
 	  get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
 
-	  if (!(flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE)))
+	  if (!(flags & EAF_NO_DIRECT_ESCAPE))
 	    {
 	      make_constraints_to (callescape->id, tmpc);
 	      if (writes_global_memory)
 		make_constraints_to (escaped_id, tmpc);
 	    }
-	  if (!(flags & EAF_NOT_RETURNED))
+	  if (!(flags & EAF_NOT_RETURNED_DIRECTLY))
 	    {
 	      struct constraint_expr *c;
 	      unsigned i;
diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c
index d67534f22a8..1df0bcc42c0 100644
--- a/gcc/tree-ssa-uninit.c
+++ b/gcc/tree-ssa-uninit.c
@@ -744,7 +744,8 @@ maybe_warn_pass_by_reference (gcall *stmt, wlimits &wlims)
 	wlims.always_executed = false;
 
       /* Ignore args we are not going to read from.  */
-      if (gimple_call_arg_flags (stmt, argno - 1) & (EAF_UNUSED | EAF_NOREAD))
+      if (gimple_call_arg_flags (stmt, argno - 1)
+	  & (EAF_UNUSED | EAF_NO_DIRECT_READ))
 	continue;
 
       tree arg = gimple_call_arg (stmt, argno - 1);
