Basic kill analysis for modref

Jan Hubicka hubicka@kam.mff.cuni.cz
Thu Nov 11 12:58:51 GMT 2021


Hi,
This patch enables optimization of stores that are killed by calls.
Modref summary is extended by array containing list of access ranges, relative
to function parameters, that are known to be killed by the function.
This array is collected during local analysis and optimized (so separate
stores are glued together). 

Kill analysis in ipa-modref.c is quite simplistic.  In particular no WPA
propagation is done and also we take very simple approach to prove that
given store is executed each invocation of the function.  I simply
require it to be in the first basic block and before anyting that can
throw externally.  I have more fancy code for that but with this patch I
want to primarily discuss interace to tree-ssa-alias.c. I wonder if thre
are some helpers I can re-use?

>From GCC linktime I get 814 functions with non-empty kill vector.

Modref stats:
  modref kill: 39 kills, 7162 queries
  modref use: 25169 disambiguations, 697722 queries
  modref clobber: 2290122 disambiguations, 22750147 queries
  5240008 tbaa queries (0.230329 per modref query)
  806190 base compares (0.035437 per modref query)

(note that more kills happens at early optimization where we did not
inlined that much yet).

For tramp3d (non-lto -O3 build):

Modref stats:
  modref kill: 45 kills, 630 queries
  modref use: 750 disambiguations, 10061 queries
  modref clobber: 35253 disambiguations, 543262 queries
  85347 tbaa queries (0.157101 per modref query)
  18727 base compares (0.034471 per modref query)

So it is not that high, but it gets better after improving the analysis side
and also with -Os and/or PGO (wehre we offline cdtors) and also wiring in
same_addr_size_stores_p which I want to discuss incrementally.

But at least there are not that many queries to slow down compile times
noticeably :)

Honza

gcc/ChangeLog:

	* ipa-modref-tree.h (struct modref_access_node): New member function
	* ipa-modref.c (modref_summary::useful_p): Kills are not useful when
	we can not analyze loads.
	(struct modref_summary_lto): Add kills.
	(modref_summary::dump): Dump kills.
	(record_access): Take access node as parameter.
	(record_access_lto): Likewise.
	(add_kill): New function.
	(merge_call_side_effects): Merge kills.
	(analyze_call): Pass around always_executed.
	(struct summary_ptrs): Add always_executed flag.
	(analyze_load): Update.
	(analyze_store): Handle kills.
	(analyze_stmt): Pass around always_executed flag; handle kills from
	clobbers.
	(analyze_function): Compute always_executed.
	(modref_summaries::duplicate): Copy kills.
	(update_signature): Release kills.
	* ipa-modref.h (struct modref_summary): Add kills.
	* tree-ssa-alias.c (dump_alias_stats): Dump kills.
	(stmt_kills_ref_p): Handle modref kills.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/modref-dse-2.c: New test.

diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 17ff6bb582c..6f8caa331a6 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -120,6 +120,8 @@ static struct {
   unsigned HOST_WIDE_INT modref_use_no_alias;
   unsigned HOST_WIDE_INT modref_clobber_may_alias;
   unsigned HOST_WIDE_INT modref_clobber_no_alias;
+  unsigned HOST_WIDE_INT modref_kill_no;
+  unsigned HOST_WIDE_INT modref_kill_yes;
   unsigned HOST_WIDE_INT modref_tests;
   unsigned HOST_WIDE_INT modref_baseptr_tests;
 } alias_stats;
@@ -169,6 +171,12 @@ dump_alias_stats (FILE *s)
 	   + alias_stats.aliasing_component_refs_p_may_alias);
   dump_alias_stats_in_alias_c (s);
   fprintf (s, "\nModref stats:\n");
+  fprintf (s, "  modref kill: "
+	   HOST_WIDE_INT_PRINT_DEC" kills, "
+	   HOST_WIDE_INT_PRINT_DEC" queries\n",
+	   alias_stats.modref_kill_yes,
+	   alias_stats.modref_kill_yes
+	   + alias_stats.modref_kill_no);
   fprintf (s, "  modref use: "
 	   HOST_WIDE_INT_PRINT_DEC" disambiguations, "
 	   HOST_WIDE_INT_PRINT_DEC" queries\n",
@@ -3373,6 +3381,107 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref)
   if (is_gimple_call (stmt))
     {
       tree callee = gimple_call_fndecl (stmt);
+      struct cgraph_node *node;
+      modref_summary *summary;
+
+      /* Try to disambiguate using modref summary.  Modref records a vector
+	 of stores with known offsets relative to function parameters that must
+	 happen every execution of function.  Find if we have a matching
+	 store and verify that function can not use the value.  */
+      if (callee != NULL_TREE
+	  && (node = cgraph_node::get (callee)) != NULL
+	  && node->binds_to_current_def_p ()
+	  && (summary = get_modref_function_summary (node)) != NULL
+	  && summary->kills.length ())
+	{
+	  tree base = ao_ref_base (ref);
+	  for (unsigned int i = 0; i < summary->kills.length (); i++)
+	    {
+	      modref_access_node &a = summary->kills[i];
+	      tree op;
+	      poly_offset_int off1_adj = 0, off2_adj = 0;
+	      poly_int64 off1, off2;
+	      tree base_ptr = NULL;
+	      tree base_decl = NULL;
+
+	      if (a.parm_index >= 0)
+		op = gimple_call_arg (stmt, a.parm_index);
+	      else if (a.parm_index == MODREF_STATIC_CHAIN_PARM)
+		op = gimple_call_chain (stmt);
+	      else
+		gcc_unreachable ();
+
+	      off2_adj += a.parm_offset * BITS_PER_UNIT;
+	      if (!(off2_adj + a.offset).to_shwi (&off2))
+		continue;
+	      if (TREE_CODE (base) == MEM_REF)
+		{
+		  off1_adj = mem_ref_offset (base) << LOG2_BITS_PER_UNIT;
+		  if (TREE_CODE (TREE_OPERAND (base, 0)) == ADDR_EXPR)
+		    base_decl = TREE_OPERAND (TREE_OPERAND (base, 0), 0);
+		  else
+		    base_ptr = TREE_OPERAND (base, 0);
+		}
+	      /* Give up on TMRs for now.  */
+	      else if (TREE_CODE (base) == TARGET_MEM_REF)
+		break;
+	      else
+		base_decl = base;
+
+	      gcc_checking_assert (!base_decl || DECL_P (base_decl));
+	      gcc_checking_assert (!base_ptr
+				   || TREE_CODE (base_ptr) == SSA_NAME);
+
+	      /* OP is a pointer and we have access range from its
+		 dereference.  */
+	      if (TREE_CODE (op) == ADDR_EXPR)
+		{
+		  poly_int64 size, offset, max_size;
+		  bool reverse;
+		  tree op_base = get_ref_base_and_extent
+			  (TREE_OPERAND (op, 0), &offset, &size,
+			   &max_size, &reverse);
+		  if (!known_size_p (size) || !known_eq (size, max_size))
+		    continue;
+		  off2_adj += offset;
+		  /* &str->foo are not passed as gimple operands directly,
+		     would need to look up the def stmt.  */
+		  gcc_checking_assert (TREE_CODE (op_base) != MEM_REF);
+		  if (!base_decl
+		      || compare_base_decls (op_base, base_decl) != 1)
+		    continue;
+		}
+	      else if (!base_ptr || !operand_equal_p (base_ptr, op))
+		continue;
+
+	      if (!(off1_adj + ref->offset).to_shwi (&off1))
+		continue;
+	      if (!(off2_adj + a.offset).to_shwi (&off2))
+		continue;
+
+	      if (known_subrange_p (off1, ref->max_size, off2, a.size)
+		  && dbg_cnt (ipa_mod_ref))
+		{
+		  /* For store to be killed it needs to not be used earlier.  */
+		  if (ref_maybe_used_by_call_p_1 (as_a <gcall *> (stmt), ref,
+						  true))
+		    break;
+		  if (dump_file && (dump_flags & TDF_DETAILS))
+		    {
+		      fprintf (dump_file,
+			       "ipa-modref: call stmt ");
+		      print_gimple_stmt (dump_file, stmt, 0);
+		      fprintf (dump_file,
+			       "ipa-modref: call to %s kills ",
+			       node->dump_name ());
+		      print_generic_expr (dump_file, ref->base);
+		    }
+		  ++alias_stats.modref_kill_yes;
+		  return true;
+		}
+	    }
+	  ++alias_stats.modref_kill_no;
+	}
       if (callee != NULL_TREE
 	  && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
 	switch (DECL_FUNCTION_CODE (callee))
diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h
index 3e213b23d79..dc3b910801d 100644
--- a/gcc/ipa-modref-tree.h
+++ b/gcc/ipa-modref-tree.h
@@ -287,6 +287,55 @@ struct GTY(()) modref_access_node
 	      size, max_size, record_adjustments);
       return true;
     }
+  /* Merge in access A if it is possible to do without losing
+     precision.  Return true if successful.
+     Unlike merge assume that both accesses are always executed
+     and merge size the same was as max_size.  */
+  bool merge_for_kills (const modref_access_node &a)
+    {
+      poly_int64 offset1 = 0;
+      poly_int64 aoffset1 = 0;
+      poly_int64 new_parm_offset = 0;
+
+      /* We assume that containment was tested earlier.  */
+      gcc_checking_assert (!contains (a) && !a.contains (*this));
+      /* For kills we allways need to know parameter.  */
+      gcc_checking_assert (parm_index != MODREF_UNKNOWN_PARM
+			   && a.parm_index != MODREF_UNKNOWN_PARM);
+      if (parm_index != a.parm_index)
+	return false;
+      /* Unknown offsets are useless.  */
+      gcc_checking_assert (parm_offset_known && a.parm_offset_known);
+      if (!combined_offsets (a, &new_parm_offset, &offset1, &aoffset1))
+	return false;
+      /* Ranges must be known and sizes matching max sizes.  */
+      gcc_checking_assert (range_info_useful_p ()
+			  && a.range_info_useful_p ()
+			  && known_size_p (size) && known_eq (size, max_size)
+			  && known_size_p (a.size)
+			  && known_eq (a.size, a.max_size));
+      if (known_le (offset1, aoffset1))
+	{
+	  if (!known_size_p (max_size)
+	      || known_ge (offset1 + max_size, aoffset1))
+	    {
+	      update_for_kills (new_parm_offset, offset1, max_size,
+				aoffset1, a.max_size);
+	      return true;
+	    }
+	}
+      else if (known_le (aoffset1, offset1))
+	{
+	  if (!known_size_p (a.max_size)
+	      || known_ge (aoffset1 + a.max_size, offset1))
+	    {
+	      update_for_kills (new_parm_offset, offset1, max_size,
+				aoffset1, a.max_size);
+	      return true;
+	    }
+	}
+      return false;
+    }
   /* Return true if A1 and B1 can be merged with lower informatoin
      less than A2 and B2.
      Assume that no containment or lossless merging is possible.  */
@@ -430,6 +479,33 @@ private:
       update (parm_offset1, offset1,
 	      new_size, new_max_size, record_adjustments);
     }
+  /* Merge two ranges both starting at parm_offset1 and update THIS
+     with result.  */
+  void update_for_kills (poly_int64 parm_offset1,
+			 poly_int64 offset1,
+			 poly_int64 max_size1,
+			 poly_int64 offset2,
+			 poly_int64 max_size2)
+    {
+      if (known_le (offset1, offset2))
+	;
+      else if (known_le (offset2, offset1))
+	{
+	  std::swap (offset1, offset2);
+	  std::swap (max_size1, max_size2);
+	}
+      else
+	gcc_unreachable ();
+
+      poly_int64 new_max_size = max_size2 + offset2 - offset1;
+      if (known_le (new_max_size, max_size1))
+	new_max_size = max_size1;
+
+      parm_offset = parm_offset1;
+      offset = offset1;
+      max_size = new_max_size;
+      size = new_max_size;
+    }
   /* Given access nodes THIS and A, return true if they
      can be done with common parm_offsets.  In this case
      return parm offset in new_parm_offset, new_offset
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index f8b7b900527..d9859daa18e 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -334,6 +334,8 @@ modref_summary::useful_p (int ecf_flags, bool check_flags)
     return false;
   if (loads && !loads->every_base)
     return true;
+  else
+    kills.release ();
   if (ecf_flags & ECF_PURE)
     return false;
   return stores && !stores->every_base;
@@ -370,6 +372,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<modref_access_node> GTY((skip)) kills;
   auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
   eaf_flags_t retslot_flags;
   eaf_flags_t static_chain_flags;
@@ -617,6 +620,12 @@ modref_summary::dump (FILE *out)
       fprintf (out, "  stores:\n");
       dump_records (stores, out);
     }
+  if (kills.length ())
+    {
+      fprintf (out, "  kills:\n");
+      for (unsigned int i = 0; i < kills.length (); i++)
+	dump_access (&kills[i], out);
+    }
   if (writes_errno)
     fprintf (out, "  Writes errno\n");
   if (side_effects)
@@ -750,13 +759,12 @@ get_access (ao_ref *ref)
 /* Record access into the modref_records data structure.  */
 
 static void
-record_access (modref_records *tt, ao_ref *ref)
+record_access (modref_records *tt, ao_ref *ref, modref_access_node &a)
 {
   alias_set_type base_set = !flag_strict_aliasing ? 0
 			    : ao_ref_base_alias_set (ref);
   alias_set_type ref_set = !flag_strict_aliasing ? 0
 			    : (ao_ref_alias_set (ref));
-  modref_access_node a = get_access (ref);
   if (dump_file)
     {
        fprintf (dump_file, "   - Recording base_set=%i ref_set=%i ",
@@ -769,7 +777,7 @@ record_access (modref_records *tt, ao_ref *ref)
 /* IPA version of record_access_tree.  */
 
 static void
-record_access_lto (modref_records_lto *tt, ao_ref *ref)
+record_access_lto (modref_records_lto *tt, ao_ref *ref, modref_access_node &a)
 {
   /* get_alias_set sometimes use different type to compute the alias set
      than TREE_TYPE (base).  Do same adjustments.  */
@@ -816,7 +824,6 @@ record_access_lto (modref_records_lto *tt, ao_ref *ref)
 		       || variably_modified_type_p (ref_type, NULL_TREE)))
 	ref_type = NULL_TREE;
     }
-  modref_access_node a = get_access (ref);
   if (dump_file)
     {
       fprintf (dump_file, "   - Recording base type:");
@@ -910,6 +917,82 @@ parm_map_for_arg (tree op)
   return parm_map;
 }
 
+/* Insert new kill A into KILLS.  */
+
+static void
+add_kill (vec<modref_access_node> &kills, modref_access_node &a)
+{
+  size_t index;
+  modref_access_node *a2;
+  bool merge = false;
+
+  /* See if we have corresponding etry already or we can merge with
+     neighbouring entry.  */
+  FOR_EACH_VEC_ELT (kills, index, a2)
+    {
+      if (a2->contains (a))
+	return;
+      if (a.contains (*a2))
+	{
+	  *a2 = a;
+	  merge = true;
+	  break;
+	}
+      if (a2->merge_for_kills (a))
+	{
+	  merge = true;
+	  break;
+	}
+    }
+  /* If entry was not found, insert it.  */
+  if (!merge)
+    {
+      if ((int)kills.length () >= param_modref_max_accesses)
+	{
+	  if (dump_file)
+	    fprintf (dump_file,
+		     "--param param=modref-max-accesses limit reached:");
+	  return;
+	}
+      kills.safe_push (a);
+      return;
+    }
+  /* Extenidng range in an entry may make it possible to merge it with
+     other entries.  */
+    {
+      size_t i;
+
+      for (i = 0; i < kills.length ();)
+	if (i != index)
+	  {
+	    bool found = false, restart = false;
+	    modref_access_node *a = &kills[i];
+	    modref_access_node *n = &kills[index];
+
+	    if (n->contains (*a))
+	      found = true;
+	    if (!found && n->merge_for_kills (*a))
+	      found = restart = true;
+	    gcc_checking_assert (found || !a->merge_for_kills (*n));
+	    if (found)
+	      {
+		kills.unordered_remove (i);
+		if (index == kills.length ())
+		  {
+		    index = i;
+		    i++;
+		  }
+		if (restart)
+		  i = 0;
+	      }
+	    else
+	      i++;
+	  }
+	else
+	  i++;
+    }
+}
+
 /* Merge side effects of call STMT to function with CALLEE_SUMMARY
    int CUR_SUMMARY.  Return true if something changed.
    If IGNORE_STORES is true, do not merge stores.
@@ -920,7 +1003,7 @@ bool
 merge_call_side_effects (modref_summary *cur_summary,
 			 gimple *stmt, modref_summary *callee_summary,
 			 bool ignore_stores, cgraph_node *callee_node,
-			 bool record_adjustments)
+			 bool record_adjustments, bool always_executed)
 {
   auto_vec <modref_parm_map, 32> parm_map;
   modref_parm_map chain_map;
@@ -988,6 +1071,29 @@ merge_call_side_effects (modref_summary *cur_summary,
 	  changed = true;
 	}
     }
+  if (always_executed)
+    {
+      size_t i;
+      modref_access_node *a2;
+      FOR_EACH_VEC_ELT (callee_summary->kills, i, a2)
+	{
+	  modref_access_node n = *a2;
+	  if (n.parm_index >= (int)parm_map.length ())
+	    continue;
+	  modref_parm_map &m
+		  = n.parm_index == MODREF_STATIC_CHAIN_PARM
+		    ? chain_map
+		    : parm_map[n.parm_index];
+	  if (m.parm_index == MODREF_LOCAL_MEMORY_PARM
+	      || m.parm_index == MODREF_UNKNOWN_PARM
+	      || m.parm_index == MODREF_RETSLOT_PARM
+	      || !m.parm_offset_known)
+	    continue;
+	  n.parm_index = m.parm_index;
+	  n.parm_offset += m.parm_offset;
+	  add_kill (cur_summary->kills, n);
+	}
+    }
   if (!cur_summary->side_effects
       && callee_summary->side_effects)
     {
@@ -1198,7 +1304,8 @@ process_fnspec (modref_summary *cur_summary,
 
 static bool
 analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
-	      gcall *stmt, vec <gimple *> *recursive_calls)
+	      gcall *stmt, vec <gimple *> *recursive_calls,
+	      bool always_executed)
 {
   /* Check flags on the function call.  In certain cases, analysis can be
      simplified.  */
@@ -1284,7 +1391,7 @@ analyze_call (modref_summary *cur_summary, modref_summary_lto *cur_summary_lto,
     }
 
   merge_call_side_effects (cur_summary, stmt, callee_summary, ignore_stores,
-			   callee_node, false);
+			   callee_node, false, always_executed);
 
   return true;
 }
@@ -1295,6 +1402,7 @@ struct summary_ptrs
 {
   struct modref_summary *nolto;
   struct modref_summary_lto *lto;
+  bool always_executed;
 };
 
 /* Helper for analyze_stmt.  */
@@ -1329,11 +1437,12 @@ analyze_load (gimple *, tree, tree op, void *data)
 
   ao_ref r;
   ao_ref_init (&r, op);
+  modref_access_node a = get_access (&r);
 
   if (summary)
-    record_access (summary->loads, &r);
+    record_access (summary->loads, &r, a);
   if (summary_lto)
-    record_access_lto (summary_lto->loads, &r);
+    record_access_lto (summary_lto->loads, &r, a);
   return false;
 }
 
@@ -1369,11 +1478,24 @@ analyze_store (gimple *, tree, tree op, void *data)
 
   ao_ref r;
   ao_ref_init (&r, op);
+  modref_access_node a = get_access (&r);
 
   if (summary)
-    record_access (summary->stores, &r);
+    record_access (summary->stores, &r, a);
   if (summary_lto)
-    record_access_lto (summary_lto->stores, &r);
+    record_access_lto (summary_lto->stores, &r, a);
+  if (summary
+      && ((summary_ptrs *)data)->always_executed
+      && a.parm_offset_known == true
+      && a.parm_index != MODREF_UNKNOWN_PARM
+      && a.parm_index != MODREF_RETSLOT_PARM
+      && known_size_p (r.size)
+      && known_eq (r.max_size, r.size))
+    {
+      if (dump_file)
+	fprintf (dump_file, "   - Recording kill\n");
+      add_kill (summary->kills, a);
+    }
   return false;
 }
 
@@ -1382,16 +1504,36 @@ analyze_store (gimple *, tree, tree op, void *data)
 
 static bool
 analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
-	      gimple *stmt, bool ipa, vec <gimple *> *recursive_calls)
+	      gimple *stmt, bool ipa, vec <gimple *> *recursive_calls,
+	      bool always_executed)
 {
   /* In general we can not ignore clobbers because they are barriers for code
      motion, however after inlining it is safe to do because local optimization
      passes do not consider clobbers from other functions.
      Similar logic is in ipa-pure-const.c.  */
   if ((ipa || cfun->after_inlining) && gimple_clobber_p (stmt))
-    return true;
+    {
+      if (summary
+	  && always_executed && record_access_p (gimple_assign_lhs (stmt)))
+	{
+	  ao_ref r;
+	  ao_ref_init (&r, gimple_assign_lhs (stmt));
+	  modref_access_node a = get_access (&r);
+	  if (a.parm_offset_known == true
+	      && a.parm_index != MODREF_UNKNOWN_PARM
+	      && a.parm_index != MODREF_RETSLOT_PARM
+	      && known_size_p (r.size)
+	      && known_eq (r.max_size, r.size))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "   - Recording kill\n");
+	      add_kill (summary->kills, a);
+	    }
+	}
+      return true;
+    }
 
-  struct summary_ptrs sums = {summary, summary_lto};
+  struct summary_ptrs sums = {summary, summary_lto, always_executed};
 
   /* Analyze all loads and stores in STMT.  */
   walk_stmt_load_store_ops (stmt, &sums,
@@ -1420,7 +1562,8 @@ analyze_stmt (modref_summary *summary, modref_summary_lto *summary_lto,
    case GIMPLE_CALL:
      if (!ipa || gimple_call_internal_p (stmt))
        return analyze_call (summary, summary_lto,
-			    as_a <gcall *> (stmt), recursive_calls);
+			    as_a <gcall *> (stmt), recursive_calls,
+			    always_executed);
      else
       {
 	attr_fnspec fnspec = gimple_call_fnspec (as_a <gcall *>(stmt));
@@ -2729,11 +2872,15 @@ analyze_function (function *f, bool ipa)
   FOR_EACH_BB_FN (bb, f)
     {
       gimple_stmt_iterator si;
+      bool always_executed
+	      = bb == single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest;
+
       for (si = gsi_start_nondebug_after_labels_bb (bb);
 	   !gsi_end_p (si); gsi_next_nondebug (&si))
 	{
 	  if (!analyze_stmt (summary, summary_lto,
-			     gsi_stmt (si), ipa, &recursive_calls)
+			     gsi_stmt (si), ipa, &recursive_calls,
+			     always_executed)
 	      || ((!summary || !summary->useful_p (ecf_flags, false))
 		  && (!summary_lto
 		      || !summary_lto->useful_p (ecf_flags, false))))
@@ -2742,6 +2889,9 @@ analyze_function (function *f, bool ipa)
 	      collapse_stores (summary, summary_lto);
 	      break;
 	    }
+	  if (always_executed
+	      && stmt_can_throw_external (cfun, gsi_stmt (si)))
+	    always_executed = false;
 	}
     }
 
@@ -2761,7 +2911,7 @@ analyze_function (function *f, bool ipa)
 			   ignore_stores_p (current_function_decl,
 					    gimple_call_flags
 						 (recursive_calls[i])),
-			   fnode, !first);
+			   fnode, !first, false);
 	      if (!summary->useful_p (ecf_flags, false))
 		{
 		  remove_summary (lto, nolto, ipa);
@@ -2993,6 +3143,8 @@ modref_summaries::duplicate (cgraph_node *, cgraph_node *dst,
 			 src_data->loads->max_refs,
 			 src_data->loads->max_accesses);
   dst_data->loads->copy_from (src_data->loads);
+  dst_data->kills.reserve_exact (src_data->kills.length ());
+  dst_data->kills.splice (src_data->kills);
   dst_data->writes_errno = src_data->writes_errno;
   dst_data->side_effects = src_data->side_effects;
   if (src_data->arg_flags.length ())
@@ -3640,6 +3792,8 @@ update_signature (struct cgraph_node *node)
     {
       r->loads->remap_params (&map);
       r->stores->remap_params (&map);
+      /* TODO: One we do IPA kills analysis, update the table here.  */
+      r->kills.release ();
       if (r->arg_flags.length ())
 	remap_arg_flags (r->arg_flags, info);
     }
@@ -3647,6 +3801,8 @@ update_signature (struct cgraph_node *node)
     {
       r_lto->loads->remap_params (&map);
       r_lto->stores->remap_params (&map);
+      /* TODO: One we do IPA kills analysis, update the table here.  */
+      r_lto->kills.release ();
       if (r_lto->arg_flags.length ())
 	remap_arg_flags (r_lto->arg_flags, info);
     }
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index 49c99f263a7..b77c1aa7400 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -30,6 +30,7 @@ struct GTY(()) modref_summary
   /* Load and stores in function (transitively closed to all callees)  */
   modref_records *loads;
   modref_records *stores;
+  auto_vec<modref_access_node> GTY((skip)) kills;
   auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
   eaf_flags_t retslot_flags;
   eaf_flags_t static_chain_flags;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-dse-2.c b/gcc/testsuite/gcc.dg/tree-ssa/modref-dse-2.c
new file mode 100644
index 00000000000..2a23a9e8ccb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-dse-2.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse1"  } */
+__attribute__ ((noinline))
+void write (int *a)
+{
+	*a=1;
+	a[1]=2;
+}
+int test ()
+{
+	int a;
+	a=2;
+	write (&a);
+	return a;
+}
+int test2 (int *a)
+{
+	*a=2;
+	write (a);
+	return *a;
+}
+/* { dg-final { scan-tree-dump-times "Deleted dead store" 2 "dse1"} } */


More information about the Gcc-patches mailing list