From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by sourceware.org (Postfix) with ESMTPS id 5EE4C3861826; Tue, 10 Nov 2020 11:04:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 5EE4C3861826 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=suse.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=rguenther@suse.de X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (unknown [195.135.221.27]) by mx2.suse.de (Postfix) with ESMTP id 53BB5ABD1; Tue, 10 Nov 2020 11:04:31 +0000 (UTC) Date: Tue, 10 Nov 2020 12:04:31 +0100 (CET) From: Richard Biener Sender: rguenther@c653.arch.suse.de To: Jan Hubicka cc: gcc@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: Re: Detect EAF flags in ipa-modref In-Reply-To: <20201109231418.GF65107@kam.mff.cuni.cz> Message-ID: References: <20201108124711.GD65107@kam.mff.cuni.cz> <20201109102946.GA83507@kam.mff.cuni.cz> <20201109131632.GA12394@kam.mff.cuni.cz> <20201109231418.GF65107@kam.mff.cuni.cz> User-Agent: Alpine 2.21 (LSU 202 2017-01-01) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gcc@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 10 Nov 2020 11:04:35 -0000 On Tue, 10 Nov 2020, Jan Hubicka wrote: > Hi, > this is updated patch for autodetection of EAF flags. Still the goal is > to avoid fancy stuff and get besic logic in place (so no dataflow, no IPA > propagation, no attempts to handle trickier cases). There is one new failure > > ./gcc/testsuite/gcc/gcc.sum:FAIL: gcc.dg/sso/t2.c -Wno-scalar-storage-order -O1 -fno-inline output pattern test > ./gcc/testsuite/gcc/gcc.sum:FAIL: gcc.dg/sso/t2.c -Wno-scalar-storage-order -O2 output pattern test > ./gcc/testsuite/gcc/gcc.sum:FAIL: gcc.dg/sso/t2.c -Wno-scalar-storage-order -Os output pattern test > > Which I blieve is bug exposed by detecting dump function to be EAF_DIRECT and > NOESCAPE (which it is) and packing/updacking the bitfields leads in one bit > difference. Still no idea why. > > Patch seems to be quite effective on cc1plus turning: > > Alias oracle query stats: > refs_may_alias_p: 65808750 disambiguations, 75664890 queries > ref_maybe_used_by_call_p: 153485 disambiguations, 66711204 queries > call_may_clobber_ref_p: 22816 disambiguations, 28889 queries > nonoverlapping_component_refs_p: 0 disambiguations, 36846 queries > nonoverlapping_refs_since_match_p: 27271 disambiguations, 58917 must overlaps, 86958 queries > aliasing_component_refs_p: 65808 disambiguations, 2067256 queries > TBAA oracle: 25929211 disambiguations 60395141 queries > 12391384 are in alias set 0 > 10783783 queries asked about the same object > 126 queries asked about the same alias set > 0 access volatile > 9598698 are dependent in the DAG > 1691939 are aritificially in conflict with void * > > Modref stats: > modref use: 14284 disambiguations, 53336 queries > modref clobber: 1660281 disambiguations, 2130440 queries > 4311165 tbaa queries (2.023603 per modref query) > 685304 base compares (0.321673 per modref query) > > PTA query stats: > pt_solution_includes: 959190 disambiguations, 13169678 queries > pt_solutions_intersect: 1050969 disambiguations, 13246686 queries > > Alias oracle query stats: > refs_may_alias_p: 66914578 disambiguations, 76692648 queries > ref_maybe_used_by_call_p: 244077 disambiguations, 67732086 queries > call_may_clobber_ref_p: 111475 disambiguations, 116613 queries > nonoverlapping_component_refs_p: 0 disambiguations, 37091 queries > nonoverlapping_refs_since_match_p: 27267 disambiguations, 59019 must overlaps, 87056 queries > aliasing_component_refs_p: 65870 disambiguations, 2063394 queries > TBAA oracle: 26024415 disambiguations 60579490 queries > 12450910 are in alias set 0 > 10806673 queries asked about the same object > 125 queries asked about the same alias set > 0 access volatile > 9605837 are dependent in the DAG > 1691530 are aritificially in conflict with void * > > Modref stats: > modref use: 14272 disambiguations, 277680 queries > modref clobber: 1669753 disambiguations, 7849135 queries > 4330162 tbaa queries (0.551674 per modref query) > 699241 base compares (0.089085 per modref query) > > PTA query stats: > pt_solution_includes: 1833920 disambiguations, 13846032 queries > pt_solutions_intersect: 1093785 disambiguations, 13309954 queries > > So almost twice as many pt_solution_includes disambiguations. > I will re-run the stats overnight to be sure that it is not independent > change (but both build was from almost same checkout). > > Bootstrapped/regtested x86_64-linux, OK? > (I will analyze more the t2.c failure) > > Honza > > gcc/ChangeLog: > > 2020-11-10 Jan Hubicka > > * gimple.c: Include ipa-modref-tree.h and ipa-modref.h. > (gimple_call_arg_flags): Use modref to determine flags. > * ipa-modref.c: Include gimple-ssa.h, tree-phinodes.h, > tree-ssa-operands.h, stringpool.h and tree-ssanames.h. > (analyze_ssa_name_flags): Declare. > (modref_summary::useful_p): Summary is also useful if arg flags are > known. > (dump_eaf_flags): New function. > (modref_summary::dump): Use it. > (get_modref_function_summary): Be read for current_function_decl > being NULL. > (memory_access_to): New function. > (deref_flags): New function. > (call_lhs_flags): New function. > (analyze_parms): New function. > (analyze_function): Use it. > * ipa-modref.h (struct modref_summary): Add arg_flags. > > gcc/testsuite/ChangeLog: > > 2020-11-10 Jan Hubicka > > * gcc.dg/torture/pta-ptrarith-1.c: Escape parametrs. > > diff --git a/gcc/gimple.c b/gcc/gimple.c > index 1afed88e1f1..da90716aa23 100644 > --- a/gcc/gimple.c > +++ b/gcc/gimple.c > @@ -46,6 +46,8 @@ along with GCC; see the file COPYING3. If not see > #include "asan.h" > #include "langhooks.h" > #include "attr-fnspec.h" > +#include "ipa-modref-tree.h" > +#include "ipa-modref.h" > > > /* All the tuples have their operand vector (if present) at the very bottom > @@ -1532,24 +1534,45 @@ int > gimple_call_arg_flags (const gcall *stmt, unsigned arg) > { > attr_fnspec fnspec = gimple_call_fnspec (stmt); > - > - if (!fnspec.known_p ()) > - return 0; > - > int flags = 0; > > - if (!fnspec.arg_specified_p (arg)) > - ; > - else if (!fnspec.arg_used_p (arg)) > - flags = EAF_UNUSED; > - else > + if (fnspec.known_p ()) > { > - if (fnspec.arg_direct_p (arg)) > - flags |= EAF_DIRECT; > - if (fnspec.arg_noescape_p (arg)) > - flags |= EAF_NOESCAPE; > - if (fnspec.arg_readonly_p (arg)) > - flags |= EAF_NOCLOBBER; > + if (!fnspec.arg_specified_p (arg)) > + ; > + else if (!fnspec.arg_used_p (arg)) > + flags = EAF_UNUSED; > + else > + { > + if (fnspec.arg_direct_p (arg)) > + flags |= EAF_DIRECT; > + if (fnspec.arg_noescape_p (arg)) > + flags |= EAF_NOESCAPE; > + if (fnspec.arg_readonly_p (arg)) > + flags |= EAF_NOCLOBBER; > + } > + } > + tree callee = gimple_call_fndecl (stmt); > + if (callee) > + { > + cgraph_node *node = cgraph_node::get (callee); > + modref_summary *summary = node ? get_modref_function_summary (node) > + : NULL; > + > + if (summary && summary->arg_flags.length () > arg) So could we make modref "transform" push this as fnspec attribute or would that not really be an optimization? > + { > + int modref_flags = summary->arg_flags[arg]; > + > + /* We have possibly optimized out load. Be conservative here. */ > + if (!node->binds_to_current_def_p ()) > + { > + if ((modref_flags & EAF_UNUSED) && !(flags & EAF_UNUSED)) > + modref_flags &= ~EAF_UNUSED; > + if ((modref_flags & EAF_DIRECT) && !(flags & EAF_DIRECT)) > + modref_flags &= ~EAF_DIRECT; > + } > + flags |= modref_flags; > + } > } > return flags; > } > diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c > index 3f46bebed3c..bde626115ff 100644 > --- a/gcc/ipa-modref.c > +++ b/gcc/ipa-modref.c > @@ -61,6 +61,14 @@ along with GCC; see the file COPYING3. If not see > #include "ipa-fnsummary.h" > #include "attr-fnspec.h" > #include "symtab-clones.h" > +#include "gimple-ssa.h" > +#include "tree-phinodes.h" > +#include "tree-ssa-operands.h" > +#include "ssa-iterators.h" > +#include "stringpool.h" > +#include "tree-ssanames.h" > + > +static int analyze_ssa_name_flags (tree name, vec *known_flags, int depth); > > /* We record fnspec specifiers for call edges since they depends on actual > gimple statements. */ > @@ -186,6 +194,8 @@ modref_summary::useful_p (int ecf_flags) > { > if (ecf_flags & (ECF_CONST | ECF_NOVOPS)) > return false; > + if (arg_flags.length ()) > + return true; > if (loads && !loads->every_base) > return true; > if (ecf_flags & ECF_PURE) > @@ -355,6 +365,22 @@ dump_lto_records (modref_records_lto *tt, FILE *out) > } > } > > +/* Dump EAF flags. */ > + > +static void > +dump_eaf_flags (FILE *out, int flags) > +{ > + if (flags & EAF_DIRECT) > + fprintf (out, " direct"); > + if (flags & EAF_NOCLOBBER) > + fprintf (out, " noclobber"); > + if (flags & EAF_NOESCAPE) > + fprintf (out, " noescape"); > + if (flags & EAF_UNUSED) > + fprintf (out, " unused"); > + fprintf (out, "\n"); > +} > + > /* Dump summary. */ > > void > @@ -372,6 +398,15 @@ modref_summary::dump (FILE *out) > } > if (writes_errno) > fprintf (out, " Writes errno\n"); > + if (arg_flags.length ()) > + { > + for (unsigned int i = 0; i < arg_flags.length (); i++) > + if (arg_flags[i]) > + { > + fprintf (out, " parm %i flags:", i); > + dump_eaf_flags (out, arg_flags[i]); > + } > + } > } > > /* Dump summary. */ > @@ -402,7 +437,8 @@ get_modref_function_summary (cgraph_node *func) > function. */ > enum availability avail; > func = func->function_or_virtual_thunk_symbol > - (&avail, cgraph_node::get (current_function_decl)); > + (&avail, current_function_decl ? > + cgraph_node::get (current_function_decl) : NULL); > if (avail <= AVAIL_INTERPOSABLE) > return NULL; > > @@ -1067,6 +1103,315 @@ remove_summary (bool lto, bool nolto, bool ipa) > " - modref done with result: not tracked.\n"); > } > > +/* Return true if OP accesses memory pointed to by SSA_NAME. */ > + > +bool > +memory_access_to (tree op, tree ssa_name) > +{ > + tree base = get_base_address (op); > + if (!base) > + return false; > + if (TREE_CODE (base) != MEM_REF && TREE_CODE (base) != TARGET_MEM_REF) > + return false; > + return TREE_OPERAND (base, 0) == ssa_name; > +} > + > +/* Consider statement val = *arg. > + return EAF flags of ARG that can be determined from EAF flags of VAL > + (which are known to be FLAGS). If IGNORE_STORES is true we can ignore > + all stores to VAL, i.e. when handling noreturn function. */ > + > +static int > +deref_flags (int flags, bool ignore_stores) > +{ > + int ret = 0; > + if (flags & EAF_UNUSED) > + ret |= EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE; > + else > + { > + if ((flags & EAF_NOCLOBBER) || ignore_stores) > + ret |= EAF_NOCLOBBER; > + if ((flags & EAF_NOESCAPE) || ignore_stores) > + ret |= EAF_NOESCAPE; > + } > + return ret; > +} > + > +/* Call statements may return their parameters. Consider argument number > + ARG of USE_STMT and determine flags that can needs to be cleared > + in case pointer possibly indirectly references from ARG I is returned. > + KNOWN_FLAGS and DEPTH are same as in analyze_ssa_name_flags. */ > + > +static int > +call_lhs_flags (gimple *use_stmt, int arg, vec *known_flags, int depth) > +{ > + /* If there is no return value, no flags are affected. */ > + if (!gimple_call_lhs (use_stmt)) > + return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED; > + > + /* If we know that function returns given argument and it is not ARG > + we can still be happy. */ > + int flags = gimple_call_return_flags (as_a (use_stmt)); > + if ((flags & ERF_RETURNS_ARG) > + && (flags & ERF_RETURN_ARG_MASK) != arg) > + return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED; > + > + /* If return value is SSA name determine its flags. */ > + if (TREE_CODE (gimple_call_lhs (use_stmt)) == SSA_NAME) > + return analyze_ssa_name_flags > + (gimple_call_lhs (use_stmt), known_flags, > + depth + 1); > + /* In the case of memory store we can do nothing. */ > + else > + return 0; > +} > + > +/* Analyze EAF flags for SSA name NAME. > + KNOWN_FLAGS is a cache for flags we already determined. > + DEPTH is a recursion depth used to make debug output prettier. */ > + > +static int > +analyze_ssa_name_flags (tree name, vec *known_flags, int depth) C++ has references which makes the access to known_flags nicer ;) > +{ > + imm_use_iterator ui; > + gimple *use_stmt; > + int flags = EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE | EAF_UNUSED; > + > + /* See if value is already computed. */ > + if ((*known_flags)[SSA_NAME_VERSION (name)]) > + { > + /* Punt on cycles for now, so we do not need dataflow. */ > + if ((*known_flags)[SSA_NAME_VERSION (name)] == 1) > + { > + if (dump_file) > + fprintf (dump_file, > + "%*sGiving up on a cycle in SSA graph\n", depth * 4, ""); > + return 0; > + } > + return (*known_flags)[SSA_NAME_VERSION (name)] - 2; > + } > + /* Recursion guard. */ > + (*known_flags)[SSA_NAME_VERSION (name)] = 1; This also guards against multiple evaluations of the same stmts but only in some cases? Consider _1 = ..; _2 = _1 + _3; _4 = _1 + _5; _6 = _2 + _4; where we visit _2 = and _4 = from _1 but from both are going to visit _6. Maybe I'm blind but you're not limiting depth? Guess that asks for problems, esp. as you are recursing rather than using a worklist or so? I see you try to "optimize" the walk by only visiting def->use links from parameters but then a RPO walk over all stmts would be simpler iteration-wise ... > + if (dump_file) > + { > + fprintf (dump_file, > + "%*sAnalyzing flags of ssa name: ", depth * 4, ""); > + print_generic_expr (dump_file, name); > + fprintf (dump_file, "\n"); > + } > + > + FOR_EACH_IMM_USE_STMT (use_stmt, ui, name) > + { > + if (flags == 0) > + { > + BREAK_FROM_IMM_USE_STMT (ui); > + } > + if (is_gimple_debug (use_stmt)) > + continue; > + if (dump_file) > + { > + fprintf (dump_file, "%*s Analyzing stmt:", depth * 4, ""); > + print_gimple_stmt (dump_file, use_stmt, 0); > + } > + > + /* Gimple return may load the return value. */ > + if (gimple_code (use_stmt) == GIMPLE_RETURN) if (greturn *ret = dyn_cast (use_stmt)) makes the as_a below not needed, similar for the other cases (it also makes accesses cheaper, avoiding some checking code). > + { > + if (memory_access_to (gimple_return_retval > + (as_a (use_stmt)), name)) > + flags &= ~EAF_UNUSED; > + } > + /* Account for LHS store, arg loads and flags from callee function. */ > + else if (is_gimple_call (use_stmt)) > + { > + tree callee = gimple_call_fndecl (use_stmt); > + > + /* Recursion would require bit of propagation; give up for now. */ > + if (callee && recursive_call_p (current_function_decl, callee)) > + flags = 0; > + else > + { > + int ecf_flags = gimple_call_flags (use_stmt); > + bool ignore_stores = ignore_stores_p (current_function_decl, > + ecf_flags); > + > + /* Handle *name = func (...). */ > + if (gimple_call_lhs (use_stmt) > + && memory_access_to (gimple_call_lhs (use_stmt), name)) > + flags &= ~(EAF_UNUSED | EAF_NOCLOBBER); > + > + /* Handle all function parameters. */ > + for (unsigned i = 0; i < gimple_call_num_args (use_stmt); i++) > + /* Name is directly passed to the callee. */ > + if (gimple_call_arg (use_stmt, i) == name) > + { > + if (ecf_flags & (ECF_CONST | ECF_NOVOPS)) > + flags &= ignore_stores > + ? 0 > + : call_lhs_flags (use_stmt, i, > + known_flags, depth); > + else > + { > + int call_flags = gimple_call_arg_flags (as_a > + (use_stmt), i); > + if (ignore_stores) > + call_flags |= EAF_NOCLOBBER | EAF_NOESCAPE; > + else > + call_flags &= call_lhs_flags (use_stmt, i, > + known_flags, depth); > + > + flags &= call_flags; > + } > + } > + /* Name is dereferenced and passed to a callee. */ > + else if (memory_access_to (gimple_call_arg (use_stmt, i), name)) > + { > + if (ecf_flags & (ECF_CONST | ECF_NOVOPS)) > + flags &= ~EAF_UNUSED; > + else > + flags &= deref_flags (gimple_call_arg_flags > + (as_a (use_stmt), i), > + ignore_stores); > + if (!ignore_stores) > + flags &= call_lhs_flags (use_stmt, i, known_flags, > + depth); > + } Hmm, you forget gimple_call_chain at least which is at least argument passing? Possibly the chain argument is never a function parameter but how do you know... (partial inlining?) Then there's gimple_call_fn for indirect function calls to a function parameter? I guess it would be nice to amend the gimple_walk_ stuff to have a SSA name callback where the tree and the SSA use is passed. Well, for another time. > + } > + /* Only NOCLOBBER or DIRECT flags alone are not useful (see comments > + in tree-ssa-alias.c). Give up earlier. */ > + if ((flags & ~(EAF_DIRECT | EAF_NOCLOBBER)) == 0) > + flags = 0; > + } > + else if (gimple_assign_load_p (use_stmt)) > + { > + /* Memory to memory copy. */ > + if (gimple_store_p (use_stmt)) > + { > + /* Handle *name = *exp. */ > + if (memory_access_to (gimple_assign_lhs (use_stmt), name)) > + flags &= ~(EAF_UNUSED | EAF_NOCLOBBER); > + > + /* Handle *lhs = *name. > + > + We do not track memory locations, so assume that value > + is used arbitrarily. */ > + if (memory_access_to (gimple_assign_rhs1 (use_stmt), name)) > + flags = 0; > + } > + /* Handle lhs = *name. */ > + else if (memory_access_to (gimple_assign_rhs1 (use_stmt), name)) > + flags &= deref_flags (analyze_ssa_name_flags > + (gimple_assign_lhs (use_stmt), > + known_flags, depth + 1), false); > + } > + else if (gimple_store_p (use_stmt)) > + { > + /* Handle *lhs = name. */ > + if (is_gimple_assign (use_stmt) > + && gimple_assign_rhs1 (use_stmt) == name) > + { > + if (dump_file) > + fprintf (dump_file, "%*s ssa name saved to memory\n", > + depth * 4, ""); > + flags = 0; > + } > + /* Handle *name = exp. */ > + else if (is_gimple_assign (use_stmt) > + && memory_access_to (gimple_assign_lhs (use_stmt), name)) > + flags &= ~(EAF_UNUSED | EAF_NOCLOBBER); > + /* ASM statements etc. */ > + else if (!is_gimple_assign (use_stmt)) > + { > + if (dump_file) > + fprintf (dump_file, "%*s Unhandled store\n", > + depth * 4, ""); > + flags = 0; > + } > + } > + else if (is_gimple_assign (use_stmt)) > + { > + enum tree_code code = gimple_assign_rhs_code (use_stmt); > + > + /* See if operation is a merge as considered by > + tree-ssa-structalias.c:find_func_aliases. */ > + if (!truth_value_p (code) > + && code != POINTER_DIFF_EXPR > + && (code != POINTER_PLUS_EXPR > + || gimple_assign_rhs1 (use_stmt) == name)) > + flags &= analyze_ssa_name_flags > + (gimple_assign_lhs (use_stmt), known_flags, > + depth + 1); > + } > + else if (gimple_code (use_stmt) == GIMPLE_PHI) > + { > + flags &= analyze_ssa_name_flags > + (gimple_phi_result (use_stmt), known_flags, > + depth + 1); > + } > + /* Conditions are not considered escape points > + by tree-ssa-structalias. */ > + else if (gimple_code (use_stmt) == GIMPLE_COND) > + ; > + else > + { > + if (dump_file) > + fprintf (dump_file, "%*s Unhandled stmt\n", depth * 4, ""); > + flags = 0; > + } > + > + if (dump_file) > + { > + fprintf (dump_file, "%*s current flags of ", depth * 4, ""); > + print_generic_expr (dump_file, name); > + dump_eaf_flags (dump_file, flags); > + } > + } > + if (dump_file) > + { > + fprintf (dump_file, "%*sflags of ssa name ", depth * 4, ""); > + print_generic_expr (dump_file, name); > + dump_eaf_flags (dump_file, flags); > + } > + (*known_flags)[SSA_NAME_VERSION (name)] = flags + 2; > + return flags; > +} > + > +/* Determine EAF flags for function parameters. */ > + > +static void > +analyze_parms (modref_summary *summary) > +{ > + unsigned int parm_index = 0; > + unsigned int count = 0; > + > + for (tree parm = DECL_ARGUMENTS (current_function_decl); parm; > + parm = TREE_CHAIN (parm)) > + count++; > + > + if (!count) > + return; > + > + auto_vec known_flags; > + known_flags.safe_grow_cleared (num_ssa_names); , true for exact reserve. Could be space optimized by not using auto_vec but auto_vec? > + > + for (tree parm = DECL_ARGUMENTS (current_function_decl); parm; parm_index++, > + parm = TREE_CHAIN (parm)) > + { > + tree name = ssa_default_def (cfun, parm); > + if (!name) > + continue; looks like the vec might be quite sparse ... > + int flags = analyze_ssa_name_flags (name, &known_flags, 0); > + > + if (flags) > + { > + if (parm_index >= summary->arg_flags.length ()) > + summary->arg_flags.safe_grow_cleared (count); you want , true for exact reserve. > + summary->arg_flags[parm_index] = flags; maybe this as well - does it make sense to skip !is_gimple_reg_type params in the counting? Guess it makes lookup too complicated. But we do have testcases with >30000 parameters ... > + } > + } > +} > + > /* Analyze function F. IPA indicates whether we're running in local mode > (false) or the IPA mode (true). */ > > @@ -1174,6 +1519,10 @@ analyze_function (function *f, bool ipa) > param_modref_max_accesses); > summary_lto->writes_errno = false; > } > + > + if (!ipa) > + analyze_parms (summary); > + > int ecf_flags = flags_from_decl_or_type (current_function_decl); > auto_vec recursive_calls; > > @@ -1191,8 +1540,9 @@ analyze_function (function *f, bool ipa) > || ((!summary || !summary->useful_p (ecf_flags)) > && (!summary_lto || !summary_lto->useful_p (ecf_flags)))) > { > - remove_summary (lto, nolto, ipa); > - return; > + collapse_loads (summary, summary_lto); > + collapse_stores (summary, summary_lto); > + break; > } > } > } > diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h > index 31ceffa8d34..8fa05aaf7fb 100644 > --- a/gcc/ipa-modref.h > +++ b/gcc/ipa-modref.h > @@ -29,6 +29,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; > > modref_summary (); > ~modref_summary (); > diff --git a/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c b/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c > index 99a548840df..85b68068b12 100644 > --- a/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c > +++ b/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c > @@ -6,11 +6,14 @@ struct Foo { > int *p; > }; > > +struct Foo *ff; > + > void __attribute__((noinline)) > foo (void *p) > { > struct Foo *f = (struct Foo *)p - 1; > *f->p = 0; > + ff = f; > } > > int bar (void) > -- Richard Biener SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imend