diff mbox series

Add EAF_NOT_RETURNED flag

Message ID 20210716084126.GC91970@kam.mff.cuni.cz
State New
Headers show
Series Add EAF_NOT_RETURNED flag | expand

Commit Message

Jan Hubicka July 16, 2021, 8:41 a.m. UTC
Hi,
this patch adds EAF_NOT_RETURNED flag which is determined by ipa-modref
and used both to improve its propagation (it can stop propagating flags
from call parameter to return value if EAF_NOT_RETURNED is earlier
determined for callee) and also to improve points-to constraints in
tree-ssa-structalias (since return value constrain does not need to
contain the parameters that are not returned.

No true IPA propagatoin is done, but I will look into it incrementally
(there is general problem of lacking return functions).

We now have 8 EAF flags so it is no longer possible to store them to
char datatype so I added eaf_flags_t. I also disabled some shortcuts in
ipa-moderef which ignored CONST functions since EAF_UNUSED and
EAF_NOT_RETURNED is useful there, too.

The tree-ssa-structlias part is not very precise. I simply avoid adding
constraint copying callused to rhs if all parameters are
EAF_NOT_RETURNED.  This is overly conservative, but if one just skips
not returned parameters in call used we will optimize out initialization
of memory that is read by the callee but does not escape or gets
returned.  

It would be more precise to push arguments to rhsc vector individually,
but I would like to do this incrementally since this results in more
constraints and pehraps we should be smart and produce them only if
there is a mix of not returned and returned parameters or so.

Bootstrapped/regtested x86_64-linux, also ltobootstrapped with c++ only,
OK?

gcc/ChangeLog:

2021-07-16  Jan Hubicka  <hubicka@ucw.cz>

	* ipa-modref.c (struct escape_entry): Use eaf_flags_t.
	(dump_eaf_flags): Dump EAF_NOT_RETURNED
	(eaf_flags_useful_p): Use eaf_fleags_t; handle const functions
	and EAF_NOT_RETURNED.
	(modref_summary::useful_p): Likewise.
	(modref_summary_lto::useful_p): Likewise.
	(struct) modref_summary_lto: Use eaf_fleags_t.
	(deref_flags): Handle EAF_NOT_RETURNED.
	(struct escape_point): Use min_flags.
	(modref_lattice::init): Add EAF_NOT_RETURNED.
	(merge_call_lhs_flags): Ignore EAF_NOT_RETURNED functions
	(analyze_ssa_name_flags): Clear EAF_NOT_RETURNED on return;
	handle call flags.
	(analyze_parms): Also analyze const functions; update conition on
	flags usefulness.
	(modref_write): Update streaming.
	(read_section): Update streaming.
	(remap_arg_flags): Use eaf_flags_t.
	(modref_merge_call_site_flags): Hanlde EAF_NOT_RETURNED.
	* ipa-modref.h: (eaf_flags_t): New typedef.
	(struct modref_summary): Use eaf_flags_t.
	* tree-core.h (EAF_NOT_RETURNED): New constant.
	* tree-ssa-structalias.c (handle_rhs_call): Hanlde EAF_NOT_RETURNED.
	(handle_const_call): Handle EAF_UNUSED and EAF_NOT_RETURNED.
	(handle_pure_call): Handle EAF_NOT_RETURNED.

gcc/testsuite/ChangeLog:

2021-07-16  Jan Hubicka  <hubicka@ucw.cz>

	* gcc.dg/tree-ssa/modref-6.c: New test.

Comments

Richard Biener July 16, 2021, 10:38 a.m. UTC | #1
On Fri, 16 Jul 2021, Jan Hubicka wrote:

> Hi,
> this patch adds EAF_NOT_RETURNED flag which is determined by ipa-modref
> and used both to improve its propagation (it can stop propagating flags
> from call parameter to return value if EAF_NOT_RETURNED is earlier
> determined for callee) and also to improve points-to constraints in
> tree-ssa-structalias (since return value constrain does not need to
> contain the parameters that are not returned.
> 
> No true IPA propagatoin is done, but I will look into it incrementally
> (there is general problem of lacking return functions).
> 
> We now have 8 EAF flags so it is no longer possible to store them to
> char datatype so I added eaf_flags_t. I also disabled some shortcuts in
> ipa-moderef which ignored CONST functions since EAF_UNUSED and
> EAF_NOT_RETURNED is useful there, too.
> 
> The tree-ssa-structlias part is not very precise. I simply avoid adding
> constraint copying callused to rhs if all parameters are
> EAF_NOT_RETURNED.  This is overly conservative, but if one just skips
> not returned parameters in call used we will optimize out initialization
> of memory that is read by the callee but does not escape or gets
> returned.  
> 
> It would be more precise to push arguments to rhsc vector individually,
> but I would like to do this incrementally since this results in more
> constraints and pehraps we should be smart and produce them only if
> there is a mix of not returned and returned parameters or so.
> 
> Bootstrapped/regtested x86_64-linux, also ltobootstrapped with c++ only,
> OK?

OK.  Btw, there's some modref propagation correctness fix from Alex
which needs looking at - 
https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573137.html

Thanks,
Richard.

> gcc/ChangeLog:
> 
> 2021-07-16  Jan Hubicka  <hubicka@ucw.cz>
> 
> 	* ipa-modref.c (struct escape_entry): Use eaf_flags_t.
> 	(dump_eaf_flags): Dump EAF_NOT_RETURNED
> 	(eaf_flags_useful_p): Use eaf_fleags_t; handle const functions
> 	and EAF_NOT_RETURNED.
> 	(modref_summary::useful_p): Likewise.
> 	(modref_summary_lto::useful_p): Likewise.
> 	(struct) modref_summary_lto: Use eaf_fleags_t.
> 	(deref_flags): Handle EAF_NOT_RETURNED.
> 	(struct escape_point): Use min_flags.
> 	(modref_lattice::init): Add EAF_NOT_RETURNED.
> 	(merge_call_lhs_flags): Ignore EAF_NOT_RETURNED functions
> 	(analyze_ssa_name_flags): Clear EAF_NOT_RETURNED on return;
> 	handle call flags.
> 	(analyze_parms): Also analyze const functions; update conition on
> 	flags usefulness.
> 	(modref_write): Update streaming.
> 	(read_section): Update streaming.
> 	(remap_arg_flags): Use eaf_flags_t.
> 	(modref_merge_call_site_flags): Hanlde EAF_NOT_RETURNED.
> 	* ipa-modref.h: (eaf_flags_t): New typedef.
> 	(struct modref_summary): Use eaf_flags_t.
> 	* tree-core.h (EAF_NOT_RETURNED): New constant.
> 	* tree-ssa-structalias.c (handle_rhs_call): Hanlde EAF_NOT_RETURNED.
> 	(handle_const_call): Handle EAF_UNUSED and EAF_NOT_RETURNED.
> 	(handle_pure_call): Handle EAF_NOT_RETURNED.
> 
> gcc/testsuite/ChangeLog:
> 
> 2021-07-16  Jan Hubicka  <hubicka@ucw.cz>
> 
> 	* gcc.dg/tree-ssa/modref-6.c: New test.
> 
> diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
> index d5a8332fb55..734d7d066bc 100644
> --- a/gcc/ipa-modref.c
> +++ b/gcc/ipa-modref.c
> @@ -86,6 +86,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "stringpool.h"
>  #include "tree-ssanames.h"
>  
> +
>  namespace {
>  
>  /* We record fnspec specifiers for call edges since they depends on actual
> @@ -135,7 +136,7 @@ struct escape_entry
>    /* Argument it escapes to.  */
>    unsigned int arg;
>    /* Minimal flags known about the argument.  */
> -  char min_flags;
> +  eaf_flags_t min_flags;
>    /* Does it escape directly or indirectly?  */
>    bool direct;
>  };
> @@ -155,6 +156,8 @@ dump_eaf_flags (FILE *out, int flags, bool newline = true)
>      fprintf (out, " nodirectescape");
>    if (flags & EAF_UNUSED)
>      fprintf (out, " unused");
> +  if (flags & EAF_NOT_RETURNED)
> +    fprintf (out, " not_returned");
>    if (newline)
>    fprintf (out, "\n");
>  }
> @@ -278,12 +281,17 @@ modref_summary::~modref_summary ()
>  /* Return true if FLAGS holds some useful information.  */
>  
>  static bool
> -eaf_flags_useful_p (vec <unsigned char> &flags, int ecf_flags)
> +eaf_flags_useful_p (vec <eaf_flags_t> &flags, int ecf_flags)
>  {
>    for (unsigned i = 0; i < flags.length (); i++)
> -    if (ecf_flags & ECF_PURE)
> +    if (ecf_flags & ECF_CONST)
>        {
> -	if (flags[i] & (EAF_UNUSED | EAF_DIRECT))
> +	if (flags[i] & (EAF_UNUSED | EAF_NOT_RETURNED))
> +	  return true;
> +      }
> +    else if (ecf_flags & ECF_PURE)
> +      {
> +	if (flags[i] & (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED))
>  	  return true;
>        }
>      else
> @@ -300,13 +308,15 @@ eaf_flags_useful_p (vec <unsigned char> &flags, int ecf_flags)
>  bool
>  modref_summary::useful_p (int ecf_flags, bool check_flags)
>  {
> -  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
> +  if (ecf_flags & ECF_NOVOPS)
>      return false;
>    if (arg_flags.length () && !check_flags)
>      return true;
>    if (check_flags && eaf_flags_useful_p (arg_flags, ecf_flags))
>      return true;
>    arg_flags.release ();
> +  if (ecf_flags & ECF_CONST)
> +    return false;
>    if (loads && !loads->every_base)
>      return true;
>    if (ecf_flags & ECF_PURE)
> @@ -325,7 +335,7 @@ struct GTY(()) modref_summary_lto
>       more verbose and thus more likely to hit the limits.  */
>    modref_records_lto *loads;
>    modref_records_lto *stores;
> -  auto_vec<unsigned char> GTY((skip)) arg_flags;
> +  auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
>    bool writes_errno;
>  
>    modref_summary_lto ();
> @@ -356,13 +366,15 @@ modref_summary_lto::~modref_summary_lto ()
>  bool
>  modref_summary_lto::useful_p (int ecf_flags, bool check_flags)
>  {
> -  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
> +  if (ecf_flags & ECF_NOVOPS)
>      return false;
>    if (arg_flags.length () && !check_flags)
>      return true;
>    if (check_flags && eaf_flags_useful_p (arg_flags, ecf_flags))
>      return true;
>    arg_flags.release ();
> +  if (ecf_flags & ECF_CONST)
> +    return false;
>    if (loads && !loads->every_base)
>      return true;
>    if (ecf_flags & ECF_PURE)
> @@ -1317,6 +1329,8 @@ deref_flags (int flags, bool ignore_stores)
>        if ((flags & EAF_NOESCAPE) || ignore_stores)
>  	ret |= EAF_NOESCAPE;
>      }
> +  if (flags & EAF_NOT_RETURNED)
> +    ret |= EAF_NOT_RETURNED;
>    return ret;
>  }
>  
> @@ -1332,7 +1346,7 @@ struct escape_point
>    int arg;
>    /* Flags already known about the argument (this can save us from recording
>       esape points if local analysis did good job already).  */
> -  char min_flags;
> +  eaf_flags_t min_flags;
>    /* Does value escape directly or indiretly?  */
>    bool direct;
>  };
> @@ -1366,7 +1380,7 @@ void
>  modref_lattice::init ()
>  {
>    flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
> -	  | EAF_NODIRECTESCAPE;
> +	  | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED;
>    open = true;
>    known = false;
>  }
> @@ -1539,6 +1553,9 @@ merge_call_lhs_flags (gcall *call, int arg, int index, bool deref,
>        && (flags & ERF_RETURN_ARG_MASK) != arg)
>      return;
>  
> +  if (gimple_call_arg_flags (call, arg) & (EAF_NOT_RETURNED | EAF_UNUSED))
> +    return;
> +
>    /* If return value is SSA name determine its flags.  */
>    if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
>      {
> @@ -1613,9 +1630,12 @@ analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
>        if (greturn *ret = dyn_cast <greturn *> (use_stmt))
>  	{
>  	  if (gimple_return_retval (ret) == name)
> -	    lattice[index].merge (~EAF_UNUSED);
> +	    lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED));
>  	  else if (memory_access_to (gimple_return_retval (ret), name))
> -	    lattice[index].merge_direct_load ();
> +	    {
> +	      lattice[index].merge_direct_load ();
> +	      lattice[index].merge (~EAF_NOT_RETURNED);
> +	    }
>  	}
>        /* Account for LHS store, arg loads and flags from callee function.  */
>        else if (gcall *call = dyn_cast <gcall *> (use_stmt))
> @@ -1666,7 +1686,8 @@ analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
>  		  {
>  		    if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
>  		      {
> -			int call_flags = gimple_call_arg_flags (call, i);
> +			int call_flags = gimple_call_arg_flags (call, i)
> +					 | EAF_NOT_RETURNED;
>  			if (ignore_stores)
>  			  call_flags |= EAF_NOCLOBBER | EAF_NOESCAPE
>  					| EAF_NODIRECTESCAPE;
> @@ -1689,7 +1710,8 @@ analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
>  		    else
>  		      {
>  			int call_flags = deref_flags
> -			   (gimple_call_arg_flags (call, i), ignore_stores);
> +			   (gimple_call_arg_flags (call, i)
> +			    | EAF_NOT_RETURNED, ignore_stores);
>  			if (!record_ipa)
>  			  lattice[index].merge (call_flags);
>  			else
> @@ -1819,8 +1841,8 @@ analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
>    unsigned int count = 0;
>    int ecf_flags = flags_from_decl_or_type (current_function_decl);
>  
> -  /* For const functions we have nothing to gain by EAF flags.  */
> -  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
> +  /* For novops functions we have nothing to gain by EAF flags.  */
> +  if (ecf_flags & ECF_NOVOPS)
>      return;
>  
>    for (tree parm = DECL_ARGUMENTS (current_function_decl); parm;
> @@ -1863,7 +1885,11 @@ analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
>        /* For pure functions we have implicit NOCLOBBER
>  	 and NOESCAPE.  */
>        if (ecf_flags & ECF_PURE)
> -	flags &= ~(EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NODIRECTESCAPE);
> +	flags &= (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED);
> +      /* Only useful flags for const function are EAF_NOT_RETURNED and
> +         EAF_UNUSED.  */
> +      if (ecf_flags & ECF_CONST)
> +	flags &= (EAF_UNUSED | EAF_NOT_RETURNED);
>  
>        if (flags)
>  	{
> @@ -2518,7 +2544,7 @@ modref_write ()
>  
>  	  streamer_write_uhwi (ob, r->arg_flags.length ());
>  	  for (unsigned int i = 0; i < r->arg_flags.length (); i++)
> -	    streamer_write_char_stream (ob->main_stream, r->arg_flags[i]);
> +	    streamer_write_uhwi (ob, r->arg_flags[i]);
>  
>  	  write_modref_records (r->loads, ob);
>  	  write_modref_records (r->stores, ob);
> @@ -2609,7 +2635,7 @@ read_section (struct lto_file_decl_data *file_data, const char *data,
>  	modref_sum_lto->arg_flags.reserve_exact (args);
>        for (unsigned int i = 0; i < args; i++)
>  	{
> -	  unsigned char flags = streamer_read_uchar (&ib);
> +	  eaf_flags_t flags = streamer_read_uhwi (&ib);
>  	  if (modref_sum)
>  	    modref_sum->arg_flags.quick_push (flags);
>  	  if (modref_sum_lto)
> @@ -2713,9 +2739,9 @@ modref_read (void)
>  /* Recompute arg_flags for param adjustments in INFO.  */
>  
>  static void
> -remap_arg_flags (auto_vec <unsigned char> &arg_flags, clone_info *info)
> +remap_arg_flags (auto_vec <eaf_flags_t> &arg_flags, clone_info *info)
>  {
> -  auto_vec<unsigned char> old = arg_flags.copy ();
> +  auto_vec<eaf_flags_t> old = arg_flags.copy ();
>    int max = -1;
>    size_t i;
>    ipa_adjusted_param *p;
> @@ -3665,8 +3691,9 @@ modref_merge_call_site_flags (escape_summary *sum,
>  	  flags |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
>  	  flags_lto |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
>  	}
> -      flags |= ee->min_flags;
> -      flags_lto |= ee->min_flags;
> +      /* Returning the value is already accounted to at local propagation.  */
> +      flags |= ee->min_flags | EAF_NOT_RETURNED;
> +      flags_lto |= ee->min_flags | EAF_NOT_RETURNED;
>        if (!(flags & EAF_UNUSED)
>  	  && cur_summary && ee->parm_index < cur_summary->arg_flags.length ())
>  	{
> diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
> index 8af62b30d5e..498cc2414ac 100644
> --- a/gcc/ipa-modref.h
> +++ b/gcc/ipa-modref.h
> @@ -21,6 +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 short eaf_flags_t;
>  
>  /* Single function summary.  */
>  
> @@ -29,7 +30,7 @@ struct GTY(()) modref_summary
>    /* Load and stores in function (transitively closed to all callees)  */
>    modref_records *loads;
>    modref_records *stores;
> -  auto_vec<unsigned char> GTY((skip)) arg_flags;
> +  auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
>    bool writes_errno;
>  
>    modref_summary ();
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
> new file mode 100644
> index 00000000000..a3ac23ce666
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
> @@ -0,0 +1,37 @@
> +/* { dg-options "-O2 -fdump-tree-modref1 -fdump-tree-optimized"  } */
> +/* { dg-do compile } */
> +int c;
> +__attribute__ ((noinline))
> +int *test (int *b)
> +{
> +  c++;
> +  return *b ? &c : 0;
> +}
> +__attribute__ ((noinline, pure))
> +int *pure_test (int *b)
> +{
> +  return *b && c ? &c : 0;
> +}
> +__attribute__ ((noinline, const))
> +int *const_test (int *b)
> +{
> +  return b ? &c : 0;
> +}
> +void escape (int *);
> +
> +int test2()
> +{
> +   int a = 42;
> +   escape (test (&a));
> +   escape (pure_test (&a));
> +   escape (const_test (&a));
> +   return a;
> +}
> +/* Flags for normal call.  */
> +/* { dg-final { scan-tree-dump "parm 0 flags: direct noclobber noescape nodirectescape not_returned" "modref1"  } } */
> +/* Flags for pure call.  */
> +/* { dg-final { scan-tree-dump "parm 0 flags: direct not_returned" "modref1"  } } */
> +/* Flags for const call.  */
> +/* { dg-final { scan-tree-dump "parm 0 flags: unused not_returned" "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 e15e6c651f0..d2aa0bbbc5c 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -114,6 +114,9 @@ struct die_struct;
>     referenced by it can escape.  */
>  #define EAF_NODIRECTESCAPE	(1 << 4)
>  
> +/* Nonzero if the argument does not escape to return value.  */
> +#define EAF_NOT_RETURNED	(1 << 8)
> +
>  /* Call return flags.  */
>  /* Mask for the argument number that is returned.  Lower two bits of
>     the return flags, encodes argument slots zero to three.  */
> diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
> index 7163438e23d..71894b38ff9 100644
> --- a/gcc/tree-ssa-structalias.c
> +++ b/gcc/tree-ssa-structalias.c
> @@ -4082,9 +4082,12 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
>  	  if (!(flags & EAF_DIRECT))
>  	    make_transitive_closure_constraints (tem);
>  	  make_copy_constraint (uses, tem->id);
> +	  /* TODO: This is overly conservative when some parameters are
> +	     returned while others are not.  */
> +	  if (!(flags & EAF_NOT_RETURNED))
> +	    returns_uses = true;
>  	  if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
>  	    make_indirect_escape_constraint (tem);
> -	  returns_uses = true;
>  	}
>        else if (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE))
>  	{
> @@ -4098,6 +4101,8 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
>  	  if (!(flags & EAF_DIRECT))
>  	    make_transitive_closure_constraints (tem);
>  	  make_copy_constraint (uses, tem->id);
> +	  if (!(flags & EAF_NOT_RETURNED))
> +	    returns_uses = true;
>  	  make_copy_constraint (clobbers, tem->id);
>  	  /* Add *tem = nonlocal, do not add *tem = callused as
>  	     EAF_NOESCAPE parameters do not escape to other parameters
> @@ -4111,7 +4116,6 @@ handle_rhs_call (gcall *stmt, vec<ce_s> *results)
>  	  process_constraint (new_constraint (lhs, rhs));
>  	  if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
>  	    make_indirect_escape_constraint (tem);
> -	  returns_uses = true;
>  	}
>        else
>  	make_escape_constraint (arg);
> @@ -4261,13 +4265,18 @@ handle_const_call (gcall *stmt, vec<ce_s> *results)
>  
>    /* May return offsetted arguments.  */
>    varinfo_t tem = NULL;
> -  if (gimple_call_num_args (stmt) != 0)
> -    {
> -      tem = new_var_info (NULL_TREE, "callarg", true);
> -      tem->is_reg_var = true;
> -    }
>    for (k = 0; k < gimple_call_num_args (stmt); ++k)
>      {
> +      int flags = gimple_call_arg_flags (stmt, k);
> +
> +      /* If the argument is not used or not returned we can ignore it.  */
> +      if (flags & (EAF_UNUSED | EAF_NOT_RETURNED))
> +	continue;
> +      if (!tem)
> +	{
> +	  tem = new_var_info (NULL_TREE, "callarg", true);
> +	  tem->is_reg_var = true;
> +	}
>        tree arg = gimple_call_arg (stmt, k);
>        auto_vec<ce_s> argc;
>        get_constraint_for_rhs (arg, &argc);
> @@ -4298,6 +4307,7 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
>    struct constraint_expr rhsc;
>    unsigned i;
>    varinfo_t uses = NULL;
> +  bool record_uses = false;
>  
>    /* Memory reached from pointer arguments is call-used.  */
>    for (i = 0; i < gimple_call_num_args (stmt); ++i)
> @@ -4315,6 +4325,8 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
>  	  make_transitive_closure_constraints (uses);
>  	}
>        make_constraint_to (uses->id, arg);
> +      if (!(flags & EAF_NOT_RETURNED))
> +	record_uses = true;
>      }
>  
>    /* The static chain is used as well.  */
> @@ -4327,6 +4339,7 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
>  	  make_transitive_closure_constraints (uses);
>  	}
>        make_constraint_to (uses->id, gimple_call_chain (stmt));
> +      record_uses = true;
>      }
>  
>    /* And if we applied NRV the address of the return slot.  */
> @@ -4343,10 +4356,11 @@ handle_pure_call (gcall *stmt, vec<ce_s> *results)
>        auto_vec<ce_s> tmpc;
>        get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
>        make_constraints_to (uses->id, tmpc);
> +      record_uses = true;
>      }
>  
>    /* Pure functions may return call-used and nonlocal memory.  */
> -  if (uses)
> +  if (record_uses)
>      {
>        rhsc.var = uses->id;
>        rhsc.offset = 0;
>
diff mbox series

Patch

diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index d5a8332fb55..734d7d066bc 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -86,6 +86,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "tree-ssanames.h"
 
+
 namespace {
 
 /* We record fnspec specifiers for call edges since they depends on actual
@@ -135,7 +136,7 @@  struct escape_entry
   /* Argument it escapes to.  */
   unsigned int arg;
   /* Minimal flags known about the argument.  */
-  char min_flags;
+  eaf_flags_t min_flags;
   /* Does it escape directly or indirectly?  */
   bool direct;
 };
@@ -155,6 +156,8 @@  dump_eaf_flags (FILE *out, int flags, bool newline = true)
     fprintf (out, " nodirectescape");
   if (flags & EAF_UNUSED)
     fprintf (out, " unused");
+  if (flags & EAF_NOT_RETURNED)
+    fprintf (out, " not_returned");
   if (newline)
   fprintf (out, "\n");
 }
@@ -278,12 +281,17 @@  modref_summary::~modref_summary ()
 /* Return true if FLAGS holds some useful information.  */
 
 static bool
-eaf_flags_useful_p (vec <unsigned char> &flags, int ecf_flags)
+eaf_flags_useful_p (vec <eaf_flags_t> &flags, int ecf_flags)
 {
   for (unsigned i = 0; i < flags.length (); i++)
-    if (ecf_flags & ECF_PURE)
+    if (ecf_flags & ECF_CONST)
       {
-	if (flags[i] & (EAF_UNUSED | EAF_DIRECT))
+	if (flags[i] & (EAF_UNUSED | EAF_NOT_RETURNED))
+	  return true;
+      }
+    else if (ecf_flags & ECF_PURE)
+      {
+	if (flags[i] & (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED))
 	  return true;
       }
     else
@@ -300,13 +308,15 @@  eaf_flags_useful_p (vec <unsigned char> &flags, int ecf_flags)
 bool
 modref_summary::useful_p (int ecf_flags, bool check_flags)
 {
-  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
+  if (ecf_flags & ECF_NOVOPS)
     return false;
   if (arg_flags.length () && !check_flags)
     return true;
   if (check_flags && eaf_flags_useful_p (arg_flags, ecf_flags))
     return true;
   arg_flags.release ();
+  if (ecf_flags & ECF_CONST)
+    return false;
   if (loads && !loads->every_base)
     return true;
   if (ecf_flags & ECF_PURE)
@@ -325,7 +335,7 @@  struct GTY(()) modref_summary_lto
      more verbose and thus more likely to hit the limits.  */
   modref_records_lto *loads;
   modref_records_lto *stores;
-  auto_vec<unsigned char> GTY((skip)) arg_flags;
+  auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
   bool writes_errno;
 
   modref_summary_lto ();
@@ -356,13 +366,15 @@  modref_summary_lto::~modref_summary_lto ()
 bool
 modref_summary_lto::useful_p (int ecf_flags, bool check_flags)
 {
-  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
+  if (ecf_flags & ECF_NOVOPS)
     return false;
   if (arg_flags.length () && !check_flags)
     return true;
   if (check_flags && eaf_flags_useful_p (arg_flags, ecf_flags))
     return true;
   arg_flags.release ();
+  if (ecf_flags & ECF_CONST)
+    return false;
   if (loads && !loads->every_base)
     return true;
   if (ecf_flags & ECF_PURE)
@@ -1317,6 +1329,8 @@  deref_flags (int flags, bool ignore_stores)
       if ((flags & EAF_NOESCAPE) || ignore_stores)
 	ret |= EAF_NOESCAPE;
     }
+  if (flags & EAF_NOT_RETURNED)
+    ret |= EAF_NOT_RETURNED;
   return ret;
 }
 
@@ -1332,7 +1346,7 @@  struct escape_point
   int arg;
   /* Flags already known about the argument (this can save us from recording
      esape points if local analysis did good job already).  */
-  char min_flags;
+  eaf_flags_t min_flags;
   /* Does value escape directly or indiretly?  */
   bool direct;
 };
@@ -1366,7 +1380,7 @@  void
 modref_lattice::init ()
 {
   flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED
-	  | EAF_NODIRECTESCAPE;
+	  | EAF_NODIRECTESCAPE | EAF_NOT_RETURNED;
   open = true;
   known = false;
 }
@@ -1539,6 +1553,9 @@  merge_call_lhs_flags (gcall *call, int arg, int index, bool deref,
       && (flags & ERF_RETURN_ARG_MASK) != arg)
     return;
 
+  if (gimple_call_arg_flags (call, arg) & (EAF_NOT_RETURNED | EAF_UNUSED))
+    return;
+
   /* If return value is SSA name determine its flags.  */
   if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
     {
@@ -1613,9 +1630,12 @@  analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
       if (greturn *ret = dyn_cast <greturn *> (use_stmt))
 	{
 	  if (gimple_return_retval (ret) == name)
-	    lattice[index].merge (~EAF_UNUSED);
+	    lattice[index].merge (~(EAF_UNUSED | EAF_NOT_RETURNED));
 	  else if (memory_access_to (gimple_return_retval (ret), name))
-	    lattice[index].merge_direct_load ();
+	    {
+	      lattice[index].merge_direct_load ();
+	      lattice[index].merge (~EAF_NOT_RETURNED);
+	    }
 	}
       /* Account for LHS store, arg loads and flags from callee function.  */
       else if (gcall *call = dyn_cast <gcall *> (use_stmt))
@@ -1666,7 +1686,8 @@  analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
 		  {
 		    if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
 		      {
-			int call_flags = gimple_call_arg_flags (call, i);
+			int call_flags = gimple_call_arg_flags (call, i)
+					 | EAF_NOT_RETURNED;
 			if (ignore_stores)
 			  call_flags |= EAF_NOCLOBBER | EAF_NOESCAPE
 					| EAF_NODIRECTESCAPE;
@@ -1689,7 +1710,8 @@  analyze_ssa_name_flags (tree name, vec<modref_lattice> &lattice, int depth,
 		    else
 		      {
 			int call_flags = deref_flags
-			   (gimple_call_arg_flags (call, i), ignore_stores);
+			   (gimple_call_arg_flags (call, i)
+			    | EAF_NOT_RETURNED, ignore_stores);
 			if (!record_ipa)
 			  lattice[index].merge (call_flags);
 			else
@@ -1819,8 +1841,8 @@  analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
   unsigned int count = 0;
   int ecf_flags = flags_from_decl_or_type (current_function_decl);
 
-  /* For const functions we have nothing to gain by EAF flags.  */
-  if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
+  /* For novops functions we have nothing to gain by EAF flags.  */
+  if (ecf_flags & ECF_NOVOPS)
     return;
 
   for (tree parm = DECL_ARGUMENTS (current_function_decl); parm;
@@ -1863,7 +1885,11 @@  analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
       /* For pure functions we have implicit NOCLOBBER
 	 and NOESCAPE.  */
       if (ecf_flags & ECF_PURE)
-	flags &= ~(EAF_NOCLOBBER | EAF_NOESCAPE | EAF_NODIRECTESCAPE);
+	flags &= (EAF_UNUSED | EAF_DIRECT | EAF_NOT_RETURNED);
+      /* Only useful flags for const function are EAF_NOT_RETURNED and
+         EAF_UNUSED.  */
+      if (ecf_flags & ECF_CONST)
+	flags &= (EAF_UNUSED | EAF_NOT_RETURNED);
 
       if (flags)
 	{
@@ -2518,7 +2544,7 @@  modref_write ()
 
 	  streamer_write_uhwi (ob, r->arg_flags.length ());
 	  for (unsigned int i = 0; i < r->arg_flags.length (); i++)
-	    streamer_write_char_stream (ob->main_stream, r->arg_flags[i]);
+	    streamer_write_uhwi (ob, r->arg_flags[i]);
 
 	  write_modref_records (r->loads, ob);
 	  write_modref_records (r->stores, ob);
@@ -2609,7 +2635,7 @@  read_section (struct lto_file_decl_data *file_data, const char *data,
 	modref_sum_lto->arg_flags.reserve_exact (args);
       for (unsigned int i = 0; i < args; i++)
 	{
-	  unsigned char flags = streamer_read_uchar (&ib);
+	  eaf_flags_t flags = streamer_read_uhwi (&ib);
 	  if (modref_sum)
 	    modref_sum->arg_flags.quick_push (flags);
 	  if (modref_sum_lto)
@@ -2713,9 +2739,9 @@  modref_read (void)
 /* Recompute arg_flags for param adjustments in INFO.  */
 
 static void
-remap_arg_flags (auto_vec <unsigned char> &arg_flags, clone_info *info)
+remap_arg_flags (auto_vec <eaf_flags_t> &arg_flags, clone_info *info)
 {
-  auto_vec<unsigned char> old = arg_flags.copy ();
+  auto_vec<eaf_flags_t> old = arg_flags.copy ();
   int max = -1;
   size_t i;
   ipa_adjusted_param *p;
@@ -3665,8 +3691,9 @@  modref_merge_call_site_flags (escape_summary *sum,
 	  flags |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
 	  flags_lto |= EAF_NOESCAPE | EAF_NOCLOBBER | EAF_NODIRECTESCAPE;
 	}
-      flags |= ee->min_flags;
-      flags_lto |= ee->min_flags;
+      /* Returning the value is already accounted to at local propagation.  */
+      flags |= ee->min_flags | EAF_NOT_RETURNED;
+      flags_lto |= ee->min_flags | EAF_NOT_RETURNED;
       if (!(flags & EAF_UNUSED)
 	  && cur_summary && ee->parm_index < cur_summary->arg_flags.length ())
 	{
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 8af62b30d5e..498cc2414ac 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -21,6 +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 short eaf_flags_t;
 
 /* Single function summary.  */
 
@@ -29,7 +30,7 @@  struct GTY(()) modref_summary
   /* Load and stores in function (transitively closed to all callees)  */
   modref_records *loads;
   modref_records *stores;
-  auto_vec<unsigned char> GTY((skip)) arg_flags;
+  auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
   bool writes_errno;
 
   modref_summary ();
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
new file mode 100644
index 00000000000..a3ac23ce666
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-6.c
@@ -0,0 +1,37 @@ 
+/* { dg-options "-O2 -fdump-tree-modref1 -fdump-tree-optimized"  } */
+/* { dg-do compile } */
+int c;
+__attribute__ ((noinline))
+int *test (int *b)
+{
+  c++;
+  return *b ? &c : 0;
+}
+__attribute__ ((noinline, pure))
+int *pure_test (int *b)
+{
+  return *b && c ? &c : 0;
+}
+__attribute__ ((noinline, const))
+int *const_test (int *b)
+{
+  return b ? &c : 0;
+}
+void escape (int *);
+
+int test2()
+{
+   int a = 42;
+   escape (test (&a));
+   escape (pure_test (&a));
+   escape (const_test (&a));
+   return a;
+}
+/* Flags for normal call.  */
+/* { dg-final { scan-tree-dump "parm 0 flags: direct noclobber noescape nodirectescape not_returned" "modref1"  } } */
+/* Flags for pure call.  */
+/* { dg-final { scan-tree-dump "parm 0 flags: direct not_returned" "modref1"  } } */
+/* Flags for const call.  */
+/* { dg-final { scan-tree-dump "parm 0 flags: unused not_returned" "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 e15e6c651f0..d2aa0bbbc5c 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -114,6 +114,9 @@  struct die_struct;
    referenced by it can escape.  */
 #define EAF_NODIRECTESCAPE	(1 << 4)
 
+/* Nonzero if the argument does not escape to return value.  */
+#define EAF_NOT_RETURNED	(1 << 8)
+
 /* Call return flags.  */
 /* Mask for the argument number that is returned.  Lower two bits of
    the return flags, encodes argument slots zero to three.  */
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index 7163438e23d..71894b38ff9 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -4082,9 +4082,12 @@  handle_rhs_call (gcall *stmt, vec<ce_s> *results)
 	  if (!(flags & EAF_DIRECT))
 	    make_transitive_closure_constraints (tem);
 	  make_copy_constraint (uses, tem->id);
+	  /* TODO: This is overly conservative when some parameters are
+	     returned while others are not.  */
+	  if (!(flags & EAF_NOT_RETURNED))
+	    returns_uses = true;
 	  if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
 	    make_indirect_escape_constraint (tem);
-	  returns_uses = true;
 	}
       else if (flags & (EAF_NOESCAPE | EAF_NODIRECTESCAPE))
 	{
@@ -4098,6 +4101,8 @@  handle_rhs_call (gcall *stmt, vec<ce_s> *results)
 	  if (!(flags & EAF_DIRECT))
 	    make_transitive_closure_constraints (tem);
 	  make_copy_constraint (uses, tem->id);
+	  if (!(flags & EAF_NOT_RETURNED))
+	    returns_uses = true;
 	  make_copy_constraint (clobbers, tem->id);
 	  /* Add *tem = nonlocal, do not add *tem = callused as
 	     EAF_NOESCAPE parameters do not escape to other parameters
@@ -4111,7 +4116,6 @@  handle_rhs_call (gcall *stmt, vec<ce_s> *results)
 	  process_constraint (new_constraint (lhs, rhs));
 	  if (!(flags & (EAF_NOESCAPE | EAF_DIRECT)))
 	    make_indirect_escape_constraint (tem);
-	  returns_uses = true;
 	}
       else
 	make_escape_constraint (arg);
@@ -4261,13 +4265,18 @@  handle_const_call (gcall *stmt, vec<ce_s> *results)
 
   /* May return offsetted arguments.  */
   varinfo_t tem = NULL;
-  if (gimple_call_num_args (stmt) != 0)
-    {
-      tem = new_var_info (NULL_TREE, "callarg", true);
-      tem->is_reg_var = true;
-    }
   for (k = 0; k < gimple_call_num_args (stmt); ++k)
     {
+      int flags = gimple_call_arg_flags (stmt, k);
+
+      /* If the argument is not used or not returned we can ignore it.  */
+      if (flags & (EAF_UNUSED | EAF_NOT_RETURNED))
+	continue;
+      if (!tem)
+	{
+	  tem = new_var_info (NULL_TREE, "callarg", true);
+	  tem->is_reg_var = true;
+	}
       tree arg = gimple_call_arg (stmt, k);
       auto_vec<ce_s> argc;
       get_constraint_for_rhs (arg, &argc);
@@ -4298,6 +4307,7 @@  handle_pure_call (gcall *stmt, vec<ce_s> *results)
   struct constraint_expr rhsc;
   unsigned i;
   varinfo_t uses = NULL;
+  bool record_uses = false;
 
   /* Memory reached from pointer arguments is call-used.  */
   for (i = 0; i < gimple_call_num_args (stmt); ++i)
@@ -4315,6 +4325,8 @@  handle_pure_call (gcall *stmt, vec<ce_s> *results)
 	  make_transitive_closure_constraints (uses);
 	}
       make_constraint_to (uses->id, arg);
+      if (!(flags & EAF_NOT_RETURNED))
+	record_uses = true;
     }
 
   /* The static chain is used as well.  */
@@ -4327,6 +4339,7 @@  handle_pure_call (gcall *stmt, vec<ce_s> *results)
 	  make_transitive_closure_constraints (uses);
 	}
       make_constraint_to (uses->id, gimple_call_chain (stmt));
+      record_uses = true;
     }
 
   /* And if we applied NRV the address of the return slot.  */
@@ -4343,10 +4356,11 @@  handle_pure_call (gcall *stmt, vec<ce_s> *results)
       auto_vec<ce_s> tmpc;
       get_constraint_for_address_of (gimple_call_lhs (stmt), &tmpc);
       make_constraints_to (uses->id, tmpc);
+      record_uses = true;
     }
 
   /* Pure functions may return call-used and nonlocal memory.  */
-  if (uses)
+  if (record_uses)
     {
       rhsc.var = uses->id;
       rhsc.offset = 0;