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  <hubi...@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 <rguent...@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Ivo Totev; HRB 36809 (AG Nuernberg)

Reply via email to