Add EAF_NOT_RETURNED flag

Richard Biener rguenther@suse.de
Fri Jul 16 10:38:19 GMT 2021


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

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


More information about the Gcc-patches mailing list