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