From 3e293154d69acf16281d5b9b038ca741c1cf53d6 Mon Sep 17 00:00:00 2001 From: Martin Jambor Date: Wed, 23 Jul 2008 21:45:45 +0200 Subject: [PATCH] ipa-cp.c (ipcp_print_edge_profiles): Test for node->analyzed rather than for DECL_SAVED_TREE. 2008-07-23 Martin Jambor * 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 --- gcc/ChangeLog | 127 ++++ gcc/Makefile.in | 8 +- gcc/cgraph.c | 6 + gcc/cgraph.h | 5 +- gcc/cgraphbuild.c | 44 +- gcc/cgraphunit.c | 2 +- gcc/common.opt | 4 + gcc/doc/invoke.texi | 18 +- gcc/ipa-cp.c | 96 +-- gcc/ipa-inline.c | 82 +- gcc/ipa-prop.c | 1027 ++++++++++++++++++++++---- gcc/ipa-prop.h | 96 ++- gcc/opts.c | 1 + gcc/testsuite/g++.dg/ipa/iinline-1.C | 47 ++ gcc/testsuite/gcc.dg/ipa/iinline-1.c | 26 + gcc/testsuite/gcc.dg/ipa/modif-1.c | 44 ++ gcc/tree-inline.c | 35 +- 17 files changed, 1393 insertions(+), 275 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/iinline-1.C create mode 100644 gcc/testsuite/gcc.dg/ipa/iinline-1.c create mode 100644 gcc/testsuite/gcc.dg/ipa/modif-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9251ca3ca65..b79791e58e7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,130 @@ +2008-07-23 Martin Jambor + + * 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 PR 36907 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 28d4f5bdcfc..f73686f0300 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -860,7 +860,7 @@ TREE_INLINE_H = tree-inline.h $(VARRAY_H) pointer-set.h REAL_H = real.h $(MACHMODE_H) DBGCNT_H = dbgcnt.h dbgcnt.def 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 BITMAP_H = bitmap.h $(HASHTAB_H) statistics.h @@ -2564,8 +2564,8 @@ varpool.o : varpool.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_FLOW_H) gt-varpool.h ipa.o : ipa.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(CGRAPH_H) \ tree-pass.h $(TIMEVAR_H) -ipa-prop.o : ipa-prop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ - langhooks.h $(GGC_H) $(TARGET_H) $(CGRAPH_H) $(IPA_PROP_H) \ +ipa-prop.o : ipa-prop.c $(CONFIG_H) $(SYSTEM_H) coretypes.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) \ $(TIMEVAR_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 \ 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 \ $(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) \ 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) \ diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 3b3be13084e..14d05878808 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -625,6 +625,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee, gcc_assert (freq >= 0); gcc_assert (freq <= CGRAPH_FREQ_MAX); edge->loop_nest = nest; + edge->indirect_call = 0; edge->uid = cgraph_edge_max_uid++; if (caller->call_site_hash) { @@ -1048,6 +1049,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) edge->frequency / (double)CGRAPH_FREQ_BASE); if (!edge->inline_failed) fprintf(f, "(inlined) "); + if (edge->indirect_call) + fprintf(f, "(indirect) "); } fprintf (f, "\n calls: "); @@ -1057,6 +1060,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node) edge->callee->uid); if (!edge->inline_failed) fprintf(f, "(inlined) "); + if (edge->indirect_call) + fprintf(f, "(indirect) "); if (edge->count) fprintf (f, "("HOST_WIDEST_INT_PRINT_DEC"x) ", (HOST_WIDEST_INT)edge->count); @@ -1166,6 +1171,7 @@ cgraph_clone_edge (struct cgraph_edge *e, struct cgraph_node *n, e->loop_nest + loop_nest); new->inline_failed = e->inline_failed; + new->indirect_call = e->indirect_call; if (update_original) { e->count -= new->count; diff --git a/gcc/cgraph.h b/gcc/cgraph.h index b817f87f7f0..f36f6f577a2 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -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. */ int frequency; /* 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. */ int uid; }; @@ -376,6 +378,7 @@ void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *); /* In cgraphbuild.c */ unsigned int rebuild_cgraph_edges (void); +int compute_call_stmt_bb_frequency (basic_block bb); /* In ipa.c */ bool cgraph_remove_unreachable_nodes (bool, FILE *); diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c index 19e198344b6..23cfde892ef 100644 --- a/gcc/cgraphbuild.c +++ b/gcc/cgraphbuild.c @@ -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. Also look for functions and variables having addresses taken. */ @@ -133,10 +152,6 @@ build_cgraph_edges (void) struct pointer_set_t *visited_nodes = pointer_set_create (); block_stmt_iterator bsi; 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. body. */ @@ -151,12 +166,8 @@ build_cgraph_edges (void) { int i; 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, - bb->count, freq, + bb->count, compute_call_stmt_bb_frequency (bb), bb->loop_depth); for (i = 0; i < n; i++) walk_tree (&CALL_EXPR_ARG (call, i), @@ -227,10 +238,6 @@ rebuild_cgraph_edges (void) basic_block bb; struct cgraph_node *node = cgraph_node (current_function_decl); block_stmt_iterator bsi; - int entry_freq = ENTRY_BLOCK_PTR->frequency; - - if (!entry_freq) - entry_freq = 1; cgraph_node_remove_callees (node); @@ -244,14 +251,9 @@ rebuild_cgraph_edges (void) tree decl; if (call && (decl = get_callee_fndecl (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, - bb->count, freq, bb->loop_depth); - } + cgraph_create_edge (node, cgraph_node (decl), stmt, + bb->count, compute_call_stmt_bb_frequency (bb), + bb->loop_depth); } initialize_inline_failed (node); gcc_assert (!node->global.inlined_to); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 5994ad12a03..c9b072608d6 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -796,7 +796,7 @@ verify_cgraph_node (struct cgraph_node *node) 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", cgraph_node_name (e->caller), diff --git a/gcc/common.opt b/gcc/common.opt index 2196f749b35..5d730a8cbf4 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -571,6 +571,10 @@ finhibit-size-directive Common Report Var(flag_inhibit_size_directive) 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 ; as `static'. Prevents generation of zillions of copies of unused ; static inline functions; instead, `inlines' are written out diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f493bf710a9..bace891ffdc 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -328,8 +328,8 @@ Objective-C and Objective-C++ Dialects}. -fearly-inlining -fexpensive-optimizations -ffast-math @gol -ffinite-math-only -ffloat-store -fforward-propagate @gol -ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol --fgcse-sm -fif-conversion -fif-conversion2 -finline-functions @gol --finline-functions-called-once -finline-limit=@var{n} @gol +-fgcse-sm -fif-conversion -fif-conversion2 -findirect-inlining @gol +-finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol -finline-small-functions -fipa-cp -fipa-marix-reorg -fipa-pta @gol -fipa-pure-const -fipa-reference -fipa-struct-reorg @gol -fipa-type-escape -fivopts -fkeep-inline-functions -fkeep-static-consts @gol @@ -5199,6 +5199,7 @@ also turns on the following optimization flags: -fdelete-null-pointer-checks @gol -fexpensive-optimizations @gol -fgcse -fgcse-lm @gol +-findirect-inlining @gol -foptimize-sibling-calls @gol -fpeephole2 @gol -fregmove @gol @@ -5216,8 +5217,8 @@ invoking @option{-O2} on programs that use computed gotos. @item -O3 @opindex O3 -Optimize yet more. @option{-O3} turns on all optimizations specified by -@option{-O2} and also turns on the @option{-finline-functions}, +Optimize yet more. @option{-O3} turns on all optimizations specified +by @option{-O2} and also turns on the @option{-finline-functions}, @option{-funswitch-loops}, @option{-fpredictive-commoning}, @option{-fgcse-after-reload} and @option{-ftree-vectorize} options. @@ -5319,6 +5320,15 @@ in this way. 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 @opindex finline-functions Integrate all simple functions into their callers. The compiler diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 505f17d101c..92d12c44d91 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -183,6 +183,18 @@ ipcp_lat_is_const (struct ipcp_lattice *lat) 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. */ static inline bool ipcp_lats_are_equal (struct ipcp_lattice *lat1, struct ipcp_lattice *lat2) @@ -247,9 +259,7 @@ static void ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat, struct ipa_jump_func *jfunc) { - if (jfunc->type == IPA_UNKNOWN) - lat->type = IPA_BOTTOM; - else if (jfunc->type == IPA_CONST) + if (jfunc->type == IPA_CONST) { lat->type = IPA_CONST_VALUE; lat->constant = jfunc->value.constant; @@ -267,6 +277,8 @@ ipcp_lattice_from_jfunc (struct ipa_node_params *info, struct ipcp_lattice *lat, lat->type = caller_lat->type; lat->constant = caller_lat->constant; } + else + lat->type = IPA_BOTTOM; } /* True when OLD and NEW values are not the same. */ @@ -303,17 +315,18 @@ ipcp_print_all_lattices (FILE * f) for (i = 0; i < count; 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) { - fprintf (f, " param [%d]: ", i); fprintf (f, "type is CONST "); print_generic_expr (f, lat->constant, 0); fprintf (f, "\n"); } else if (lat->type == IPA_TOP) - fprintf (f, "param [%d]: type is TOP \n", i); + fprintf (f, "type is TOP\n"); 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) 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. */ static void ipcp_function_scale_print (FILE * f) @@ -664,7 +625,7 @@ ipcp_print_edge_profiles (FILE * f) for (node = cgraph_nodes; node; node = node->next) { fprintf (f, "function %s: \n", cgraph_node_name (node)); - if (DECL_SAVED_TREE (node->decl)) + if (node->analyzed) { bb = ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (node->decl)); @@ -751,8 +712,8 @@ ipcp_print_all_structures (FILE * f) ipcp_print_all_lattices (f); ipcp_function_scale_print (f); ipa_print_all_tree_maps (f); - ipa_print_all_params_modified (f); - ipcp_print_all_jump_functions (f); + ipa_print_all_param_flags (f); + ipa_print_all_jump_functions (f); } /* Print profile info for all functions. */ @@ -781,10 +742,8 @@ ipcp_create_replace_map (struct function *func, tree parm_tree, tree const_val; replace_map = XCNEW (struct ipa_replace_map); - gcc_assert (ipcp_lat_is_const (lat)); - if (lat->type != IPA_CONST_VALUE_REF - && is_gimple_reg (parm_tree) && gimple_default_def (func, parm_tree) - && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_default_def (func, + if (is_gimple_reg (parm_tree) && gimple_default_def (func, parm_tree) + && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_default_def (func, parm_tree))) { if (dump_file) @@ -944,7 +903,7 @@ ipcp_insert_stage (void) for (i = 0; i < count; 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++; } if (const_param == 0) @@ -953,7 +912,8 @@ ipcp_insert_stage (void) for (i = 0; i < count; 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); replace_param = @@ -990,7 +950,7 @@ ipcp_insert_stage (void) for (i = 0; i < count; 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); if (lat->type != IPA_CONST_VALUE_REF diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index b7f1597691a..3683ec772ac 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -139,6 +139,7 @@ along with GCC; see the file COPYING3. If not see #include "ggc.h" #include "tree-flow.h" #include "rtl.h" +#include "ipa-prop.h" /* Mode incremental inliner operate on: @@ -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, - 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 -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 max_depth = PARAM_VALUE (PARAM_MAX_INLINE_RECURSIVE_DEPTH_AUTO); @@ -760,6 +763,8 @@ cgraph_decide_recursive_inlining (struct cgraph_node *node) } cgraph_redirect_edge_callee (curr, master_clone); 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); n++; } @@ -817,6 +822,20 @@ compute_max_insns (int insns) * (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: All inline candidates are put into prioritized heap based on estimated growth of the overall number of instructions and then update the estimates. @@ -833,6 +852,10 @@ cgraph_decide_inlining_of_small_functions (void) fibheap_t heap = fibheap_new (); bitmap updated_nodes = BITMAP_ALLOC (NULL); 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) fprintf (dump_file, "\nDeciding on smaller functions:\n"); @@ -968,8 +991,10 @@ cgraph_decide_inlining_of_small_functions (void) where = edge->caller; if (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; + if (flag_indirect_inlining) + add_new_edges_to_heap (heap, new_indirect_edges); update_callee_keys (heap, where, updated_nodes); } else @@ -986,6 +1011,11 @@ cgraph_decide_inlining_of_small_functions (void) } callee = edge->callee; 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); } where = edge->caller; @@ -1028,6 +1058,9 @@ cgraph_decide_inlining_of_small_functions (void) &edge->inline_failed)) 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); BITMAP_FREE (updated_nodes); } @@ -1112,6 +1145,8 @@ cgraph_decide_inlining (void) continue; } cgraph_mark_inline_edge (e, true); + if (flag_indirect_inlining) + ipa_propagate_indirect_call_infos (e, NULL); if (dump_file) fprintf (dump_file, " Inlined into %s which now has %i insns.\n", @@ -1133,6 +1168,11 @@ cgraph_decide_inlining (void) if (!flag_really_no_inline) 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 && flag_inline_functions_called_once) { @@ -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. */ static void inline_generate_summary (void) @@ -1621,6 +1686,13 @@ inline_generate_summary (void) int nnodes = cgraph_postorder (order); 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--) { struct cgraph_node *node = order[i]; @@ -1632,6 +1704,10 @@ inline_generate_summary (void) push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl; compute_inline_parameters (node); + + if (flag_indirect_inlining) + inline_indirect_intraprocedural_analysis (node); + pop_cfun (); } } diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index ff833d77f23..1476d2eccca 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "flags.h" #include "timevar.h" #include "flags.h" +#include "diagnostic.h" /* Vector where the parameter infos are actually stored. */ VEC (ipa_node_params_t, heap) *ipa_node_params_vector; @@ -117,6 +118,9 @@ ipa_create_param_decls_array (struct cgraph_node *mt) int param_num; struct ipa_node_params *info = IPA_NODE_REF (mt); + if (info->param_decls) + return; + info->param_decls = XCNEWVEC (tree, ipa_get_param_count (info)); fndecl = mt->decl; fnargs = DECL_ARGUMENTS (fndecl); @@ -146,95 +150,84 @@ ipa_count_formal_params (struct cgraph_node *mt) ipa_set_param_count (IPA_NODE_REF (mt), param_num); } -/* Check STMT to detect whether a formal is modified within MT, the appropriate - entry is updated in the modified_flags array of ipa_node_params (associated - with MT). */ +/* Check STMT to detect whether a formal parameter is directly modified within + STMT, the appropriate entry is updated in the modified flags of INFO. + 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 -ipa_check_stmt_modifications (struct cgraph_node *mt, tree stmt) +ipa_check_stmt_modifications (struct ipa_node_params *info, tree stmt) { - int index, j; - tree parm_decl; - struct ipa_node_params *info; + int j; + int index; + tree lhs; switch (TREE_CODE (stmt)) { case GIMPLE_MODIFY_STMT: - if (TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 0)) == PARM_DECL) - { - info = IPA_NODE_REF (mt); - parm_decl = GIMPLE_STMT_OPERAND (stmt, 0); - index = ipa_get_param_decl_index (info, parm_decl); - if (index >= 0) - info->modified_flags[index] = true; - } + lhs = GIMPLE_STMT_OPERAND (stmt, 0); + + while (handled_component_p (lhs)) + lhs = TREE_OPERAND (lhs, 0); + if (TREE_CODE (lhs) == SSA_NAME) + lhs = SSA_NAME_VAR (lhs); + index = ipa_get_param_decl_index (info, lhs); + if (index >= 0) + info->param_flags[index].modified = true; break; + case ASM_EXPR: /* Asm code could modify any of the parameters. */ - info = IPA_NODE_REF (mt); - for (j = 0; j < ipa_get_param_count (IPA_NODE_REF (mt)); j++) - info->modified_flags[j] = true; + for (j = 0; j < ipa_get_param_count (info); j++) + info->param_flags[j].modified = true; break; + default: break; } } -/* The modify computation driver for MT. Compute which formal arguments - of function MT are locally modified. Formals may be modified in MT - if their address is taken, or if - they appear on the left hand side of an assignment. */ +/* Compute which formal parameters of function associated with NODE are locally + modified. Parameters may be modified in NODE if they are TREE_ADDRESSABLE, + if they appear on the left hand side of an assignment or if there is an + ASM_EXPR in the function. */ void -ipa_detect_param_modifications (struct cgraph_node *mt) +ipa_detect_param_modifications (struct cgraph_node *node) { - tree decl; - tree body; - int j, count; + tree decl = node->decl; basic_block bb; struct function *func; block_stmt_iterator bsi; - tree stmt, parm_tree; - struct ipa_node_params *info = IPA_NODE_REF (mt); + tree stmt; + 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; - count = ipa_get_param_count (info); - info->modified_flags = XCNEWVEC (bool, count); - decl = mt->decl; - /* ??? Handle pending sizes case. Set all parameters - of the function to be modified. */ + if (!info->param_flags) + info->param_flags = XCNEWVEC (struct ipa_param_flags, + ipa_get_param_count (info)); - if (DECL_UNINLINABLE (decl)) - { - 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_EACH_BB_FN (bb, func) { - func = DECL_STRUCT_FUNCTION (decl); - FOR_EACH_BB_FN (bb, func) - { - for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) - { - stmt = bsi_stmt (bsi); - ipa_check_stmt_modifications (mt, stmt); - } - } + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + stmt = bsi_stmt (bsi); + ipa_check_stmt_modifications (info, 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. */ void 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); } -/* 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. */ +/* The following function prints the jump functions of all arguments on all + call graph edges going from NODE to file F. */ void -ipa_compute_jump_functions (struct cgraph_edge *cs) +ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node) { - tree call_tree; - tree arg, cst_decl; - int arg_num; - struct cgraph_node *mt; - tree parm_decl; - struct function *curr_cfun; - call_expr_arg_iterator iter; - struct ipa_edge_args *args = IPA_EDGE_REF (cs); + int i, count; + struct cgraph_edge *cs; + struct ipa_jump_func *jump_func; + enum jump_func_type type; - if (ipa_get_cs_argument_count (args) == 0 || args->jump_functions) - return; - args->jump_functions = XCNEWVEC (struct ipa_jump_func, - ipa_get_cs_argument_count (args)); - call_tree = get_call_expr_in (cs->call_stmt); - gcc_assert (TREE_CODE (call_tree) == CALL_EXPR); - arg_num = 0; + fprintf (f, "JUMP FUNCTIONS OF CALLER %s:\n", cgraph_node_name (node)); + for (cs = node->callees; cs; cs = cs->next_callee) + { + if (!ipa_edge_args_info_available_for_edge_p (cs)) + continue; + + fprintf (f, "callsite %s ", cgraph_node_name (node)); + fprintf (f, "-> %s :: \n", cgraph_node_name (cs->callee)); - 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 - IPA_PASS_THROUGH and its index in the caller as the jump function - of this argument. */ - if ((TREE_CODE (arg) == SSA_NAME - && TREE_CODE (SSA_NAME_VAR (arg)) == PARM_DECL) - || TREE_CODE (arg) == PARM_DECL) + if (TREE_CODE (arg) == INTEGER_CST + || TREE_CODE (arg) == REAL_CST + || TREE_CODE (arg) == FIXED_CST) { - struct ipa_node_params *info; - int index; - - mt = cs->caller; - info = IPA_NODE_REF (mt); - parm_decl = TREE_CODE (arg) == PARM_DECL ? arg : SSA_NAME_VAR (arg); - - index = ipa_get_param_decl_index (info, parm_decl); - if (TREE_CODE (arg) == SSA_NAME && IS_VALID_JUMP_FUNC_INDEX (index)) + functions[num].type = IPA_CONST; + functions[num].value.constant = arg; + } + else if (TREE_CODE (arg) == ADDR_EXPR) + { + if (TREE_CODE (TREE_OPERAND (arg, 0)) == FUNCTION_DECL) { - curr_cfun = DECL_STRUCT_FUNCTION (mt->decl); - if (!gimple_default_def (curr_cfun, parm_decl) - || gimple_default_def (curr_cfun, parm_decl) != arg) - info->modified_flags[index] = true; + functions[num].type = IPA_CONST; + functions[num].value.constant = TREE_OPERAND (arg, 0); } - if (!IS_VALID_JUMP_FUNC_INDEX (index) || info->modified_flags[index]) - args->jump_functions[arg_num].type = IPA_UNKNOWN; - else + else if (TREE_CODE (TREE_OPERAND (arg, 0)) == CONST_DECL) + { + 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; - args->jump_functions[arg_num].value.formal_id = index; + functions[num].type = IPA_PASS_THROUGH; + 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 - of this argument. */ - else if (TREE_CODE (arg) == INTEGER_CST - || TREE_CODE (arg) == REAL_CST - || TREE_CODE (arg) == FIXED_CST) + + num++; + } +} + +/* 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; - args->jump_functions[arg_num].value.constant = arg; + if (TREE_CODE (arg) == PARM_DECL) + { + 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 - 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 - && TREE_CODE (TREE_OPERAND (arg, 0)) == CONST_DECL) + + num++; + } + + return undecided_members; +} + +/* 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 (DECL_INITIAL (cst_decl)) == INTEGER_CST - || TREE_CODE (DECL_INITIAL (cst_decl)) == REAL_CST - || TREE_CODE (DECL_INITIAL (cst_decl)) == FIXED_CST) + if (TREE_CODE (rhs) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL + && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE) { - args->jump_functions[arg_num].type = IPA_CONST_REF; - args->jump_functions[arg_num].value.constant = cst_decl; + method = TREE_OPERAND (rhs, 0); + 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: + + : + 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 ; + else + goto ; + + : + 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; + + : + # iftmp.11_1 = PHI + 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 @@ -371,8 +1053,15 @@ ipa_free_node_params_substructures (struct ipa_node_params *info) free (info->ipcp_lattices); if (info->param_decls) free (info->param_decls); - if (info->modified_flags) - free (info->modified_flags); + if (info->param_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)); } @@ -451,6 +1140,7 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, void *data) { struct ipa_node_params *old_info, *new_info; + struct ipa_param_call_note *note; int param_count; ipa_check_create_node_params (); @@ -464,12 +1154,24 @@ ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, sizeof (struct ipcp_lattice) * param_count); new_info->param_decls = (tree *) duplicate_array (old_info->param_decls, sizeof (tree) * param_count); - new_info->modified_flags = (bool *) - duplicate_array (old_info->modified_flags, sizeof (bool) * param_count); + new_info->param_flags = (struct ipa_param_flags *) + 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->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. */ } @@ -509,6 +1211,19 @@ ipa_unregister_cgraph_hooks (void) longer needed 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_node_params (); @@ -545,33 +1260,37 @@ ipa_print_all_tree_maps (FILE * f) } } -/* Print modified_flags data structures of all functions in the - callgraph to F. */ +/* Print param_flags data structures of the NODE to F. */ void -ipa_print_all_params_modified (FILE * f) +ipa_print_node_param_flags (FILE * f, struct cgraph_node *node) { int i, count; - bool temp; - struct cgraph_node *node; + struct ipa_node_params *info; - fprintf (f, "\nMODIFY PRINT\n"); - for (node = cgraph_nodes; node; node = node->next) + if (!node->analyzed) + 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; - - if (!node->analyzed) - continue; - info = IPA_NODE_REF (node); - fprintf (f, "function %s :: \n", cgraph_node_name (node)); - 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); - } + fprintf (f, " param %d flags:", i); + if (ipa_is_ith_param_modified (info, i)) + fprintf (f, " modified"); + if (ipa_is_ith_param_called (info, i)) + fprintf (f, " called"); + fprintf (f, "\n"); } } +/* 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); +} diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 2dd83326018..7d44da1813c 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "tree.h" #include "vec.h" +#include "cgraph.h" /* The following definitions and interfaces are used by interprocedural analyses. */ @@ -32,21 +33,23 @@ along with GCC; see the file COPYING3. If not see Constant - a constant is passed as an actual argument. Unknown - neither of the above. 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 { - IPA_UNKNOWN, + IPA_UNKNOWN = 0, /* newly allocated and zeroed jump functions default */ IPA_CONST, IPA_CONST_REF, + IPA_CONST_MEMBER_PTR, IPA_PASS_THROUGH }; /* All formal parameters in the program have a lattice associated with it computed by the interprocedural stage of IPCP. There are three main values of the lattice: - TOP - unknown. - BOTTOM - non constant. - CONSTANT_TYPE - constant value. + IPA_TOP - unknown, + IPA_BOTTOM - non constant, + IPA_CONST_VALUE - simple scalar constant, Cval of formal f will have a constant value if all callsites to this function have the same constant value passed to f. Integer and real constants are represented as IPA_CONST and Fortran @@ -59,14 +62,24 @@ enum ipa_lattice_type IPA_TOP }; -/* Represents a value of a jump function. - value represents a constant. - formal_id is used only in jump function context and represents - pass-through parameter (the formal of caller is passed as argument). */ +/* Structure holding a C++ member pointer constant. Holds a pointer to the + method and delta offset. */ +struct ipa_member_ptr_cst +{ + 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 { unsigned int formal_id; tree constant; + struct ipa_member_ptr_cst member_cst; }; /* A jump function for a callsite represents the values passed as actual @@ -101,10 +114,43 @@ struct ipa_replace_map 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 and some other information for interprocedural passes that operate on parameters (such as ipa-cp). */ - struct ipa_node_params { /* Number of formal parameters of this function. When set to 0, @@ -115,8 +161,10 @@ struct ipa_node_params struct ipcp_lattice *ipcp_lattices; /* Mapping each parameter to its PARM_DECL tree. */ tree *param_decls; - /* Indicating which parameter is modified in its function. */ - bool *modified_flags; + /* Various flags describing individual parameters. */ + 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, it points to the node that IPA cp cloned from. */ struct cgraph_node *ipcp_orig_node; @@ -130,6 +178,10 @@ struct ipa_node_params /* Whether this function is called with variable number of actual arguments. */ 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 @@ -164,7 +216,16 @@ ipa_get_ith_param (struct ipa_node_params *info, int i) static inline bool 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. */ @@ -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_edge_args (void); void free_all_ipa_structures_after_ipa_cp (void); +void free_all_ipa_structures_after_iinln (void); void ipa_register_cgraph_hooks (void); /* This function ensures the array of node param infos is big enough to @@ -318,9 +380,15 @@ void ipa_count_arguments (struct cgraph_edge *); void ipa_count_formal_params (struct cgraph_node *); void ipa_create_param_decls_array (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. */ 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 */ diff --git a/gcc/opts.c b/gcc/opts.c index a4572270386..ecd4e372e2e 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -934,6 +934,7 @@ decode_options (unsigned int argc, const char **argv) /* -O2 optimizations. */ opt2 = (optimize >= 2); flag_inline_small_functions = opt2; + flag_indirect_inlining = opt2; flag_thread_jumps = opt2; flag_crossjumping = opt2; flag_optimize_sibling_calls = opt2; diff --git a/gcc/testsuite/g++.dg/ipa/iinline-1.C b/gcc/testsuite/g++.dg/ipa/iinline-1.C new file mode 100644 index 00000000000..be3be71f96a --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/iinline-1.C @@ -0,0 +1,47 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-1.c b/gcc/testsuite/gcc.dg/ipa/iinline-1.c new file mode 100644 index 00000000000..da548f46648 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-1.c @@ -0,0 +1,26 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/modif-1.c b/gcc/testsuite/gcc.dg/ipa/modif-1.c new file mode 100644 index 00000000000..7d160cb7e74 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/modif-1.c @@ -0,0 +1,44 @@ +/* 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" } } */ diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 9d827ff9261..b5eb033588e 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -951,7 +951,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, pointer_set_insert (id->statements_to_fold, stmt); /* We're duplicating a CALL_EXPR. Find any corresponding callgraph edges and update or duplicate them. */ - if (call && (decl = get_callee_fndecl (call))) + if (call) { struct cgraph_node *node; struct cgraph_edge *edge; @@ -962,7 +962,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, edge = cgraph_edge (id->src_node, orig_stmt); if (edge) 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; case CB_CGE_MOVE_CLONES: @@ -971,8 +972,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, node = node->next_clone) { edge = cgraph_edge (node, orig_stmt); - gcc_assert (edge); - cgraph_set_call_stmt (edge, stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); } /* FALLTHRU */ @@ -2580,6 +2581,20 @@ add_lexical_block (tree current_block, tree new_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. */ static bool @@ -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. */ fn = get_callee_fndecl (t); if (!fn) - goto egress; + { + fn = get_indirect_callee_fndecl (id->dst_node, stmt); + if (!fn) + goto egress; + } /* Turn forward declarations into real ones. */ fn = cgraph_node (fn)->decl; @@ -2672,6 +2691,12 @@ expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) inlining. */ 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)) /* Avoid warnings during early inline pass. */ && (!flag_unit_at_a_time || cgraph_global_info_ready)) -- 2.30.2