Message ID | 20210716084126.GC91970@kam.mff.cuni.cz |
---|---|
State | New |
Headers | show |
Series | Add EAF_NOT_RETURNED flag | expand |
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 --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;