This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Support multi-indirect inlining
- From: Martin Jambor <mjambor at suse dot cz>
- To: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Cc: Jan Hubicka <hubicka at ucw dot cz>
- Date: Tue, 18 May 2010 21:48:47 +0200
- Subject: [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" } } */