print_generic_expr (f, av.value);
if (av.by_ref)
fprintf (f, "(by_ref)");
+ if (av.killed)
+ fprintf (f, "(killed)");
comma = true;
}
fprintf (f, "\n");
new_av.unit_offset = av->unit_offset - unit_delta;
new_av.index = dest_index;
new_av.by_ref = av->by_ref;
+ gcc_assert (!av->killed);
+ new_av.killed = false;
/* Quick check that the offsets we push are indeed increasing. */
gcc_assert (first
iav.unit_offset = aglat->offset / BITS_PER_UNIT - unit_delta;
iav.index = dest_index;
iav.by_ref = plats->aggs_by_ref;
+ iav.killed = false;
gcc_assert (first
|| iav.unit_offset > prev_unit_offset);
iav.unit_offset = item.offset / BITS_PER_UNIT;
iav.index = dst_index;
iav.by_ref = agg_jfunc->by_ref;
+ iav.killed = 0;
gcc_assert (first
|| iav.unit_offset > prev_unit_offset);
avals.m_known_aggs[j].unit_offset = unit_offset;
avals.m_known_aggs[j].index = index;
avals.m_known_aggs[j].by_ref = plats->aggs_by_ref;
+ avals.m_known_aggs[j].killed = false;
perform_estimation_of_a_value (node, &avals,
removable_params_cost, 0, val);
iav.unit_offset = agg_jf.offset / BITS_PER_UNIT;
iav.index = index;
iav.by_ref = jfunc->agg.by_ref;
+ iav.killed = false;
gcc_assert (first
|| iav.unit_offset > prev_unit_offset);
i++;
}
+/* Return true if the V can overlap with KILL. */
+
+static bool
+ipcp_argagg_and_kill_overlap_p (const ipa_argagg_value &v,
+ const modref_access_node &kill)
+{
+ if (kill.parm_index == v.index)
+ {
+ gcc_assert (kill.parm_offset_known);
+ gcc_assert (known_eq (kill.max_size, kill.size));
+ poly_int64 repl_size;
+ bool ok = poly_int_tree_p (TYPE_SIZE (TREE_TYPE (v.value)),
+ &repl_size);
+ gcc_assert (ok);
+ poly_int64 repl_offset (v.unit_offset);
+ repl_offset <<= LOG2_BITS_PER_UNIT;
+ poly_int64 combined_offset
+ = (kill.parm_offset << LOG2_BITS_PER_UNIT) + kill.offset;
+ if (ranges_maybe_overlap_p (repl_offset, repl_size,
+ combined_offset, kill.size))
+ return true;
+ }
+ return false;
+}
+
/* If signature changed, update the summary. */
static void
update_signature (struct cgraph_node *node)
{
- clone_info *info = clone_info::get (node);
- if (!info || !info->param_adjustments)
- return;
-
modref_summary *r = optimization_summaries
? optimization_summaries->get (node) : NULL;
modref_summary_lto *r_lto = summaries_lto
? summaries_lto->get (node) : NULL;
if (!r && !r_lto)
return;
+
+ /* Propagating constants in killed memory can lead to eliminated stores in
+ both callees (because they are considered redundant) and callers, leading
+ to missing them altogether. */
+ ipcp_transformation *ipcp_ts = ipcp_get_transformation_summary (node);
+ if (ipcp_ts)
+ {
+ for (auto &v : ipcp_ts->m_agg_values)
+ {
+ if (!v.by_ref)
+ continue;
+ if (r)
+ for (const modref_access_node &kill : r->kills)
+ if (ipcp_argagg_and_kill_overlap_p (v, kill))
+ {
+ v.killed = true;
+ break;
+ }
+ if (!v.killed && r_lto)
+ for (const modref_access_node &kill : r_lto->kills)
+ if (ipcp_argagg_and_kill_overlap_p (v, kill))
+ {
+ v.killed = true;
+ break;
+ }
+ }
+ }
+
+ clone_info *info = clone_info::get (node);
+ if (!info || !info->param_adjustments)
+ return;
+
if (dump_file)
{
fprintf (dump_file, "Updating summary for %s from:\n",
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, av.by_ref, 1);
+ bp_pack_value (&bp, av.killed, 1);
streamer_write_bitpack (&bp);
}
bitpack_d bp = streamer_read_bitpack (ib);
av->by_ref = bp_unpack_value (&bp, 1);
+ av->killed = bp_unpack_value (&bp, 1);
}
}
/* If IPA-CP discovered a constant in parameter PARM at OFFSET of a given SIZE
- whether passed by reference or not is given by BY_REF - return that
- constant. Otherwise return NULL_TREE. */
+ constant. Otherwise return NULL_TREE. The is supposed to be used only
+ after clone materialization and transformation is done (because it asserts
+ that killed constants have been pruned). */
tree
ipcp_get_aggregate_const (struct function *func, tree parm, bool by_ref,
ipa_argagg_value_list avl (ts);
unsigned unit_offset = bit_offset / BITS_PER_UNIT;
- tree v = avl.get_value (index, unit_offset, by_ref);
+ const ipa_argagg_value *av = avl.get_elt (index, unit_offset);
+ if (!av || av->by_ref != by_ref)
+ return NULL_TREE;
+ gcc_assert (!av->killed);
+ tree v = av->value;
if (!v
|| maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (v))), bit_size))
return NULL_TREE;
free_ipa_bb_info (bi);
fbi.bb_infos.release ();
+ ts->remove_argaggs_if ([](const ipa_argagg_value &v)
+ {
+ return v.killed;
+ });
+
vec_free (descriptors);
if (cfg_changed)
delete_unreachable_blocks_update_callgraph (node, false);
unsigned index : IPA_PROP_ARG_INDEX_LIMIT_BITS;
/* Whether the value was passed by reference. */
unsigned by_ref : 1;
+ /* Set if the value should not be used after materialization in
+ value_numbering. It is kept around just so that clone materialization can
+ distinguish a combined IPA-CP and IPA-SRA from a deleted argument. */
+ unsigned killed : 1;
};
/* A view into a sorted list of aggregate values in a particular context, be it
--- /dev/null
+/* { dg-lto-do run } */
+/* { dg-lto-options { { -O2 -flto=auto } } } */
+/* { dg-extra-ld-options { -flto-partition=1to1 } } */
+
+extern __attribute__((noinline))
+void foo (int *p);
+
+
+void __attribute__((noinline))
+bar (void)
+{
+ int istat;
+
+ istat = 1234;
+ foo (&istat);
+ if (istat != 1234)
+ __builtin_abort ();
+}
+
+int main (int argc, char **argv)
+{
+ bar ();
+ return 0;
+}
--- /dev/null
+volatile int v = 0;
+
+void __attribute__((noinline))
+foo (int *p)
+{
+ *p = 1234;
+ if (v)
+ *p = 0;
+ return;
+}