Hi, this patch adds several sanity checks that inline summaries are up to date and makes sense; it also fixes several minor updating bugs and two perhaps important integer overflows.
Bootstrapped/regtested x86_64-linux, will commit it shortly. Honza * ipa-cp.c (ipcp_discover_new_direct_edges): If something was turned to direct call update the summary. * ipa-inline-transform.c (inline_call): Sanity check that summaries match the predicted effect; fix updating of summary after edge redirection. * ipa-inline-analysis.c (inline_node_duplication_hook): Do not try to update the summary and recompute it instead. (estimate_function_body_sizes): Fix self size estimation; double check that it agrees with inline_update_overall_summary. (estimate_edge_size_and_time): Handle devirtualizaiton costs. (estimate_edge_devirt_benefit): Update to be called from estimate_edge_size_and_time. (estimate_calls_size_and_time): Update. (estimate_node_size_and_time): Watch overflows. (inline_merge_summary): Likewise. * ipa-prob.c: Include ipa-inline.h (ipa_make_edge_direct_to_target): After redirection update the summary. Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 192809) --- ipa-cp.c (working copy) *************** ipcp_discover_new_direct_edges (struct c *** 1688,1693 **** --- 1688,1694 ---- VEC (tree, heap) *known_vals) { struct cgraph_edge *ie, *next_ie; + bool found = false; for (ie = node->indirect_calls; ie; ie = next_ie) { *************** ipcp_discover_new_direct_edges (struct c *** 1696,1703 **** next_ie = ie->next_callee; target = ipa_get_indirect_edge_target (ie, known_vals, NULL, NULL); if (target) ! ipa_make_edge_direct_to_target (ie, target); } } /* Vector of pointers which for linked lists of clones of an original crgaph --- 1697,1710 ---- next_ie = ie->next_callee; target = ipa_get_indirect_edge_target (ie, known_vals, NULL, NULL); if (target) ! { ! ipa_make_edge_direct_to_target (ie, target); ! found = true; ! } } + /* Turning calls to direct calls will improve overall summary. */ + if (found) + inline_update_overall_summary (node); } /* Vector of pointers which for linked lists of clones of an original crgaph Index: ipa-inline-transform.c =================================================================== *** ipa-inline-transform.c (revision 192809) --- ipa-inline-transform.c (working copy) *************** inline_call (struct cgraph_edge *e, bool *** 209,214 **** --- 209,219 ---- struct cgraph_node *to = NULL; struct cgraph_edge *curr = e; struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL); + bool new_edges_found = false; + + #ifdef ENABLE_CHECKING + int estimated_growth = estimate_edge_growth (e); + #endif /* Don't inline inlined edges. */ gcc_assert (e->inline_failed); *************** inline_call (struct cgraph_edge *e, bool *** 248,266 **** old_size = inline_summary (to)->size; inline_merge_summary (e); if (update_overall_summary) inline_update_overall_summary (to); new_size = inline_summary (to)->size; if (overall_size) *overall_size += new_size - old_size; ncalls_inlined++; /* This must happen after inline_merge_summary that rely on jump functions of callee to not be updated. */ ! if (optimize) ! return ipa_propagate_indirect_call_infos (curr, new_edges); ! else ! return false; } --- 253,277 ---- old_size = inline_summary (to)->size; inline_merge_summary (e); + if (optimize) + new_edges_found = ipa_propagate_indirect_call_infos (curr, new_edges); if (update_overall_summary) inline_update_overall_summary (to); new_size = inline_summary (to)->size; + #ifdef ENABLE_CHECKING + /* Verify that estimated growth match real growth. Allow off-by-one + error due to INLINE_SIZE_SCALE roudoff errors. */ + gcc_assert (!update_overall_summary || !overall_size + || abs (estimated_growth - (new_size - old_size)) <= 1); + #endif + if (overall_size) *overall_size += new_size - old_size; ncalls_inlined++; /* This must happen after inline_merge_summary that rely on jump functions of callee to not be updated. */ ! return new_edges_found; } Index: ipa-inline-analysis.c =================================================================== *** ipa-inline-analysis.c (revision 192809) --- ipa-inline-analysis.c (working copy) *************** inline_node_duplication_hook (struct cgr *** 1081,1087 **** struct predicate true_pred = true_predicate (); size_time_entry *e; int optimized_out_size = 0; - gcov_type optimized_out_time = 0; bool inlined_to_p = false; struct cgraph_edge *edge; --- 1081,1086 ---- *************** inline_node_duplication_hook (struct cgr *** 1123,1132 **** possible_truths, info); if (false_predicate_p (&new_predicate)) ! { ! optimized_out_size += e->size; ! optimized_out_time += e->time; ! } else account_size_time (info, e->size, e->time, &new_predicate); } --- 1122,1128 ---- possible_truths, info); if (false_predicate_p (&new_predicate)) ! optimized_out_size += e->size; else account_size_time (info, e->size, e->time, &new_predicate); } *************** inline_node_duplication_hook (struct cgr *** 1149,1157 **** && !false_predicate_p (es->predicate)) { optimized_out_size += es->call_stmt_size * INLINE_SIZE_SCALE; - optimized_out_time += (es->call_stmt_time - * (INLINE_TIME_SCALE / CGRAPH_FREQ_BASE) - * edge->frequency); edge->frequency = 0; } edge_set_predicate (edge, &new_predicate); --- 1145,1150 ---- *************** inline_node_duplication_hook (struct cgr *** 1174,1182 **** && !false_predicate_p (es->predicate)) { optimized_out_size += es->call_stmt_size * INLINE_SIZE_SCALE; - optimized_out_time += (es->call_stmt_time - * (INLINE_TIME_SCALE / CGRAPH_FREQ_BASE) - * edge->frequency); edge->frequency = 0; } edge_set_predicate (edge, &new_predicate); --- 1167,1172 ---- *************** inline_node_duplication_hook (struct cgr *** 1193,1214 **** about updating self sizes, because size vectors already contains sizes of the calees. */ gcc_assert (!inlined_to_p ! || (!optimized_out_size && !optimized_out_time)); ! ! info->size -= optimized_out_size / INLINE_SIZE_SCALE; ! info->self_size -= optimized_out_size / INLINE_SIZE_SCALE; ! gcc_assert (info->size > 0); ! gcc_assert (info->self_size > 0); ! ! optimized_out_time /= INLINE_TIME_SCALE; ! if (optimized_out_time > MAX_TIME) ! optimized_out_time = MAX_TIME; ! info->time -= optimized_out_time; ! info->self_time -= optimized_out_time; ! if (info->time < 0) ! info->time = 0; ! if (info->self_time < 0) ! info->self_time = 0; } else { --- 1183,1189 ---- about updating self sizes, because size vectors already contains sizes of the calees. */ gcc_assert (!inlined_to_p ! || !optimized_out_size); } else { *************** inline_node_duplication_hook (struct cgr *** 1226,1231 **** --- 1201,1207 ---- set_hint_predicate (&info->loop_stride, p); } } + inline_update_overall_summary (dst); } *************** estimate_function_body_sizes (struct cgr *** 2405,2412 **** struct predicate p; this_time *= freq; - time += this_time; - size += this_size; prob = eliminated_by_inlining_prob (stmt); if (prob == 1 && dump_file && (dump_flags & TDF_DETAILS)) --- 2381,2386 ---- *************** estimate_function_body_sizes (struct cgr *** 2420,2425 **** --- 2394,2405 ---- else p = true_predicate (); + if (!false_predicate_p (&p)) + { + time += this_time; + size += this_size; + } + /* We account everything but the calls. Calls have their own size/time info attached to cgraph edges. This is necessary in order to make the cost disappear after inlining. */ *************** compute_inline_parameters (struct cgraph *** 2621,2626 **** --- 2601,2612 ---- info->size = info->self_size; info->stack_frame_offset = 0; info->estimated_stack_size = info->estimated_self_stack_size; + #ifdef ENABLE_CHECKING + inline_update_overall_summary (node); + gcc_assert (info->time == info->self_time + && info->size == info->self_size); + #endif + pop_cfun (); } *************** struct gimple_opt_pass pass_inline_param *** 2655,2687 **** }; - /* Increase SIZE and TIME for size and time needed to handle edge E. */ - - static void - estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *time, - int prob) - { - struct inline_edge_summary *es = inline_edge_summary (e); - *size += es->call_stmt_size * INLINE_SIZE_SCALE; - *time += (es->call_stmt_time * prob / REG_BR_PROB_BASE - * e->frequency * (INLINE_TIME_SCALE / CGRAPH_FREQ_BASE)); - if (*time > MAX_TIME * INLINE_TIME_SCALE) - *time = MAX_TIME * INLINE_TIME_SCALE; - } - - /* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and KNOWN_BINFOS. */ static bool estimate_edge_devirt_benefit (struct cgraph_edge *ie, ! int *size, int *time, int prob, VEC (tree, heap) *known_vals, VEC (tree, heap) *known_binfos, VEC (ipa_agg_jump_function_p, heap) *known_aggs) { tree target; - int time_diff, size_diff; struct cgraph_node *callee; struct inline_summary *isummary; --- 2641,2657 ---- }; /* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and KNOWN_BINFOS. */ static bool estimate_edge_devirt_benefit (struct cgraph_edge *ie, ! int *size, int *time, VEC (tree, heap) *known_vals, VEC (tree, heap) *known_binfos, VEC (ipa_agg_jump_function_p, heap) *known_aggs) { tree target; struct cgraph_node *callee; struct inline_summary *isummary; *************** estimate_edge_devirt_benefit (struct cgr *** 2694,2705 **** return false; /* Account for difference in cost between indirect and direct calls. */ ! size_diff = ((eni_size_weights.indirect_call_cost - eni_size_weights.call_cost) ! * INLINE_SIZE_SCALE); ! *size -= size_diff; ! time_diff = ((eni_time_weights.indirect_call_cost - eni_time_weights.call_cost) ! * INLINE_TIME_SCALE * prob / REG_BR_PROB_BASE); ! *time -= time_diff; callee = cgraph_get_node (target); if (!callee || !callee->analyzed) --- 2664,2673 ---- return false; /* Account for difference in cost between indirect and direct calls. */ ! *size -= (eni_size_weights.indirect_call_cost - eni_size_weights.call_cost); ! *time -= (eni_time_weights.indirect_call_cost - eni_time_weights.call_cost); ! gcc_checking_assert (*time >= 0); ! gcc_checking_assert (*size >= 0); callee = cgraph_get_node (target); if (!callee || !callee->analyzed) *************** estimate_edge_devirt_benefit (struct cgr *** 2708,2713 **** --- 2676,2709 ---- return isummary->inlinable; } + /* Increase SIZE and TIME for size and time needed to handle edge E. */ + + static inline void + estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *time, + int prob, + VEC (tree, heap) *known_vals, + VEC (tree, heap) *known_binfos, + VEC (ipa_agg_jump_function_p, heap) *known_aggs, + inline_hints *hints) + + { + struct inline_edge_summary *es = inline_edge_summary (e); + int call_size = es->call_stmt_size; + int call_time = es->call_stmt_time; + if (!e->callee + && estimate_edge_devirt_benefit (e, &call_size, &call_time, + known_vals, known_binfos, known_aggs) + && hints + && cgraph_maybe_hot_edge_p (e)) + *hints |= INLINE_HINT_indirect_call; + *size += call_size * INLINE_SIZE_SCALE; + *time += call_time * prob / REG_BR_PROB_BASE + * e->frequency * (INLINE_TIME_SCALE / CGRAPH_FREQ_BASE); + if (*time > MAX_TIME * INLINE_TIME_SCALE) + *time = MAX_TIME * INLINE_TIME_SCALE; + } + + /* Increase SIZE and TIME for size and time needed to handle all calls in NODE. POSSIBLE_TRUTHS, KNOWN_VALS and KNOWN_BINFOS describe context of the call *************** estimate_calls_size_and_time (struct cgr *** 2731,2737 **** { /* Predicates of calls shall not use NOT_CHANGED codes, sowe do not need to compute probabilities. */ ! estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); } else estimate_calls_size_and_time (e->callee, size, time, hints, --- 2727,2735 ---- { /* Predicates of calls shall not use NOT_CHANGED codes, sowe do not need to compute probabilities. */ ! estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE, ! known_vals, known_binfos, known_aggs, ! hints); } else estimate_calls_size_and_time (e->callee, size, time, hints, *************** estimate_calls_size_and_time (struct cgr *** 2743,2756 **** { struct inline_edge_summary *es = inline_edge_summary (e); if (!es->predicate || evaluate_predicate (es->predicate, possible_truths)) ! { ! estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); ! if (estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE, ! known_vals, known_binfos, known_aggs) ! && hints ! && cgraph_maybe_hot_edge_p (e)) ! *hints |= INLINE_HINT_indirect_call; ! } } } --- 2741,2749 ---- { struct inline_edge_summary *es = inline_edge_summary (e); if (!es->predicate || evaluate_predicate (es->predicate, possible_truths)) ! estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE, ! known_vals, known_binfos, known_aggs, ! hints); } } *************** estimate_node_size_and_time (struct cgra *** 2772,2778 **** { struct inline_summary *info = inline_summary (node); size_time_entry *e; ! int size = 0, time = 0; inline_hints hints = 0; int i; --- 2765,2772 ---- { struct inline_summary *info = inline_summary (node); size_time_entry *e; ! int size = 0; ! int time = 0; inline_hints hints = 0; int i; *************** estimate_node_size_and_time (struct cgra *** 2801,2806 **** --- 2795,2802 ---- if (evaluate_predicate (&e->predicate, possible_truths)) { size += e->size; + gcc_checking_assert (e->time >= 0); + gcc_checking_assert (time >= 0); if (!inline_param_summary) time += e->time; else *************** estimate_node_size_and_time (struct cgra *** 2809,2818 **** &e->predicate, possible_truths, inline_param_summary); ! time += e->time * prob / REG_BR_PROB_BASE; } } if (info->loop_iterations && !evaluate_predicate (info->loop_iterations, possible_truths)) --- 2805,2821 ---- &e->predicate, possible_truths, inline_param_summary); ! gcc_checking_assert (prob >= 0); ! gcc_checking_assert (prob <= REG_BR_PROB_BASE); ! time += ((gcov_type)e->time * prob) / REG_BR_PROB_BASE; } + if (time > MAX_TIME * INLINE_TIME_SCALE) + time = MAX_TIME * INLINE_TIME_SCALE; + gcc_checking_assert (time >= 0); } + gcc_checking_assert (size >= 0); + gcc_checking_assert (time >= 0); if (info->loop_iterations && !evaluate_predicate (info->loop_iterations, possible_truths)) *************** estimate_node_size_and_time (struct cgra *** 2821,2838 **** && !evaluate_predicate (info->loop_stride, possible_truths)) hints |=INLINE_HINT_loop_stride; - if (time > MAX_TIME * INLINE_TIME_SCALE) - time = MAX_TIME * INLINE_TIME_SCALE; estimate_calls_size_and_time (node, &size, &time, &hints, possible_truths, known_vals, known_binfos, known_aggs); ! time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; ! size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; if (dump_file && (dump_flags & TDF_DETAILS)) ! fprintf (dump_file, "\n size:%i time:%i\n", size, time); if (ret_time) *ret_time = time; if (ret_size) --- 2824,2841 ---- && !evaluate_predicate (info->loop_stride, possible_truths)) hints |=INLINE_HINT_loop_stride; estimate_calls_size_and_time (node, &size, &time, &hints, possible_truths, known_vals, known_binfos, known_aggs); ! gcc_checking_assert (size >= 0); ! gcc_checking_assert (time >= 0); ! time = RDIV (time, INLINE_TIME_SCALE); ! size = RDIV (size, INLINE_SIZE_SCALE); if (dump_file && (dump_flags & TDF_DETAILS)) ! fprintf (dump_file, "\n size:%i time:%i\n", (int)size, (int)time); if (ret_time) *ret_time = time; if (ret_size) *************** inline_merge_summary (struct cgraph_edge *** 3224,3230 **** int prob = predicate_probability (callee_info->conds, &e->predicate, clause, es->param); ! add_time = add_time * prob / REG_BR_PROB_BASE; if (add_time > MAX_TIME * INLINE_TIME_SCALE) add_time = MAX_TIME * INLINE_TIME_SCALE; if (prob != REG_BR_PROB_BASE --- 3227,3233 ---- int prob = predicate_probability (callee_info->conds, &e->predicate, clause, es->param); ! add_time = ((gcov_type)add_time * prob) / REG_BR_PROB_BASE; if (add_time > MAX_TIME * INLINE_TIME_SCALE) add_time = MAX_TIME * INLINE_TIME_SCALE; if (prob != REG_BR_PROB_BASE Index: ipa-prop.c =================================================================== *** ipa-prop.c (revision 192809) --- ipa-prop.c (working copy) *************** along with GCC; see the file COPYING3. *** 30,35 **** --- 30,36 ---- #include "tree-flow.h" #include "tree-pass.h" #include "tree-inline.h" + #include "ipa-inline.h" #include "gimple.h" #include "flags.h" #include "diagnostic.h" *************** struct cgraph_edge * *** 2100,2105 **** --- 2101,2107 ---- ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target) { struct cgraph_node *callee; + struct inline_edge_summary *es = inline_edge_summary (ie); if (TREE_CODE (target) == ADDR_EXPR) target = TREE_OPERAND (target, 0); *************** ipa_make_edge_direct_to_target (struct c *** 2115,2120 **** --- 2117,2127 ---- gcc_assert (!callee->global.inlined_to); cgraph_make_edge_direct (ie, callee); + es = inline_edge_summary (ie); + es->call_stmt_size -= (eni_size_weights.indirect_call_cost + - eni_size_weights.call_cost); + es->call_stmt_time -= (eni_time_weights.indirect_call_cost + - eni_time_weights.call_cost); if (dump_file) { fprintf (dump_file, "ipa-prop: Discovered %s call to a known target "