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] Support multi-indirect inlining


Hi,

This patch calculates the jump functions also for the indirect edges
so that they can be pulled into the indirect inlining machinery as
soon as they are made direct and so we can indirect-inline calls in
previously indirect-inlined functions.

I have also simplified the initialization phase a little bit because
all the functions have safeguards not to compute the same thing over
again within them and the having this simplified outweighs a slim
overhead when doing ipa-cp and not indirect inlining.

Bootstrapped and regression tested on x86_64-linux.  OK for trunk?

Thanks,

Martin


2010-05-17  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.c (ipa_print_node_jump_functions): Print jump functions
	also for indirect edges.  Actual printing moved...
	(ipa_print_node_jump_functions_for_edge): ...here.
	(ipa_compute_jump_functions): Renamed to
	ipa_compute_jump_functions_for_edge and made static.
	(ipa_compute_jump_functions): New function.
	(make_edge_direct_to_target): Check if the number of arguments on
	the newly direct edge is the same as the number of parametrs of
	the callee.
	* ipa-cp.c (ipcp_init_stage): Most functionality moved to new
	ipa_compute_jump_functions.  Call ipa_analyze_params_uses.
	* ipa-inline.c (inline_indirect_intraprocedural_analysis): Call
	analysis functions unconditionally, call the new
	ipa_analyze_params_uses on the node instead of every edge.

	* testsuite/g++.dg/ipa/ivinline-8.C: New test.
	* testsuite/gcc.dg/ipa/iinline-2.c: New test.


Index: icln/gcc/ipa-prop.c
===================================================================
*** icln.orig/gcc/ipa-prop.c
--- icln/gcc/ipa-prop.c
*************** ipa_count_arguments (struct cgraph_edge
*** 262,277 ****
    ipa_set_cs_argument_count (IPA_EDGE_REF (cs), arg_num);
  }
  
  /* Print the jump functions of all arguments on all call graph edges going from
     NODE to file F.  */
  
  void
  ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node)
  {
-   int i, count;
    struct cgraph_edge *cs;
!   struct ipa_jump_func *jump_func;
!   enum jump_func_type type;
  
    fprintf (f, "  Jump functions of caller  %s:\n", cgraph_node_name (node));
    for (cs = node->callees; cs; cs = cs->next_callee)
--- 262,348 ----
    ipa_set_cs_argument_count (IPA_EDGE_REF (cs), arg_num);
  }
  
+ /* Print the jump functions associated with call graph edge CS to file F.  */
+ 
+ static void
+ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs)
+ {
+   int i, count;
+ 
+   count = ipa_get_cs_argument_count (IPA_EDGE_REF (cs));
+   for (i = 0; i < count; i++)
+     {
+       struct ipa_jump_func *jump_func;
+       enum jump_func_type type;
+ 
+       jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
+       type = jump_func->type;
+ 
+       fprintf (f, "       param %d: ", i);
+       if (type == IPA_JF_UNKNOWN)
+ 	fprintf (f, "UNKNOWN\n");
+       else if (type == IPA_JF_KNOWN_TYPE)
+ 	{
+ 	  tree binfo_type = TREE_TYPE (jump_func->value.base_binfo);
+ 	  fprintf (f, "KNOWN TYPE, type in binfo is: ");
+ 	  print_generic_expr (f, binfo_type, 0);
+ 	  fprintf (f, " (%u)\n", TYPE_UID (binfo_type));
+ 	}
+       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
+ 	      && TREE_CODE (TREE_OPERAND (val, 0)) == CONST_DECL)
+ 	    {
+ 	      fprintf (f, " -> ");
+ 	      print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (val, 0)),
+ 				  0);
+ 	    }
+ 	  fprintf (f, "\n");
+ 	}
+       else if (type == IPA_JF_CONST_MEMBER_PTR)
+ 	{
+ 	  fprintf (f, "CONST MEMBER PTR: ");
+ 	  print_generic_expr (f, jump_func->value.member_cst.pfn, 0);
+ 	  fprintf (f, ", ");
+ 	  print_generic_expr (f, jump_func->value.member_cst.delta, 0);
+ 	  fprintf (f, "\n");
+ 	}
+       else if (type == IPA_JF_PASS_THROUGH)
+ 	{
+ 	  fprintf (f, "PASS THROUGH: ");
+ 	  fprintf (f, "%d, op %s ",
+ 		   jump_func->value.pass_through.formal_id,
+ 		   tree_code_name[(int)
+ 				  jump_func->value.pass_through.operation]);
+ 	  if (jump_func->value.pass_through.operation != NOP_EXPR)
+ 	    print_generic_expr (dump_file,
+ 				jump_func->value.pass_through.operand, 0);
+ 	  fprintf (dump_file, "\n");
+ 	}
+       else if (type == IPA_JF_ANCESTOR)
+ 	{
+ 	  fprintf (f, "ANCESTOR: ");
+ 	  fprintf (f, "%d, offset "HOST_WIDE_INT_PRINT_DEC", ",
+ 		   jump_func->value.ancestor.formal_id,
+ 		   jump_func->value.ancestor.offset);
+ 	  print_generic_expr (f, jump_func->value.ancestor.type, 0);
+ 	  fprintf (dump_file, "\n");
+ 	}
+     }
+ }
+ 
+ 
  /* Print the jump functions of all arguments on all call graph edges going from
     NODE to file F.  */
  
  void
  ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node)
  {
    struct cgraph_edge *cs;
!   int i;
  
    fprintf (f, "  Jump functions of caller  %s:\n", cgraph_node_name (node));
    for (cs = node->callees; cs; cs = cs->next_callee)
*************** ipa_print_node_jump_functions (FILE *f,
*** 279,347 ****
        if (!ipa_edge_args_info_available_for_edge_p (cs))
  	continue;
  
!       fprintf (f, "    callsite  %s ", cgraph_node_name (node));
!       fprintf (f, "-> %s :: \n", cgraph_node_name (cs->callee));
  
!       count = ipa_get_cs_argument_count (IPA_EDGE_REF (cs));
!       for (i = 0; i < count; i++)
! 	{
! 	  jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i);
! 	  type = jump_func->type;
  
! 	  fprintf (f, "       param %d: ", i);
! 	  if (type == IPA_JF_UNKNOWN)
! 	    fprintf (f, "UNKNOWN\n");
! 	  else if (type == IPA_JF_KNOWN_TYPE)
! 	    {
! 	      tree binfo_type = TREE_TYPE (jump_func->value.base_binfo);
! 	      fprintf (f, "KNOWN TYPE, type in binfo is: ");
! 	      print_generic_expr (f, binfo_type, 0);
! 	      fprintf (f, " (%u)\n", TYPE_UID (binfo_type));
! 	    }
! 	  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
! 		  && TREE_CODE (TREE_OPERAND (val, 0)) == CONST_DECL)
! 		{
! 		  fprintf (f, " -> ");
! 		  print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (val, 0)),
! 						       0);
! 		}
! 	      fprintf (f, "\n");
! 	    }
! 	  else if (type == IPA_JF_CONST_MEMBER_PTR)
! 	    {
! 	      fprintf (f, "CONST MEMBER PTR: ");
! 	      print_generic_expr (f, jump_func->value.member_cst.pfn, 0);
! 	      fprintf (f, ", ");
! 	      print_generic_expr (f, jump_func->value.member_cst.delta, 0);
! 	      fprintf (f, "\n");
! 	    }
! 	  else if (type == IPA_JF_PASS_THROUGH)
!  	    {
! 	      fprintf (f, "PASS THROUGH: ");
! 	      fprintf (f, "%d, op %s ",
! 		       jump_func->value.pass_through.formal_id,
! 		       tree_code_name[(int)
! 				      jump_func->value.pass_through.operation]);
! 	      if (jump_func->value.pass_through.operation != NOP_EXPR)
! 		print_generic_expr (dump_file,
! 				    jump_func->value.pass_through.operand, 0);
! 	      fprintf (dump_file, "\n");
!  	    }
! 	  else if (type == IPA_JF_ANCESTOR)
! 	    {
! 	      fprintf (f, "ANCESTOR: ");
! 	      fprintf (f, "%d, offset "HOST_WIDE_INT_PRINT_DEC", ",
! 		       jump_func->value.ancestor.formal_id,
! 		       jump_func->value.ancestor.offset);
! 	      print_generic_expr (f, jump_func->value.ancestor.type, 0);
! 	      fprintf (dump_file, "\n");
! 	    }
  	}
      }
  }
  
--- 350,375 ----
        if (!ipa_edge_args_info_available_for_edge_p (cs))
  	continue;
  
!       fprintf (f, "    callsite  %s/%i -> %s/%i : \n",
! 	       cgraph_node_name (node), node->uid,
! 	       cgraph_node_name (cs->callee), cs->callee->uid);
!       ipa_print_node_jump_functions_for_edge (f, cs);
!     }
  
!   for (cs = node->indirect_calls, i = 0; cs; cs = cs->next_callee, i++)
!     {
!       if (!ipa_edge_args_info_available_for_edge_p (cs))
! 	continue;
  
!       if (cs->call_stmt)
! 	{
! 	  fprintf (f, "    indirect callsite %d for stmt ", i);
! 	  print_gimple_stmt (f, cs->call_stmt, 0, TDF_SLIM);
  	}
+       else
+ 	fprintf (f, "    indirect callsite %d :\n", i);
+       ipa_print_node_jump_functions_for_edge (f, cs);
+ 
      }
  }
  
*************** compute_cst_member_ptr_arguments (struct
*** 810,817 ****
     information in the jump_functions array in the ipa_edge_args corresponding
     to this callsite.  */
  
! void
! ipa_compute_jump_functions (struct cgraph_edge *cs)
  {
    struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
    struct ipa_edge_args *arguments = IPA_EDGE_REF (cs);
--- 838,845 ----
     information in the jump_functions array in the ipa_edge_args corresponding
     to this callsite.  */
  
! static void
! ipa_compute_jump_functions_for_edge (struct cgraph_edge *cs)
  {
    struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
    struct ipa_edge_args *arguments = IPA_EDGE_REF (cs);
*************** ipa_compute_jump_functions (struct cgrap
*** 838,843 ****
--- 866,899 ----
    compute_cst_member_ptr_arguments (arguments->jump_functions, call);
  }
  
+ /* Compute jump functions for all edges - both direct and indirect - outgoing
+    from NODE.  Also count the actual arguments in the process.  */
+ 
+ void
+ ipa_compute_jump_functions (struct cgraph_node *node)
+ {
+   struct cgraph_edge *cs;
+ 
+   for (cs = node->callees; cs; cs = cs->next_callee)
+     {
+       /* We do not need to bother analyzing calls to unknown
+ 	 functions unless they may become known during lto/whopr.  */
+       if (!cs->callee->analyzed && !flag_lto && !flag_whopr)
+ 	continue;
+       ipa_count_arguments (cs);
+       if (ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
+ 	  != ipa_get_param_count (IPA_NODE_REF (cs->callee)))
+ 	ipa_set_called_with_variable_arg (IPA_NODE_REF (cs->callee));
+       ipa_compute_jump_functions_for_edge (cs);
+     }
+ 
+   for (cs = node->indirect_calls; cs; cs = cs->next_callee)
+     {
+       ipa_count_arguments (cs);
+       ipa_compute_jump_functions_for_edge (cs);
+     }
+ }
+ 
  /* If RHS looks like a rhs of a statement loading pfn from a member
     pointer formal parameter, return the parameter, otherwise return
     NULL.  If USE_DELTA, then we look for a use of the delta field
*************** make_edge_direct_to_target (struct cgrap
*** 1303,1308 ****
--- 1359,1369 ----
        else
  	fprintf (dump_file, "with uid %i\n", ie->lto_stmt_uid);
      }
+ 
+   if (ipa_get_cs_argument_count (IPA_EDGE_REF (ie))
+       != ipa_get_param_count (IPA_NODE_REF (callee)))
+     ipa_set_called_with_variable_arg (IPA_NODE_REF (callee));
+ 
    return ie;
  }
  
Index: icln/gcc/ipa-cp.c
===================================================================
*** icln.orig/gcc/ipa-cp.c
--- icln/gcc/ipa-cp.c
*************** static void
*** 614,620 ****
  ipcp_init_stage (void)
  {
    struct cgraph_node *node;
-   struct cgraph_edge *cs;
  
    for (node = cgraph_nodes; node; node = node->next)
      if (node->analyzed)
--- 614,619 ----
*************** ipcp_init_stage (void)
*** 623,641 ****
      {
        if (!node->analyzed)
  	continue;
        /* building jump functions  */
!       for (cs = node->callees; cs; cs = cs->next_callee)
! 	{
! 	  /* We do not need to bother analyzing calls to unknown
! 	     functions unless they may become known during lto/whopr.  */
! 	  if (!cs->callee->analyzed && !flag_lto && !flag_whopr)
! 	    continue;
! 	  ipa_count_arguments (cs);
! 	  if (ipa_get_cs_argument_count (IPA_EDGE_REF (cs))
! 	      != ipa_get_param_count (IPA_NODE_REF (cs->callee)))
! 	    ipa_set_called_with_variable_arg (IPA_NODE_REF (cs->callee));
! 	  ipa_compute_jump_functions (cs);
! 	}
      }
  }
  
--- 622,631 ----
      {
        if (!node->analyzed)
  	continue;
+ 
+       ipa_analyze_params_uses (node);
        /* building jump functions  */
!       ipa_compute_jump_functions (node);
      }
  }
  
Index: icln/gcc/ipa-inline.c
===================================================================
*** icln.orig/gcc/ipa-inline.c
--- icln/gcc/ipa-inline.c
*************** struct gimple_opt_pass pass_inline_param
*** 1971,1991 ****
  static void
  inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
  {
!   struct cgraph_edge *cs;
! 
!   if (!flag_ipa_cp)
!     {
!       ipa_initialize_node_params (node);
!       ipa_detect_param_modifications (node);
!     }
    ipa_analyze_params_uses (node);
! 
!   if (!flag_ipa_cp)
!     for (cs = node->callees; cs; cs = cs->next_callee)
!       {
! 	ipa_count_arguments (cs);
! 	ipa_compute_jump_functions (cs);
!       }
  
    if (dump_file)
      {
--- 1971,1980 ----
  static void
  inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
  {
!   ipa_initialize_node_params (node);
!   ipa_detect_param_modifications (node);
    ipa_analyze_params_uses (node);
!   ipa_compute_jump_functions (node);
  
    if (dump_file)
      {
Index: icln/gcc/ipa-prop.h
===================================================================
*** icln.orig/gcc/ipa-prop.h
--- icln/gcc/ipa-prop.h
*************** ipa_push_func_to_list (struct ipa_func_l
*** 402,408 ****
  }
  
  /* Callsite related calculations.  */
! void ipa_compute_jump_functions (struct cgraph_edge *);
  void ipa_count_arguments (struct cgraph_edge *);
  
  /* Function formal parameters related computations.  */
--- 402,408 ----
  }
  
  /* Callsite related calculations.  */
! void ipa_compute_jump_functions (struct cgraph_node *);
  void ipa_count_arguments (struct cgraph_edge *);
  
  /* Function formal parameters related computations.  */
Index: icln/gcc/testsuite/g++.dg/ipa/ivinline-8.C
===================================================================
*** /dev/null
--- icln/gcc/testsuite/g++.dg/ipa/ivinline-8.C
***************
*** 0 ****
--- 1,77 ----
+ /* Verify that virtual calls are inlined (ithout early inlining) even
+    when their caller is itself indirectly inlined.  */
+ /* { dg-do run } */
+ /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+ 
+ extern "C" void abort (void);
+ 
+ class A
+ {
+ public:
+   int data;
+   virtual int bar (int i);
+   virtual int foo (int i);
+ };
+ 
+ class B : public A
+ {
+ public:
+   virtual int bar (int i);
+   virtual int foo (int i);
+ };
+ 
+ class C : public A
+ {
+ public:
+   virtual int foo (int i);
+ };
+ 
+ int A::bar (int i)
+ {
+   return i + 100 * i;
+ }
+ 
+ int A::foo (int i)
+ {
+   return bar (i) + 1;
+ }
+ 
+ int B::bar (int i)
+ {
+   return i + 100 * (i + 2);
+ }
+ 
+ int B::foo (int i)
+ {
+   return bar (i) + 2;
+ }
+ 
+ int C::foo (int i)
+ {
+   return i + 3;
+ }
+ 
+ int middleman (class A *obj, int i)
+ {
+   return obj->foo (i);
+ }
+ 
+ int __attribute__ ((noinline,noclone)) get_input(void)
+ {
+   return 1;
+ }
+ 
+ int main (int argc, char *argv[])
+ {
+   class B b;
+   int i;
+ 
+   for (i = 0; i < get_input (); i++)
+     if (middleman (&b, get_input ()) != 303)
+       abort ();
+   return 0;
+ }
+ 
+ /* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int main" "inline"  } } */
+ /* { dg-final { scan-ipa-dump "B::bar\[^\\n\]*inline copy in int main" "inline"  } } */
+ /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: icln/gcc/testsuite/gcc.dg/ipa/iinline-2.c
===================================================================
*** /dev/null
--- icln/gcc/testsuite/gcc.dg/ipa/iinline-2.c
***************
*** 0 ****
--- 1,41 ----
+ /* Verify that simple indirect calls are inlined even without early
+    inlining..  */
+ /* { dg-do compile } */
+ /* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp"  } */
+ 
+ extern void non_existent(int);
+ 
+ int __attribute__ ((noinline,noclone)) get_input(void)
+ {
+   return 1;
+ }
+ 
+ static void hooray ()
+ {
+   non_existent (1);
+ }
+ 
+ static void hip2 (void (*g)())
+ {
+   non_existent (2);
+   g ();
+ }
+ 
+ static void hip1 (void (*f)(void (*)()), void (*g)())
+ {
+   non_existent (2);
+   f (g);
+ }
+ 
+ int main (int argc, int *argv[])
+ {
+   int i;
+ 
+   for (i = 0; i < get_input (); i++)
+     hip1 (hip2, hooray);
+   return 0;
+ }
+ 
+ /* { dg-final { scan-ipa-dump "hooray\[^\\n\]*inline copy in main" "inline"  } } */
+ /* { dg-final { scan-ipa-dump "hip2\[^\\n\]*inline copy in main" "inline"  } } */
+ /* { dg-final { cleanup-ipa-dump "inline" } } */


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