Commit 3e293154 by Martin Jambor Committed by Martin Jambor

ipa-cp.c (ipcp_print_edge_profiles): Test for node->analyzed rather than for DECL_SAVED_TREE.

2008-07-23  Martin Jambor  <mjambor@suse.cz>

        * ipa-cp.c (ipcp_print_edge_profiles): Test for node->analyzed
	rather than for DECL_SAVED_TREE.
	* ipa-prop.c: Include diagnostic.h.
        (ipa_check_stmt_modifications): Check LHS of GIMPLE_MODIFY_EXPRs
	thoroughly.
	(ipa_detect_param_modifications): Function rewritten from scratch.
	(ipa_compute_jump_functions): Changed accesses to modification flags.
	(ipa_free_node_params_substructures): Update flags destruction.
	(ipa_node_duplication_hook): Update flags duplication.
	(ipa_print_all_params_modified): Updated flag access.
	* ipa-prop.h (struct ipa_param_flags): New structure.
        (struct ipa_node_params): New field modification_analysis_done,
        modified_flags changed into param_flags.
        (ipa_is_ith_param_modified): Changed to use new flags.
	* Makefile.in (ipa-prop.o): Add $(DIAGNOSTIC_H) to dependencies.

	* ipa-prop.c (ipa_print_all_jump_functions): Moved here from
	ipa-cp.c and split into two functions.
	(ipa_print_node_jump_functions): New function.
	(compute_scalar_jump_functions): New function.
	(type_like_member_ptr_p): New function.
	(compute_pass_through_member_ptrs): New function.
	(fill_member_ptr_cst_jump_function): New function.
	(determine_cst_member_ptr): New function.
	(compute_cst_member_ptr_arguments): New function.
	(ipa_compute_jump_functions): Complete rewrite.
	* ipa-prop.h (enum jump_func_type): Make explicit that we depend
	on IPA_UNKNOWN being zero. Added value IPA_CONST_MEMBER_PTR.
	(struct ipa_member_ptr_cst): New structure.
	(union jump_func_value): New field member_cst.
	* ipa-cp.c (ipcp_lat_is_insertable): New function.
	(ipcp_lattice_from_jfunc): Produces bottom lattices for unhandled
	jump function types.
	(ipcp_print_all_lattices): Slight fprintf rearrangement.
	(ipcp_print_all_structures): Call ipa_print_all_jump_functions
	instead of ipcp_print_all_jump_functions.
	(ipcp_insert_stage): Use ipcp_lat_is_insertable, create replace maps
	only for replacable scalars.

	* doc/invoke.texi (Optimize options): Add description of
	-findirect-inlining.
	* common.opt (flag_indirect_inlining): New flag.
	* opts.c (decode_options): Set flag_indirect_inlining when 
	optimize >= 3.

	* ipa-inline.c: Include ipa-prop.h.
	(inline_indirect_intraprocedural_analysis): New function.
	(inline_generate_summary): Allocate parameter and argument info
	structures, call inline_indirect_intraprocedural_analysis on each
	node when doing indirect inlining and deallocate indirect inlining 
	data structures in the end.
	* ipa-prop.c (ipa_create_param_decls_array): Return if already done.
	(free_all_ipa_structures_after_iinln): New function.
	(free_all_ipa_structures_after_ipa_cp): Checks whether iinln will be 
	done.
	* Makefile.in (ipa-inline.o): Added $(IPA_PROP_H) to dependencies.

	* cgraphbuild.c (compute_call_stmt_bb_frequency): New function.
	(build_cgraph_edges): Call compute_call_stmt_bb_frequency instead
	of computing the frequency separately.
	(rebuild_cgraph_edges): Call compute_call_stmt_bb_frequency instead
	of computing the frequency separately.
	* ipa-cp.c (ipcp_print_all_structures): Replace a call to 
	ipa_print_all_param_modified with a call to ipa_print_all_param_flags.
	* ipa-prop.c (ipa_get_member_ptr_load_param): New function.
	(ipa_get_stmt_member_ptr_load_param): New function.
	(ipa_is_ssa_with_stmt_def): New function.
	(ipa_note_param_call): New function.
	(ipa_analyze_call_uses): New function.
	(ipa_analyze_stmt_uses): New function.
	(ipa_analyze_params_uses): New function.
	(ipa_free_node_params_substructures): Also free the param_calls linked
	list.
	(ipa_node_duplication_hook): Also duplicate the param_calls linked list.
	(ipa_print_node_param_flags): New function.
	(ipa_print_all_params_modified): Renamed to ipa_print_all_param_flags.
	(ipa_print_all_param_flags): Calls ipa_print_node_param_flags.
	* ipa-prop.h (struct ipa_param_flags): New field called.
	(struct ipa_param_call_note): New structure.
	(struct ipa_node_params): New fields param_calls and
	uses_analysis_done.
	(ipa_is_ith_param_called): New function.
	* ipa-inline.c (inline_indirect_intraprocedural_analysis): Call
	ipa_analyze_params_uses and dump parameter flags.

	* ipa-inline.c (cgraph_decide_recursive_inlining): Call
	ipa_propagate_indirect_call_infos if performing indirect inlining,
	pass a new parameter new_edges to it.
	(add_new_edges_to_heap): New fucntion.
	(cgraph_decide_inlining_of_small_functions): New vector
	new_indirect_edges for newly found indirect edges , call
	ipa_propagate_indirect_call_infos after inlining.
	(cgraph_decide_inlining): Call ipa_propagate_indirect_call_infos after
	inlining if performing indirect inlining.  Call
	free_all_ipa_structures_after_iinln when doing so too.
	(inline_generate_summary): Do not call
	free_all_ipa_structures_after_iinln here.
	* ipa-prop.c (update_jump_functions_after_inlining): New function.
	(print_edge_addition_message): New function.
	(update_call_notes_after_inlining): New function.
	(propagate_info_to_inlined_callees): New function.
	(ipa_propagate_indirect_call_infos): New function.
	* ipa-prop.h: Include cgraph.h
	(struct ipa_param_call_note): Fields reordered, new field processed.
	* cgraph.h (cgraph_edge): Shrink loop_nest field to 31 bits, add a new
	flag indirect_call.
	* cgraphunit.c (verify_cgraph_node): Allow indirect edges not to have
	rediscovered call statements.
	* cgraph.c (cgraph_create_edge): Initialize indirect_call to zero.
	(dump_cgraph_node): Dump also the indirect_call flag.
	(cgraph_clone_edge): Copy also the indirect_call flag.
	* tree-inline.c (copy_bb): Do not check for fndecls from call
	expressions, check for edge availability when moving clones.
	(get_indirect_callee_fndecl): New function.
	(expand_call_inline): If callee declaration is not apprent from
	the statement, try calling get_indirect_callee_fndecl.  Do not
	issue warnings or call sorry when not inlinings an indirect edge.
	* Makefile.in (IPA_PROP_H): Added $(CGRAPH_H) to dependencies.

	* ipa-prop.c (ipa_print_node_param_flags): Make the dump format a
	bit more frandly to matching.
	* testsuite/g++.dg/ipa/iinline-1.C: New testcase.
	* testsuite/gcc.dg/ipa/iinline-1.c: New testcase.
	* testsuite/gcc.dg/ipa/modif-1.c: New testcase.

From-SVN: r138092
parent e62fe68a
2008-07-23 Martin Jambor <mjambor@suse.cz>
* ipa-cp.c (ipcp_print_edge_profiles): Test for node->analyzed
rather than for DECL_SAVED_TREE.
* ipa-prop.c: Include diagnostic.h.
(ipa_check_stmt_modifications): Check LHS of GIMPLE_MODIFY_EXPRs
thoroughly.
(ipa_detect_param_modifications): Function rewritten from scratch.
(ipa_compute_jump_functions): Changed accesses to modification flags.
(ipa_free_node_params_substructures): Update flags destruction.
(ipa_node_duplication_hook): Update flags duplication.
(ipa_print_all_params_modified): Updated flag access.
* ipa-prop.h (struct ipa_param_flags): New structure.
(struct ipa_node_params): New field modification_analysis_done,
modified_flags changed into param_flags.
(ipa_is_ith_param_modified): Changed to use new flags.
* Makefile.in (ipa-prop.o): Add $(DIAGNOSTIC_H) to dependencies.
* ipa-prop.c (ipa_print_all_jump_functions): Moved here from
ipa-cp.c and split into two functions.
(ipa_print_node_jump_functions): New function.
(compute_scalar_jump_functions): New function.
(type_like_member_ptr_p): New function.
(compute_pass_through_member_ptrs): New function.
(fill_member_ptr_cst_jump_function): New function.
(determine_cst_member_ptr): New function.
(compute_cst_member_ptr_arguments): New function.
(ipa_compute_jump_functions): Complete rewrite.
* ipa-prop.h (enum jump_func_type): Make explicit that we depend
on IPA_UNKNOWN being zero. Added value IPA_CONST_MEMBER_PTR.
(struct ipa_member_ptr_cst): New structure.
(union jump_func_value): New field member_cst.
* ipa-cp.c (ipcp_lat_is_insertable): New function.
(ipcp_lattice_from_jfunc): Produces bottom lattices for unhandled
jump function types.
(ipcp_print_all_lattices): Slight fprintf rearrangement.
(ipcp_print_all_structures): Call ipa_print_all_jump_functions
instead of ipcp_print_all_jump_functions.
(ipcp_insert_stage): Use ipcp_lat_is_insertable, create replace maps
only for replacable scalars.
* doc/invoke.texi (Optimize options): Add description of
-findirect-inlining.
* common.opt (flag_indirect_inlining): New flag.
* opts.c (decode_options): Set flag_indirect_inlining when
optimize >= 3.
* ipa-inline.c: Include ipa-prop.h.
(inline_indirect_intraprocedural_analysis): New function.
(inline_generate_summary): Allocate parameter and argument info
structures, call inline_indirect_intraprocedural_analysis on each
node when doing indirect inlining and deallocate indirect inlining
data structures in the end.
* ipa-prop.c (ipa_create_param_decls_array): Return if already done.
(free_all_ipa_structures_after_iinln): New function.
(free_all_ipa_structures_after_ipa_cp): Checks whether iinln will be
done.
* Makefile.in (ipa-inline.o): Added $(IPA_PROP_H) to dependencies.
* cgraphbuild.c (compute_call_stmt_bb_frequency): New function.
(build_cgraph_edges): Call compute_call_stmt_bb_frequency instead
of computing the frequency separately.
(rebuild_cgraph_edges): Call compute_call_stmt_bb_frequency instead
of computing the frequency separately.
* ipa-cp.c (ipcp_print_all_structures): Replace a call to
ipa_print_all_param_modified with a call to ipa_print_all_param_flags.
* ipa-prop.c (ipa_get_member_ptr_load_param): New function.
(ipa_get_stmt_member_ptr_load_param): New function.
(ipa_is_ssa_with_stmt_def): New function.
(ipa_note_param_call): New function.
(ipa_analyze_call_uses): New function.
(ipa_analyze_stmt_uses): New function.
(ipa_analyze_params_uses): New function.
(ipa_free_node_params_substructures): Also free the param_calls linked
list.
(ipa_node_duplication_hook): Also duplicate the param_calls linked list.
(ipa_print_node_param_flags): New function.
(ipa_print_all_params_modified): Renamed to ipa_print_all_param_flags.
(ipa_print_all_param_flags): Calls ipa_print_node_param_flags.
* ipa-prop.h (struct ipa_param_flags): New field called.
(struct ipa_param_call_note): New structure.
(struct ipa_node_params): New fields param_calls and
uses_analysis_done.
(ipa_is_ith_param_called): New function.
* ipa-inline.c (inline_indirect_intraprocedural_analysis): Call
ipa_analyze_params_uses and dump parameter flags.
* ipa-inline.c (cgraph_decide_recursive_inlining): Call
ipa_propagate_indirect_call_infos if performing indirect inlining,
pass a new parameter new_edges to it.
(add_new_edges_to_heap): New fucntion.
(cgraph_decide_inlining_of_small_functions): New vector
new_indirect_edges for newly found indirect edges , call
ipa_propagate_indirect_call_infos after inlining.
(cgraph_decide_inlining): Call ipa_propagate_indirect_call_infos after
inlining if performing indirect inlining. Call
free_all_ipa_structures_after_iinln when doing so too.
(inline_generate_summary): Do not call
free_all_ipa_structures_after_iinln here.
* ipa-prop.c (update_jump_functions_after_inlining): New function.
(print_edge_addition_message): New function.
(update_call_notes_after_inlining): New function.
(propagate_info_to_inlined_callees): New function.
(ipa_propagate_indirect_call_infos): New function.
* ipa-prop.h: Include cgraph.h
(struct ipa_param_call_note): Fields reordered, new field processed.
* cgraph.h (cgraph_edge): Shrink loop_nest field to 31 bits, add a new
flag indirect_call.
* cgraphunit.c (verify_cgraph_node): Allow indirect edges not to have
rediscovered call statements.
* cgraph.c (cgraph_create_edge): Initialize indirect_call to zero.
(dump_cgraph_node): Dump also the indirect_call flag.
(cgraph_clone_edge): Copy also the indirect_call flag.
* tree-inline.c (copy_bb): Do not check for fndecls from call
expressions, check for edge availability when moving clones.
(get_indirect_callee_fndecl): New function.
(expand_call_inline): If callee declaration is not apprent from
the statement, try calling get_indirect_callee_fndecl. Do not
issue warnings or call sorry when not inlinings an indirect edge.
* Makefile.in (IPA_PROP_H): Added $(CGRAPH_H) to dependencies.
* ipa-prop.c (ipa_print_node_param_flags): Make the dump format a
bit more frandly to matching.
* testsuite/g++.dg/ipa/iinline-1.C: New testcase.
* testsuite/gcc.dg/ipa/iinline-1.c: New testcase.
* testsuite/gcc.dg/ipa/modif-1.c: New testcase.
2008-07-23 Michael Meissner <gnu@the-meissners.org> 2008-07-23 Michael Meissner <gnu@the-meissners.org>
PR 36907 PR 36907
......
...@@ -860,7 +860,7 @@ TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h ...@@ -860,7 +860,7 @@ TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h
REAL_H = real.h $(MACHMODE_H) REAL_H = real.h $(MACHMODE_H)
DBGCNT_H = dbgcnt.h dbgcnt.def DBGCNT_H = dbgcnt.h dbgcnt.def
EBIMAP_H = ebitmap.h sbitmap.h EBIMAP_H = ebitmap.h sbitmap.h
IPA_PROP_H = ipa-prop.h $(TREE_H) vec.h IPA_PROP_H = ipa-prop.h $(TREE_H) vec.h $(CGRAPH_H)
GSTAB_H = gstab.h stab.def GSTAB_H = gstab.h stab.def
BITMAP_H = bitmap.h $(HASHTAB_H) statistics.h BITMAP_H = bitmap.h $(HASHTAB_H) statistics.h
...@@ -2564,8 +2564,8 @@ varpool.o : varpool.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ ...@@ -2564,8 +2564,8 @@ varpool.o : varpool.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(TREE_FLOW_H) gt-varpool.h $(TREE_FLOW_H) gt-varpool.h
ipa.o : ipa.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(CGRAPH_H) \ ipa.o : ipa.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(CGRAPH_H) \
tree-pass.h $(TIMEVAR_H) tree-pass.h $(TIMEVAR_H)
ipa-prop.o : ipa-prop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ ipa-prop.o : ipa-prop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
langhooks.h $(GGC_H) $(TARGET_H) $(CGRAPH_H) $(IPA_PROP_H) \ langhooks.h $(GGC_H) $(TARGET_H) $(CGRAPH_H) $(IPA_PROP_H) $(DIAGNOSTIC_H) \
$(TREE_FLOW_H) $(TM_H) tree-pass.h $(FLAGS_H) $(TREE_H) $(TREE_INLINE_H) \ $(TREE_FLOW_H) $(TM_H) tree-pass.h $(FLAGS_H) $(TREE_H) $(TREE_INLINE_H) \
$(TIMEVAR_H) $(TIMEVAR_H)
ipa-cp.o : ipa-cp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ ipa-cp.o : ipa-cp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
...@@ -2581,7 +2581,7 @@ matrix-reorg.o : matrix-reorg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ ...@@ -2581,7 +2581,7 @@ matrix-reorg.o : matrix-reorg.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
ipa-inline.o : ipa-inline.c gt-ipa-inline.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ ipa-inline.o : ipa-inline.c gt-ipa-inline.h $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
$(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \ $(TREE_H) langhooks.h $(TREE_INLINE_H) $(FLAGS_H) $(CGRAPH_H) intl.h \
$(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) tree-pass.h \ $(DIAGNOSTIC_H) $(FIBHEAP_H) $(PARAMS_H) $(TIMEVAR_H) tree-pass.h \
$(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(RTL_H) $(HASHTAB_H) $(COVERAGE_H) $(GGC_H) $(TREE_FLOW_H) $(RTL_H) $(IPA_PROP_H)
ipa-utils.o : ipa-utils.c $(IPA_UTILS_H) $(CONFIG_H) $(SYSTEM_H) \ ipa-utils.o : ipa-utils.c $(IPA_UTILS_H) $(CONFIG_H) $(SYSTEM_H) \
coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) $(TREE_INLINE_H) langhooks.h \ coretypes.h $(TM_H) $(TREE_H) $(TREE_FLOW_H) $(TREE_INLINE_H) langhooks.h \
pointer-set.h $(GGC_H) $(C_COMMON_H) $(TREE_GIMPLE_H) \ pointer-set.h $(GGC_H) $(C_COMMON_H) $(TREE_GIMPLE_H) \
......
...@@ -625,6 +625,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee, ...@@ -625,6 +625,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
gcc_assert (freq >= 0); gcc_assert (freq >= 0);
gcc_assert (freq <= CGRAPH_FREQ_MAX); gcc_assert (freq <= CGRAPH_FREQ_MAX);
edge->loop_nest = nest; edge->loop_nest = nest;
edge->indirect_call = 0;
edge->uid = cgraph_edge_max_uid++; edge->uid = cgraph_edge_max_uid++;
if (caller->call_site_hash) if (caller->call_site_hash)
{ {
...@@ -1048,6 +1049,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) ...@@ -1048,6 +1049,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
edge->frequency / (double)CGRAPH_FREQ_BASE); edge->frequency / (double)CGRAPH_FREQ_BASE);
if (!edge->inline_failed) if (!edge->inline_failed)
fprintf(f, "(inlined) "); fprintf(f, "(inlined) ");
if (edge->indirect_call)
fprintf(f, "(indirect) ");
} }
fprintf (f, "\n calls: "); fprintf (f, "\n calls: ");
...@@ -1057,6 +1060,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) ...@@ -1057,6 +1060,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
edge->callee->uid); edge->callee->uid);
if (!edge->inline_failed) if (!edge->inline_failed)
fprintf(f, "(inlined) "); fprintf(f, "(inlined) ");
if (edge->indirect_call)
fprintf(f, "(indirect) ");
if (edge->count) if (edge->count)
fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ", fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ",
(HOST_WIDEST_INT)edge->count); (HOST_WIDEST_INT)edge->count);
...@@ -1166,6 +1171,7 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n, ...@@ -1166,6 +1171,7 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n,
e->loop_nest + loop_nest); e->loop_nest + loop_nest);
new->inline_failed = e->inline_failed; new->inline_failed = e->inline_failed;
new->indirect_call = e->indirect_call;
if (update_original) if (update_original)
{ {
e->count -= new->count; e->count -= new->count;
......
...@@ -208,7 +208,9 @@ struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_call ...@@ -208,7 +208,9 @@ struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_call
per function call. The range is 0 to CGRAPH_FREQ_MAX. */ per function call. The range is 0 to CGRAPH_FREQ_MAX. */
int frequency; int frequency;
/* Depth of loop nest, 1 means no loop nest. */ /* Depth of loop nest, 1 means no loop nest. */
int loop_nest; unsigned int loop_nest : 31;
/* Whether this edge describes a call that was originally indirect. */
unsigned int indirect_call : 1;
/* Unique id of the edge. */ /* Unique id of the edge. */
int uid; int uid;
}; };
...@@ -376,6 +378,7 @@ void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *); ...@@ -376,6 +378,7 @@ void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *);
/* In cgraphbuild.c */ /* In cgraphbuild.c */
unsigned int rebuild_cgraph_edges (void); unsigned int rebuild_cgraph_edges (void);
int compute_call_stmt_bb_frequency (basic_block bb);
/* In ipa.c */ /* In ipa.c */
bool cgraph_remove_unreachable_nodes (bool, FILE *); bool cgraph_remove_unreachable_nodes (bool, FILE *);
......
...@@ -122,6 +122,25 @@ initialize_inline_failed (struct cgraph_node *node) ...@@ -122,6 +122,25 @@ initialize_inline_failed (struct cgraph_node *node)
} }
} }
/* Computes the frequency of the call statement so that it can be stored in
cgraph_edge. BB is the basic block of the call statement. */
int
compute_call_stmt_bb_frequency (basic_block bb)
{
int entry_freq = ENTRY_BLOCK_PTR->frequency;
int freq;
if (!entry_freq)
entry_freq = 1;
freq = (!bb->frequency && !entry_freq ? CGRAPH_FREQ_BASE
: bb->frequency * CGRAPH_FREQ_BASE / entry_freq);
if (freq > CGRAPH_FREQ_MAX)
freq = CGRAPH_FREQ_MAX;
return freq;
}
/* Create cgraph edges for function calls. /* Create cgraph edges for function calls.
Also look for functions and variables having addresses taken. */ Also look for functions and variables having addresses taken. */
...@@ -133,10 +152,6 @@ build_cgraph_edges (void) ...@@ -133,10 +152,6 @@ build_cgraph_edges (void)
struct pointer_set_t *visited_nodes = pointer_set_create (); struct pointer_set_t *visited_nodes = pointer_set_create ();
block_stmt_iterator bsi; block_stmt_iterator bsi;
tree step; tree step;
int entry_freq = ENTRY_BLOCK_PTR->frequency;
if (!entry_freq)
entry_freq = 1;
/* Create the callgraph edges and record the nodes referenced by the function. /* Create the callgraph edges and record the nodes referenced by the function.
body. */ body. */
...@@ -151,12 +166,8 @@ build_cgraph_edges (void) ...@@ -151,12 +166,8 @@ build_cgraph_edges (void)
{ {
int i; int i;
int n = call_expr_nargs (call); int n = call_expr_nargs (call);
int freq = (!bb->frequency && !entry_freq ? CGRAPH_FREQ_BASE
: bb->frequency * CGRAPH_FREQ_BASE / entry_freq);
if (freq > CGRAPH_FREQ_MAX)
freq = CGRAPH_FREQ_MAX;
cgraph_create_edge (node, cgraph_node (decl), stmt, cgraph_create_edge (node, cgraph_node (decl), stmt,
bb->count, freq, bb->count, compute_call_stmt_bb_frequency (bb),
bb->loop_depth); bb->loop_depth);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
walk_tree (&CALL_EXPR_ARG (call, i), walk_tree (&CALL_EXPR_ARG (call, i),
...@@ -227,10 +238,6 @@ rebuild_cgraph_edges (void) ...@@ -227,10 +238,6 @@ rebuild_cgraph_edges (void)
basic_block bb; basic_block bb;
struct cgraph_node *node = cgraph_node (current_function_decl); struct cgraph_node *node = cgraph_node (current_function_decl);
block_stmt_iterator bsi; block_stmt_iterator bsi;
int entry_freq = ENTRY_BLOCK_PTR->frequency;
if (!entry_freq)
entry_freq = 1;
cgraph_node_remove_callees (node); cgraph_node_remove_callees (node);
...@@ -244,14 +251,9 @@ rebuild_cgraph_edges (void) ...@@ -244,14 +251,9 @@ rebuild_cgraph_edges (void)
tree decl; tree decl;
if (call && (decl = get_callee_fndecl (call))) if (call && (decl = get_callee_fndecl (call)))
{ cgraph_create_edge (node, cgraph_node (decl), stmt,
int freq = (!bb->frequency && !entry_freq ? CGRAPH_FREQ_BASE bb->count, compute_call_stmt_bb_frequency (bb),
: bb->frequency * CGRAPH_FREQ_BASE / entry_freq); bb->loop_depth);
if (freq > CGRAPH_FREQ_MAX)
freq = CGRAPH_FREQ_MAX;
cgraph_create_edge (node, cgraph_node (decl), stmt,
bb->count, freq, bb->loop_depth);
}
} }
initialize_inline_failed (node); initialize_inline_failed (node);
gcc_assert (!node->global.inlined_to); gcc_assert (!node->global.inlined_to);
......
...@@ -796,7 +796,7 @@ verify_cgraph_node (struct cgraph_node *node) ...@@ -796,7 +796,7 @@ verify_cgraph_node (struct cgraph_node *node)
for (e = node->callees; e; e = e->next_callee) for (e = node->callees; e; e = e->next_callee)
{ {
if (!e->aux) if (!e->aux && !e->indirect_call)
{ {
error ("edge %s->%s has no corresponding call_stmt", error ("edge %s->%s has no corresponding call_stmt",
cgraph_node_name (e->caller), cgraph_node_name (e->caller),
......
...@@ -571,6 +571,10 @@ finhibit-size-directive ...@@ -571,6 +571,10 @@ finhibit-size-directive
Common Report Var(flag_inhibit_size_directive) Common Report Var(flag_inhibit_size_directive)
Do not generate .size directives Do not generate .size directives
findirect-inlining
Common Report Var(flag_indirect_inlining)
Perform indirect inlining
; Nonzero means that functions declared `inline' will be treated ; Nonzero means that functions declared `inline' will be treated
; as `static'. Prevents generation of zillions of copies of unused ; as `static'. Prevents generation of zillions of copies of unused
; static inline functions; instead, `inlines' are written out ; static inline functions; instead, `inlines' are written out
......
...@@ -328,8 +328,8 @@ Objective-C and Objective-C++ Dialects}. ...@@ -328,8 +328,8 @@ Objective-C and Objective-C++ Dialects}.
-fearly-inlining -fexpensive-optimizations -ffast-math @gol -fearly-inlining -fexpensive-optimizations -ffast-math @gol
-ffinite-math-only -ffloat-store -fforward-propagate @gol -ffinite-math-only -ffloat-store -fforward-propagate @gol
-ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol -ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol
-fgcse-sm -fif-conversion -fif-conversion2 -finline-functions @gol -fgcse-sm -fif-conversion -fif-conversion2 -findirect-inlining @gol
-finline-functions-called-once -finline-limit=@var{n} @gol -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol
-finline-small-functions -fipa-cp -fipa-marix-reorg -fipa-pta @gol -finline-small-functions -fipa-cp -fipa-marix-reorg -fipa-pta @gol
-fipa-pure-const -fipa-reference -fipa-struct-reorg @gol -fipa-pure-const -fipa-reference -fipa-struct-reorg @gol
-fipa-type-escape -fivopts -fkeep-inline-functions -fkeep-static-consts @gol -fipa-type-escape -fivopts -fkeep-inline-functions -fkeep-static-consts @gol
...@@ -5199,6 +5199,7 @@ also turns on the following optimization flags: ...@@ -5199,6 +5199,7 @@ also turns on the following optimization flags:
-fdelete-null-pointer-checks @gol -fdelete-null-pointer-checks @gol
-fexpensive-optimizations @gol -fexpensive-optimizations @gol
-fgcse -fgcse-lm @gol -fgcse -fgcse-lm @gol
-findirect-inlining @gol
-foptimize-sibling-calls @gol -foptimize-sibling-calls @gol
-fpeephole2 @gol -fpeephole2 @gol
-fregmove @gol -fregmove @gol
...@@ -5216,8 +5217,8 @@ invoking @option{-O2} on programs that use computed gotos. ...@@ -5216,8 +5217,8 @@ invoking @option{-O2} on programs that use computed gotos.
@item -O3 @item -O3
@opindex O3 @opindex O3
Optimize yet more. @option{-O3} turns on all optimizations specified by Optimize yet more. @option{-O3} turns on all optimizations specified
@option{-O2} and also turns on the @option{-finline-functions}, by @option{-O2} and also turns on the @option{-finline-functions},
@option{-funswitch-loops}, @option{-fpredictive-commoning}, @option{-funswitch-loops}, @option{-fpredictive-commoning},
@option{-fgcse-after-reload} and @option{-ftree-vectorize} options. @option{-fgcse-after-reload} and @option{-ftree-vectorize} options.
...@@ -5319,6 +5320,15 @@ in this way. ...@@ -5319,6 +5320,15 @@ in this way.
Enabled at level @option{-O2}. Enabled at level @option{-O2}.
@item -findirect-inlining
@opindex findirect-inlining
Inline also indirect calls that are discovered to be known at compile
time thanks to previous inlining. This option has any effect only
when inlining itself is turned on by the @option{-finline-functions}
or @option{-finline-small-functions} options.
Enabled at level @option{-O2}.
@item -finline-functions @item -finline-functions
@opindex finline-functions @opindex finline-functions
Integrate all simple functions into their callers. The compiler Integrate all simple functions into their callers. The compiler
......
...@@ -183,6 +183,18 @@ ipcp_lat_is_const (struct ipcp_lattice *lat) ...@@ -183,6 +183,18 @@ ipcp_lat_is_const (struct ipcp_lattice *lat)
return false; return false;
} }
/* Return whether LAT is a constant lattice that ipa-cp can actually insert
into the code (i.e. constants excluding member pointers and pointers). */
static inline bool
ipcp_lat_is_insertable (struct ipcp_lattice *lat)
{
if ((lat->type == IPA_CONST_VALUE || lat->type == IPA_CONST_VALUE_REF)
&& !POINTER_TYPE_P (TREE_TYPE (lat->constant)))
return true;
else
return false;
}
/* Return true if LAT1 and LAT2 are equal. */ /* Return true if LAT1 and LAT2 are equal. */
static inline bool static inline bool
ipcp_lats_are_equal (struct ipcp_lattice *lat1, struct ipcp_lattice *lat2) ipcp_lats_are_equal (struct ipcp_lattice *lat1, struct ipcp_lattice *lat2)
...@@ -247,9 +259,7 @@ static void ...@@ -247,9 +259,7 @@ static void
ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat, ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat,
struct ipa_jump_func *jfunc) struct ipa_jump_func *jfunc)
{ {
if (jfunc->type == IPA_UNKNOWN) if (jfunc->type == IPA_CONST)
lat->type = IPA_BOTTOM;
else if (jfunc->type == IPA_CONST)
{ {
lat->type = IPA_CONST_VALUE; lat->type = IPA_CONST_VALUE;
lat->constant = jfunc->value.constant; lat->constant = jfunc->value.constant;
...@@ -267,6 +277,8 @@ ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat, ...@@ -267,6 +277,8 @@ ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat,
lat->type = caller_lat->type; lat->type = caller_lat->type;
lat->constant = caller_lat->constant; lat->constant = caller_lat->constant;
} }
else
lat->type = IPA_BOTTOM;
} }
/* True when OLD and NEW values are not the same. */ /* True when OLD and NEW values are not the same. */
...@@ -303,17 +315,18 @@ ipcp_print_all_lattices (FILE * f) ...@@ -303,17 +315,18 @@ ipcp_print_all_lattices (FILE * f)
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i);
fprintf (f, " param [%d]: ", i);
if (lat->type == IPA_CONST_VALUE || lat->type == IPA_CONST_VALUE_REF) if (lat->type == IPA_CONST_VALUE || lat->type == IPA_CONST_VALUE_REF)
{ {
fprintf (f, " param [%d]: ", i);
fprintf (f, "type is CONST "); fprintf (f, "type is CONST ");
print_generic_expr (f, lat->constant, 0); print_generic_expr (f, lat->constant, 0);
fprintf (f, "\n"); fprintf (f, "\n");
} }
else if (lat->type == IPA_TOP) else if (lat->type == IPA_TOP)
fprintf (f, "param [%d]: type is TOP \n", i); fprintf (f, "type is TOP\n");
else else
fprintf (f, "param [%d]: type is BOTTOM \n", i); fprintf (f, "type is BOTTOM\n");
} }
} }
} }
...@@ -551,58 +564,6 @@ ipcp_node_not_modifiable_p (struct cgraph_node *node) ...@@ -551,58 +564,6 @@ ipcp_node_not_modifiable_p (struct cgraph_node *node)
return false; return false;
} }
/* Print ipa_jump_func data structures to F. */
static void
ipcp_print_all_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;
tree info_type;
fprintf (f, "\nCALLSITE PARAM PRINT\n");
for (node = cgraph_nodes; node; node = node->next)
{
if (!node->analyzed)
continue;
for (cs = node->callees; cs; cs = cs->next_callee)
{
fprintf (f, "callsite %s ", cgraph_node_name (node));
fprintf (f, "-> %s :: \n", cgraph_node_name (cs->callee));
if (!ipa_edge_args_info_available_for_edge_p (cs)
|| ipa_is_called_with_var_arguments (IPA_NODE_REF (cs->callee)))
continue;
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_UNKNOWN)
fprintf (f, "UNKNOWN\n");
else if (type == IPA_CONST || type == IPA_CONST_REF)
{
info_type = jump_func->value.constant;
fprintf (f, "CONST : ");
print_generic_expr (f, info_type, 0);
fprintf (f, "\n");
}
else if (type == IPA_PASS_THROUGH)
{
fprintf (f, "PASS THROUGH : ");
fprintf (f, "%d\n", jump_func->value.formal_id);
}
}
}
}
}
/* Print count scale data structures. */ /* Print count scale data structures. */
static void static void
ipcp_function_scale_print (FILE * f) ipcp_function_scale_print (FILE * f)
...@@ -664,7 +625,7 @@ ipcp_print_edge_profiles (FILE * f) ...@@ -664,7 +625,7 @@ ipcp_print_edge_profiles (FILE * f)
for (node = cgraph_nodes; node; node = node->next) for (node = cgraph_nodes; node; node = node->next)
{ {
fprintf (f, "function %s: \n", cgraph_node_name (node)); fprintf (f, "function %s: \n", cgraph_node_name (node));
if (DECL_SAVED_TREE (node->decl)) if (node->analyzed)
{ {
bb = bb =
ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (node->decl)); ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (node->decl));
...@@ -751,8 +712,8 @@ ipcp_print_all_structures (FILE * f) ...@@ -751,8 +712,8 @@ ipcp_print_all_structures (FILE * f)
ipcp_print_all_lattices (f); ipcp_print_all_lattices (f);
ipcp_function_scale_print (f); ipcp_function_scale_print (f);
ipa_print_all_tree_maps (f); ipa_print_all_tree_maps (f);
ipa_print_all_params_modified (f); ipa_print_all_param_flags (f);
ipcp_print_all_jump_functions (f); ipa_print_all_jump_functions (f);
} }
/* Print profile info for all functions. */ /* Print profile info for all functions. */
...@@ -781,10 +742,8 @@ ipcp_create_replace_map (struct function *func, tree parm_tree, ...@@ -781,10 +742,8 @@ ipcp_create_replace_map (struct function *func, tree parm_tree,
tree const_val; tree const_val;
replace_map = XCNEW (struct ipa_replace_map); replace_map = XCNEW (struct ipa_replace_map);
gcc_assert (ipcp_lat_is_const (lat)); if (is_gimple_reg (parm_tree) && gimple_default_def (func, parm_tree)
if (lat->type != IPA_CONST_VALUE_REF && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_default_def (func,
&& is_gimple_reg (parm_tree) && gimple_default_def (func, parm_tree)
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_default_def (func,
parm_tree))) parm_tree)))
{ {
if (dump_file) if (dump_file)
...@@ -944,7 +903,7 @@ ipcp_insert_stage (void) ...@@ -944,7 +903,7 @@ ipcp_insert_stage (void)
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i);
if (ipcp_lat_is_const (lat)) if (ipcp_lat_is_insertable (lat))
const_param++; const_param++;
} }
if (const_param == 0) if (const_param == 0)
...@@ -953,7 +912,8 @@ ipcp_insert_stage (void) ...@@ -953,7 +912,8 @@ ipcp_insert_stage (void)
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i);
if (ipcp_lat_is_const (lat)) if (lat->type == IPA_CONST_VALUE
&& !POINTER_TYPE_P (TREE_TYPE (lat->constant)))
{ {
parm_tree = ipa_get_ith_param (info, i); parm_tree = ipa_get_ith_param (info, i);
replace_param = replace_param =
...@@ -990,7 +950,7 @@ ipcp_insert_stage (void) ...@@ -990,7 +950,7 @@ ipcp_insert_stage (void)
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i);
if (ipcp_lat_is_const (lat)) if (ipcp_lat_is_insertable (lat))
{ {
parm_tree = ipa_get_ith_param (info, i); parm_tree = ipa_get_ith_param (info, i);
if (lat->type != IPA_CONST_VALUE_REF if (lat->type != IPA_CONST_VALUE_REF
......
...@@ -139,6 +139,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -139,6 +139,7 @@ along with GCC; see the file COPYING3. If not see
#include "ggc.h" #include "ggc.h"
#include "tree-flow.h" #include "tree-flow.h"
#include "rtl.h" #include "rtl.h"
#include "ipa-prop.h"
/* Mode incremental inliner operate on: /* Mode incremental inliner operate on:
...@@ -660,10 +661,12 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where, ...@@ -660,10 +661,12 @@ lookup_recursive_calls (struct cgraph_node *node, struct cgraph_node *where,
} }
/* Decide on recursive inlining: in the case function has recursive calls, /* Decide on recursive inlining: in the case function has recursive calls,
inline until body size reaches given argument. */ inline until body size reaches given argument. If any new indirect edges
are discovered in the process, add them to NEW_EDGES, unless it is NULL. */
static bool static bool
cgraph_decide_recursive_inlining (struct cgraph_node *node) cgraph_decide_recursive_inlining (struct cgraph_node *node,
VEC (cgraph_edge_p, heap) *new_edges)
{ {
int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO); int limit = PARAM_VALUE (PARAM_MAX_INLINE_INSNS_RECURSIVE_AUTO);
int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO); int max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO);
...@@ -760,6 +763,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node) ...@@ -760,6 +763,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node)
} }
cgraph_redirect_edge_callee (curr, master_clone); cgraph_redirect_edge_callee (curr, master_clone);
cgraph_mark_inline_edge (curr, false); cgraph_mark_inline_edge (curr, false);
if (flag_indirect_inlining)
ipa_propagate_indirect_call_infos (curr, new_edges);
lookup_recursive_calls (node, curr->callee, heap); lookup_recursive_calls (node, curr->callee, heap);
n++; n++;
} }
...@@ -817,6 +822,20 @@ compute_max_insns (int insns) ...@@ -817,6 +822,20 @@ compute_max_insns (int insns)
* (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100); * (100 + PARAM_VALUE (PARAM_INLINE_UNIT_GROWTH)) / 100);
} }
/* Compute badness of all edges in NEW_EDGES and add them to the HEAP. */
static void
add_new_edges_to_heap (fibheap_t heap, VEC (cgraph_edge_p, heap) *new_edges)
{
while (VEC_length (cgraph_edge_p, new_edges) > 0)
{
struct cgraph_edge *edge = VEC_pop (cgraph_edge_p, new_edges);
gcc_assert (!edge->aux);
edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge), edge);
}
}
/* We use greedy algorithm for inlining of small functions: /* We use greedy algorithm for inlining of small functions:
All inline candidates are put into prioritized heap based on estimated All inline candidates are put into prioritized heap based on estimated
growth of the overall number of instructions and then update the estimates. growth of the overall number of instructions and then update the estimates.
...@@ -833,6 +852,10 @@ cgraph_decide_inlining_of_small_functions (void) ...@@ -833,6 +852,10 @@ cgraph_decide_inlining_of_small_functions (void)
fibheap_t heap = fibheap_new (); fibheap_t heap = fibheap_new ();
bitmap updated_nodes = BITMAP_ALLOC (NULL); bitmap updated_nodes = BITMAP_ALLOC (NULL);
int min_insns, max_insns; int min_insns, max_insns;
VEC (cgraph_edge_p, heap) *new_indirect_edges = NULL;
if (flag_indirect_inlining)
new_indirect_edges = VEC_alloc (cgraph_edge_p, heap, 8);
if (dump_file) if (dump_file)
fprintf (dump_file, "\nDeciding on smaller functions:\n"); fprintf (dump_file, "\nDeciding on smaller functions:\n");
...@@ -968,8 +991,10 @@ cgraph_decide_inlining_of_small_functions (void) ...@@ -968,8 +991,10 @@ cgraph_decide_inlining_of_small_functions (void)
where = edge->caller; where = edge->caller;
if (where->global.inlined_to) if (where->global.inlined_to)
where = where->global.inlined_to; where = where->global.inlined_to;
if (!cgraph_decide_recursive_inlining (where)) if (!cgraph_decide_recursive_inlining (where, new_indirect_edges))
continue; continue;
if (flag_indirect_inlining)
add_new_edges_to_heap (heap, new_indirect_edges);
update_callee_keys (heap, where, updated_nodes); update_callee_keys (heap, where, updated_nodes);
} }
else else
...@@ -986,6 +1011,11 @@ cgraph_decide_inlining_of_small_functions (void) ...@@ -986,6 +1011,11 @@ cgraph_decide_inlining_of_small_functions (void)
} }
callee = edge->callee; callee = edge->callee;
cgraph_mark_inline_edge (edge, true); cgraph_mark_inline_edge (edge, true);
if (flag_indirect_inlining)
{
ipa_propagate_indirect_call_infos (edge, new_indirect_edges);
add_new_edges_to_heap (heap, new_indirect_edges);
}
update_callee_keys (heap, callee, updated_nodes); update_callee_keys (heap, callee, updated_nodes);
} }
where = edge->caller; where = edge->caller;
...@@ -1028,6 +1058,9 @@ cgraph_decide_inlining_of_small_functions (void) ...@@ -1028,6 +1058,9 @@ cgraph_decide_inlining_of_small_functions (void)
&edge->inline_failed)) &edge->inline_failed))
edge->inline_failed = N_("--param inline-unit-growth limit reached"); edge->inline_failed = N_("--param inline-unit-growth limit reached");
} }
if (new_indirect_edges)
VEC_free (cgraph_edge_p, heap, new_indirect_edges);
fibheap_delete (heap); fibheap_delete (heap);
BITMAP_FREE (updated_nodes); BITMAP_FREE (updated_nodes);
} }
...@@ -1112,6 +1145,8 @@ cgraph_decide_inlining (void) ...@@ -1112,6 +1145,8 @@ cgraph_decide_inlining (void)
continue; continue;
} }
cgraph_mark_inline_edge (e, true); cgraph_mark_inline_edge (e, true);
if (flag_indirect_inlining)
ipa_propagate_indirect_call_infos (e, NULL);
if (dump_file) if (dump_file)
fprintf (dump_file, fprintf (dump_file,
" Inlined into %s which now has %i insns.\n", " Inlined into %s which now has %i insns.\n",
...@@ -1133,6 +1168,11 @@ cgraph_decide_inlining (void) ...@@ -1133,6 +1168,11 @@ cgraph_decide_inlining (void)
if (!flag_really_no_inline) if (!flag_really_no_inline)
cgraph_decide_inlining_of_small_functions (); cgraph_decide_inlining_of_small_functions ();
/* After this point, any edge discovery performed by indirect inlining is no
good so let's give up. */
if (flag_indirect_inlining)
free_all_ipa_structures_after_iinln ();
if (!flag_really_no_inline if (!flag_really_no_inline
&& flag_inline_functions_called_once) && flag_inline_functions_called_once)
{ {
...@@ -1612,6 +1652,31 @@ struct gimple_opt_pass pass_inline_parameters = ...@@ -1612,6 +1652,31 @@ struct gimple_opt_pass pass_inline_parameters =
} }
}; };
/* This function performs intraprocedural analyzis in NODE that is required to
inline indirect calls. */
static void
inline_indirect_intraprocedural_analysis (struct cgraph_node *node)
{
struct cgraph_edge *cs;
ipa_count_formal_params (node);
ipa_create_param_decls_array (node);
ipa_detect_param_modifications (node);
ipa_analyze_params_uses (node);
if (dump_file)
ipa_print_node_param_flags (dump_file, node);
for (cs = node->callees; cs; cs = cs->next_callee)
{
ipa_count_arguments (cs);
ipa_compute_jump_functions (cs);
}
if (dump_file)
ipa_print_node_jump_functions (dump_file, node);
}
/* Note function body size. */ /* Note function body size. */
static void static void
inline_generate_summary (void) inline_generate_summary (void)
...@@ -1621,6 +1686,13 @@ inline_generate_summary (void) ...@@ -1621,6 +1686,13 @@ inline_generate_summary (void)
int nnodes = cgraph_postorder (order); int nnodes = cgraph_postorder (order);
int i; int i;
if (flag_indirect_inlining)
{
ipa_register_cgraph_hooks ();
ipa_check_create_node_params ();
ipa_check_create_edge_args ();
}
for (i = nnodes - 1; i >= 0; i--) for (i = nnodes - 1; i >= 0; i--)
{ {
struct cgraph_node *node = order[i]; struct cgraph_node *node = order[i];
...@@ -1632,6 +1704,10 @@ inline_generate_summary (void) ...@@ -1632,6 +1704,10 @@ inline_generate_summary (void)
push_cfun (DECL_STRUCT_FUNCTION (node->decl)); push_cfun (DECL_STRUCT_FUNCTION (node->decl));
current_function_decl = node->decl; current_function_decl = node->decl;
compute_inline_parameters (node); compute_inline_parameters (node);
if (flag_indirect_inlining)
inline_indirect_intraprocedural_analysis (node);
pop_cfun (); pop_cfun ();
} }
} }
......
...@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h" #include "flags.h"
#include "timevar.h" #include "timevar.h"
#include "flags.h" #include "flags.h"
#include "diagnostic.h"
/* Vector where the parameter infos are actually stored. */ /* Vector where the parameter infos are actually stored. */
VEC (ipa_node_params_t, heap) *ipa_node_params_vector; VEC (ipa_node_params_t, heap) *ipa_node_params_vector;
...@@ -117,6 +118,9 @@ ipa_create_param_decls_array (struct cgraph_node *mt) ...@@ -117,6 +118,9 @@ ipa_create_param_decls_array (struct cgraph_node *mt)
int param_num; int param_num;
struct ipa_node_params *info = IPA_NODE_REF (mt); struct ipa_node_params *info = IPA_NODE_REF (mt);
if (info->param_decls)
return;
info->param_decls = XCNEWVEC (tree, ipa_get_param_count (info)); info->param_decls = XCNEWVEC (tree, ipa_get_param_count (info));
fndecl = mt->decl; fndecl = mt->decl;
fnargs = DECL_ARGUMENTS (fndecl); fnargs = DECL_ARGUMENTS (fndecl);
...@@ -146,95 +150,84 @@ ipa_count_formal_params (struct cgraph_node *mt) ...@@ -146,95 +150,84 @@ ipa_count_formal_params (struct cgraph_node *mt)
ipa_set_param_count (IPA_NODE_REF (mt), param_num); ipa_set_param_count (IPA_NODE_REF (mt), param_num);
} }
/* Check STMT to detect whether a formal is modified within MT, the appropriate /* Check STMT to detect whether a formal parameter is directly modified within
entry is updated in the modified_flags array of ipa_node_params (associated STMT, the appropriate entry is updated in the modified flags of INFO.
with MT). */ Directly means that this function does not check for modifications through
pointers or escaping addresses because all TREE_ADDRESSABLE parameters are
considered modified anyway. */
static void static void
ipa_check_stmt_modifications (struct cgraph_node *mt, tree stmt) ipa_check_stmt_modifications (struct ipa_node_params *info, tree stmt)
{ {
int index, j; int j;
tree parm_decl; int index;
struct ipa_node_params *info; tree lhs;
switch (TREE_CODE (stmt)) switch (TREE_CODE (stmt))
{ {
case GIMPLE_MODIFY_STMT: case GIMPLE_MODIFY_STMT:
if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 0)) == PARM_DECL) lhs = GIMPLE_STMT_OPERAND (stmt, 0);
{
info = IPA_NODE_REF (mt); while (handled_component_p (lhs))
parm_decl = GIMPLE_STMT_OPERAND (stmt, 0); lhs = TREE_OPERAND (lhs, 0);
index = ipa_get_param_decl_index (info, parm_decl); if (TREE_CODE (lhs) == SSA_NAME)
if (index >= 0) lhs = SSA_NAME_VAR (lhs);
info->modified_flags[index] = true; index = ipa_get_param_decl_index (info, lhs);
} if (index >= 0)
info->param_flags[index].modified = true;
break; break;
case ASM_EXPR: case ASM_EXPR:
/* Asm code could modify any of the parameters. */ /* Asm code could modify any of the parameters. */
info = IPA_NODE_REF (mt); for (j = 0; j < ipa_get_param_count (info); j++)
for (j = 0; j < ipa_get_param_count (IPA_NODE_REF (mt)); j++) info->param_flags[j].modified = true;
info->modified_flags[j] = true;
break; break;
default: default:
break; break;
} }
} }
/* The modify computation driver for MT. Compute which formal arguments /* Compute which formal parameters of function associated with NODE are locally
of function MT are locally modified. Formals may be modified in MT modified. Parameters may be modified in NODE if they are TREE_ADDRESSABLE,
if their address is taken, or if if they appear on the left hand side of an assignment or if there is an
they appear on the left hand side of an assignment. */ ASM_EXPR in the function. */
void void
ipa_detect_param_modifications (struct cgraph_node *mt) ipa_detect_param_modifications (struct cgraph_node *node)
{ {
tree decl; tree decl = node->decl;
tree body;
int j, count;
basic_block bb; basic_block bb;
struct function *func; struct function *func;
block_stmt_iterator bsi; block_stmt_iterator bsi;
tree stmt, parm_tree; tree stmt;
struct ipa_node_params *info = IPA_NODE_REF (mt); struct ipa_node_params *info = IPA_NODE_REF (node);
int i, count;
if (ipa_get_param_count (info) == 0 || info->modified_flags) if (ipa_get_param_count (info) == 0 || info->modification_analysis_done)
return; return;
count = ipa_get_param_count (info); if (!info->param_flags)
info->modified_flags = XCNEWVEC (bool, count); info->param_flags = XCNEWVEC (struct ipa_param_flags,
decl = mt->decl; ipa_get_param_count (info));
/* ??? Handle pending sizes case. Set all parameters
of the function to be modified. */
if (DECL_UNINLINABLE (decl)) func = DECL_STRUCT_FUNCTION (decl);
{ FOR_EACH_BB_FN (bb, func)
for (j = 0; j < count; j++)
info->modified_flags[j] = true;
return;
}
/* Formals whose address is taken are considered modified. */
for (j = 0; j < count; j++)
{
parm_tree = ipa_get_ith_param (info, j);
if (!is_gimple_reg (parm_tree)
&& TREE_ADDRESSABLE (parm_tree))
info->modified_flags[j] = true;
}
body = DECL_SAVED_TREE (decl);
if (body != NULL)
{ {
func = DECL_STRUCT_FUNCTION (decl); for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
FOR_EACH_BB_FN (bb, func) {
{ stmt = bsi_stmt (bsi);
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) ipa_check_stmt_modifications (info, stmt);
{ }
stmt = bsi_stmt (bsi);
ipa_check_stmt_modifications (mt, stmt);
}
}
} }
count = ipa_get_param_count (info);
for (i = 0; i < count; i++)
if (TREE_ADDRESSABLE (ipa_get_ith_param (info, i)))
info->param_flags[i].modified = true;
info->modification_analysis_done = 1;
} }
/* Count number of arguments callsite CS has and store it in /* Count number of arguments callsite CS has and store it in
ipa_edge_args structure corresponding to this callsite. */ ipa_edge_args structure corresponding to this callsite. */
void void
ipa_count_arguments (struct cgraph_edge *cs) ipa_count_arguments (struct cgraph_edge *cs)
...@@ -248,91 +241,780 @@ ipa_count_arguments (struct cgraph_edge *cs) ...@@ -248,91 +241,780 @@ ipa_count_arguments (struct cgraph_edge *cs)
ipa_set_cs_argument_count (IPA_EDGE_REF (cs), arg_num); ipa_set_cs_argument_count (IPA_EDGE_REF (cs), arg_num);
} }
/* Compute jump function for all arguments of callsite CS /* The following function prints the jump functions of all arguments on all
and insert the information in the jump_functions array call graph edges going from NODE to file F. */
in the ipa_edge_args corresponding to this callsite. */
void void
ipa_compute_jump_functions (struct cgraph_edge *cs) ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node)
{ {
tree call_tree; int i, count;
tree arg, cst_decl; struct cgraph_edge *cs;
int arg_num; struct ipa_jump_func *jump_func;
struct cgraph_node *mt; enum jump_func_type type;
tree parm_decl;
struct function *curr_cfun;
call_expr_arg_iterator iter;
struct ipa_edge_args *args = IPA_EDGE_REF (cs);
if (ipa_get_cs_argument_count (args) == 0 || args->jump_functions) fprintf (f, "JUMP FUNCTIONS OF CALLER %s:\n", cgraph_node_name (node));
return; for (cs = node->callees; cs; cs = cs->next_callee)
args->jump_functions = XCNEWVEC (struct ipa_jump_func, {
ipa_get_cs_argument_count (args)); if (!ipa_edge_args_info_available_for_edge_p (cs))
call_tree = get_call_expr_in (cs->call_stmt); continue;
gcc_assert (TREE_CODE (call_tree) == CALL_EXPR);
arg_num = 0; fprintf (f, "callsite %s ", cgraph_node_name (node));
fprintf (f, "-> %s :: \n", cgraph_node_name (cs->callee));
FOR_EACH_CALL_EXPR_ARG (arg, iter, call_tree) 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_UNKNOWN)
fprintf (f, "UNKNOWN\n");
else if (type == IPA_CONST || type == IPA_CONST_REF)
{
tree val = jump_func->value.constant;
fprintf (f, "CONST: ");
print_generic_expr (f, val, 0);
fprintf (f, "\n");
}
else if (type == IPA_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_PASS_THROUGH)
{
fprintf (f, "PASS THROUGH: ");
fprintf (f, "%d\n", jump_func->value.formal_id);
}
}
}
}
/* Print ipa_jump_func data structures of all nodes in the call graph to F. */
void
ipa_print_all_jump_functions (FILE *f)
{
struct cgraph_node *node;
fprintf (f, "\nCALLSITE PARAM PRINT\n");
for (node = cgraph_nodes; node; node = node->next)
{
ipa_print_node_jump_functions (f, node);
}
}
/* The following function determines the jump functions of scalar arguments.
Scalar means SSA names and constants of a number of selected types. INFO is
the ipa_node_params structure associated with the caller, FUNCTIONS is a
pointer to an array of jump function structures associated with CALL which
is the call statement being examined.*/
static void
compute_scalar_jump_functions (struct ipa_node_params *info,
struct ipa_jump_func *functions,
tree call)
{
call_expr_arg_iterator iter;
tree arg;
int num = 0;
FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
{ {
/* If the formal parameter was passed as argument, we store if (TREE_CODE (arg) == INTEGER_CST
IPA_PASS_THROUGH and its index in the caller as the jump function || TREE_CODE (arg) == REAL_CST
of this argument. */ || TREE_CODE (arg) == FIXED_CST)
if ((TREE_CODE (arg) == SSA_NAME
&& TREE_CODE (SSA_NAME_VAR (arg)) == PARM_DECL)
|| TREE_CODE (arg) == PARM_DECL)
{ {
struct ipa_node_params *info; functions[num].type = IPA_CONST;
int index; functions[num].value.constant = arg;
}
mt = cs->caller; else if (TREE_CODE (arg) == ADDR_EXPR)
info = IPA_NODE_REF (mt); {
parm_decl = TREE_CODE (arg) == PARM_DECL ? arg : SSA_NAME_VAR (arg); if (TREE_CODE (TREE_OPERAND (arg, 0)) == FUNCTION_DECL)
index = ipa_get_param_decl_index (info, parm_decl);
if (TREE_CODE (arg) == SSA_NAME && IS_VALID_JUMP_FUNC_INDEX (index))
{ {
curr_cfun = DECL_STRUCT_FUNCTION (mt->decl); functions[num].type = IPA_CONST;
if (!gimple_default_def (curr_cfun, parm_decl) functions[num].value.constant = TREE_OPERAND (arg, 0);
|| gimple_default_def (curr_cfun, parm_decl) != arg)
info->modified_flags[index] = true;
} }
if (!IS_VALID_JUMP_FUNC_INDEX (index) || info->modified_flags[index]) else if (TREE_CODE (TREE_OPERAND (arg, 0)) == CONST_DECL)
args->jump_functions[arg_num].type = IPA_UNKNOWN; {
else tree cst_decl = TREE_OPERAND (arg, 0);
if (TREE_CODE (DECL_INITIAL (cst_decl)) == INTEGER_CST
|| TREE_CODE (DECL_INITIAL (cst_decl)) == REAL_CST
|| TREE_CODE (DECL_INITIAL (cst_decl)) == FIXED_CST)
{
functions[num].type = IPA_CONST_REF;
functions[num].value.constant = cst_decl;
}
}
}
else if ((TREE_CODE (arg) == SSA_NAME) && SSA_NAME_IS_DEFAULT_DEF (arg))
{
int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
if (index >= 0)
{ {
args->jump_functions[arg_num].type = IPA_PASS_THROUGH; functions[num].type = IPA_PASS_THROUGH;
args->jump_functions[arg_num].value.formal_id = index; functions[num].value.formal_id = index;
} }
} }
/* If a constant value was passed as argument,
we store IPA_CONST and its value as the jump function num++;
of this argument. */ }
else if (TREE_CODE (arg) == INTEGER_CST }
|| TREE_CODE (arg) == REAL_CST
|| TREE_CODE (arg) == FIXED_CST) /* This function inspects the given TYPE and returns true iff it has the same
structure (the same number of fields of the same types) as a C++ member
pointer. If METHOD_PTR and DELTA are non-NULL, the trees representing the
corresponding fields are stored there. */
static bool
type_like_member_ptr_p (tree type, tree *method_ptr, tree *delta)
{
tree fld;
if (TREE_CODE (type) != RECORD_TYPE)
return false;
fld = TYPE_FIELDS (type);
if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
|| TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE)
return false;
if (method_ptr)
*method_ptr = fld;
fld = TREE_CHAIN (fld);
if (!fld || INTEGRAL_TYPE_P (fld))
return false;
if (delta)
*delta = fld;
if (TREE_CHAIN (fld))
return false;
return true;
}
/* This function goes through arguments of the CALL and for every one that
looks like a member pointer, it checks whether it can be safely declared
pass-through and if so, marks that to the corresponding item of jum
FUNCTIONS . It returns true iff there were non-pass-through member pointers
within the arguments. INFO describes formal parameters of the caller. */
static bool
compute_pass_through_member_ptrs (struct ipa_node_params *info,
struct ipa_jump_func *functions,
tree call)
{
call_expr_arg_iterator iter;
bool undecided_members = false;
int num = 0;
tree arg;
FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
{
if (type_like_member_ptr_p (TREE_TYPE (arg), NULL, NULL))
{ {
args->jump_functions[arg_num].type = IPA_CONST; if (TREE_CODE (arg) == PARM_DECL)
args->jump_functions[arg_num].value.constant = arg; {
int index = ipa_get_param_decl_index (info, arg);
gcc_assert (index >=0);
if (!ipa_is_ith_param_modified (info, index))
{
functions[num].type = IPA_PASS_THROUGH;
functions[num].value.formal_id = index;
}
else
undecided_members = true;
}
else
undecided_members = true;
} }
/* This is for the case of Fortran. If the address of a const_decl
was passed as argument then we store num++;
IPA_CONST_REF/IPA_CONST_REF and the constant }
value as the jump function corresponding to this argument. */
else if (TREE_CODE (arg) == ADDR_EXPR return undecided_members;
&& TREE_CODE (TREE_OPERAND (arg, 0)) == CONST_DECL) }
/* Simple function filling in a member pointer constant jump function (with PFN
and DELTA as the constant value) into JFUNC. */
static void
fill_member_ptr_cst_jump_function (struct ipa_jump_func *jfunc,
tree pfn, tree delta)
{
jfunc->type = IPA_CONST_MEMBER_PTR;
jfunc->value.member_cst.pfn = pfn;
jfunc->value.member_cst.delta = delta;
}
/* Traverse statements from CALL_STMT backwards, scanning whether the argument
ARG which is a member pointer is filled in with constant values. If it is,
fill the jump function JFUNC in appropriately. METHOD_FIELD and DELTA_FIELD
are fields of the record type of the member pointer. To give an example, we
look for a pattern looking like the following:
D.2515.__pfn ={v} printStuff;
D.2515.__delta ={v} 0;
i_1 = doprinting (D.2515); */
static void
determine_cst_member_ptr (tree call_stmt, tree arg, tree method_field,
tree delta_field, struct ipa_jump_func *jfunc)
{
block_stmt_iterator bsi;
tree method = NULL_TREE;
tree delta = NULL_TREE;
bsi = bsi_for_stmt (call_stmt);
bsi_prev (&bsi);
for (; !bsi_end_p (bsi); bsi_prev (&bsi))
{
tree stmt = bsi_stmt (bsi);
tree lhs, rhs, fld;
if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
return;
rhs = GIMPLE_STMT_OPERAND (stmt, 1);
if (TREE_CODE (rhs) == CALL_EXPR)
return;
lhs = GIMPLE_STMT_OPERAND (stmt, 0);
if (TREE_CODE (lhs) != COMPONENT_REF
|| TREE_OPERAND (lhs, 0) != arg)
continue;
fld = TREE_OPERAND (lhs, 1);
if (!method && fld == method_field)
{ {
cst_decl = TREE_OPERAND (arg, 0); if (TREE_CODE (rhs) == ADDR_EXPR
if (TREE_CODE (DECL_INITIAL (cst_decl)) == INTEGER_CST && TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL
|| TREE_CODE (DECL_INITIAL (cst_decl)) == REAL_CST && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE)
|| TREE_CODE (DECL_INITIAL (cst_decl)) == FIXED_CST)
{ {
args->jump_functions[arg_num].type = IPA_CONST_REF; method = TREE_OPERAND (rhs, 0);
args->jump_functions[arg_num].value.constant = cst_decl; if (delta)
{
fill_member_ptr_cst_jump_function (jfunc, method, delta);
return;
}
} }
else
return;
}
if (!delta && fld == delta_field)
{
if (TREE_CODE (rhs) == INTEGER_CST)
{
delta = rhs;
if (method)
{
fill_member_ptr_cst_jump_function (jfunc, method, delta);
return;
}
}
else
return;
}
}
return;
}
/* Go through the arguments of the call in CALL_STMT and for every member
pointer within tries determine whether it is a constant. If it is, create a
corresponding constant jump function in FUNCTIONS which is an array of jump
functions associated with the call. */
static void
compute_cst_member_ptr_arguments (struct ipa_jump_func *functions,
tree call_stmt)
{
call_expr_arg_iterator iter;
int num = 0;
tree call = get_call_expr_in (call_stmt);
tree arg, method_field, delta_field;
FOR_EACH_CALL_EXPR_ARG (arg, iter, call)
{
if (functions[num].type == IPA_UNKNOWN
&& type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
&delta_field))
determine_cst_member_ptr (call_stmt, arg, method_field,
delta_field, &functions[num]);
num++;
}
}
/* Compute jump function for all arguments of callsite CS and insert the
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);
tree call;
if (ipa_get_cs_argument_count (arguments) == 0 || arguments->jump_functions)
return;
arguments->jump_functions = XCNEWVEC (struct ipa_jump_func,
ipa_get_cs_argument_count (arguments));
call = get_call_expr_in (cs->call_stmt);
/* We will deal with constants and SSA scalars first: */
compute_scalar_jump_functions (info, arguments->jump_functions, call);
/* Let's check whether there are any potential member pointers and if so,
whether we can determine their functions as pass_through. */
if (!compute_pass_through_member_ptrs (info, arguments->jump_functions, call))
return;
/* Finally, let's check whether we actually pass a new constant membeer
pointer here... */
compute_cst_member_ptr_arguments (arguments->jump_functions, cs->call_stmt);
}
/* If RHS looks like a rhs of a statement loading pfn from a member pointer
formal parameter, return the parameter, otherwise return NULL. */
static tree
ipa_get_member_ptr_load_param (tree rhs)
{
tree rec, fld;
tree ptr_field;
if (TREE_CODE (rhs) != COMPONENT_REF)
return NULL_TREE;
rec = TREE_OPERAND (rhs, 0);
if (TREE_CODE (rec) != PARM_DECL
|| !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, NULL))
return NULL_TREE;
fld = TREE_OPERAND (rhs, 1);
if (fld == ptr_field)
return rec;
else
return NULL_TREE;
}
/* If STMT looks like a statement loading a value from a member pointer formal
parameter, this function retuns that parameter. */
static tree
ipa_get_stmt_member_ptr_load_param (tree stmt)
{
tree rhs;
if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
return NULL_TREE;
rhs = GIMPLE_STMT_OPERAND (stmt, 1);
return ipa_get_member_ptr_load_param (rhs);
}
/* Returns true iff T is an SSA_NAME defined by a statement. */
static bool
ipa_is_ssa_with_stmt_def (tree t)
{
if (TREE_CODE (t) == SSA_NAME
&& !SSA_NAME_IS_DEFAULT_DEF (t))
return true;
else
return false;
}
/* Creates a new note describing a call to a parameter number FORMAL_ID and
attaches it to the linked list of INFO. It also sets the called flag of the
parameter. STMT is the corresponding call statement. */
static void
ipa_note_param_call (struct ipa_node_params *info, int formal_id,
tree stmt)
{
struct ipa_param_call_note *note;
basic_block bb = bb_for_stmt (stmt);
info->param_flags[formal_id].called = 1;
note = XCNEW (struct ipa_param_call_note);
note->formal_id = formal_id;
note->stmt = stmt;
note->count = bb->count;
note->frequency = compute_call_stmt_bb_frequency (bb);
note->next = info->param_calls;
info->param_calls = note;
return;
}
/* Analyze the CALL (which itself must be a part of statement STMT) and examine
uses of formal parameters of the caller (described by INFO). Currently it
checks whether the call calls a pointer that is a formal parameter and if
so, the parameter is marked with the called flag and a note describing the
call is created. This is very simple for ordinary pointers represented in
SSA but not-so-nice when it comes to member pointers. The ugly part of this
function does nothing more than tries to match the pattern of such a call.
An example of such a pattern is the gimple dump below, the call is on the
last line:
<bb 2>:
f$__delta_5 = f.__delta;
f$__pfn_24 = f.__pfn;
D.2496_3 = (int) f$__pfn_24;
D.2497_4 = D.2496_3 & 1;
if (D.2497_4 != 0)
goto <bb 3>;
else
goto <bb 4>;
<bb 3>:
D.2500_7 = (unsigned int) f$__delta_5;
D.2501_8 = &S + D.2500_7;
D.2502_9 = (int (*__vtbl_ptr_type) (void) * *) D.2501_8;
D.2503_10 = *D.2502_9;
D.2504_12 = f$__pfn_24 + -1;
D.2505_13 = (unsigned int) D.2504_12;
D.2506_14 = D.2503_10 + D.2505_13;
D.2507_15 = *D.2506_14;
iftmp.11_16 = (String:: *) D.2507_15;
<bb 4>:
# iftmp.11_1 = PHI <iftmp.11_16(3), f$__pfn_24(2)>
D.2500_19 = (unsigned int) f$__delta_5;
D.2508_20 = &S + D.2500_19;
D.2493_21 = iftmp.11_1 (D.2508_20, 4);
Such patterns are results of simple calls to a member pointer:
int doprinting (int (MyString::* f)(int) const)
{
MyString S ("somestring");
return (S.*f)(4);
}
*/
static void
ipa_analyze_call_uses (struct ipa_node_params *info, tree call, tree stmt)
{
tree target = CALL_EXPR_FN (call);
tree var, def;
tree n1, n2;
tree d1, d2;
tree rec, rec2;
tree branch, cond;
int index;
basic_block bb, virt_bb, join;
if (TREE_CODE (target) != SSA_NAME)
return;
var = SSA_NAME_VAR (target);
if (SSA_NAME_IS_DEFAULT_DEF (target))
{
/* assuming TREE_CODE (var) == PARM_DECL */
index = ipa_get_param_decl_index (info, var);
if (index >= 0)
ipa_note_param_call (info, index, stmt);
return;
}
/* Now we need to try to match the complex pattern of calling a member
pointer. */
if (!POINTER_TYPE_P (TREE_TYPE (target))
|| TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
return;
def = SSA_NAME_DEF_STMT (target);
if (TREE_CODE (def) != PHI_NODE)
return;
if (PHI_NUM_ARGS (def) != 2)
return;
/* First, we need to check whether one of these is a load from a member
pointer that is a parameter to this function. */
n1 = PHI_ARG_DEF (def, 0);
n2 = PHI_ARG_DEF (def, 1);
if (SSA_NAME_IS_DEFAULT_DEF (n1) || SSA_NAME_IS_DEFAULT_DEF (n2))
return;
d1 = SSA_NAME_DEF_STMT (n1);
d2 = SSA_NAME_DEF_STMT (n2);
if ((rec = ipa_get_stmt_member_ptr_load_param (d1)))
{
if (ipa_get_stmt_member_ptr_load_param (d2))
return;
bb = bb_for_stmt (d1);
virt_bb = bb_for_stmt (d2);
}
else if ((rec = ipa_get_stmt_member_ptr_load_param (d2)))
{
bb = bb_for_stmt (d2);
virt_bb = bb_for_stmt (d1);
}
else
return;
/* Second, we need to check that the basic blocks are laid out in the way
corresponding to the pattern. */
join = bb_for_stmt (def);
if (!single_pred_p (virt_bb) || !single_succ_p (virt_bb)
|| single_pred (virt_bb) != bb
|| single_succ (virt_bb) != join)
return;
/* Third, let's see that the branching is done depending on the least
significant bit of the pfn. */
branch = last_stmt (bb);
if (TREE_CODE (branch) != COND_EXPR)
return;
cond = TREE_OPERAND (branch, 0);
if (TREE_CODE (cond) != NE_EXPR
|| !integer_zerop (TREE_OPERAND (cond, 1)))
return;
cond = TREE_OPERAND (cond, 0);
if (!ipa_is_ssa_with_stmt_def (cond))
return;
cond = SSA_NAME_DEF_STMT (cond);
if (TREE_CODE (cond) != GIMPLE_MODIFY_STMT)
return;
cond = GIMPLE_STMT_OPERAND (cond, 1);
if (TREE_CODE (cond) != BIT_AND_EXPR
|| !integer_onep (TREE_OPERAND (cond, 1)))
return;
cond = TREE_OPERAND (cond, 0);
if (!ipa_is_ssa_with_stmt_def (cond))
return;
cond = SSA_NAME_DEF_STMT (cond);
if (TREE_CODE (cond) != GIMPLE_MODIFY_STMT)
return;
cond = GIMPLE_STMT_OPERAND (cond, 1);
if (TREE_CODE (cond) == NOP_EXPR)
{
cond = TREE_OPERAND (cond, 0);
if (!ipa_is_ssa_with_stmt_def (cond))
return;
cond = SSA_NAME_DEF_STMT (cond);
if (TREE_CODE (cond) != GIMPLE_MODIFY_STMT)
return;
cond = GIMPLE_STMT_OPERAND (cond, 1);
}
rec2 = ipa_get_member_ptr_load_param (cond);
if (rec != rec2)
return;
index = ipa_get_param_decl_index (info, rec);
if (index >= 0 && !ipa_is_ith_param_modified (info, index))
ipa_note_param_call (info, index, stmt);
return;
}
/* Analyze the statement STMT with respect to formal parameters (described in
INFO) and their uses. Currently it only checks whether formal parameters
are called. */
static void
ipa_analyze_stmt_uses (struct ipa_node_params *info, tree stmt)
{
tree call = get_call_expr_in (stmt);
if (call)
ipa_analyze_call_uses (info, call, stmt);
}
/* Scan the function body of NODE and inspect the uses of formal parameters.
Store the findings in various structures of the associated ipa_node_params
structure, such as parameter flags, notes etc. */
void
ipa_analyze_params_uses (struct cgraph_node *node)
{
tree decl = node->decl;
basic_block bb;
struct function *func;
block_stmt_iterator bsi;
struct ipa_node_params *info = IPA_NODE_REF (node);
if (ipa_get_param_count (info) == 0 || info->uses_analysis_done
|| !DECL_SAVED_TREE (decl))
return;
if (!info->param_flags)
info->param_flags = XCNEWVEC (struct ipa_param_flags,
ipa_get_param_count (info));
func = DECL_STRUCT_FUNCTION (decl);
FOR_EACH_BB_FN (bb, func)
{
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
tree stmt = bsi_stmt (bsi);
ipa_analyze_stmt_uses (info, stmt);
} }
else
args->jump_functions[arg_num].type = IPA_UNKNOWN;
arg_num++;
} }
info->uses_analysis_done = 1;
}
/* Update the jump functions assocated with call graph edge E when the call
graph edge CS is being inlined, assuming that E->caller is already (possibly
indirectly) inlined into CS->callee and that E has not been inlined. */
static void
update_jump_functions_after_inlining (struct cgraph_edge *cs,
struct cgraph_edge *e)
{
struct ipa_edge_args *top = IPA_EDGE_REF (cs);
struct ipa_edge_args *args = IPA_EDGE_REF (e);
int count = ipa_get_cs_argument_count (args);
int i;
for (i = 0; i < count; i++)
{
struct ipa_jump_func *src, *dst = ipa_get_ith_jump_func (args, i);
if (dst->type != IPA_PASS_THROUGH)
continue;
/* We must check range due to calls with variable number of arguments: */
if (dst->value.formal_id >= (unsigned) ipa_get_cs_argument_count (top))
{
dst->type = IPA_BOTTOM;
continue;
}
src = ipa_get_ith_jump_func (top, dst->value.formal_id);
*dst = *src;
}
}
/* Print out a debug message to file F that we have discovered that an indirect
call descibed by NT is in fact a call of a known constant function descibed
by JFUNC. NODE is the node where the call is. */
static void
print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt,
struct ipa_jump_func *jfunc,
struct cgraph_node *node)
{
fprintf (f, "ipa-prop: Discovered an indirect call to a known target (");
if (jfunc->type == IPA_CONST_MEMBER_PTR)
{
print_node_brief (f, "", jfunc->value.member_cst.pfn, 0);
print_node_brief (f, ", ", jfunc->value.member_cst.delta, 0);
}
else
print_node_brief(f, "", jfunc->value.constant, 0);
fprintf (f, ") in %s: ", cgraph_node_name (node));
print_generic_stmt (f, nt->stmt, 2);
}
/* Update the param called notes associated with NODE when CS is being inlined,
assuming NODE is (potentially indirectly) inlined into CS->callee.
Moreover, if the callee is discovered to be constant, create a new cgraph
edge for it. Newly discovered indirect edges will be added to NEW_EDGES,
unless it is NULL. */
static void
update_call_notes_after_inlining (struct cgraph_edge *cs,
struct cgraph_node *node,
VEC (cgraph_edge_p, heap) *new_edges)
{
struct ipa_node_params *info = IPA_NODE_REF (node);
struct ipa_edge_args *top = IPA_EDGE_REF (cs);
struct ipa_param_call_note *nt;
for (nt = info->param_calls; nt; nt = nt->next)
{
struct ipa_jump_func *jfunc;
if (nt->processed)
continue;
/* We must check range due to calls with variable number of arguments: */
if (nt->formal_id >= (unsigned) ipa_get_cs_argument_count (top))
{
nt->processed = true;
continue;
}
jfunc = ipa_get_ith_jump_func (top, nt->formal_id);
if (jfunc->type == IPA_PASS_THROUGH)
nt->formal_id = jfunc->value.formal_id;
else if (jfunc->type == IPA_CONST || jfunc->type == IPA_CONST_MEMBER_PTR)
{
struct cgraph_node *callee;
struct cgraph_edge *new_indirect_edge;
tree decl;
nt->processed = true;
if (jfunc->type == IPA_CONST_MEMBER_PTR)
decl = jfunc->value.member_cst.pfn;
else
decl = jfunc->value.constant;
if (TREE_CODE (decl) != FUNCTION_DECL)
continue;
callee = cgraph_node (decl);
if (!callee || !callee->local.inlinable)
continue;
if (dump_file)
print_edge_addition_message (dump_file, nt, jfunc, node);
new_indirect_edge = cgraph_create_edge (node, callee, nt->stmt,
nt->count, nt->frequency,
nt->loop_nest);
new_indirect_edge->indirect_call = 1;
ipa_check_create_edge_args ();
if (new_edges)
VEC_safe_push (cgraph_edge_p, heap, new_edges, new_indirect_edge);
}
}
}
/* Recursively traverse subtree of NODE (including node) made of inlined
cgraph_edges when CS has been inlined and invoke
update_call_notes_after_inlining on all nodes and
update_jump_functions_after_inlining on all non-inlined edges that lead out
of this subtree. Newly discovered indirect edges will be added to
NEW_EDGES, unless it is NULL. */
static void
propagate_info_to_inlined_callees (struct cgraph_edge *cs,
struct cgraph_node *node,
VEC (cgraph_edge_p, heap) *new_edges)
{
struct cgraph_edge *e;
update_call_notes_after_inlining (cs, node, new_edges);
for (e = node->callees; e; e = e->next_callee)
if (!e->inline_failed)
propagate_info_to_inlined_callees (cs, e->callee, new_edges);
else
update_jump_functions_after_inlining (cs, e);
}
/* 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
NEW_EDGES, unless it is NULL. */
void
ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
VEC (cgraph_edge_p, heap) *new_edges)
{
propagate_info_to_inlined_callees (cs, cs->callee, new_edges);
} }
/* Frees all dynamically allocated structures that the argument info points /* Frees all dynamically allocated structures that the argument info points
...@@ -371,8 +1053,15 @@ ipa_free_node_params_substructures (struct ipa_node_params *info) ...@@ -371,8 +1053,15 @@ ipa_free_node_params_substructures (struct ipa_node_params *info)
free (info->ipcp_lattices); free (info->ipcp_lattices);
if (info->param_decls) if (info->param_decls)
free (info->param_decls); free (info->param_decls);
if (info->modified_flags) if (info->param_flags)
free (info->modified_flags); free (info->param_flags);
while (info->param_calls)
{
struct ipa_param_call_note *note = info->param_calls;
info->param_calls = note->next;
free (note);
}
memset (info, 0, sizeof (*info)); memset (info, 0, sizeof (*info));
} }
...@@ -451,6 +1140,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, ...@@ -451,6 +1140,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
void *data) void *data)
{ {
struct ipa_node_params *old_info, *new_info; struct ipa_node_params *old_info, *new_info;
struct ipa_param_call_note *note;
int param_count; int param_count;
ipa_check_create_node_params (); ipa_check_create_node_params ();
...@@ -464,12 +1154,24 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, ...@@ -464,12 +1154,24 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
sizeof (struct ipcp_lattice) * param_count); sizeof (struct ipcp_lattice) * param_count);
new_info->param_decls = (tree *) new_info->param_decls = (tree *)
duplicate_array (old_info->param_decls, sizeof (tree) * param_count); duplicate_array (old_info->param_decls, sizeof (tree) * param_count);
new_info->modified_flags = (bool *) new_info->param_flags = (struct ipa_param_flags *)
duplicate_array (old_info->modified_flags, sizeof (bool) * param_count); duplicate_array (old_info->param_flags,
sizeof (struct ipa_param_flags) * param_count);
new_info->ipcp_orig_node = old_info->ipcp_orig_node; new_info->ipcp_orig_node = old_info->ipcp_orig_node;
new_info->count_scale = old_info->count_scale; new_info->count_scale = old_info->count_scale;
for (note = old_info->param_calls; note; note = note->next)
{
struct ipa_param_call_note *nn;
nn = (struct ipa_param_call_note *)
xcalloc (1, sizeof (struct ipa_param_call_note));
memcpy (nn, note, sizeof (struct ipa_param_call_note));
nn->next = new_info->param_calls;
new_info->param_calls = nn;
}
data = data; /* Suppressing compiler warning. */ data = data; /* Suppressing compiler warning. */
} }
...@@ -510,6 +1212,19 @@ ipa_unregister_cgraph_hooks (void) ...@@ -510,6 +1212,19 @@ ipa_unregister_cgraph_hooks (void)
void void
free_all_ipa_structures_after_ipa_cp (void) free_all_ipa_structures_after_ipa_cp (void)
{ {
if (!flag_indirect_inlining || !flag_inline_trees)
{
ipa_free_all_edge_args ();
ipa_free_all_node_params ();
ipa_unregister_cgraph_hooks ();
}
}
/* Free all ipa_node_params and all ipa_edge_args structures if they are no
longer needed after indirect inlining. */
void
free_all_ipa_structures_after_iinln (void)
{
ipa_free_all_edge_args (); ipa_free_all_edge_args ();
ipa_free_all_node_params (); ipa_free_all_node_params ();
ipa_unregister_cgraph_hooks (); ipa_unregister_cgraph_hooks ();
...@@ -545,33 +1260,37 @@ ipa_print_all_tree_maps (FILE * f) ...@@ -545,33 +1260,37 @@ ipa_print_all_tree_maps (FILE * f)
} }
} }
/* Print modified_flags data structures of all functions in the /* Print param_flags data structures of the NODE to F. */
callgraph to F. */
void void
ipa_print_all_params_modified (FILE * f) ipa_print_node_param_flags (FILE * f, struct cgraph_node *node)
{ {
int i, count; int i, count;
bool temp; struct ipa_node_params *info;
struct cgraph_node *node;
fprintf (f, "\nMODIFY PRINT\n"); if (!node->analyzed)
for (node = cgraph_nodes; node; node = node->next) return;
info = IPA_NODE_REF (node);
fprintf (f, "PARAM FLAGS of function %s: \n", cgraph_node_name (node));
count = ipa_get_param_count (info);
for (i = 0; i < count; i++)
{ {
struct ipa_node_params *info; fprintf (f, " param %d flags:", i);
if (ipa_is_ith_param_modified (info, i))
if (!node->analyzed) fprintf (f, " modified");
continue; if (ipa_is_ith_param_called (info, i))
info = IPA_NODE_REF (node); fprintf (f, " called");
fprintf (f, "function %s :: \n", cgraph_node_name (node)); fprintf (f, "\n");
count = ipa_get_param_count (info);
for (i = 0; i < count; i++)
{
temp = info->modified_flags[i];
if (temp)
fprintf (f, " param [%d] true \n", i);
else
fprintf (f, " param [%d] false \n", i);
}
} }
} }
/* Print param_flags data structures of all functions in the
callgraph to F. */
void
ipa_print_all_param_flags (FILE * f)
{
struct cgraph_node *node;
fprintf (f, "\nIPA PARAM FLAGS DUMP\n");
for (node = cgraph_nodes; node; node = node->next)
ipa_print_node_param_flags (f, node);
}
...@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h" #include "tree.h"
#include "vec.h" #include "vec.h"
#include "cgraph.h"
/* The following definitions and interfaces are used by /* The following definitions and interfaces are used by
interprocedural analyses. */ interprocedural analyses. */
...@@ -32,21 +33,23 @@ along with GCC; see the file COPYING3. If not see ...@@ -32,21 +33,23 @@ along with GCC; see the file COPYING3. If not see
Constant - a constant is passed as an actual argument. Constant - a constant is passed as an actual argument.
Unknown - neither of the above. Unknown - neither of the above.
Integer and real constants are represented as IPA_CONST and Fortran Integer and real constants are represented as IPA_CONST and Fortran
constants are represented as IPA_CONST_REF. */ constants are represented as IPA_CONST_REF. Finally, IPA_CONST_MEMBER_PTR
stands for C++ member pointers constants. */
enum jump_func_type enum jump_func_type
{ {
IPA_UNKNOWN, IPA_UNKNOWN = 0, /* newly allocated and zeroed jump functions default */
IPA_CONST, IPA_CONST,
IPA_CONST_REF, IPA_CONST_REF,
IPA_CONST_MEMBER_PTR,
IPA_PASS_THROUGH IPA_PASS_THROUGH
}; };
/* All formal parameters in the program have a lattice associated with it /* All formal parameters in the program have a lattice associated with it
computed by the interprocedural stage of IPCP. computed by the interprocedural stage of IPCP.
There are three main values of the lattice: There are three main values of the lattice:
TOP - unknown. IPA_TOP - unknown,
BOTTOM - non constant. IPA_BOTTOM - non constant,
CONSTANT_TYPE - constant value. IPA_CONST_VALUE - simple scalar constant,
Cval of formal f will have a constant value if all callsites to this Cval of formal f will have a constant value if all callsites to this
function have the same constant value passed to f. function have the same constant value passed to f.
Integer and real constants are represented as IPA_CONST and Fortran Integer and real constants are represented as IPA_CONST and Fortran
...@@ -59,14 +62,24 @@ enum ipa_lattice_type ...@@ -59,14 +62,24 @@ enum ipa_lattice_type
IPA_TOP IPA_TOP
}; };
/* Represents a value of a jump function. /* Structure holding a C++ member pointer constant. Holds a pointer to the
value represents a constant. method and delta offset. */
formal_id is used only in jump function context and represents struct ipa_member_ptr_cst
pass-through parameter (the formal of caller is passed as argument). */ {
tree pfn;
tree delta;
};
/* Represents a value of a jump function. formal_id is used only in jump
function context and represents pass-through parameter (the formal parameter
of the caller is passed as argument). constant represents the actual
constant in constant jump functions and member_cst holds constant c++ member
functions. */
union jump_func_value union jump_func_value
{ {
unsigned int formal_id; unsigned int formal_id;
tree constant; tree constant;
struct ipa_member_ptr_cst member_cst;
}; };
/* A jump function for a callsite represents the values passed as actual /* A jump function for a callsite represents the values passed as actual
...@@ -101,10 +114,43 @@ struct ipa_replace_map ...@@ -101,10 +114,43 @@ struct ipa_replace_map
bool ref_p; bool ref_p;
}; };
/* ipa_param_flags contains various flags that describe how the associated
parameter is treated within a function. */
struct ipa_param_flags
{
/* Whether the value parameter has been modified within the function. */
unsigned modified : 1;
/* Whether the parameter has been used as a call destination. */
unsigned called : 1;
};
/* Each instance of the following structure describes a statement that calls a
function parameter. Those referring to statements within the same function
are linked in a list. */
struct ipa_param_call_note
{
/* Linked list's next */
struct ipa_param_call_note *next;
/* Statement that contains the call to the parameter above. */
tree stmt;
/* Index of the parameter that is called. */
unsigned int formal_id;
/* Expected number of executions: calculated in profile.c. */
gcov_type count;
/* Expected frequency of executions within the function. see cgraph_edge in
cgraph.h for more on this. */
int frequency;
/* Depth of loop nest, 1 means no loop nest. */
int loop_nest;
/* Set when we have already found the target to be a compile time constant
and turned this into an edge or when the note was found unusable for some
reason. */
bool processed;
};
/* ipa_node_params stores information related to formal parameters of functions /* ipa_node_params stores information related to formal parameters of functions
and some other information for interprocedural passes that operate on and some other information for interprocedural passes that operate on
parameters (such as ipa-cp). */ parameters (such as ipa-cp). */
struct ipa_node_params struct ipa_node_params
{ {
/* Number of formal parameters of this function. When set to 0, /* Number of formal parameters of this function. When set to 0,
...@@ -115,8 +161,10 @@ struct ipa_node_params ...@@ -115,8 +161,10 @@ struct ipa_node_params
struct ipcp_lattice *ipcp_lattices; struct ipcp_lattice *ipcp_lattices;
/* Mapping each parameter to its PARM_DECL tree. */ /* Mapping each parameter to its PARM_DECL tree. */
tree *param_decls; tree *param_decls;
/* Indicating which parameter is modified in its function. */ /* Various flags describing individual parameters. */
bool *modified_flags; struct ipa_param_flags *param_flags;
/* List of structures enumerating calls to a formal parameter. */
struct ipa_param_call_note *param_calls;
/* Only for versioned nodes this field would not be NULL, /* Only for versioned nodes this field would not be NULL,
it points to the node that IPA cp cloned from. */ it points to the node that IPA cp cloned from. */
struct cgraph_node *ipcp_orig_node; struct cgraph_node *ipcp_orig_node;
...@@ -130,6 +178,10 @@ struct ipa_node_params ...@@ -130,6 +178,10 @@ struct ipa_node_params
/* Whether this function is called with variable number of actual /* Whether this function is called with variable number of actual
arguments. */ arguments. */
unsigned called_with_var_arguments : 1; unsigned called_with_var_arguments : 1;
/* Whether the modification analysis has already been performed. */
unsigned modification_analysis_done : 1;
/* Whether the param uses analysis has already been performed. */
unsigned uses_analysis_done : 1;
}; };
/* ipa_node_params access functions. Please use these to access fields that /* ipa_node_params access functions. Please use these to access fields that
...@@ -164,7 +216,16 @@ ipa_get_ith_param (struct ipa_node_params *info, int i) ...@@ -164,7 +216,16 @@ ipa_get_ith_param (struct ipa_node_params *info, int i)
static inline bool static inline bool
ipa_is_ith_param_modified (struct ipa_node_params *info, int i) ipa_is_ith_param_modified (struct ipa_node_params *info, int i)
{ {
return info->modified_flags[i]; return info->param_flags[i].modified;
}
/* Returns the called flag corresponding o the ith paramterer. Note there is
no setter method as the goal is to set all flags when building the array in
ipa_detect_called_params. */
static inline bool
ipa_is_ith_param_called (struct ipa_node_params *info, int i)
{
return info->param_flags[i].called;
} }
/* Flag this node as having callers with variable number of arguments. */ /* Flag this node as having callers with variable number of arguments. */
...@@ -255,6 +316,7 @@ void ipa_free_node_params_substructures (struct ipa_node_params *); ...@@ -255,6 +316,7 @@ void ipa_free_node_params_substructures (struct ipa_node_params *);
void ipa_free_all_node_params (void); void ipa_free_all_node_params (void);
void ipa_free_all_edge_args (void); void ipa_free_all_edge_args (void);
void free_all_ipa_structures_after_ipa_cp (void); void free_all_ipa_structures_after_ipa_cp (void);
void free_all_ipa_structures_after_iinln (void);
void ipa_register_cgraph_hooks (void); void ipa_register_cgraph_hooks (void);
/* This function ensures the array of node param infos is big enough to /* This function ensures the array of node param infos is big enough to
...@@ -318,9 +380,15 @@ void ipa_count_arguments (struct cgraph_edge *); ...@@ -318,9 +380,15 @@ void ipa_count_arguments (struct cgraph_edge *);
void ipa_count_formal_params (struct cgraph_node *); void ipa_count_formal_params (struct cgraph_node *);
void ipa_create_param_decls_array (struct cgraph_node *); void ipa_create_param_decls_array (struct cgraph_node *);
void ipa_detect_param_modifications (struct cgraph_node *); void ipa_detect_param_modifications (struct cgraph_node *);
void ipa_analyze_params_uses (struct cgraph_node *);
void ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
VEC (cgraph_edge_p, heap) *new_edges);
/* Debugging interface. */ /* Debugging interface. */
void ipa_print_all_tree_maps (FILE *); void ipa_print_all_tree_maps (FILE *);
void ipa_print_all_params_modified (FILE *); void ipa_print_node_param_flags (FILE * f, struct cgraph_node *node);
void ipa_print_all_param_flags (FILE *);
void ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node);
void ipa_print_all_jump_functions (FILE * f);
#endif /* IPA_PROP_H */ #endif /* IPA_PROP_H */
...@@ -934,6 +934,7 @@ decode_options (unsigned int argc, const char **argv) ...@@ -934,6 +934,7 @@ decode_options (unsigned int argc, const char **argv)
/* -O2 optimizations. */ /* -O2 optimizations. */
opt2 = (optimize >= 2); opt2 = (optimize >= 2);
flag_inline_small_functions = opt2; flag_inline_small_functions = opt2;
flag_indirect_inlining = opt2;
flag_thread_jumps = opt2; flag_thread_jumps = opt2;
flag_crossjumping = opt2; flag_crossjumping = opt2;
flag_optimize_sibling_calls = opt2; flag_optimize_sibling_calls = opt2;
......
/* Verify that simple indirect calls are inlined even without early
inlining.. */
/* { dg-do compile } */
/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining" } */
extern void non_existent (const char *, int);
class String
{
private:
const char *data;
public:
String (const char *d) : data(d)
{}
int funcOne (int delim) const;
int printStuffTwice (int delim) const;
};
int String::funcOne (int delim) const
{
int i;
for (i = 0; i < delim; i++)
non_existent(data, i);
return 1;
}
int docalling (int (String::* f)(int delim) const)
{
String S ("muhehehe");
return (S.*f)(4);
}
int main (int argc, char *argv[])
{
int i;
i = docalling (&String::funcOne);
non_existent ("done", i);
return 0;
}
/* { dg-final { scan-ipa-dump "String::funcOne\[^\\n\]*inline copy in int main" "inline" } } */
/* { dg-final { cleanup-tree-dump "inline" } } */
/* Verify that simple indirect calls are inlined even without early
inlining.. */
/* { dg-do compile } */
/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining" } */
extern void non_existent(int);
static void hooray ()
{
non_existent (1);
}
static void hiphip (void (*f)())
{
non_existent (2);
f ();
}
int main (int argc, int *argv[])
{
hiphip (hooray);
return 0;
}
/* { dg-final { scan-ipa-dump "hooray\[^\\n\]*inline copy in main" "inline" } } */
/* { dg-final { cleanup-tree-dump "inline" } } */
/* Verify that modification analysis detects modfications. */
/* { dg-do compile } */
/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining" } */
struct whatever
{
int first;
unsigned second;
};
void func1 (struct whatever w);
void func2 (struct whatever *pw);
void func3 (int i);
void func4 (int *pi);
void the_test (struct whatever u, struct whatever v,
struct whatever w, struct whatever x,
int i, int j, int k, int l)
{
struct whatever *pw = &w;
int *pk = &k;
j = l+3;
v.first = 9;
func1 (u);
func1 (v);
func2 (pw);
func2 (&x);
func3 (i);
func3 (j);
func4 (pk);
func4 (&l);
}
/* { dg-final { scan-ipa-dump-not "param 0 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 1 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 2 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 3 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump-not "param 4 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 5 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 6 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { scan-ipa-dump "param 7 flags:\[^\\n\]*modified" "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
...@@ -951,7 +951,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, ...@@ -951,7 +951,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
pointer_set_insert (id->statements_to_fold, stmt); pointer_set_insert (id->statements_to_fold, stmt);
/* We're duplicating a CALL_EXPR. Find any corresponding /* We're duplicating a CALL_EXPR. Find any corresponding
callgraph edges and update or duplicate them. */ callgraph edges and update or duplicate them. */
if (call && (decl = get_callee_fndecl (call))) if (call)
{ {
struct cgraph_node *node; struct cgraph_node *node;
struct cgraph_edge *edge; struct cgraph_edge *edge;
...@@ -962,7 +962,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, ...@@ -962,7 +962,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
edge = cgraph_edge (id->src_node, orig_stmt); edge = cgraph_edge (id->src_node, orig_stmt);
if (edge) if (edge)
cgraph_clone_edge (edge, id->dst_node, stmt, cgraph_clone_edge (edge, id->dst_node, stmt,
REG_BR_PROB_BASE, 1, edge->frequency, true); REG_BR_PROB_BASE, 1,
edge->frequency, true);
break; break;
case CB_CGE_MOVE_CLONES: case CB_CGE_MOVE_CLONES:
...@@ -971,8 +972,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, ...@@ -971,8 +972,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale,
node = node->next_clone) node = node->next_clone)
{ {
edge = cgraph_edge (node, orig_stmt); edge = cgraph_edge (node, orig_stmt);
gcc_assert (edge); if (edge)
cgraph_set_call_stmt (edge, stmt); cgraph_set_call_stmt (edge, stmt);
} }
/* FALLTHRU */ /* FALLTHRU */
...@@ -2580,6 +2581,20 @@ add_lexical_block (tree current_block, tree new_block) ...@@ -2580,6 +2581,20 @@ add_lexical_block (tree current_block, tree new_block)
BLOCK_SUPERCONTEXT (new_block) = current_block; BLOCK_SUPERCONTEXT (new_block) = current_block;
} }
/* Fetch callee declaration from the call graph edge going from NODE and
associated with STMR call statement. Return NULL_TREE if not found. */
static tree
get_indirect_callee_fndecl (struct cgraph_node *node, tree stmt)
{
struct cgraph_edge *cs;
cs = cgraph_edge (node, stmt);
if (cs)
return cs->callee->decl;
return NULL_TREE;
}
/* If *TP is a CALL_EXPR, replace it with its inline expansion. */ /* If *TP is a CALL_EXPR, replace it with its inline expansion. */
static bool static bool
...@@ -2621,7 +2636,11 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) ...@@ -2621,7 +2636,11 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
If we cannot, then there is no hope of inlining the function. */ If we cannot, then there is no hope of inlining the function. */
fn = get_callee_fndecl (t); fn = get_callee_fndecl (t);
if (!fn) if (!fn)
goto egress; {
fn = get_indirect_callee_fndecl (id->dst_node, stmt);
if (!fn)
goto egress;
}
/* Turn forward declarations into real ones. */ /* Turn forward declarations into real ones. */
fn = cgraph_node (fn)->decl; fn = cgraph_node (fn)->decl;
...@@ -2672,6 +2691,12 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) ...@@ -2672,6 +2691,12 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
inlining. */ inlining. */
if (!cgraph_inline_p (cg_edge, &reason)) if (!cgraph_inline_p (cg_edge, &reason))
{ {
/* If this call was originally indirect, we do not want to emit any
inlining related warnings or sorry messages because there are no
guarantees regarding those. */
if (cg_edge->indirect_call)
goto egress;
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
/* Avoid warnings during early inline pass. */ /* Avoid warnings during early inline pass. */
&& (!flag_unit_at_a_time || cgraph_global_info_ready)) && (!flag_unit_at_a_time || cgraph_global_info_ready))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment