Make EAF flags more regular (and expressive)

Richard Biener rguenther@suse.de
Wed Nov 10 07:25:51 GMT 2021


On Tue, 9 Nov 2021, Jan Hubicka wrote:

> Hi,
> I hoped that I am done with EAF flags related changes, but while looking into
> the Fortran testcases I noticed that I have designed them in unnecesarily
> restricted way.  I followed the scheme of NOESCAPE and NODIRECTESCAPE which is
> however the only property tht is naturally transitive.
> 
> This patch replaces the existing flags by 9 flags:
> 
> EAF_UNUSED
> EAF_NO_DIRECT_CLOBBER and EAF_NO_INDIRECT_CLOBBER
> EAF_NO_DIRECT_READ and EAF_NO_INDIRECT_READ
> EAF_NO_DIRECT_ESCAPE and EAF_NO_INDIRECT_ESCAPE
> EAF_NO_DIRECT_READ and EAF_NO_INDIRECT_READ
> 
> So I have removed the unified EAF_DIRECT flag and made each of the flags to come
> in direct and indirect variant.  Newly the indirect variant is not implied by direct
> (well except for escape but it is not special cased in the code)
> Consequently we can analyse i.e. the case where function reads directly and clobber
> indirectly as in the following testcase:
> 
> 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;
> }
> 
> This is pretty common in array descriptors and also C++ pointer wrappers or structures
> containing pointers to arrays.
> 
> Other advantage is that !binds_to_current_def_p functions we can still track the fact
> that the value is not clobbered indirectly while previously we implied EAF_DIRECT
> for all three cases.
> 
> We can now also, in some cases, make difference between single and
> multiple indirection becaue from EAF_READ_INDIRECTLY we know when
> function can not produce deeper indirections.
> 
> Finally the propagation becomes more regular and I hope easier to understand
> because the flags are handled in a symmetric way.
> 
> In tree-ssa-structalias I now produce "callarg" var_info as before and if necessary
> also "indircallarg" for the indirect accesses.  I added some logic to optimize the
> common case where we can not make difference between direct and indirect.
> 
> The patch changes cc1plus build stats from:
> 
> Alias oracle query stats:
>   refs_may_alias_p: 76837664 disambiguations, 101409766 queries
>   ref_maybe_used_by_call_p: 653271 disambiguations, 77840575 queries
>   call_may_clobber_ref_p: 390312 disambiguations, 393172 queries
>   nonoverlapping_component_refs_p: 0 disambiguations, 26140 queries
>   nonoverlapping_refs_since_match_p: 30203 disambiguations, 65079 must overlaps, 96241 queries
>   aliasing_component_refs_p: 57089 disambiguations, 15404304 queries
>   TBAA oracle: 28153258 disambiguations 104252784 queries
>                14994692 are in alias set 0
>                8948831 queries asked about the same object
>                98 queries asked about the same alias set
>                0 access volatile
>                50240521 are dependent in the DAG
>                1915384 are aritificially in conflict with void *
> 
> Modref stats:
>   modref use: 25340 disambiguations, 712706 queries
>   modref clobber: 2341693 disambiguations, 22800331 queries
>   5368354 tbaa queries (0.235451 per modref query)
>   765195 base compares (0.033561 per modref query)
> 
> PTA query stats:
>   pt_solution_includes: 13551618 disambiguations, 40800084 queries
>   pt_solutions_intersect: 1698854 disambiguations, 13763853 queries
> 
> to:
> 
> Alias oracle query stats:
>   refs_may_alias_p: 76925524 disambiguations, 101500189 queries
>   ref_maybe_used_by_call_p: 653442 disambiguations, 77928881 queries
>   call_may_clobber_ref_p: 390427 disambiguations, 393209 queries
>   nonoverlapping_component_refs_p: 0 disambiguations, 26158 queries
>   nonoverlapping_refs_since_match_p: 30370 disambiguations, 65145 must overlaps, 96477 queries
>   aliasing_component_refs_p: 57095 disambiguations, 15404308 queries
>   TBAA oracle: 28166182 disambiguations 104266578 queries
>                15000259 are in alias set 0
>                8961963 queries asked about the same object
>                98 queries asked about the same alias set
>                0 access volatile
>                50218808 are dependent in the DAG
>                1919268 are aritificially in conflict with void *
> 
> Modref stats:
>   modref use: 25381 disambiguations, 712966 queries
>   modref clobber: 2331197 disambiguations, 22827861 queries
>   5350923 tbaa queries (0.234403 per modref query)
>   765444 base compares (0.033531 per modref query)
> 
> PTA query stats:
>   pt_solution_includes: 13593046 disambiguations, 40826434 queries
>   pt_solutions_intersect: 1726400 disambiguations, 13796247 queries
> 
> So only by about 1%. However for tramp3d the difference is more noticeable. From
> 
> Alias oracle query stats:
>   refs_may_alias_p: 5462729 disambiguations, 5799042 queries
>   ref_maybe_used_by_call_p: 23575 disambiguations, 5511000 queries
>   call_may_clobber_ref_p: 2188 disambiguations, 2188 queries
>   nonoverlapping_component_refs_p: 0 disambiguations, 6216 queries
>   nonoverlapping_refs_since_match_p: 270 disambiguations, 4803 must overlaps, 5164 queries
>   aliasing_component_refs_p: 2242 disambiguations, 41884 queries
>   TBAA oracle: 2287136 disambiguations 3598543 queries
>                166373 are in alias set 0
>                769743 queries asked about the same object
>                0 queries asked about the same alias set
>                0 access volatile
>                375144 are dependent in the DAG
>                147 are aritificially in conflict with void *
> 
> Modref stats:
>   modref use: 6124 disambiguations, 55063 queries
>   modref clobber: 43317 disambiguations, 581346 queries
>   121869 tbaa queries (0.209632 per modref query)
>   32045 base compares (0.055122 per modref query)
> 
> PTA query stats:
>   pt_solution_includes: 872055 disambiguations, 1119697 queries
>   pt_solutions_intersect: 106431 disambiguations, 536171 queries
> 
> to:
> 
> Alias oracle query stats:
>   refs_may_alias_p: 5759154 disambiguations, 6095629 queries
>   ref_maybe_used_by_call_p: 24364 disambiguations, 5806584 queries
>   call_may_clobber_ref_p: 2484 disambiguations, 2484 queries
>   nonoverlapping_component_refs_p: 0 disambiguations, 6204 queries
>   nonoverlapping_refs_since_match_p: 283 disambiguations, 4823 must overlaps, 5197 queries
>   aliasing_component_refs_p: 2217 disambiguations, 40633 queries
>   TBAA oracle: 2314375 disambiguations 3649316 queries
>                166981 are in alias set 0
>                785650 queries asked about the same object
>                0 queries asked about the same alias set
>                0 access volatile
>                382163 are dependent in the DAG
>                147 are aritificially in conflict with void *
> 
> Modref stats:
>   modref use: 6132 disambiguations, 55004 queries
>   modref clobber: 45192 disambiguations, 634478 queries
>   126552 tbaa queries (0.199458 per modref query)
>   32340 base compares (0.050971 per modref query)
> 
> PTA query stats:
>   pt_solution_includes: 972939 disambiguations, 1205610 queries
>   pt_solutions_intersect: 103741 disambiguations, 533181 queries
> 
> This is 11% improvement on PTA query stats and 5% overall that is quite nice.
> 
> Bootstrapped/regtested x86_64-linux, OK?

OK.

Thanks,
Richard.

> gcc/ChangeLog:
> 
> 2021-11-09  Jan Hubicka  <hubicka@ucw.cz>
> 
> 	* tree-core.h (EAF_DIRECT): Remove.
> 	(EAF_NOCLOBBER): Remove.
> 	(EAF_UNUSED): Remove.
> 	(EAF_NOESCAPE): Remove.
> 	(EAF_NO_DIRECT_CLOBBER): New.
> 	(EAF_NO_INDIRECT_CLOBBER): New.
> 	(EAF_NODIRECTESCAPE): Remove.
> 	(EAF_NO_DIRECT_ESCAPE): New.
> 	(EAF_NO_INDIRECT_ESCAPE): New.
> 	(EAF_NOT_RETURNED): Remove.
> 	(EAF_NOT_RETURNED_INDIRECTLY): New.
> 	(EAF_NOREAD): Remove.
> 	(EAF_NO_DIRECT_READ): New.
> 	(EAF_NO_INDIRECT_READ): New.
> 	* gimple.c (gimple_call_arg_flags): Update for new flags.
> 	(gimple_call_retslot_flags): Update for new flags.
> 	* ipa-modref.c (dump_eaf_flags): Likewise.
> 	(remove_useless_eaf_flags): Likewise.
> 	(deref_flags): Likewise.
> 	(modref_lattice::init): Likewise.
> 	(modref_lattice::merge): Likewise.
> 	(modref_lattice::merge_direct_load): Likewise.
> 	(modref_lattice::merge_direct_store): Likewise.
> 	(modref_eaf_analysis::merge_call_lhs_flags): Likewise.
> 	(callee_to_caller_flags): Likewise.
> 	(modref_eaf_analysis::analyze_ssa_name): Likewise.
> 	(modref_eaf_analysis::propagate): Likewise.
> 	(modref_merge_call_site_flags): Likewise.
> 	* ipa-modref.h (interposable_eaf_flags): Likewise.
> 	* tree-ssa-alias.c: (ref_maybe_used_by_call_p_1) Likewise.
> 	* tree-ssa-structalias.c (handle_call_arg): Likewise.
> 	(handle_rhs_call): Likewise.
> 	* tree-ssa-uninit.c (maybe_warn_pass_by_reference): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ipa/modref-1.C: Update template.
> 	* gcc.dg/ipa/modref-3.c: Update template.
> 	* gcc.dg/lto/modref-3_0.c: Update template.
> 	* gcc.dg/lto/modref-4_0.c: Update template.
> 	* gcc.dg/tree-ssa/modref-10.c: Update template.
> 	* gcc.dg/tree-ssa/modref-11.c: Update template.
> 	* gcc.dg/tree-ssa/modref-5.c: Update template.
> 	* gcc.dg/tree-ssa/modref-6.c: Update template.
> 	* gcc.dg/tree-ssa/modref-13.c: New test.
> 
> 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);
> 
> 

-- 
Richard Biener <rguenther@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)


More information about the Gcc-patches mailing list