Patchwork Ping: [PATCH, PR 42371] Remove references to functions from symbol table during inlining

login
register
mail settings
Submitter Martin Jambor
Date May 10, 2013, 4:43 p.m.
Message ID <20130510164317.GE3568@virgil.suse>
Download mbox | patch
Permalink /patch/243039/
State New
Headers show

Comments

Martin Jambor - May 10, 2013, 4:43 p.m.
Hi,

I thought I was hitting the ipa-inline-transform.c:263 assert with
this patch when LTO-building Mozilla Firefox but it turned out it was
caused by a different patch so I'm handling that there.

On Thu, May 02, 2013 at 02:37:26PM +0200, Jan Hubicka wrote:
> > 2013-03-22  Martin Jambor  <mjambor@suse.cz>
> > 
> > 	PR middle-end/42371
> > 	* ipa-prop.h (IPA_UNDESCRIBED_USE): New macro.
> > 	(ipa_constant_data): New type.
> > 	(ipa_jump_func): Use ipa_constant_data to hold information about
> > 	constant jump functions.
> > 	(ipa_get_jf_constant): Adjust to jump function type changes.
> > 	(ipa_get_jf_constant_rdesc): New function.
> > 	(ipa_param_descriptor): New field controlled_uses.
> > 	(ipa_get_controlled_uses): New function.
> > 	(ipa_set_controlled_uses): Likewise.
> > 	* ipa-ref.h (ipa_find_reference): Declare.
> > 	* ipa-prop.c (ipa_cst_ref_desc): New type.
> > 	(ipa_print_node_jump_functions_for_edge): Adjust for jump function type
> > 	changes.
> > 	(ipa_set_jf_constant): Likewise.  Also create reference descriptions.
> > 	New parameter cs.  Adjust all callers.
> > 	(ipa_analyze_params_uses): Detect uncontrolled and controlled uses.
> > 	(remove_described_reference): New function.
> > 	(jfunc_rdesc_usable): Likewise.
> > 	(try_make_edge_direct_simple_call): Decrement controlled use count,
> > 	attempt to remove reference if it hits zero.
> > 	(combine_controlled_uses_counters): New function.
> > 	(propagate_controlled_uses): Likewise.
> > 	(ipa_propagate_indirect_call_infos): Call propagate_controlled_uses.
> > 	(ipa_edge_duplication_hook): Duplicate reference descriptions.
> > 	(ipa_print_node_params): Print described use counter.
> > 	(ipa_write_jump_function): Adjust to jump function type changes.
> > 	(ipa_read_jump_function): New parameter CS, pass it to
> > 	ipa_set_jf_constant.  Adjust caller.
> > 	(ipa_write_node_info): Stream controlled use count
> > 	(ipa_read_node_info): Likewise.
> > 	* cgraph.c (cgraph_mark_address_taken_node): Bail out instead of
> > 	asserting.
> > 	* ipa-cp.c (ipcp_discover_new_direct_edges): Decrement controlled use
> > 	count.  Remove cloning-added reference if it reaches zero.
> > 	* ipa-ref.c (ipa_find_reference): New function.
> 
> As mentioned offline, I think the patch should be extended to work on references in general,
> so we can do more of promoting to !TREE_ADDRESSABLE. But it can be handled incrementally.

Yeah, adding that is easy.  But since I add no use for that, I
refrained from allocating data for them.

> 
> > Index: src/gcc/cgraph.c
> > ===================================================================
> > *** src.orig/gcc/cgraph.c
> > --- src/gcc/cgraph.c
> > *************** cgraph_remove_node (struct cgraph_node *
> > *** 1409,1415 ****
> >   void
> >   cgraph_mark_address_taken_node (struct cgraph_node *node)
> >   {
> > !   gcc_assert (!node->global.inlined_to);
> >     /* FIXME: address_taken flag is used both as a shortcut for testing whether
> >        IPA_REF_ADDR reference exists (and thus it should be set on node
> >        representing alias we take address of) and as a test whether address
> > --- 1409,1418 ----
> >   void
> >   cgraph_mark_address_taken_node (struct cgraph_node *node)
> >   {
> > !   /* Indirect inlining can figure out that all uses of the address are
> > !      inlined.  */
> > !   if (node->global.inlined_to)
> > !     return;
> >     /* FIXME: address_taken flag is used both as a shortcut for testing whether
> >        IPA_REF_ADDR reference exists (and thus it should be set on node
> >        representing alias we take address of) and as a test whether address
> 
> Perhaps add assert that after_inlining is set?

I added asserting:
      gcc_assert (cfun->after_inlining);
      gcc_assert (node->callers->indirect_inlining_edge);

> 
> Paths is OK,

OK, the updated patch is below (the only change is the extra asserts),
re-bootstrapped and re-tested on x86_64-linux.  I will commit it on
Monday if there are no objections.

Thanks,

Martin


2013-05-09  Martin Jambor  <mjambor@suse.cz>

	PR middle-end/42371
	* ipa-prop.h (IPA_UNDESCRIBED_USE): New macro.
	(ipa_constant_data): New type.
	(ipa_jump_func): Use ipa_constant_data to hold information about
	constant jump functions.
	(ipa_get_jf_constant): Adjust to jump function type changes.
	(ipa_get_jf_constant_rdesc): New function.
	(ipa_param_descriptor): New field controlled_uses.
	(ipa_get_controlled_uses): New function.
	(ipa_set_controlled_uses): Likewise.
	* ipa-ref.h (ipa_find_reference): Declare.
	* ipa-prop.c (ipa_cst_ref_desc): New type.
	(ipa_print_node_jump_functions_for_edge): Adjust for jump function type
	changes.
	(ipa_set_jf_constant): Likewise.  Also create reference descriptions.
	New parameter cs.  Adjust all callers.
	(ipa_analyze_params_uses): Detect uncontrolled and controlled uses.
	(remove_described_reference): New function.
	(jfunc_rdesc_usable): Likewise.
	(try_make_edge_direct_simple_call): Decrement controlled use count,
	attempt to remove reference if it hits zero.
	(combine_controlled_uses_counters): New function.
	(propagate_controlled_uses): Likewise.
	(ipa_propagate_indirect_call_infos): Call propagate_controlled_uses.
	(ipa_edge_duplication_hook): Duplicate reference descriptions.
	(ipa_print_node_params): Print described use counter.
	(ipa_write_jump_function): Adjust to jump function type changes.
	(ipa_read_jump_function): New parameter CS, pass it to
	ipa_set_jf_constant.  Adjust caller.
	(ipa_write_node_info): Stream controlled use count
	(ipa_read_node_info): Likewise.
	* cgraph.c (cgraph_mark_address_taken_node): Bail out instead of
	asserting.
	* ipa-cp.c (ipcp_discover_new_direct_edges): Decrement controlled use
	count.  Remove cloning-added reference if it reaches zero.
	* ipa-ref.c (ipa_find_reference): New function.

testsuite/
	* gcc.dg/ipa/remref-0.c: New test.
	* gcc.dg/ipa/remref-1a.c: Likewise.
	* gcc.dg/ipa/remref-1b.c: Likewise.
	* gcc.dg/ipa/remref-2a.c: Likewise.
	* gcc.dg/ipa/remref-2b.c: Likewise.
Jan Hubicka - May 10, 2013, 5:17 p.m.
> OK, the updated patch is below (the only change is the extra asserts),
> re-bootstrapped and re-tested on x86_64-linux.  I will commit it on
> Monday if there are no objections.

No objections from my side. Path looks OK.

Thanks,
Honza

Patch

Index: src/gcc/ipa-prop.c
===================================================================
--- src.orig/gcc/ipa-prop.c
+++ src/gcc/ipa-prop.c
@@ -62,6 +62,22 @@  static struct cgraph_2edge_hook_list *ed
 static struct cgraph_2node_hook_list *node_duplication_hook_holder;
 static struct cgraph_node_hook_list *function_insertion_hook_holder;
 
+/* Description of a reference to an IPA constant.  */
+struct ipa_cst_ref_desc
+{
+  /* Edge that corresponds to the statement which took the reference.  */
+  struct cgraph_edge *cs;
+  /* Linked list of duplicates created when call graph edges are cloned.  */
+  struct ipa_cst_ref_desc *next_duplicate;
+  /* Number of references in IPA structures, IPA_UNDESCRIBED_USE if the value
+     if out of control.  */
+  int refcount;
+};
+
+/* Allocation pool for reference descriptions.  */
+
+static alloc_pool ipa_refdesc_pool;
+
 /* Return index of the formal whose tree is PTREE in function which corresponds
    to INFO.  */
 
@@ -175,7 +191,7 @@  ipa_print_node_jump_functions_for_edge (
 	}
       else if (type == IPA_JF_CONST)
 	{
-	  tree val = jump_func->value.constant;
+	  tree val = jump_func->value.constant.value;
 	  fprintf (f, "CONST: ");
 	  print_generic_expr (f, val, 0);
 	  if (TREE_CODE (val) == ADDR_EXPR
@@ -309,13 +325,31 @@  ipa_set_jf_known_type (struct ipa_jump_f
 /* Set JFUNC to be a constant jmp function.  */
 
 static void
-ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant)
+ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant,
+		     struct cgraph_edge *cs)
 {
   constant = unshare_expr (constant);
   if (constant && EXPR_P (constant))
     SET_EXPR_LOCATION (constant, UNKNOWN_LOCATION);
   jfunc->type = IPA_JF_CONST;
-  jfunc->value.constant = unshare_expr_without_location (constant);
+  jfunc->value.constant.value = unshare_expr_without_location (constant);
+
+  if (TREE_CODE (constant) == ADDR_EXPR
+      && TREE_CODE (TREE_OPERAND (constant, 0)) == FUNCTION_DECL)
+    {
+      struct ipa_cst_ref_desc *rdesc;
+      if (!ipa_refdesc_pool)
+	ipa_refdesc_pool = create_alloc_pool ("IPA-PROP ref descriptions",
+					sizeof (struct ipa_cst_ref_desc), 32);
+
+      rdesc = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+      rdesc->cs = cs;
+      rdesc->next_duplicate = NULL;
+      rdesc->refcount = 1;
+      jfunc->value.constant.rdesc = rdesc;
+    }
+  else
+    jfunc->value.constant.rdesc = NULL;
 }
 
 /* Set JFUNC to be a simple pass-through jump function.  */
@@ -1404,7 +1438,7 @@  ipa_compute_jump_functions_for_edge (str
       tree arg = gimple_call_arg (call, n);
 
       if (is_gimple_ip_invariant (arg))
-	ipa_set_jf_constant (jfunc, arg);
+	ipa_set_jf_constant (jfunc, arg, cs);
       else if (!is_gimple_reg_type (TREE_TYPE (arg))
 	       && TREE_CODE (arg) == PARM_DECL)
 	{
@@ -1891,14 +1925,35 @@  ipa_analyze_params_uses (struct cgraph_n
   for (i = 0; i < ipa_get_param_count (info); i++)
     {
       tree parm = ipa_get_param (info, i);
-      tree ddef;
+      int controlled_uses = 0;
+
       /* For SSA regs see if parameter is used.  For non-SSA we compute
 	 the flag during modification analysis.  */
-      if (is_gimple_reg (parm)
-	  && (ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->symbol.decl),
-				      parm)) != NULL_TREE
-	  && !has_zero_uses (ddef))
-	ipa_set_param_used (info, i, true);
+      if (is_gimple_reg (parm))
+	{
+	  tree ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->symbol.decl),
+				       parm);
+	  if (ddef && !has_zero_uses (ddef))
+	    {
+	      imm_use_iterator imm_iter;
+	      use_operand_p use_p;
+
+	      ipa_set_param_used (info, i, true);
+	      FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ddef)
+		if (!is_gimple_call (USE_STMT (use_p)))
+		  {
+		    controlled_uses = IPA_UNDESCRIBED_USE;
+		    break;
+		  }
+		else
+		  controlled_uses++;
+	    }
+	  else
+	    controlled_uses = 0;
+	}
+      else
+	controlled_uses = IPA_UNDESCRIBED_USE;
+      ipa_set_controlled_uses (info, i, controlled_uses);
     }
 
   func = DECL_STRUCT_FUNCTION (decl);
@@ -2226,6 +2281,40 @@  ipa_find_agg_cst_for_param (struct ipa_a
   return NULL;
 }
 
+/* Remove a reference to SYMBOL from the list of references of a node given by
+   reference description RDESC.  */
+
+static void
+remove_described_reference (symtab_node symbol, struct ipa_cst_ref_desc *rdesc)
+{
+  struct ipa_ref *to_del;
+  struct cgraph_edge *origin;
+
+  origin = rdesc->cs;
+  to_del = ipa_find_reference ((symtab_node) origin->caller, symbol,
+			       origin->call_stmt);
+  gcc_assert (to_del);
+  ipa_remove_reference (to_del);
+  if (dump_file)
+    fprintf (dump_file, "ipa-prop: Removed a reference from %s/%i to %s.\n",
+	     xstrdup (cgraph_node_name (origin->caller)),
+	     origin->caller->uid, xstrdup (symtab_node_name (symbol)));
+}
+
+/* If JFUNC has a reference description with refcount different from
+   IPA_UNDESCRIBED_USE, return the reference description, otherwise return
+   NULL.  JFUNC must be a constant jump function.  */
+
+static struct ipa_cst_ref_desc *
+jfunc_rdesc_usable (struct ipa_jump_func *jfunc)
+{
+  struct ipa_cst_ref_desc *rdesc = ipa_get_jf_constant_rdesc (jfunc);
+  if (rdesc && rdesc->refcount != IPA_UNDESCRIBED_USE)
+    return rdesc;
+  else
+    return NULL;
+}
+
 /* Try to find a destination for indirect edge IE that corresponds to a simple
    call or a call of a member function pointer and where the destination is a
    pointer formal parameter described by jump function JFUNC.  If it can be
@@ -2237,6 +2326,8 @@  try_make_edge_direct_simple_call (struct
 				  struct ipa_jump_func *jfunc,
 				  struct ipa_node_params *new_root_info)
 {
+  struct ipa_cst_ref_desc *rdesc;
+  struct cgraph_edge *cs;
   tree target;
 
   if (ie->indirect_info->agg_contents)
@@ -2247,7 +2338,15 @@  try_make_edge_direct_simple_call (struct
     target = ipa_value_from_jfunc (new_root_info, jfunc);
   if (!target)
     return NULL;
-  return ipa_make_edge_direct_to_target (ie, target);
+  cs = ipa_make_edge_direct_to_target (ie, target);
+
+  if (cs && !ie->indirect_info->agg_contents
+      && jfunc->type == IPA_JF_CONST
+      && (rdesc = jfunc_rdesc_usable (jfunc))
+      && --rdesc->refcount == 0)
+    remove_described_reference ((symtab_node) cs->callee, rdesc);
+
+  return cs;
 }
 
 /* Try to find a destination for indirect edge IE that corresponds to a virtual
@@ -2411,6 +2510,135 @@  propagate_info_to_inlined_callees (struc
   return res;
 }
 
+/* Combine two controlled uses counts as done during inlining.  */
+
+static int
+combine_controlled_uses_counters (int c, int d)
+{
+  if (c == IPA_UNDESCRIBED_USE || d == IPA_UNDESCRIBED_USE)
+    return IPA_UNDESCRIBED_USE;
+  else
+    return c + d - 1;
+}
+
+/* Propagate number of controlled users from CS->caleee to the new root of the
+   tree of inlined nodes.  */
+
+static void
+propagate_controlled_uses (struct cgraph_edge *cs)
+{
+  struct ipa_edge_args *args = IPA_EDGE_REF (cs);
+  struct cgraph_node *new_root = cs->caller->global.inlined_to
+    ? cs->caller->global.inlined_to : cs->caller;
+  struct ipa_node_params *new_root_info = IPA_NODE_REF (new_root);
+  struct ipa_node_params *old_root_info = IPA_NODE_REF (cs->callee);
+  int count, i;
+
+  count = MIN (ipa_get_cs_argument_count (args),
+	       ipa_get_param_count (old_root_info));
+  for (i = 0; i < count; i++)
+    {
+      struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i);
+      struct ipa_cst_ref_desc *rdesc;
+
+      if (jf->type == IPA_JF_PASS_THROUGH)
+	{
+	  int src_idx, c, d;
+	  src_idx = ipa_get_jf_pass_through_formal_id (jf);
+	  c = ipa_get_controlled_uses (new_root_info, src_idx);
+	  d = ipa_get_controlled_uses (old_root_info, i);
+
+	  gcc_checking_assert (ipa_get_jf_pass_through_operation (jf)
+			       == NOP_EXPR || c == IPA_UNDESCRIBED_USE);
+	  c = combine_controlled_uses_counters (c, d);
+	  ipa_set_controlled_uses (new_root_info, src_idx, c);
+	  if (c == 0 && new_root_info->ipcp_orig_node)
+	    {
+	      struct cgraph_node *n;
+	      struct ipa_ref *ref;
+	      tree t = new_root_info->known_vals[src_idx];
+
+	      if (t && TREE_CODE (t) == ADDR_EXPR
+		  && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
+		  && (n = cgraph_get_node (TREE_OPERAND (t, 0)))
+		  && (ref = ipa_find_reference ((symtab_node) new_root,
+						(symtab_node) n, NULL)))
+		{
+		  if (dump_file)
+		    fprintf (dump_file, "ipa-prop: Removing cloning-created "
+			     "reference from %s/%i to %s/%i.\n",
+			     xstrdup (cgraph_node_name (new_root)),
+			     new_root->uid,
+			     xstrdup (cgraph_node_name (n)), n->uid);
+		  ipa_remove_reference (ref);
+		}
+	    }
+	}
+      else if (jf->type == IPA_JF_CONST
+	       && (rdesc = jfunc_rdesc_usable (jf)))
+	{
+	  int d = ipa_get_controlled_uses (old_root_info, i);
+	  int c = rdesc->refcount;
+	  rdesc->refcount = combine_controlled_uses_counters (c, d);
+	  if (rdesc->refcount == 0)
+	    {
+	      tree cst = ipa_get_jf_constant (jf);
+	      struct cgraph_node *n;
+	      gcc_checking_assert (TREE_CODE (cst) == ADDR_EXPR
+				   && TREE_CODE (TREE_OPERAND (cst, 0))
+				   == FUNCTION_DECL);
+	      n = cgraph_get_node (TREE_OPERAND (cst, 0));
+	      if (n)
+		{
+		  struct cgraph_node *clone;
+		  remove_described_reference ((symtab_node) n, rdesc);
+
+		  clone = cs->caller;
+		  while (clone->global.inlined_to
+			 && clone != rdesc->cs->caller
+			 && IPA_NODE_REF (clone)->ipcp_orig_node)
+		    {
+		      struct ipa_ref *ref;
+		      ref = ipa_find_reference ((symtab_node) clone,
+						(symtab_node) n, NULL);
+		      if (ref)
+			{
+			  if (dump_file)
+			    fprintf (dump_file, "ipa-prop: Removing "
+				     "cloning-created reference "
+				     "from %s/%i to %s/%i.\n",
+				     xstrdup (cgraph_node_name (clone)),
+				     clone->uid,
+				     xstrdup (cgraph_node_name (n)),
+				     n->uid);
+			  ipa_remove_reference (ref);
+			}
+		      clone = clone->callers->caller;
+		    }
+		}
+	    }
+	}
+    }
+
+  for (i = ipa_get_param_count (old_root_info);
+       i < ipa_get_cs_argument_count (args);
+       i++)
+    {
+      struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i);
+
+      if (jf->type == IPA_JF_CONST)
+	{
+	  struct ipa_cst_ref_desc *rdesc = jfunc_rdesc_usable (jf);
+	  if (rdesc)
+	    rdesc->refcount = IPA_UNDESCRIBED_USE;
+	}
+      else if (jf->type == IPA_JF_PASS_THROUGH)
+	ipa_set_controlled_uses (new_root_info,
+				 jf->value.pass_through.formal_id,
+				 IPA_UNDESCRIBED_USE);
+    }
+}
+
 /* Update jump functions and call note functions on inlining the call site CS.
    CS is expected to lead to a node already cloned by
    cgraph_clone_inline_nodes.  Newly discovered indirect edges will be added to
@@ -2428,6 +2656,7 @@  ipa_propagate_indirect_call_infos (struc
     return false;
   gcc_assert (ipa_edge_args_vector);
 
+  propagate_controlled_uses (cs);
   changed = propagate_info_to_inlined_callees (cs, cs->callee, new_edges);
 
   /* We do not keep jump functions of inlined edges up to date. Better to free
@@ -2543,8 +2772,53 @@  ipa_edge_duplication_hook (struct cgraph
   new_args->jump_functions = vec_safe_copy (old_args->jump_functions);
 
   for (i = 0; i < vec_safe_length (old_args->jump_functions); i++)
-    (*new_args->jump_functions)[i].agg.items
-	= vec_safe_copy ((*old_args->jump_functions)[i].agg.items);
+    {
+      struct ipa_jump_func *src_jf = ipa_get_ith_jump_func (old_args, i);
+      struct ipa_jump_func *dst_jf = ipa_get_ith_jump_func (new_args, i);
+
+      dst_jf->agg.items = vec_safe_copy (dst_jf->agg.items);
+
+      if (src_jf->type == IPA_JF_CONST)
+	{
+	  struct ipa_cst_ref_desc *src_rdesc = jfunc_rdesc_usable (src_jf);
+
+	  if (!src_rdesc)
+	    dst_jf->value.constant.rdesc = NULL;
+	  else if (src_rdesc->cs == src)
+	    {
+	      struct ipa_cst_ref_desc *dst_rdesc;
+	      gcc_checking_assert (ipa_refdesc_pool);
+	      dst_rdesc
+		= (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+	      dst_rdesc->cs = dst;
+	      dst_rdesc->next_duplicate = src_rdesc->next_duplicate;
+	      src_rdesc->next_duplicate = dst_rdesc;
+	      dst_rdesc->refcount = src_rdesc->refcount;
+	      dst_jf->value.constant.rdesc = dst_rdesc;
+	    }
+	  else
+	    {
+	      struct ipa_cst_ref_desc *dst_rdesc;
+	      /* This can happen during inlining, when a JFUNC can refer to a
+		 reference taken in a function up in the tree of inline clones.
+		 We need to find the duplicate that refers to our tree of
+		 inline clones.  */
+
+	      gcc_assert (dst->caller->global.inlined_to);
+	      for (dst_rdesc = src_rdesc->next_duplicate;
+		   dst_rdesc;
+		   dst_rdesc = dst_rdesc->next_duplicate)
+		{
+		  gcc_assert (dst_rdesc->cs->caller->global.inlined_to);
+		  if (dst_rdesc->cs->caller->global.inlined_to
+		      == dst->caller->global.inlined_to)
+		    break;
+		}
+
+	      dst_jf->value.constant.rdesc = dst_rdesc;
+	    }
+	}
+    }
 }
 
 /* Hook that is called by cgraph.c when a node is duplicated.  */
@@ -2646,6 +2920,8 @@  ipa_free_all_structures_after_ipa_cp (vo
       free_alloc_pool (ipcp_values_pool);
       free_alloc_pool (ipcp_agg_lattice_pool);
       ipa_unregister_cgraph_hooks ();
+      if (ipa_refdesc_pool)
+	free_alloc_pool (ipa_refdesc_pool);
     }
 }
 
@@ -2664,6 +2940,8 @@  ipa_free_all_structures_after_iinln (voi
     free_alloc_pool (ipcp_values_pool);
   if (ipcp_agg_lattice_pool)
     free_alloc_pool (ipcp_agg_lattice_pool);
+  if (ipa_refdesc_pool)
+    free_alloc_pool (ipa_refdesc_pool);
 }
 
 /* Print ipa_tree_map data structures of all functions in the
@@ -2684,6 +2962,8 @@  ipa_print_node_params (FILE *f, struct c
   count = ipa_get_param_count (info);
   for (i = 0; i < count; i++)
     {
+      int c;
+
       temp = ipa_get_param (info, i);
       if (TREE_CODE (temp) == PARM_DECL)
 	fprintf (f, "    param %d : %s", i,
@@ -2692,6 +2972,11 @@  ipa_print_node_params (FILE *f, struct c
                   : "(unnamed)"));
       if (ipa_is_param_used (info, i))
 	fprintf (f, " used");
+      c = ipa_get_controlled_uses (info, i);
+      if (c == IPA_UNDESCRIBED_USE)
+	fprintf (f, " undescribed_use");
+      else
+	fprintf (f, "  controlled_uses=%i", c);
       fprintf (f, "\n");
     }
 }
@@ -3311,8 +3596,8 @@  ipa_write_jump_function (struct output_b
       break;
     case IPA_JF_CONST:
       gcc_assert (
-	  EXPR_LOCATION (jump_func->value.constant) == UNKNOWN_LOCATION);
-      stream_write_tree (ob, jump_func->value.constant, true);
+	  EXPR_LOCATION (jump_func->value.constant.value) == UNKNOWN_LOCATION);
+      stream_write_tree (ob, jump_func->value.constant.value, true);
       break;
     case IPA_JF_PASS_THROUGH:
       streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
@@ -3360,6 +3645,7 @@  ipa_write_jump_function (struct output_b
 static void
 ipa_read_jump_function (struct lto_input_block *ib,
 			struct ipa_jump_func *jump_func,
+			struct cgraph_edge *cs,
 			struct data_in *data_in)
 {
   enum jump_func_type jftype;
@@ -3382,7 +3668,7 @@  ipa_read_jump_function (struct lto_input
 	break;
       }
     case IPA_JF_CONST:
-      ipa_set_jf_constant (jump_func, stream_read_tree (ib, data_in));
+      ipa_set_jf_constant (jump_func, stream_read_tree (ib, data_in), cs);
       break;
     case IPA_JF_PASS_THROUGH:
       operation = (enum tree_code) streamer_read_uhwi (ib);
@@ -3503,6 +3789,8 @@  ipa_write_node_info (struct output_block
   for (j = 0; j < ipa_get_param_count (info); j++)
     bp_pack_value (&bp, ipa_is_param_used (info, j), 1);
   streamer_write_bitpack (&bp);
+  for (j = 0; j < ipa_get_param_count (info); j++)
+    streamer_write_hwi (ob, ipa_get_controlled_uses (info, j));
   for (e = node->callees; e; e = e->next_callee)
     {
       struct ipa_edge_args *args = IPA_EDGE_REF (e);
@@ -3540,6 +3828,8 @@  ipa_read_node_info (struct lto_input_blo
     info->uses_analysis_done = true;
   info->node_enqueued = false;
   for (k = 0; k < ipa_get_param_count (info); k++)
+    ipa_set_controlled_uses (info, k, streamer_read_hwi (ib));
+  for (k = 0; k < ipa_get_param_count (info); k++)
     ipa_set_param_used (info, k, bp_unpack_value (&bp, 1));
   for (e = node->callees; e; e = e->next_callee)
     {
@@ -3551,7 +3841,8 @@  ipa_read_node_info (struct lto_input_blo
       vec_safe_grow_cleared (args->jump_functions, count);
 
       for (k = 0; k < ipa_get_cs_argument_count (args); k++)
-	ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), data_in);
+	ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), e,
+				data_in);
     }
   for (e = node->indirect_calls; e; e = e->next_callee)
     {
@@ -3562,7 +3853,7 @@  ipa_read_node_info (struct lto_input_blo
 	{
 	  vec_safe_grow_cleared (args->jump_functions, count);
           for (k = 0; k < ipa_get_cs_argument_count (args); k++)
-	    ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k),
+	    ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), e,
 				    data_in);
 	}
       ipa_read_indirect_edge_info (ib, data_in, e);
Index: src/gcc/ipa-prop.h
===================================================================
--- src.orig/gcc/ipa-prop.h
+++ src/gcc/ipa-prop.h
@@ -29,6 +29,8 @@  along with GCC; see the file COPYING3.
 /* The following definitions and interfaces are used by
    interprocedural analyses or parameters.  */
 
+#define IPA_UNDESCRIBED_USE -1
+
 /* ipa-prop.c stuff (ipa-cp, indirect inlining):  */
 
 /* A jump function for a callsite represents the values passed as actual
@@ -84,6 +86,17 @@  struct GTY(()) ipa_known_type_data
   tree component_type;
 };
 
+struct ipa_cst_ref_desc;
+
+/* Structure holding data required to describe a constant jump function.  */
+struct GTY(()) ipa_constant_data
+{
+  /* THe value of the constant.  */
+  tree value;
+  /* Pointer to the structure that describes the reference.  */
+  struct ipa_cst_ref_desc GTY((skip)) *rdesc;
+};
+
 /* Structure holding data required to describe a pass-through jump function.  */
 
 struct GTY(()) ipa_pass_through_data
@@ -172,7 +185,7 @@  typedef struct GTY (()) ipa_jump_func
   union jump_func_value
   {
     struct ipa_known_type_data GTY ((tag ("IPA_JF_KNOWN_TYPE"))) known_type;
-    tree GTY ((tag ("IPA_JF_CONST"))) constant;
+    struct ipa_constant_data GTY ((tag ("IPA_JF_CONST"))) constant;
     struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
     struct ipa_ancestor_jf_data GTY ((tag ("IPA_JF_ANCESTOR"))) ancestor;
   } GTY ((desc ("%1.type"))) value;
@@ -213,7 +226,14 @@  static inline tree
 ipa_get_jf_constant (struct ipa_jump_func *jfunc)
 {
   gcc_checking_assert (jfunc->type == IPA_JF_CONST);
-  return jfunc->value.constant;
+  return jfunc->value.constant.value;
+}
+
+static inline struct ipa_cst_ref_desc *
+ipa_get_jf_constant_rdesc (struct ipa_jump_func *jfunc)
+{
+  gcc_checking_assert (jfunc->type == IPA_JF_CONST);
+  return jfunc->value.constant.rdesc;
 }
 
 /* Return the operand of a pass through jmp function JFUNC.  */
@@ -296,6 +316,10 @@  struct ipa_param_descriptor
 {
   /* PARAM_DECL of this parameter.  */
   tree decl;
+  /* If all uses of the parameter are described by ipa-prop structures, this
+     says how many there are.  If any use could not be described by means of
+     ipa-prop structures, this is IPA_UNDESCRIBED_USE.  */
+  int controlled_uses;
   /* The parameter is used.  */
   unsigned used : 1;
 };
@@ -365,6 +389,23 @@  ipa_set_param_used (struct ipa_node_para
   info->descriptors[i].used = val;
 }
 
+/* Return how many uses described by ipa-prop a parameter has or
+   IPA_UNDESCRIBED_USE if there is a use that is not described by these
+   structures.  */
+static inline int
+ipa_get_controlled_uses (struct ipa_node_params *info, int i)
+{
+  return info->descriptors[i].controlled_uses;
+}
+
+/* Set the controlled counter of a given parameter.  */
+
+static inline void
+ipa_set_controlled_uses (struct ipa_node_params *info, int i, int val)
+{
+  info->descriptors[i].controlled_uses = val;
+}
+
 /* Return the used flag corresponding to the Ith formal parameter of the
    function associated with INFO.  */
 
Index: src/gcc/cgraph.c
===================================================================
--- src.orig/gcc/cgraph.c
+++ src/gcc/cgraph.c
@@ -1409,7 +1409,14 @@  cgraph_remove_node (struct cgraph_node *
 void
 cgraph_mark_address_taken_node (struct cgraph_node *node)
 {
-  gcc_assert (!node->global.inlined_to);
+  /* Indirect inlining can figure out that all uses of the address are
+     inlined.  */
+  if (node->global.inlined_to)
+    {
+      gcc_assert (cfun->after_inlining);
+      gcc_assert (node->callers->indirect_inlining_edge);
+      return;
+    }
   /* FIXME: address_taken flag is used both as a shortcut for testing whether
      IPA_REF_ADDR reference exists (and thus it should be set on node
      representing alias we take address of) and as a test whether address
Index: src/gcc/ipa-cp.c
===================================================================
--- src.orig/gcc/ipa-cp.c
+++ src/gcc/ipa-cp.c
@@ -2276,8 +2276,36 @@  ipcp_discover_new_direct_edges (struct c
 					       aggvals);
       if (target)
 	{
-	  ipa_make_edge_direct_to_target (ie, target);
+	  struct cgraph_edge *cs = ipa_make_edge_direct_to_target (ie, target);
 	  found = true;
+
+	  if (cs && !ie->indirect_info->agg_contents
+	      && !ie->indirect_info->polymorphic)
+	    {
+	      struct ipa_node_params *info = IPA_NODE_REF (node);
+	      int param_index = ie->indirect_info->param_index;
+	      int c = ipa_get_controlled_uses (info, param_index);
+	      if (c != IPA_UNDESCRIBED_USE)
+		{
+		  struct ipa_ref *to_del;
+
+		  c--;
+		  ipa_set_controlled_uses (info, param_index, c);
+		  if (dump_file && (dump_flags & TDF_DETAILS))
+		    fprintf (dump_file, "     controlled uses count of param "
+			     "%i bumped down to %i\n", param_index, c);
+		  if (c == 0
+		      && (to_del = ipa_find_reference ((symtab_node) node,
+						       (symtab_node) cs->callee,
+						       NULL)))
+		    {
+		      if (dump_file && (dump_flags & TDF_DETAILS))
+			fprintf (dump_file, "       and even removing its "
+				 "cloning-created reference\n");
+		      ipa_remove_reference (to_del);
+		    }
+		}
+	    }
 	}
     }
   /* Turning calls to direct calls will improve overall summary.  */
Index: src/gcc/ipa-ref.c
===================================================================
--- src.orig/gcc/ipa-ref.c
+++ src/gcc/ipa-ref.c
@@ -198,3 +198,20 @@  ipa_ref_has_aliases_p (struct ipa_ref_li
       return true;
   return false;
 }
+
+/* Find the structure describing a reference in REFERRING_NODE to REFERRED_NODE
+   and associated with statement STMT.  */
+
+struct ipa_ref *
+ipa_find_reference (symtab_node referring_node, symtab_node referred_node,
+		    gimple stmt)
+{
+  struct ipa_ref *r = NULL;
+  int i;
+
+  FOR_EACH_VEC_SAFE_ELT (referring_node->symbol.ref_list.references, i, r)
+    if (r->referred == referred_node
+	&& (in_lto_p || r->stmt == stmt))
+      return r;
+  return NULL;
+}
Index: src/gcc/ipa-ref.h
===================================================================
--- src.orig/gcc/ipa-ref.h
+++ src/gcc/ipa-ref.h
@@ -71,3 +71,4 @@  void ipa_clone_references (symtab_node,
 void ipa_clone_referring (symtab_node, struct ipa_ref_list *);
 bool ipa_ref_cannot_lead_to_return (struct ipa_ref *);
 bool ipa_ref_has_aliases_p (struct ipa_ref_list *);
+struct ipa_ref * ipa_find_reference (symtab_node, symtab_node, gimple);
Index: src/gcc/testsuite/gcc.dg/ipa/remref-0.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/remref-0.c
@@ -0,0 +1,30 @@ 
+/* Verify that indirect inlining machinery can remove references to functions
+   passed as parameters that are never used.  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-sra -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+
+extern int __attribute__ ((noinline, noclone, used))
+stuff (int i)
+{
+  return 0;
+}
+
+static void hooray ()
+{
+  stuff (1);
+}
+
+static int hiphip (void (*f)())
+{
+  return stuff (2);
+}
+
+int main (void)
+{
+  return hiphip (hooray);
+}
+
+/* { dg-final { scan-ipa-dump "ipa-prop: Removed a reference"  "inline"  } } */
+/* { dg-final { scan-tree-dump-not "hooray"  "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: src/gcc/testsuite/gcc.dg/ipa/remref-1a.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/remref-1a.c
@@ -0,0 +1,34 @@ 
+/* Verify that indirect inlining can also remove references of the functions it
+   discovers calls for.  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+
+int global;
+
+void __attribute__ ((noinline, noclone, used))
+stuff (int i)
+{
+  global = i;
+}
+
+static void hooray ()
+{
+  stuff (1);
+}
+
+static void hiphip (void (*f)())
+{
+  stuff (2);
+  f ();
+}
+
+int main (void)
+{
+  hiphip (hooray);
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "ipa-prop: Removed a reference"  "inline"  } } */
+/* { dg-final { scan-tree-dump-not "hooray"  "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: src/gcc/testsuite/gcc.dg/ipa/remref-1b.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/remref-1b.c
@@ -0,0 +1,37 @@ 
+/* Verify that indirect inlining can also remove references of the functions it
+   discovers calls for, even when nodes being inlined are virtual IPA-CP
+   clones.  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized"  } */
+
+int global;
+
+void __attribute__ ((noinline, noclone, used))
+stuff (int i)
+{
+  global = i;
+}
+
+static void hooray ()
+{
+  stuff (1);
+}
+
+static void hiphip (void (*f)())
+{
+  stuff (2);
+  f ();
+}
+
+int main (void)
+{
+  hiphip (hooray);
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "removing its cloning-created reference"  "cp"  } } */
+/* { dg-final { scan-ipa-dump "ipa-prop: Removed a reference"  "inline"  } } */
+/* { dg-final { scan-tree-dump-not "hooray"  "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: src/gcc/testsuite/gcc.dg/ipa/remref-2a.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/remref-2a.c
@@ -0,0 +1,90 @@ 
+/* Verify that indirect inlining can also remove references of the functions it
+   discovers calls for.  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining -fno-ipa-cp -fdump-ipa-inline -fdump-tree-optimized"  } */
+
+int global;
+
+void __attribute__ ((noinline, noclone, used))
+stuff (int i)
+{
+  global = i;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static void
+hooray_1 ()
+{
+  stuff (1);
+}
+
+static inline void
+hip2_1 (void (*g)())
+{
+  int i;
+  g ();
+  /* Some stuff to make the function bigger so that hip1_1 gets inlined
+     fiorst. */
+  for (i = 0; i < get_input (); i++)
+    {
+      stuff (2);
+      stuff (2+2);
+    }
+}
+
+static inline void
+hip1_1 (void (*g)())
+{
+  hip2_1 (g);
+}
+
+static void
+hooray_2 ()
+{
+  stuff (1);
+}
+
+static inline void
+hip2_2 (void (*g)())
+{
+  g ();
+}
+
+static inline void
+hip1_2 (void (*g)())
+{
+  int i;
+
+  hip2_2 (g);
+
+  /* Some stuff to make the function bigger so that hip2_2 gets inlined
+     fiorst. */
+  for (i = 0; i < get_input (); i++)
+    {
+      stuff (2);
+      stuff (2+2);
+    }
+}
+
+
+int
+main (int argc, int *argv[])
+{
+  int i;
+
+  for (i = 0; i < get_input (); i++)
+    {
+      hip1_1 (hooray_1);
+      hip1_2 (hooray_2);
+    }
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-times "ipa-prop: Removed a reference" 2 "inline"  } } */
+/* { dg-final { scan-tree-dump-not "hooray"  "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: src/gcc/testsuite/gcc.dg/ipa/remref-2b.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.dg/ipa/remref-2b.c
@@ -0,0 +1,94 @@ 
+/* Verify that indirect inlining can also remove references of the functions it
+   discovers calls for, even when nodes being inlined are virtual IPA-CP
+   clones.  */
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-early-inlining -fdump-ipa-cp-details -fdump-ipa-inline -fdump-tree-optimized"  } */
+
+
+int global;
+
+void __attribute__ ((noinline, noclone, used))
+stuff (int i)
+{
+  global = i;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+  return 1;
+}
+
+static void
+hooray_1 ()
+{
+  stuff (1);
+}
+
+static inline void
+hip2_1 (void (*g)())
+{
+  int i;
+  g ();
+  /* Some stuff to make the function bigger so that hip1_1 gets inlined
+     fiorst. */
+  for (i = 0; i < get_input (); i++)
+    {
+      stuff (2);
+      stuff (2+2);
+    }
+}
+
+static inline void
+hip1_1 (void (*g)())
+{
+  hip2_1 (g);
+}
+
+static void
+hooray_2 ()
+{
+  stuff (1);
+}
+
+static inline void
+hip2_2 (void (*g)())
+{
+  g ();
+}
+
+static inline void
+hip1_2 (void (*g)())
+{
+  int i;
+
+  hip2_2 (g);
+
+  /* Some stuff to make the function bigger so that hip2_2 gets inlined
+     fiorst. */
+  for (i = 0; i < get_input (); i++)
+    {
+      stuff (2);
+      stuff (2+2);
+    }
+}
+
+int
+main (int argc, int *argv[])
+{
+  int i;
+
+  for (i = 0; i < get_input (); i++)
+    {
+      hip1_1 (hooray_1);
+      hip1_2 (hooray_2);
+    }
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-times "removing its cloning-created reference" 2 "cp"  } } */
+/* { dg-final { scan-ipa-dump "ipa-prop: Removed a reference"  "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "ipa-prop: Removing cloning-created reference" 2 "inline"  } } */
+/* { dg-final { scan-tree-dump-not "hooray"  "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */