From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from nikam.ms.mff.cuni.cz (nikam.ms.mff.cuni.cz [195.113.20.16]) by sourceware.org (Postfix) with ESMTPS id 5E464399E026 for ; Fri, 16 Jul 2021 08:41:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5E464399E026 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=ucw.cz Authentication-Results: sourceware.org; spf=none smtp.mailfrom=kam.mff.cuni.cz Received: by nikam.ms.mff.cuni.cz (Postfix, from userid 16202) id 1093A280D61; Fri, 16 Jul 2021 10:41:26 +0200 (CEST) Date: Fri, 16 Jul 2021 10:41:26 +0200 From: Jan Hubicka To: gcc-patches@gcc.gnu.org, rguenther@suse.de Subject: Add EAF_NOT_RETURNED flag Message-ID: <20210716084126.GC91970@kam.mff.cuni.cz> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.10.1 (2018-07-13) X-Spam-Status: No, score=-14.1 required=5.0 tests=BAYES_00, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_DMARC_STATUS, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 16 Jul 2021 08:41:31 -0000 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 * 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 * 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 &flags, int ecf_flags) +eaf_flags_useful_p (vec &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 &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 GTY((skip)) arg_flags; + auto_vec 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 &lattice, int depth, if (greturn *ret = dyn_cast (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 (use_stmt)) @@ -1666,7 +1686,8 @@ analyze_ssa_name_flags (tree name, vec &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 &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 &arg_flags, clone_info *info) +remap_arg_flags (auto_vec &arg_flags, clone_info *info) { - auto_vec old = arg_flags.copy (); + auto_vec 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 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 GTY((skip)) arg_flags; + auto_vec 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 *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 *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 *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 *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 argc; get_constraint_for_rhs (arg, &argc); @@ -4298,6 +4307,7 @@ handle_pure_call (gcall *stmt, vec *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 *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 *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 *results) auto_vec 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;