This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Hi,

the patch below is a fix for PR 42371 removing references to functions
from symbol table when we know that all their uses were inlined.  This
then allows us to remove out-of-line copies of functions when they are
not needed.

The patch adds ability to count the uses of a parameter value that are
all well described by jump functions and call graph indirect edges (or
mark that we do not have uses under control).  These counts are then
combined or transferred to a new helper structure (ipa_cst_ref_desc)
associated with constant jump functions when functions are inlined and
decremented when the reference is used to turn an indirect call graph
edges into a direct ones.  When the count eventually drops to zero,
the other information in the structure are used to identify and remove
an item in the appropriate reference list in the symbol table.  The
data structures allow for non-trivial duplication when whole trees of
inlined call graph nodes are duplicated.

I currently allocate the new structures only when a constant pointer
to a function is passed as a parameter.  It is trivial to have one for
all references but I'm not sure what good that would be, at least not
now.  Perhaps once we will also attempt to track references to virtual
tables but it will be much more difficult to prove it does not escape
somewhere in the callee(s).

Bootstrapped and tested on x86_64-linux without any problems.  OK for
trunk?

Thanks,

Martin


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.

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.

Index: src/gcc/ipa-prop.c
===================================================================
*** src.orig/gcc/ipa-prop.c
--- src/gcc/ipa-prop.c
*************** static struct cgraph_2edge_hook_list *ed
*** 62,67 ****
--- 62,83 ----
  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.  */
  
*************** ipa_print_node_jump_functions_for_edge (
*** 175,181 ****
  	}
        else if (type == IPA_JF_CONST)
  	{
! 	  tree val = jump_func->value.constant;
  	  fprintf (f, "CONST: ");
  	  print_generic_expr (f, val, 0);
  	  if (TREE_CODE (val) == ADDR_EXPR
--- 191,197 ----
  	}
        else if (type == IPA_JF_CONST)
  	{
! 	  tree val = jump_func->value.constant.value;
  	  fprintf (f, "CONST: ");
  	  print_generic_expr (f, val, 0);
  	  if (TREE_CODE (val) == ADDR_EXPR
*************** ipa_set_jf_known_type (struct ipa_jump_f
*** 309,321 ****
  /* Set JFUNC to be a constant jmp function.  */
  
  static void
! ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant)
  {
    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);
  }
  
  /* Set JFUNC to be a simple pass-through jump function.  */
--- 325,355 ----
  /* Set JFUNC to be a constant jmp function.  */
  
  static void
! 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.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.  */
*************** ipa_compute_jump_functions_for_edge (str
*** 1390,1396 ****
        tree arg = gimple_call_arg (call, n);
  
        if (is_gimple_ip_invariant (arg))
! 	ipa_set_jf_constant (jfunc, arg);
        else if (!is_gimple_reg_type (TREE_TYPE (arg))
  	       && TREE_CODE (arg) == PARM_DECL)
  	{
--- 1424,1430 ----
        tree arg = gimple_call_arg (call, n);
  
        if (is_gimple_ip_invariant (arg))
! 	ipa_set_jf_constant (jfunc, arg, cs);
        else if (!is_gimple_reg_type (TREE_TYPE (arg))
  	       && TREE_CODE (arg) == PARM_DECL)
  	{
*************** ipa_analyze_params_uses (struct cgraph_n
*** 1877,1890 ****
    for (i = 0; i < ipa_get_param_count (info); i++)
      {
        tree parm = ipa_get_param (info, i);
!       tree ddef;
        /* 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);
      }
  
    func = DECL_STRUCT_FUNCTION (decl);
--- 1911,1945 ----
    for (i = 0; i < ipa_get_param_count (info); i++)
      {
        tree parm = ipa_get_param (info, i);
!       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))
! 	{
! 	  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);
*************** ipa_find_agg_cst_for_param (struct ipa_a
*** 2188,2193 ****
--- 2243,2282 ----
    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
*************** try_make_edge_direct_simple_call (struct
*** 2199,2204 ****
--- 2288,2295 ----
  				  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)
*************** try_make_edge_direct_simple_call (struct
*** 2209,2215 ****
      target = ipa_value_from_jfunc (new_root_info, jfunc);
    if (!target)
      return NULL;
!   return ipa_make_edge_direct_to_target (ie, target);
  }
  
  /* Try to find a destination for indirect edge IE that corresponds to a virtual
--- 2300,2314 ----
      target = ipa_value_from_jfunc (new_root_info, jfunc);
    if (!target)
      return NULL;
!   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
*************** propagate_info_to_inlined_callees (struc
*** 2373,2378 ****
--- 2472,2606 ----
    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
*************** ipa_propagate_indirect_call_infos (struc
*** 2390,2395 ****
--- 2618,2624 ----
      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
*************** ipa_edge_duplication_hook (struct cgraph
*** 2505,2512 ****
    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);
  }
  
  /* Hook that is called by cgraph.c when a node is duplicated.  */
--- 2734,2786 ----
    new_args->jump_functions = vec_safe_copy (old_args->jump_functions);
  
    for (i = 0; i < vec_safe_length (old_args->jump_functions); i++)
!     {
!       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.  */
*************** ipa_free_all_structures_after_ipa_cp (vo
*** 2608,2613 ****
--- 2882,2889 ----
        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);
      }
  }
  
*************** ipa_free_all_structures_after_iinln (voi
*** 2626,2631 ****
--- 2902,2909 ----
      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
*************** ipa_print_node_params (FILE *f, struct c
*** 2646,2651 ****
--- 2924,2931 ----
    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,
*************** ipa_print_node_params (FILE *f, struct c
*** 2654,2659 ****
--- 2934,2944 ----
                    : "(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");
      }
  }
*************** ipa_write_jump_function (struct output_b
*** 3273,3280 ****
        break;
      case IPA_JF_CONST:
        gcc_assert (
! 	  EXPR_LOCATION (jump_func->value.constant) == UNKNOWN_LOCATION);
!       stream_write_tree (ob, jump_func->value.constant, true);
        break;
      case IPA_JF_PASS_THROUGH:
        streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
--- 3558,3565 ----
        break;
      case IPA_JF_CONST:
        gcc_assert (
! 	  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);
*************** ipa_write_jump_function (struct output_b
*** 3322,3327 ****
--- 3607,3613 ----
  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;
*************** ipa_read_jump_function (struct lto_input
*** 3344,3350 ****
  	break;
        }
      case IPA_JF_CONST:
!       ipa_set_jf_constant (jump_func, stream_read_tree (ib, data_in));
        break;
      case IPA_JF_PASS_THROUGH:
        operation = (enum tree_code) streamer_read_uhwi (ib);
--- 3630,3636 ----
  	break;
        }
      case IPA_JF_CONST:
!       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);
*************** ipa_write_node_info (struct output_block
*** 3465,3470 ****
--- 3751,3758 ----
    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);
*************** ipa_read_node_info (struct lto_input_blo
*** 3502,3507 ****
--- 3790,3797 ----
      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)
      {
*************** ipa_read_node_info (struct lto_input_blo
*** 3513,3519 ****
        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);
      }
    for (e = node->indirect_calls; e; e = e->next_callee)
      {
--- 3803,3810 ----
        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), e,
! 				data_in);
      }
    for (e = node->indirect_calls; e; e = e->next_callee)
      {
*************** ipa_read_node_info (struct lto_input_blo
*** 3524,3530 ****
  	{
  	  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_indirect_edge_info (ib, data_in, e);
--- 3815,3821 ----
  	{
  	  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), 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
*************** along with GCC; see the file COPYING3.
*** 29,34 ****
--- 29,36 ----
  /* 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
*************** struct GTY(()) ipa_known_type_data
*** 84,89 ****
--- 86,102 ----
    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
*************** typedef struct GTY (()) ipa_jump_func
*** 172,178 ****
    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_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;
--- 185,191 ----
    union jump_func_value
    {
      struct ipa_known_type_data GTY ((tag ("IPA_JF_KNOWN_TYPE"))) known_type;
!     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;
*************** static inline tree
*** 213,219 ****
  ipa_get_jf_constant (struct ipa_jump_func *jfunc)
  {
    gcc_checking_assert (jfunc->type == IPA_JF_CONST);
!   return jfunc->value.constant;
  }
  
  /* Return the operand of a pass through jmp function JFUNC.  */
--- 226,239 ----
  ipa_get_jf_constant (struct ipa_jump_func *jfunc)
  {
    gcc_checking_assert (jfunc->type == IPA_JF_CONST);
!   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.  */
*************** struct ipa_param_descriptor
*** 296,301 ****
--- 316,325 ----
  {
    /* 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;
  };
*************** ipa_set_param_used (struct ipa_node_para
*** 365,370 ****
--- 389,411 ----
    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
*************** 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
Index: src/gcc/ipa-cp.c
===================================================================
*** src.orig/gcc/ipa-cp.c
--- src/gcc/ipa-cp.c
*************** ipcp_discover_new_direct_edges (struct c
*** 2289,2296 ****
  					       aggvals);
        if (target)
  	{
! 	  ipa_make_edge_direct_to_target (ie, target);
  	  found = true;
  	}
      }
    /* Turning calls to direct calls will improve overall summary.  */
--- 2289,2324 ----
  					       aggvals);
        if (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
*************** ipa_ref_has_aliases_p (struct ipa_ref_li
*** 198,200 ****
--- 198,217 ----
        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
*************** void ipa_clone_references (symtab_node,
*** 71,73 ****
--- 71,74 ----
  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 ****
--- 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 ****
--- 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 ****
--- 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 ****
--- 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 ****
--- 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" } } */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]