#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 <fnspec_summary *>
+{
+public:
+ fnspec_summaries_t (symbol_table *symtab)
+ : call_summary <fnspec_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 <modref_summary *, va_gc>
{
/* 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 <modref_summary_lto *, va_gc>
{
/* Global variable holding all modref summaries
(from analysis to IPA propagation time). */
+
static GTY(()) fast_function_summary <modref_summary *, va_gc>
*summaries;
/* Global variable holding all modref optimizaiton summaries
(from IPA propagation time or used by local optimization pass). */
+
static GTY(()) fast_function_summary <modref_summary *, va_gc>
*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 <modref_summary_lto *, va_gc>
*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)
{
}
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 ();
/* 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)
{
}
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");
}
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. */
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 ())
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++)
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++)
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;
}
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 <gimple *> *recursive_calls)
{
/* Check flags on the function call. In certain cases, analysis can be
/* 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);
{
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
{
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,
"which clobbers memory.\n");
return false;
case GIMPLE_CALL:
- if (!ipa)
- return analyze_call (summary, as_a <gcall *> (stmt), recursive_calls);
+ if (!ipa || gimple_call_internal_p (stmt))
+ return analyze_call (summary, summary_lto,
+ as_a <gcall *> (stmt), recursive_calls);
+ else
+ {
+ attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(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. */
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);
}
(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 <gimple *, 32> recursive_calls;
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
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)))
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);
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));
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",
|| (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++]))
{
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;
}
/* Compute parm_map for CALLE_EDGE. */
-static void
+static bool
compute_parm_map (cgraph_edge *callee_edge, vec<modref_parm_map> *parm_map)
{
class ipa_edge_args *args;
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. */
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 <modref_parm_map, 32> 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. */
{
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)
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
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
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)
{
((modref_summaries_lto *)summaries_lto)->propagated = true;
ipa_free_postorder_info ();
free (order);
+ delete fnspec_summaries;
+ fnspec_summaries = NULL;
return 0;
}
ggc_delete (summaries_lto);
summaries_lto = NULL;
}
+ if (fnspec_summaries)
+ delete fnspec_summaries;
+ fnspec_summaries = NULL;
}
#include "gt-ipa-modref.h"