Make EAF flags more regular (and expressive)

Message ID 20211109204548.GA97553@kam.mff.cuni.cz
State New
Headers
Series Make EAF flags more regular (and expressive) |

Commit Message

Jan Hubicka Nov. 9, 2021, 8:45 p.m. UTC
  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?

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.
  

Comments

Richard Biener Nov. 10, 2021, 7:25 a.m. UTC | #1
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);
> 
>
  

Patch

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);