From 6cef01c32817b3d08af2cadcdb0e23c72ceed426 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Fri, 6 Nov 2020 10:23:58 +0100 Subject: [PATCH] Add fnspec handling to ipa mode of ipa-modef. gcc/: * attr-fnspec.h (attr_fnspec::get_str): New accessor * ipa-fnsummary.c (read_ipa_call_summary): Store also parm info for builtins. * ipa-modref.c (class fnspec_summary): New type. (class fnspec_summaries_t): New type. (modref_summary::modref_summary): Initialize writes_errno. (struct modref_summary_lto): Add writes_errno. (modref_summary_lto::modref_summary_lto): Initialize writes_errno. (modref_summary::dump): Check for NULL pointers. (modref_summary_lto::dump): Dump writes_errno. (collapse_loads): Move up in source file. (collapse_stores): New function. (process_fnspec): Handle also internal calls. (analyze_call): Likewise. (analyze_stmt): Store fnspec string if needed. (analyze_function): Initialize fnspec_sumarries. (modref_summaries_lto::duplicate): Copy writes_errno. (modref_write): Store writes_errno and fnspec summaries. (read_section): Read writes_errno and fnspec summaries. (modref_read): Initialize fnspec summaries. (update_signature): Fix formating. (compute_parm_map): Return true if sucessful. (get_parm_type): New function. (get_access_for_fnspec): New function. (propagate_unknown_call): New function. (modref_propagate_in_scc): Use it. (pass_ipa_modref::execute): Delete fnspec_summaries. (ipa_modref_c_finalize): Delete fnspec_summaries. * ipa-prop.c: Include attr-fnspec.h. (ipa_compute_jump_functions_for_bb): Also compute jump functions for functions with fnspecs. (ipa_read_edge_info): Read jump functions for builtins. gcc/testsuite/ChangeLog: * gcc.dg/ipa/modref-2.c: New test. * gcc.dg/lto/modref-2_0.c: New test. --- gcc/attr-fnspec.h | 7 + gcc/ipa-fnsummary.c | 6 +- gcc/ipa-modref.c | 584 +++++++++++++++++++------- gcc/ipa-prop.c | 10 +- gcc/testsuite/gcc.dg/ipa/modref-2.c | 15 + gcc/testsuite/gcc.dg/lto/modref-2_0.c | 27 ++ 6 files changed, 501 insertions(+), 148 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/ipa/modref-2.c create mode 100644 gcc/testsuite/gcc.dg/lto/modref-2_0.c diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h index 78b1a5a2b1c..28135328437 100644 --- a/gcc/attr-fnspec.h +++ b/gcc/attr-fnspec.h @@ -246,6 +246,13 @@ public: /* Check validity of the string. */ void verify (); + + /* Return the fnspec string. */ + const char * + get_str () + { + return str; + } }; extern attr_fnspec gimple_call_fnspec (const gcall *stmt); diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c index f2781d041b9..b8f4a0a9091 100644 --- a/gcc/ipa-fnsummary.c +++ b/gcc/ipa-fnsummary.c @@ -4301,7 +4301,11 @@ read_ipa_call_summary (class lto_input_block *ib, struct cgraph_edge *e, if (es) edge_set_predicate (e, &p); length = streamer_read_uhwi (ib); - if (length && es && e->possibly_call_in_translation_unit_p ()) + if (length && es + && (e->possibly_call_in_translation_unit_p () + /* Also stream in jump functions to builtins in hope that they + will get fnspecs. */ + || fndecl_built_in_p (e->callee->decl, BUILT_IN_NORMAL))) { es->param.safe_grow_cleared (length, true); for (i = 0; i < length; i++) diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c index 9df3d2bcf2d..95109825b39 100644 --- a/gcc/ipa-modref.c +++ b/gcc/ipa-modref.c @@ -62,8 +62,47 @@ along with GCC; see the file COPYING3. If not see #include "attr-fnspec.h" #include "symtab-clones.h" +/* We record fnspec specifiers for call edges since they depends on actual + gimple statements. */ + +class fnspec_summary +{ +public: + char *fnspec; + + fnspec_summary () + : fnspec (NULL) + { + } + + ~fnspec_summary () + { + free (fnspec); + } +}; + +/* Summary holding fnspec string for a given call. */ + +class fnspec_summaries_t : public call_summary +{ +public: + fnspec_summaries_t (symbol_table *symtab) + : call_summary (symtab) {} + /* Hook that is called by summary when an edge is duplicated. */ + virtual void duplicate (cgraph_edge *, + cgraph_edge *, + fnspec_summary *src, + fnspec_summary *dst) + { + dst->fnspec = xstrdup (src->fnspec); + } +}; + +static fnspec_summaries_t *fnspec_summaries = NULL; + /* Class (from which there is one global instance) that holds modref summaries for all analyzed functions. */ + class GTY((user)) modref_summaries : public fast_function_summary { @@ -86,6 +125,7 @@ class modref_summary_lto; /* Class (from which there is one global instance) that holds modref summaries for all analyzed functions. */ + class GTY((user)) modref_summaries_lto : public fast_function_summary { @@ -108,23 +148,26 @@ public: /* Global variable holding all modref summaries (from analysis to IPA propagation time). */ + static GTY(()) fast_function_summary *summaries; /* Global variable holding all modref optimizaiton summaries (from IPA propagation time or used by local optimization pass). */ + static GTY(()) fast_function_summary *optimization_summaries; /* LTO summaries hold info from analysis to LTO streaming or from LTO stream-in through propagation to LTO stream-out. */ + static GTY(()) fast_function_summary *summaries_lto; /* Summary for a single function which this pass produces. */ modref_summary::modref_summary () - : loads (NULL), stores (NULL) + : loads (NULL), stores (NULL), writes_errno (NULL) { } @@ -161,6 +204,7 @@ struct GTY(()) modref_summary_lto more verbose and thus more likely to hit the limits. */ modref_records_lto *loads; modref_records_lto *stores; + bool writes_errno; modref_summary_lto (); ~modref_summary_lto (); @@ -171,7 +215,7 @@ struct GTY(()) modref_summary_lto /* Summary for a single function which this pass produces. */ modref_summary_lto::modref_summary_lto () - : loads (NULL), stores (NULL) + : loads (NULL), stores (NULL), writes_errno (NULL) { } @@ -316,10 +360,16 @@ dump_lto_records (modref_records_lto *tt, FILE *out) void modref_summary::dump (FILE *out) { - fprintf (out, " loads:\n"); - dump_records (loads, out); - fprintf (out, " stores:\n"); - dump_records (stores, out); + if (loads) + { + fprintf (out, " loads:\n"); + dump_records (loads, out); + } + if (stores) + { + fprintf (out, " stores:\n"); + dump_records (stores, out); + } if (writes_errno) fprintf (out, " Writes errno\n"); } @@ -333,6 +383,8 @@ modref_summary_lto::dump (FILE *out) dump_lto_records (loads, out); fprintf (out, " stores:\n"); dump_lto_records (stores, out); + if (writes_errno) + fprintf (out, " Writes errno\n"); } /* Get function summary for FUNC if it exists, return NULL otherwise. */ @@ -653,12 +705,59 @@ get_access_for_fnspec (gcall *call, attr_fnspec &fnspec, return a; } +/* Collapse loads and return true if something changed. */ + +static bool +collapse_loads (modref_summary *cur_summary, + modref_summary_lto *cur_summary_lto) +{ + bool changed = false; + + if (cur_summary && !cur_summary->loads->every_base) + { + cur_summary->loads->collapse (); + changed = true; + } + if (cur_summary_lto + && !cur_summary_lto->loads->every_base) + { + cur_summary_lto->loads->collapse (); + changed = true; + } + return changed; +} + +/* Collapse loads and return true if something changed. */ + +static bool +collapse_stores (modref_summary *cur_summary, + modref_summary_lto *cur_summary_lto) +{ + bool changed = false; + + if (cur_summary && !cur_summary->stores->every_base) + { + cur_summary->stores->collapse (); + changed = true; + } + if (cur_summary_lto + && !cur_summary_lto->stores->every_base) + { + cur_summary_lto->stores->collapse (); + changed = true; + } + return changed; +} + + /* Apply side effects of call STMT to CUR_SUMMARY using FNSPEC. If IGNORE_STORES is true ignore them. Return false if no useful summary can be produced. */ static bool -process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores) +process_fnspec (modref_summary *cur_summary, + modref_summary_lto *cur_summary_lto, + gcall *call, bool ignore_stores) { attr_fnspec fnspec = gimple_call_fnspec (call); if (!fnspec.known_p ()) @@ -668,13 +767,13 @@ process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores) IDENTIFIER_POINTER (DECL_NAME (gimple_call_fndecl (call)))); if (ignore_stores) { - cur_summary->loads->collapse (); + collapse_loads (cur_summary, cur_summary_lto); return true; } return false; } if (fnspec.global_memory_read_p ()) - cur_summary->loads->collapse (); + collapse_loads (cur_summary, cur_summary_lto); else { for (unsigned int i = 0; i < gimple_call_num_args (call); i++) @@ -689,18 +788,25 @@ process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores) continue; if (map.parm_index == -1) { - cur_summary->loads->collapse (); + collapse_loads (cur_summary, cur_summary_lto); break; } - cur_summary->loads->insert (0, 0, - get_access_for_fnspec (call, - fnspec, i, map)); + if (cur_summary) + cur_summary->loads->insert (0, 0, + get_access_for_fnspec (call, + fnspec, i, + map)); + if (cur_summary_lto) + cur_summary_lto->loads->insert (0, 0, + get_access_for_fnspec (call, + fnspec, i, + map)); } } if (ignore_stores) return true; if (fnspec.global_memory_written_p ()) - cur_summary->stores->collapse (); + collapse_stores (cur_summary, cur_summary_lto); else { for (unsigned int i = 0; i < gimple_call_num_args (call); i++) @@ -715,16 +821,27 @@ process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores) continue; if (map.parm_index == -1) { - cur_summary->stores->collapse (); + collapse_stores (cur_summary, cur_summary_lto); break; } - cur_summary->stores->insert (0, 0, - get_access_for_fnspec (call, - fnspec, i, - map)); + if (cur_summary) + cur_summary->stores->insert (0, 0, + get_access_for_fnspec (call, + fnspec, i, + map)); + if (cur_summary_lto) + cur_summary_lto->stores->insert (0, 0, + get_access_for_fnspec (call, + fnspec, i, + map)); } if (fnspec.errno_maybe_written_p () && flag_errno_math) - cur_summary->writes_errno = true; + { + if (cur_summary) + cur_summary->writes_errno = true; + if (cur_summary_lto) + cur_summary_lto->writes_errno = true; + } } return true; } @@ -733,7 +850,7 @@ process_fnspec (modref_summary *cur_summary, gcall *call, bool ignore_stores) Remember recursive calls in RECURSIVE_CALLS. */ static bool -analyze_call (modref_summary *cur_summary, +analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto, gcall *stmt, vec *recursive_calls) { /* Check flags on the function call. In certain cases, analysis can be @@ -759,21 +876,13 @@ analyze_call (modref_summary *cur_summary, /* Check if this is an indirect call. */ if (!callee) { - /* If the indirect call does not write memory, our store summary is - unaffected, but we have to discard our loads summary (we don't know - anything about the loads that the called function performs). */ - if (ignore_stores) - { - if (dump_file) - fprintf (dump_file, " - Indirect call which does not write memory, " - "discarding loads.\n"); - cur_summary->loads->collapse (); - return true; - } if (dump_file) - fprintf (dump_file, " - Indirect call.\n"); - return false; + fprintf (dump_file, gimple_call_internal_p (stmt) + ? " - Internal call" : " - Indirect call.\n"); + return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores); } + /* We only need to handle internal calls in IPA mode. */ + gcc_checking_assert (!cur_summary_lto); struct cgraph_node *callee_node = cgraph_node::get_create (callee); @@ -796,7 +905,7 @@ analyze_call (modref_summary *cur_summary, { if (dump_file) fprintf (dump_file, " - Function availability <= AVAIL_INTERPOSABLE.\n"); - return process_fnspec (cur_summary, stmt, ignore_stores); + return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores); } /* Get callee's modref summary. As above, if there's no summary, we either @@ -806,7 +915,7 @@ analyze_call (modref_summary *cur_summary, { if (dump_file) fprintf (dump_file, " - No modref summary available for callee.\n"); - return process_fnspec (cur_summary, stmt, ignore_stores); + return process_fnspec (cur_summary, cur_summary_lto, stmt, ignore_stores); } merge_call_side_effects (cur_summary, stmt, callee_summary, ignore_stores, @@ -911,8 +1020,24 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto, "which clobbers memory.\n"); return false; case GIMPLE_CALL: - if (!ipa) - return analyze_call (summary, as_a (stmt), recursive_calls); + if (!ipa || gimple_call_internal_p (stmt)) + return analyze_call (summary, summary_lto, + as_a (stmt), recursive_calls); + else + { + attr_fnspec fnspec = gimple_call_fnspec (as_a (stmt)); + + if (fnspec.known_p () + && (!fnspec.global_memory_read_p () + || !fnspec.global_memory_written_p ())) + { + fnspec_summaries->get_create + (cgraph_node::get (current_function_decl)->get_edge (stmt)) + ->fnspec = xstrdup (fnspec.get_str ()); + if (dump_file) + fprintf (dump_file, " Recorded fnspec %s\n", fnspec.get_str ()); + } + } return true; default: /* Nothing to do for other types of statements. */ @@ -1015,6 +1140,8 @@ analyze_function (function *f, bool ipa) summaries_lto->remove (cgraph_node::get (f->decl)); summary_lto = summaries_lto->get_create (cgraph_node::get (f->decl)); } + if (!fnspec_summaries) + fnspec_summaries = new fnspec_summaries_t (symtab); } @@ -1045,6 +1172,7 @@ analyze_function (function *f, bool ipa) (param_modref_max_bases, param_modref_max_refs, param_modref_max_accesses); + summary_lto->writes_errno = false; } int ecf_flags = flags_from_decl_or_type (current_function_decl); auto_vec recursive_calls; @@ -1221,6 +1349,7 @@ modref_summaries_lto::duplicate (cgraph_node *, cgraph_node *, src_data->loads->max_refs, src_data->loads->max_accesses); dst_data->loads->copy_from (src_data->loads); + dst_data->writes_errno = src_data->writes_errno; } namespace @@ -1484,7 +1613,6 @@ modref_write () if (cnode && cnode->definition && !cnode->alias) { - modref_summary_lto *r = summaries_lto->get (cnode); if (!r || !r->useful_p (flags_from_decl_or_type (cnode->decl))) @@ -1494,6 +1622,28 @@ modref_write () write_modref_records (r->loads, ob); write_modref_records (r->stores, ob); + + struct bitpack_d bp = bitpack_create (ob->main_stream); + bp_pack_value (&bp, r->writes_errno, 1); + if (!flag_wpa) + { + for (cgraph_edge *e = cnode->indirect_calls; + e; e = e->next_callee) + { + class fnspec_summary *sum = fnspec_summaries->get (e); + bp_pack_value (&bp, sum != NULL, 1); + if (sum) + bp_pack_string (ob, &bp, sum->fnspec, true); + } + for (cgraph_edge *e = cnode->callees; e; e = e->next_callee) + { + class fnspec_summary *sum = fnspec_summaries->get (e); + bp_pack_value (&bp, sum != NULL, 1); + if (sum) + bp_pack_string (ob, &bp, sum->fnspec, true); + } + } + streamer_write_bitpack (&bp); } } streamer_write_char_stream (ob->main_stream, 0); @@ -1541,6 +1691,8 @@ read_section (struct lto_file_decl_data *file_data, const char *data, if (modref_sum) modref_sum->writes_errno = false; + if (modref_sum_lto) + modref_sum_lto->writes_errno = false; gcc_assert (!modref_sum || (!modref_sum->loads && !modref_sum->stores)); @@ -1552,6 +1704,33 @@ read_section (struct lto_file_decl_data *file_data, const char *data, read_modref_records (&ib, data_in, modref_sum ? &modref_sum->stores : NULL, modref_sum_lto ? &modref_sum_lto->stores : NULL); + struct bitpack_d bp = streamer_read_bitpack (&ib); + if (bp_unpack_value (&bp, 1)) + { + if (modref_sum) + modref_sum->writes_errno = true; + if (modref_sum_lto) + modref_sum_lto->writes_errno = true; + } + if (!flag_ltrans) + { + for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee) + { + if (bp_unpack_value (&bp, 1)) + { + class fnspec_summary *sum = fnspec_summaries->get_create (e); + sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp)); + } + } + for (cgraph_edge *e = node->callees; e; e = e->next_callee) + { + if (bp_unpack_value (&bp, 1)) + { + class fnspec_summary *sum = fnspec_summaries->get_create (e); + sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp)); + } + } + } if (dump_file) { fprintf (dump_file, "Read modref for %s\n", @@ -1588,6 +1767,8 @@ modref_read (void) || (flag_incremental_link == INCREMENTAL_LINK_LTO && flag_fat_lto_objects)) summaries = modref_summaries::create_ggc (symtab); + if (!fnspec_summaries) + fnspec_summaries = new fnspec_summaries_t (symtab); } while ((file_data = file_data_vec[j++])) @@ -1664,9 +1845,9 @@ update_signature (struct cgraph_node *node) { fprintf (dump_file, "to:\n"); if (r) - r->dump (dump_file); + r->dump (dump_file); if (r_lto) - r_lto->dump (dump_file); + r_lto->dump (dump_file); } return; } @@ -1755,7 +1936,7 @@ ignore_edge (struct cgraph_edge *e) /* Compute parm_map for CALLE_EDGE. */ -static void +static bool compute_parm_map (cgraph_edge *callee_edge, vec *parm_map) { class ipa_edge_args *args; @@ -1837,7 +2018,9 @@ compute_parm_map (cgraph_edge *callee_edge, vec *parm_map) fprintf (dump_file, " %i", (*parm_map)[i].parm_index); fprintf (dump_file, "\n"); } + return true; } + return false; } /* Call EDGE was inlined; merge summary from callee to the caller. */ @@ -1948,26 +2131,171 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge *edge) return; } -/* Collapse loads and return true if something changed. */ +/* Get parameter type from DECL. This is only safe for special cases + like builtins we create fnspec for because the type match is checked + at fnspec creation time. */ -bool -collapse_loads (modref_summary *cur_summary, - modref_summary_lto *cur_summary_lto) +static tree +get_parm_type (tree decl, unsigned int i) { - bool changed = false; + tree t = TYPE_ARG_TYPES (TREE_TYPE (decl)); - if (cur_summary && !cur_summary->loads->every_base) + for (unsigned int p = 0; p < i; p++) + t = TREE_CHAIN (t); + return TREE_VALUE (t); +} + +/* Return access mode for argument I of call E with FNSPEC. */ + +static modref_access_node +get_access_for_fnspec (cgraph_edge *e, attr_fnspec &fnspec, + unsigned int i, modref_parm_map &map) +{ + tree size = NULL_TREE; + unsigned int size_arg; + + if (!fnspec.arg_specified_p (i)) + ; + else if (fnspec.arg_max_access_size_given_by_arg_p (i, &size_arg)) { - cur_summary->loads->collapse (); - changed = true; + cgraph_node *node = e->caller->inlined_to + ? e->caller->inlined_to : e->caller; + class ipa_node_params *caller_parms_info = IPA_NODE_REF (node); + class ipa_edge_args *args = IPA_EDGE_REF (e); + struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, size_arg); + + if (jf) + size = ipa_value_from_jfunc (caller_parms_info, jf, + get_parm_type (e->callee->decl, size_arg)); } - if (cur_summary_lto - && !cur_summary_lto->loads->every_base) + else if (fnspec.arg_access_size_given_by_type_p (i)) + size = TYPE_SIZE_UNIT (get_parm_type (e->callee->decl, i)); + modref_access_node a = {0, -1, -1, + map.parm_offset, map.parm_index, + map.parm_offset_known}; + poly_int64 size_hwi; + if (size + && poly_int_tree_p (size, &size_hwi) + && coeffs_in_range_p (size_hwi, 0, + HOST_WIDE_INT_MAX / BITS_PER_UNIT)) { - cur_summary_lto->loads->collapse (); - changed = true; + a.size = -1; + a.max_size = size_hwi << LOG2_BITS_PER_UNIT; } - return changed; + return a; +} + +/* Call E in NODE with ECF_FLAGS has no summary; update MODREF_SUMMARY and + CUR_SUMMARY_LTO accordingly. Return true if something changed. */ + +static bool +propagate_unknown_call (cgraph_node *node, + cgraph_edge *e, int ecf_flags, + modref_summary **cur_summary_ptr, + modref_summary_lto **cur_summary_lto_ptr) +{ + bool changed = false; + modref_summary *cur_summary = cur_summary_ptr ? *cur_summary_ptr : NULL; + modref_summary_lto *cur_summary_lto = cur_summary_lto_ptr + ? *cur_summary_lto_ptr : NULL; + class fnspec_summary *fnspec_sum = fnspec_summaries->get (e); + auto_vec parm_map; + if (fnspec_sum + && compute_parm_map (e, &parm_map)) + { + attr_fnspec fnspec (fnspec_sum->fnspec); + + gcc_checking_assert (fnspec.known_p ()); + if (fnspec.global_memory_read_p ()) + collapse_loads (cur_summary, cur_summary_lto); + else + { + tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl)); + for (unsigned i = 0; i < parm_map.length () && t; + i++, t = TREE_CHAIN (t)) + if (!POINTER_TYPE_P (TREE_VALUE (t))) + ; + else if (!fnspec.arg_specified_p (i) + || fnspec.arg_maybe_read_p (i)) + { + modref_parm_map map = parm_map[i]; + if (map.parm_index == -2) + continue; + if (map.parm_index == -1) + { + collapse_loads (cur_summary, cur_summary_lto); + break; + } + if (cur_summary) + changed |= cur_summary->loads->insert + (0, 0, get_access_for_fnspec (e, fnspec, i, map)); + if (cur_summary_lto) + changed |= cur_summary_lto->loads->insert + (0, 0, get_access_for_fnspec (e, fnspec, i, map)); + } + } + if (ignore_stores_p (node->decl, ecf_flags)) + ; + else if (fnspec.global_memory_written_p ()) + collapse_stores (cur_summary, cur_summary_lto); + else + { + tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl)); + for (unsigned i = 0; i < parm_map.length () && t; + i++, t = TREE_CHAIN (t)) + if (!POINTER_TYPE_P (TREE_VALUE (t))) + ; + else if (!fnspec.arg_specified_p (i) + || fnspec.arg_maybe_written_p (i)) + { + modref_parm_map map = parm_map[i]; + if (map.parm_index == -2) + continue; + if (map.parm_index == -1) + { + collapse_stores (cur_summary, cur_summary_lto); + break; + } + if (cur_summary) + changed |= cur_summary->stores->insert + (0, 0, get_access_for_fnspec (e, fnspec, i, map)); + if (cur_summary_lto) + changed |= cur_summary_lto->stores->insert + (0, 0, get_access_for_fnspec (e, fnspec, i, map)); + } + } + if (fnspec.errno_maybe_written_p () && flag_errno_math) + { + if (cur_summary && !cur_summary->writes_errno) + { + cur_summary->writes_errno = true; + changed = true; + } + if (cur_summary_lto && !cur_summary_lto->writes_errno) + { + cur_summary_lto->writes_errno = true; + changed = true; + } + } + return changed; + } + if (ignore_stores_p (node->decl, ecf_flags)) + { + if (dump_file) + fprintf (dump_file, " collapsing loads\n"); + return collapse_loads (cur_summary, cur_summary_lto); + } + if (optimization_summaries) + optimization_summaries->remove (node); + if (summaries_lto) + summaries_lto->remove (node); + if (cur_summary_ptr) + *cur_summary_ptr = NULL; + if (cur_summary_lto_ptr) + *cur_summary_lto_ptr = NULL; + if (dump_file) + fprintf (dump_file, " Giving up\n"); + return true; } /* Perform iterative dataflow on SCC component starting in COMPONENT_NODE. */ @@ -2005,26 +2333,14 @@ modref_propagate_in_scc (cgraph_node *component_node) { if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS)) continue; - if (ignore_stores_p (cur->decl, e->indirect_info->ecf_flags)) - { - if (dump_file) - fprintf (dump_file, " Indirect call: " - "collapsing loads\n"); - changed |= collapse_loads (cur_summary, cur_summary_lto); - } - else - { - if (dump_file) - fprintf (dump_file, " Indirect call: giving up\n"); - if (optimization_summaries) - optimization_summaries->remove (node); - if (summaries_lto) - summaries_lto->remove (node); - changed = true; - cur_summary = NULL; - cur_summary_lto = NULL; - break; - } + if (dump_file) + fprintf (dump_file, " Indirect call" + "collapsing loads\n"); + changed |= propagate_unknown_call + (node, e, e->indirect_info->ecf_flags, + &cur_summary, &cur_summary_lto); + if (!cur_summary && !cur_summary_lto) + break; } if (!cur_summary && !cur_summary_lto) @@ -2063,30 +2379,15 @@ modref_propagate_in_scc (cgraph_node *component_node) if (avail <= AVAIL_INTERPOSABLE) { - if (!ignore_stores) - { - if (dump_file) - fprintf (dump_file, " Call target interposable" - " or not available\n"); - - if (optimization_summaries) - optimization_summaries->remove (node); - if (summaries_lto) - summaries_lto->remove (node); - cur_summary = NULL; - cur_summary_lto = NULL; - changed = true; - break; - } - else - { - if (dump_file) - fprintf (dump_file, " Call target interposable" - " or not available; collapsing loads\n"); - - changed |= collapse_loads (cur_summary, cur_summary_lto); - continue; - } + if (dump_file) + fprintf (dump_file, " Call target interposable" + " or not available\n"); + changed |= propagate_unknown_call + (node, callee_edge, flags, + &cur_summary, &cur_summary_lto); + if (!cur_summary && !cur_summary_lto) + break; + continue; } /* We don't know anything about CALLEE, hence we cannot tell @@ -2095,52 +2396,24 @@ modref_propagate_in_scc (cgraph_node *component_node) if (cur_summary && !(callee_summary = optimization_summaries->get (callee))) { - if (!ignore_stores) - { - if (dump_file) - fprintf (dump_file, " No call target summary\n"); - - optimization_summaries->remove (node); - cur_summary = NULL; - changed = true; - } - else - { - if (dump_file) - fprintf (dump_file, " No call target summary;" - " collapsing loads\n"); - - if (!cur_summary->loads->every_base) - { - cur_summary->loads->collapse (); - changed = true; - } - } + if (dump_file) + fprintf (dump_file, " No call target summary\n"); + changed |= propagate_unknown_call + (node, callee_edge, flags, + &cur_summary, NULL); + if (!cur_summary && !cur_summary_lto) + break; } if (cur_summary_lto && !(callee_summary_lto = summaries_lto->get (callee))) { - if (!ignore_stores) - { - if (dump_file) - fprintf (dump_file, " No call target summary\n"); - - summaries_lto->remove (node); - cur_summary_lto = NULL; - changed = true; - } - else - { - if (dump_file) - fprintf (dump_file, " No call target summary;" - " collapsing loads\n"); - - if (!cur_summary_lto->loads->every_base) - { - cur_summary_lto->loads->collapse (); - changed = true; - } - } + if (dump_file) + fprintf (dump_file, " No call target summary\n"); + changed |= propagate_unknown_call + (node, callee_edge, flags, + NULL, &cur_summary_lto); + if (!cur_summary && !cur_summary_lto) + break; } /* We can not safely optimize based on summary of callee if it @@ -2166,16 +2439,32 @@ modref_propagate_in_scc (cgraph_node *component_node) changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map); if (!ignore_stores) - changed |= cur_summary->stores->merge - (callee_summary->stores, &parm_map); + { + changed |= cur_summary->stores->merge + (callee_summary->stores, &parm_map); + if (!cur_summary->writes_errno + && callee_summary->writes_errno) + { + cur_summary->writes_errno = true; + changed = true; + } + } } if (callee_summary_lto) { changed |= cur_summary_lto->loads->merge (callee_summary_lto->loads, &parm_map); if (!ignore_stores) - changed |= cur_summary_lto->stores->merge - (callee_summary_lto->stores, &parm_map); + { + changed |= cur_summary_lto->stores->merge + (callee_summary_lto->stores, &parm_map); + if (!cur_summary_lto->writes_errno + && callee_summary_lto->writes_errno) + { + cur_summary_lto->writes_errno = true; + changed = true; + } + } } if (dump_file && changed) { @@ -2266,6 +2555,8 @@ pass_ipa_modref::execute (function *) ((modref_summaries_lto *)summaries_lto)->propagated = true; ipa_free_postorder_info (); free (order); + delete fnspec_summaries; + fnspec_summaries = NULL; return 0; } @@ -2283,6 +2574,9 @@ ipa_modref_c_finalize () ggc_delete (summaries_lto); summaries_lto = NULL; } + if (fnspec_summaries) + delete fnspec_summaries; + fnspec_summaries = NULL; } #include "gt-ipa-modref.h" diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index d4ef6ff8a8e..7a5fa59ecec 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfgcleanup.h" #include "options.h" #include "symtab-clones.h" +#include "attr-fnspec.h" /* Function summary where the parameter infos are actually stored. */ ipa_node_params_t *ipa_node_params_sum = NULL; @@ -2364,7 +2365,8 @@ ipa_compute_jump_functions_for_bb (struct ipa_func_body_info *fbi, basic_block b callee = callee->ultimate_alias_target (); /* We do not need to bother analyzing calls to unknown functions unless they may become known during lto/whopr. */ - if (!callee->definition && !flag_lto) + if (!callee->definition && !flag_lto + && !gimple_call_fnspec (cs->call_stmt).known_p ()) continue; } ipa_compute_jump_functions_for_edge (fbi, cs); @@ -4974,7 +4976,11 @@ ipa_read_edge_info (class lto_input_block *ib, count /= 2; if (!count) return; - if (prevails && e->possibly_call_in_translation_unit_p ()) + if (prevails + && (e->possibly_call_in_translation_unit_p () + /* Also stream in jump functions to builtins in hope that they + will get fnspecs. */ + || fndecl_built_in_p (e->callee->decl, BUILT_IN_NORMAL))) { class ipa_edge_args *args = IPA_EDGE_REF_GET_CREATE (e); vec_safe_grow_cleared (args->jump_functions, count, true); diff --git a/gcc/testsuite/gcc.dg/ipa/modref-2.c b/gcc/testsuite/gcc.dg/ipa/modref-2.c new file mode 100644 index 00000000000..5ac2c65aff4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/modref-2.c @@ -0,0 +1,15 @@ +/* { dg-options "-O2 -fdump-ipa-modref" } */ +/* { dg-do compile } */ +void +test (int *a, int size) +{ + __builtin_memset (a, 0, 321); +} +void +test2 (double x, double *y) +{ + __builtin_modf (x,y); +} +/* 321*8 */ +/* { dg-final { scan-ipa-dump "Parm 0 param offset:0 offset:0 size:-1 max_size:2568" "modref" } } */ +/* { dg-final { scan-ipa-dump "Parm 1 param offset:0 offset:0 size:-1 max_size:64" "modref" } } */ diff --git a/gcc/testsuite/gcc.dg/lto/modref-2_0.c b/gcc/testsuite/gcc.dg/lto/modref-2_0.c new file mode 100644 index 00000000000..cf84ed95e77 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/modref-2_0.c @@ -0,0 +1,27 @@ +/* { dg-lto-do run } */ +/* { dg-lto-options {"-O2 -flto-partition=max -flto -fno-ipa-sra"} } */ +__attribute__ ((noinline)) +void +test (char *a) +{ + __builtin_memset (a,0,321); +} +__attribute__ ((noinline)) +void +test2 (double *x, double *y) +{ + __builtin_modf (*x,y); +} +int +main (void) +{ + char array[321]; + double x=1, y=2; + char arrayz[321]; + arrayz[0]=1; + test (array); + test2 (&x,&y); + if (!__builtin_constant_p (x==2) || !__builtin_constant_p (arrayz[0]==1)) + __builtin_abort (); + return 0; +} -- 2.30.2