diff options
author | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-08-29 12:37:05 +0000 |
---|---|---|
committer | bstarynk <bstarynk@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-08-29 12:37:05 +0000 |
commit | 12cb78d1cca1387a092ec0bd49c250340bff4afc (patch) | |
tree | 1eab97da96906e0a2786d51d9f25f20de02befcf /gcc/ipa-inline-analysis.c | |
parent | 31879e18aea3222fe3e56f2c0319c9f230645ff3 (diff) | |
download | gcc-12cb78d1cca1387a092ec0bd49c250340bff4afc.tar.gz |
2012-08-29 Basile Starynkevitch <basile@starynkevitch.net>
MELT branch merged with trunk rev 190745 using svnmerge, notably
C++ conversion.
[gcc/]
2012-08-29 Basile Starynkevitch <basile@starynkevitch.net>
{{merging with trunk, converted to C++}}
* melt-runtime.h (MELT_FLEXIBLE_DIM): Set when C++.
* melt-runtime.c (melt_tempdir_path): Don't use choose_tmpdir from
libiberty.
(meltgc_start_module_by_index): Use address-of & on VEC_index.
(melt_really_initialize): When printing builtin settings, handle
GCC 4.8 as with implicit ENABLE_BUILD_WITH_CXX.
(meltgc_out_edge): Provide additional flag TDF_DETAILS for dump_edge_info.
(melt_val2passflag): Handle PROP_referenced_vars only when defined.
* melt-module.mk: Use GCCMELT_COMPILER instead of GCCMELT_CC.
* melt-build-script.tpl: Transmit GCCMELT_COMPILER on every make
using melt-module.mk and improve the error message.
* melt-build-script.sh: Regenerate.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/melt-branch@190778 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-inline-analysis.c')
-rw-r--r-- | gcc/ipa-inline-analysis.c | 853 |
1 files changed, 628 insertions, 225 deletions
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index a38d2608cbd..ca80a8b88ba 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -76,7 +76,6 @@ along with GCC; see the file COPYING3. If not see #include "cgraph.h" #include "diagnostic.h" #include "gimple-pretty-print.h" -#include "timevar.h" #include "params.h" #include "tree-pass.h" #include "coverage.h" @@ -88,6 +87,9 @@ along with GCC; see the file COPYING3. If not see #include "tree-streamer.h" #include "ipa-inline.h" #include "alloc-pool.h" +#include "cfgloop.h" +#include "cfgloop.h" +#include "tree-scalar-evolution.h" /* Estimate runtime of function can easilly run into huge numbers with many nested loops. Be sure we can compute time * INLINE_SIZE_SCALE * 2 in an @@ -204,22 +206,54 @@ not_inlined_predicate (void) return single_cond_predicate (predicate_not_inlined_condition); } +/* Simple description of whether a memory load or a condition refers to a load + from an aggregate and if so, how and where from in the aggregate. + Individual fields have the same meaning like fields with the same name in + struct condition. */ -/* Add condition to condition list CONDS. */ +struct agg_position_info +{ + HOST_WIDE_INT offset; + bool agg_contents; + bool by_ref; +}; + +/* Add condition to condition list CONDS. AGGPOS describes whether the used + oprand is loaded from an aggregate and where in the aggregate it is. It can + be NULL, which means this not a load from an aggregate. */ static struct predicate add_condition (struct inline_summary *summary, int operand_num, + struct agg_position_info *aggpos, enum tree_code code, tree val) { int i; struct condition *c; struct condition new_cond; + HOST_WIDE_INT offset; + bool agg_contents, by_ref; + + if (aggpos) + { + offset = aggpos->offset; + agg_contents = aggpos->agg_contents; + by_ref = aggpos->by_ref; + } + else + { + offset = 0; + agg_contents = false; + by_ref = false; + } + gcc_checking_assert (operand_num >= 0); for (i = 0; VEC_iterate (condition, summary->conds, i, c); i++) { if (c->operand_num == operand_num && c->code == code - && c->val == val) + && c->val == val + && c->agg_contents == agg_contents + && (!agg_contents || (c->offset == offset && c->by_ref == by_ref))) return single_cond_predicate (i + predicate_first_dynamic_condition); } /* Too many conditions. Give up and return constant true. */ @@ -229,6 +263,9 @@ add_condition (struct inline_summary *summary, int operand_num, new_cond.operand_num = operand_num; new_cond.code = code; new_cond.val = val; + new_cond.agg_contents = agg_contents; + new_cond.by_ref = by_ref; + new_cond.offset = offset; VEC_safe_push (condition, gc, summary->conds, &new_cond); return single_cond_predicate (i + predicate_first_dynamic_condition); } @@ -296,9 +333,9 @@ add_clause (conditions conditions, struct predicate *p, clause_t clause) condition *cc1; if (!(clause & (1 << c1))) continue; - cc1 = VEC_index (condition, - conditions, - c1 - predicate_first_dynamic_condition); + cc1 = &VEC_index (condition, + conditions, + c1 - predicate_first_dynamic_condition); /* We have no way to represent !CHANGED and !IS_NOT_CONSTANT and thus there is no point for looking for them. */ if (cc1->code == CHANGED @@ -307,12 +344,12 @@ add_clause (conditions conditions, struct predicate *p, clause_t clause) for (c2 = c1 + 1; c2 <= NUM_CONDITIONS; c2++) if (clause & (1 << c2)) { - condition *cc1 = VEC_index (condition, - conditions, - c1 - predicate_first_dynamic_condition); - condition *cc2 = VEC_index (condition, - conditions, - c2 - predicate_first_dynamic_condition); + condition *cc1 = &VEC_index (condition, + conditions, + c1 - predicate_first_dynamic_condition); + condition *cc2 = &VEC_index (condition, + conditions, + c2 - predicate_first_dynamic_condition); if (cc1->operand_num == cc2->operand_num && cc1->val == cc2->val && cc2->code != IS_NOT_CONSTANT @@ -477,7 +514,7 @@ predicate_probability (conditions conds, { if (i2 >= predicate_first_dynamic_condition) { - condition *c = VEC_index + condition *c = &VEC_index (condition, conds, i2 - predicate_first_dynamic_condition); if (c->code == CHANGED @@ -487,7 +524,7 @@ predicate_probability (conditions conds, { int iprob = VEC_index (inline_param_summary_t, inline_param_summary, - c->operand_num)->change_prob; + c->operand_num).change_prob; this_prob = MAX (this_prob, iprob); } else @@ -517,9 +554,12 @@ dump_condition (FILE *f, conditions conditions, int cond) fprintf (f, "not inlined"); else { - c = VEC_index (condition, conditions, - cond - predicate_first_dynamic_condition); + c = &VEC_index (condition, conditions, + cond - predicate_first_dynamic_condition); fprintf (f, "op%i", c->operand_num); + if (c->agg_contents) + fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]", + c->by_ref ? "ref " : "", c->offset); if (c->code == IS_NOT_CONSTANT) { fprintf (f, " not constant"); @@ -577,6 +617,27 @@ dump_predicate (FILE *f, conditions conds, struct predicate *pred) } +/* Dump inline hints. */ +void +dump_inline_hints (FILE *f, inline_hints hints) +{ + if (!hints) + return; + fprintf (f, "inline hints:"); + if (hints & INLINE_HINT_indirect_call) + { + hints &= ~INLINE_HINT_indirect_call; + fprintf (f, " indirect_call"); + } + if (hints & INLINE_HINT_loop_iterations) + { + hints &= ~INLINE_HINT_loop_iterations; + fprintf (f, " loop_iterations"); + } + gcc_assert (!hints); +} + + /* Record SIZE and TIME under condition PRED into the inline summary. */ static void @@ -610,7 +671,7 @@ account_size_time (struct inline_summary *summary, int size, int time, { i = 0; found = true; - e = VEC_index (size_time_entry, summary->entry, 0); + e = &VEC_index (size_time_entry, summary->entry, 0); gcc_assert (!e->predicate.clause[0]); } if (dump_file && (dump_flags & TDF_DETAILS) && (time || size)) @@ -660,15 +721,17 @@ edge_set_predicate (struct cgraph_edge *e, struct predicate *predicate) /* KNOWN_VALS is partial mapping of parameters of NODE to constant values. - Return clause of possible truths. When INLINE_P is true, assume that - we are inlining. + KNOWN_AGGS is a vector of aggreggate jump functions for each parameter. + Return clause of possible truths. When INLINE_P is true, assume that we are + inlining. ERROR_MARK means compile time invariant. */ static clause_t evaluate_conditions_for_known_args (struct cgraph_node *node, - bool inline_p, - VEC (tree, heap) *known_vals) + bool inline_p, + VEC (tree, heap) *known_vals, + VEC (ipa_agg_jump_function_p, heap) *known_aggs) { clause_t clause = inline_p ? 0 : 1 << predicate_not_inlined_condition; struct inline_summary *info = inline_summary (node); @@ -680,16 +743,45 @@ evaluate_conditions_for_known_args (struct cgraph_node *node, tree val; tree res; - /* We allow call stmt to have fewer arguments than the callee - function (especially for K&R style programs). So bound - check here. */ - if (c->operand_num < (int)VEC_length (tree, known_vals)) - val = VEC_index (tree, known_vals, c->operand_num); - else - val = NULL; + /* We allow call stmt to have fewer arguments than the callee function + (especially for K&R style programs). So bound check here (we assume + known_aggs vector, if non-NULL, has the same length as + known_vals). */ + gcc_checking_assert (!known_aggs + || (VEC_length (tree, known_vals) + == VEC_length (ipa_agg_jump_function_p, + known_aggs))); + if (c->operand_num >= (int) VEC_length (tree, known_vals)) + { + clause |= 1 << (i + predicate_first_dynamic_condition); + continue; + } + + if (c->agg_contents) + { + struct ipa_agg_jump_function *agg; + + if (c->code == CHANGED + && !c->by_ref + && (VEC_index (tree, known_vals, c->operand_num) + == error_mark_node)) + continue; - if (val == error_mark_node && c->code != CHANGED) - val = NULL; + if (known_aggs) + { + agg = VEC_index (ipa_agg_jump_function_p, known_aggs, + c->operand_num); + val = ipa_find_agg_cst_for_param (agg, c->offset, c->by_ref); + } + else + val = NULL_TREE; + } + else + { + val = VEC_index (tree, known_vals, c->operand_num); + if (val == error_mark_node && c->code != CHANGED) + val = NULL_TREE; + } if (!val) { @@ -712,13 +804,15 @@ evaluate_conditions_for_known_args (struct cgraph_node *node, static void evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, - clause_t *clause_ptr, - VEC (tree, heap) **known_vals_ptr, - VEC (tree, heap) **known_binfos_ptr) + clause_t *clause_ptr, + VEC (tree, heap) **known_vals_ptr, + VEC (tree, heap) **known_binfos_ptr, + VEC (ipa_agg_jump_function_p, heap) **known_aggs_ptr) { struct cgraph_node *callee = cgraph_function_or_thunk_node (e->callee, NULL); struct inline_summary *info = inline_summary (callee); VEC (tree, heap) *known_vals = NULL; + VEC (ipa_agg_jump_function_p, heap) *known_aggs = NULL; if (clause_ptr) *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition; @@ -743,13 +837,16 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, if (count && (info->conds || known_vals_ptr)) VEC_safe_grow_cleared (tree, heap, known_vals, count); + if (count && (info->conds || known_aggs_ptr)) + VEC_safe_grow_cleared (ipa_agg_jump_function_p, heap, known_aggs, + count); if (count && known_binfos_ptr) VEC_safe_grow_cleared (tree, heap, *known_binfos_ptr, count); for (i = 0; i < count; i++) { - tree cst = ipa_value_from_jfunc (parms_info, - ipa_get_ith_jump_func (args, i)); + struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i); + tree cst = ipa_value_from_jfunc (parms_info, jf); if (cst) { if (known_vals && TREE_CODE (cst) != TREE_BINFO) @@ -760,19 +857,28 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, else if (inline_p && !VEC_index (inline_param_summary_t, es->param, - i)->change_prob) + i).change_prob) VEC_replace (tree, known_vals, i, error_mark_node); + /* TODO: When IPA-CP starts propagating and merging aggregate jump + functions, use its knowledge of the caller too, just like the + scalar case above. */ + VEC_replace (ipa_agg_jump_function_p, known_aggs, i, &jf->agg); } } if (clause_ptr) *clause_ptr = evaluate_conditions_for_known_args (callee, inline_p, - known_vals); + known_vals, known_aggs); if (known_vals_ptr) *known_vals_ptr = known_vals; else VEC_free (tree, heap, known_vals); + + if (known_aggs_ptr) + *known_aggs_ptr = known_aggs; + else + VEC_free (ipa_agg_jump_function_p, heap, known_aggs); } @@ -842,6 +948,11 @@ reset_inline_summary (struct cgraph_node *node) info->stack_frame_offset = 0; info->size = 0; info->time = 0; + if (info->loop_iterations) + { + pool_free (edge_predicate_pool, info->loop_iterations); + info->loop_iterations = NULL; + } VEC_free (condition, gc, info->conds); VEC_free (size_time_entry,gc, info->entry); for (e = node->callees; e; e = e->next_callee) @@ -918,8 +1029,8 @@ inline_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, } } } - possible_truths = evaluate_conditions_for_known_args (dst, - false, known_vals); + possible_truths = evaluate_conditions_for_known_args (dst, false, + known_vals, NULL); VEC_free (tree, heap, known_vals); account_size_time (info, 0, 0, &true_pred); @@ -979,7 +1090,7 @@ inline_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, * edge->frequency); edge->frequency = 0; } - *es->predicate = new_predicate; + edge_set_predicate (edge, &new_predicate); } /* Remap indirect edge predicates with the same simplificaiton as above. @@ -1011,7 +1122,29 @@ inline_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, * edge->frequency); edge->frequency = 0; } - *es->predicate = new_predicate; + edge_set_predicate (edge, &new_predicate); + } + if (info->loop_iterations) + { + struct predicate new_predicate = true_predicate (); + + for (j = 0; info->loop_iterations->clause[j]; j++) + if (!(possible_truths & info->loop_iterations->clause[j])) + { + new_predicate = false_predicate (); + break; + } + else + add_clause (info->conds, &new_predicate, + possible_truths & info->loop_iterations->clause[j]); + if (false_predicate_p (&new_predicate) + || true_predicate_p (&new_predicate)) + info->loop_iterations = NULL; + else + { + info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); + *info->loop_iterations = new_predicate; + } } /* If inliner or someone after inliner will ever start producing @@ -1037,7 +1170,15 @@ inline_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst, info->self_time = 0; } else - info->entry = VEC_copy (size_time_entry, gc, info->entry); + { + info->entry = VEC_copy (size_time_entry, gc, info->entry); + if (info->loop_iterations) + { + predicate p = *info->loop_iterations; + info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); + *info->loop_iterations = p; + } + } } @@ -1135,7 +1276,7 @@ dump_inline_edge_summary (FILE * f, int indent, struct cgraph_node *node, i++) { int prob = VEC_index (inline_param_summary_t, - es->param, i)->change_prob; + es->param, i).change_prob; if (!prob) fprintf (f, "%*s op%i is compile time invariant\n", @@ -1209,6 +1350,11 @@ dump_inline_summary (FILE * f, struct cgraph_node *node) (double) e->time / INLINE_TIME_SCALE); dump_predicate (f, s->conds, &e->predicate); } + if (s->loop_iterations) + { + fprintf (f, " loop iterations:"); + dump_predicate (f, s->conds, s->loop_iterations); + } fprintf (f, " calls:\n"); dump_inline_edge_summary (f, 4, node, s); fprintf (f, "\n"); @@ -1263,11 +1409,11 @@ mark_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED, return true; } -/* If OP reffers to value of function parameter, return - the corresponding parameter. */ +/* If OP refers to value of function parameter, return the corresponding + parameter. */ static tree -unmodified_parm (gimple stmt, tree op) +unmodified_parm_1 (gimple stmt, tree op) { /* SSA_NAME referring to parm default def? */ if (TREE_CODE (op) == SSA_NAME @@ -1286,13 +1432,67 @@ unmodified_parm (gimple stmt, tree op) if (!modified) return op; } - /* Assignment from a parameter? */ + return NULL_TREE; +} + +/* If OP refers to value of function parameter, return the corresponding + parameter. Also traverse chains of SSA register assignments. */ + +static tree +unmodified_parm (gimple stmt, tree op) +{ + tree res = unmodified_parm_1 (stmt, op); + if (res) + return res; + if (TREE_CODE (op) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (op) && gimple_assign_single_p (SSA_NAME_DEF_STMT (op))) return unmodified_parm (SSA_NAME_DEF_STMT (op), gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op))); - return NULL; + return NULL_TREE; +} + +/* If OP refers to a value of a function parameter or value loaded from an + aggregate passed to a parameter (either by value or reference), return TRUE + and store the number of the parameter to *INDEX_P and information whether + and how it has been loaded from an aggregate into *AGGPOS. INFO describes + the function parameters, STMT is the statement in which OP is used or + loaded. */ + +static bool +unmodified_parm_or_parm_agg_item (struct ipa_node_params *info, + gimple stmt, tree op, int *index_p, + struct agg_position_info *aggpos) +{ + tree res = unmodified_parm_1 (stmt, op); + + gcc_checking_assert (aggpos); + if (res) + { + *index_p = ipa_get_param_decl_index (info, res); + if (*index_p < 0) + return false; + aggpos->agg_contents = false; + aggpos->by_ref = false; + return true; + } + + if (TREE_CODE (op) == SSA_NAME) + { + if (SSA_NAME_IS_DEFAULT_DEF (op) + || !gimple_assign_single_p (SSA_NAME_DEF_STMT (op))) + return false; + stmt = SSA_NAME_DEF_STMT (op); + op = gimple_assign_rhs1 (stmt); + if (!REFERENCE_CLASS_P (op)) + return unmodified_parm_or_parm_agg_item (info, stmt, op, index_p, + aggpos); + } + + aggpos->agg_contents = true; + return ipa_load_from_parm_agg (info, stmt, op, index_p, &aggpos->offset, + &aggpos->by_ref); } /* See if statement might disappear after inlining. @@ -1395,9 +1595,9 @@ eliminated_by_inlining_prob (gimple stmt) || (TREE_CODE(inner_lhs) == MEM_REF && (unmodified_parm (stmt, TREE_OPERAND (inner_lhs, 0)) || (TREE_CODE (TREE_OPERAND (inner_lhs, 0)) == SSA_NAME - && TREE_CODE (SSA_NAME_VAR - (TREE_OPERAND (inner_lhs, 0))) - == RESULT_DECL)))) + && SSA_NAME_VAR (TREE_OPERAND (inner_lhs, 0)) + && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND + (inner_lhs, 0))) == RESULT_DECL)))) lhs_free = true; if (lhs_free && (is_gimple_reg (rhs) || is_gimple_min_invariant (rhs))) @@ -1423,13 +1623,12 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info, gimple last; tree op; int index; + struct agg_position_info aggpos; enum tree_code code, inverted_code; edge e; edge_iterator ei; gimple set_stmt; tree op2; - tree parm; - tree base; last = last_stmt (bb); if (!last @@ -1441,12 +1640,8 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info, /* TODO: handle conditionals like var = op0 < 4; if (var != 0). */ - parm = unmodified_parm (last, op); - if (parm) + if (unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos)) { - index = ipa_get_param_decl_index (info, parm); - if (index == -1) - return; code = gimple_cond_code (last); inverted_code = invert_tree_comparison (code, @@ -1454,8 +1649,7 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info, FOR_EACH_EDGE (e, ei, bb->succs) { - struct predicate p = add_condition (summary, - index, + struct predicate p = add_condition (summary, index, &aggpos, e->flags & EDGE_TRUE_VALUE ? code : inverted_code, gimple_cond_rhs (last)); @@ -1476,28 +1670,21 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info, for this and also the constant code is not known to be optimized away when inliner doen't see operand is constant. Other optimizers might think otherwise. */ + if (gimple_cond_code (last) != NE_EXPR + || !integer_zerop (gimple_cond_rhs (last))) + return; set_stmt = SSA_NAME_DEF_STMT (op); if (!gimple_call_builtin_p (set_stmt, BUILT_IN_CONSTANT_P) || gimple_call_num_args (set_stmt) != 1) return; op2 = gimple_call_arg (set_stmt, 0); - base = get_base_address (op2); - parm = unmodified_parm (set_stmt, base ? base : op2); - if (!parm) - return; - index = ipa_get_param_decl_index (info, parm); - if (index == -1) - return; - if (gimple_cond_code (last) != NE_EXPR - || !integer_zerop (gimple_cond_rhs (last))) + if (!unmodified_parm_or_parm_agg_item (info, set_stmt, op2, &index, &aggpos)) return; FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE) { - struct predicate p = add_condition (summary, - index, - IS_NOT_CONSTANT, - NULL); + struct predicate p = add_condition (summary, index, &aggpos, + IS_NOT_CONSTANT, NULL_TREE); e->aux = pool_alloc (edge_predicate_pool); *(struct predicate *)e->aux = p; } @@ -1515,22 +1702,18 @@ set_switch_stmt_execution_predicate (struct ipa_node_params *info, gimple last; tree op; int index; + struct agg_position_info aggpos; edge e; edge_iterator ei; size_t n; size_t case_idx; - tree parm; last = last_stmt (bb); if (!last || gimple_code (last) != GIMPLE_SWITCH) return; op = gimple_switch_index (last); - parm = unmodified_parm (last, op); - if (!parm) - return; - index = ipa_get_param_decl_index (info, parm); - if (index == -1) + if (!unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos)) return; FOR_EACH_EDGE (e, ei, bb->succs) @@ -1555,18 +1738,12 @@ set_switch_stmt_execution_predicate (struct ipa_node_params *info, if (!min && !max) p = true_predicate (); else if (!max) - p = add_condition (summary, index, - EQ_EXPR, - min); + p = add_condition (summary, index, &aggpos, EQ_EXPR, min); else { struct predicate p1, p2; - p1 = add_condition (summary, index, - GE_EXPR, - min); - p2 = add_condition (summary, index, - LE_EXPR, - max); + p1 = add_condition (summary, index, &aggpos, GE_EXPR, min); + p2 = add_condition (summary, index, &aggpos, LE_EXPR, max); p = and_predicates (summary->conds, &p1, &p2); } *(struct predicate *)e->aux @@ -1650,6 +1827,46 @@ compute_bb_predicates (struct cgraph_node *node, typedef struct predicate predicate_t; DEF_VEC_O (predicate_t); DEF_VEC_ALLOC_O (predicate_t, heap); +/* Return predicate specifying when the STMT might have result that is not + a compile time constant. */ + +static struct predicate +will_be_nonconstant_expr_predicate (struct ipa_node_params *info, + struct inline_summary *summary, + tree expr, + VEC (predicate_t, heap) *nonconstant_names) +{ + tree parm; + int index; + + while (UNARY_CLASS_P (expr)) + expr = TREE_OPERAND (expr, 0); + + parm = unmodified_parm (NULL, expr); + if (parm + && (index = ipa_get_param_decl_index (info, parm)) >= 0) + return add_condition (summary, index, NULL, CHANGED, NULL_TREE); + if (is_gimple_min_invariant (expr)) + return false_predicate (); + if (TREE_CODE (expr) == SSA_NAME) + return VEC_index (predicate_t, nonconstant_names, + SSA_NAME_VERSION (expr)); + if (BINARY_CLASS_P (expr)) + { + struct predicate p1 = will_be_nonconstant_expr_predicate (info, summary, TREE_OPERAND (expr, 0), nonconstant_names); + struct predicate p2; + if (true_predicate_p (&p1)) + return p1; + p2 = will_be_nonconstant_expr_predicate (info, summary, TREE_OPERAND (expr, 0), nonconstant_names); + return or_predicates (summary->conds, &p1, &p2); + } + else + { + debug_tree (expr); + gcc_unreachable (); + } + return false_predicate (); +} /* Return predicate specifying when the STMT might have result that is not @@ -1660,13 +1877,14 @@ will_be_nonconstant_predicate (struct ipa_node_params *info, struct inline_summary *summary, gimple stmt, VEC (predicate_t, heap) *nonconstant_names) - { struct predicate p = true_predicate (); ssa_op_iter iter; tree use; struct predicate op_non_const; bool is_load; + int base_index; + struct agg_position_info aggpos; /* What statments might be optimized away when their arguments are constant @@ -1682,23 +1900,18 @@ will_be_nonconstant_predicate (struct ipa_node_params *info, return p; is_load = gimple_vuse (stmt) != NULL; - /* Loads can be optimized when the value is known. */ if (is_load) { - tree op = gimple_assign_rhs1 (stmt); - tree base = get_base_address (op); - tree parm; - + tree op; gcc_assert (gimple_assign_single_p (stmt)); - if (!base) - return p; - parm = unmodified_parm (stmt, base); - if (!parm ) - return p; - if (ipa_get_param_decl_index (info, parm) < 0) + op = gimple_assign_rhs1 (stmt); + if (!unmodified_parm_or_parm_agg_item (info, stmt, op, &base_index, + &aggpos)) return p; } + else + base_index = -1; /* See if we understand all operands before we start adding conditionals. */ @@ -1712,37 +1925,38 @@ will_be_nonconstant_predicate (struct ipa_node_params *info, return p; /* If we know when operand is constant, we still can say something useful. */ - if (!true_predicate_p (VEC_index (predicate_t, nonconstant_names, - SSA_NAME_VERSION (use)))) + if (!true_predicate_p (&VEC_index (predicate_t, nonconstant_names, + SSA_NAME_VERSION (use)))) continue; return p; } - op_non_const = false_predicate (); + if (is_load) - { - tree parm = unmodified_parm - (stmt, get_base_address (gimple_assign_rhs1 (stmt))); - p = add_condition (summary, - ipa_get_param_decl_index (info, parm), - CHANGED, NULL); - op_non_const = or_predicates (summary->conds, &p, &op_non_const); - } + op_non_const = add_condition (summary, base_index, &aggpos, CHANGED, NULL); + else + op_non_const = false_predicate (); FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_USE) { tree parm = unmodified_parm (stmt, use); - if (parm && ipa_get_param_decl_index (info, parm) >= 0) - p = add_condition (summary, - ipa_get_param_decl_index (info, parm), - CHANGED, NULL); + int index; + + if (parm + && (index = ipa_get_param_decl_index (info, parm)) >= 0) + { + if (index != base_index) + p = add_condition (summary, index, NULL, CHANGED, NULL_TREE); + else + continue; + } else - p = *VEC_index (predicate_t, nonconstant_names, - SSA_NAME_VERSION (use)); + p = VEC_index (predicate_t, nonconstant_names, + SSA_NAME_VERSION (use)); op_non_const = or_predicates (summary->conds, &p, &op_non_const); } if (gimple_code (stmt) == GIMPLE_ASSIGN && TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME) VEC_replace (predicate_t, nonconstant_names, - SSA_NAME_VERSION (gimple_assign_lhs (stmt)), &op_non_const); + SSA_NAME_VERSION (gimple_assign_lhs (stmt)), op_non_const); return op_non_const; } @@ -1957,7 +2171,7 @@ estimate_function_body_sizes (struct cgraph_node *node, bool early) struct predicate false_p = false_predicate (); VEC_replace (predicate_t, nonconstant_names, SSA_NAME_VERSION (gimple_call_lhs (stmt)), - &false_p); + false_p); } if (ipa_node_params_vector) { @@ -1972,13 +2186,13 @@ estimate_function_body_sizes (struct cgraph_node *node, bool early) int prob = param_change_prob (stmt, i); gcc_assert (prob >= 0 && prob <= REG_BR_PROB_BASE); VEC_index (inline_param_summary_t, - es->param, i)->change_prob = prob; + es->param, i).change_prob = prob; } } es->call_stmt_size = this_size; es->call_stmt_time = this_time; - es->loop_depth = bb->loop_depth; + es->loop_depth = bb_loop_depth (bb); edge_set_predicate (edge, &bb_predicate); } @@ -2049,6 +2263,51 @@ estimate_function_body_sizes (struct cgraph_node *node, bool early) time = (time + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE; if (time > MAX_TIME) time = MAX_TIME; + + if (!early && nonconstant_names) + { + struct loop *loop; + loop_iterator li; + predicate loop_iterations = true_predicate (); + + calculate_dominance_info (CDI_DOMINATORS); + loop_optimizer_init (LOOPS_NORMAL + | LOOPS_HAVE_RECORDED_EXITS); + if (dump_file && (dump_flags & TDF_DETAILS)) + flow_loops_dump (dump_file, NULL, 0); + scev_initialize (); + FOR_EACH_LOOP (li, loop, 0) + { + VEC (edge, heap) *exits; + edge ex; + unsigned int j; + struct tree_niter_desc niter_desc; + + exits = get_loop_exit_edges (loop); + FOR_EACH_VEC_ELT (edge, exits, j, ex) + if (number_of_iterations_exit (loop, ex, &niter_desc, false) + && !is_gimple_min_invariant (niter_desc.niter)) + { + predicate will_be_nonconstant + = will_be_nonconstant_expr_predicate (parms_info, info, + niter_desc.niter, nonconstant_names); + if (!true_predicate_p (&will_be_nonconstant) + && !false_predicate_p (&will_be_nonconstant)) + /* This is slightly inprecise. We may want to represent each loop with + independent predicate. */ + loop_iterations = and_predicates (info->conds, &loop_iterations, &will_be_nonconstant); + } + VEC_free (edge, heap, exits); + } + if (!true_predicate_p (&loop_iterations)) + { + inline_summary (node)->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); + *inline_summary (node)->loop_iterations = loop_iterations; + } + scev_finalize (); + loop_optimizer_finalize (); + free_dominance_info (CDI_DOMINATORS); + } inline_summary (node)->self_time = time; inline_summary (node)->self_size = size; VEC_free (predicate_t, heap, nonconstant_names); @@ -2163,7 +2422,7 @@ struct gimple_opt_pass pass_inline_parameters = NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - TV_INLINE_HEURISTICS, /* tv_id */ + TV_INLINE_PARAMETERS, /* tv_id */ 0, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ @@ -2191,21 +2450,25 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *time, /* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and KNOWN_BINFOS. */ -static void +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 (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; if (!known_vals && !known_binfos) - return; + return false; - target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos); + target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos, + known_aggs); if (!target) - return; + 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) @@ -2215,40 +2478,11 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, * INLINE_TIME_SCALE * prob / REG_BR_PROB_BASE); *time -= time_diff; - /* TODO: This code is trying to benefit indirect calls that will be inlined later. - The logic however do not belong into local size/time estimates and can not be - done here, or the accounting of changes will get wrong and we result with - negative function body sizes. We need to introduce infrastructure for independent - benefits to the inliner. */ -#if 0 - struct cgraph_node *callee; - struct inline_summary *isummary; - int edge_size, edge_time, time_diff, size_diff; - callee = cgraph_get_node (target); if (!callee || !callee->analyzed) - return; + return false; isummary = inline_summary (callee); - if (!isummary->inlinable) - return; - - estimate_edge_size_and_time (ie, &edge_size, &edge_time, prob); - - /* Count benefit only from functions that definitely will be inlined - if additional context from NODE's caller were available. - - We just account overall size change by inlining. TODO: - we really need to add sort of benefit metrics for these kind of - cases. */ - if (edge_size - size_diff >= isummary->size * INLINE_SIZE_SCALE) - { - /* Subtract size and time that we added for edge IE. */ - *size -= edge_size - size_diff; - - /* Account inlined call. */ - *size += isummary->size * INLINE_SIZE_SCALE; - } -#endif + return isummary->inlinable; } @@ -2258,9 +2492,11 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, static void estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, + inline_hints *hints, clause_t possible_truths, VEC (tree, heap) *known_vals, - VEC (tree, heap) *known_binfos) + VEC (tree, heap) *known_binfos, + VEC (ipa_agg_jump_function_p, heap) *known_aggs) { struct cgraph_edge *e; for (e = node->callees; e; e = e->next_callee) @@ -2275,9 +2511,9 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); } else - estimate_calls_size_and_time (e->callee, size, time, + estimate_calls_size_and_time (e->callee, size, time, hints, possible_truths, - known_vals, known_binfos); + known_vals, known_binfos, known_aggs); } } for (e = node->indirect_calls; e; e = e->next_callee) @@ -2286,8 +2522,11 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, if (!es->predicate || evaluate_predicate (es->predicate, possible_truths)) { estimate_edge_size_and_time (e, size, time, REG_BR_PROB_BASE); - estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE, - known_vals, known_binfos); + 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; } } } @@ -2302,13 +2541,16 @@ estimate_node_size_and_time (struct cgraph_node *node, clause_t possible_truths, VEC (tree, heap) *known_vals, VEC (tree, heap) *known_binfos, + VEC (ipa_agg_jump_function_p, heap) *known_aggs, int *ret_size, int *ret_time, + inline_hints *ret_hints, VEC (inline_param_summary_t, heap) *inline_param_summary) { struct inline_summary *info = inline_summary (node); size_time_entry *e; int size = 0, time = 0; + inline_hints hints = 0; int i; if (dump_file @@ -2349,11 +2591,15 @@ estimate_node_size_and_time (struct cgraph_node *node, } + if (info->loop_iterations + && !evaluate_predicate (info->loop_iterations, possible_truths)) + hints |=INLINE_HINT_loop_iterations; + if (time > MAX_TIME * INLINE_TIME_SCALE) time = MAX_TIME * INLINE_TIME_SCALE; - estimate_calls_size_and_time (node, &size, &time, possible_truths, - known_vals, known_binfos); + 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; @@ -2365,6 +2611,8 @@ estimate_node_size_and_time (struct cgraph_node *node, *ret_time = time; if (ret_size) *ret_size = size; + if (ret_hints) + *ret_hints = hints; return; } @@ -2382,27 +2630,31 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node, { clause_t clause; - clause = evaluate_conditions_for_known_args (node, false, known_vals); - estimate_node_size_and_time (node, clause, known_vals, known_binfos, - ret_size, ret_time, + clause = evaluate_conditions_for_known_args (node, false, known_vals, NULL); + estimate_node_size_and_time (node, clause, known_vals, known_binfos, NULL, + ret_size, ret_time, NULL, NULL); } - /* Translate all conditions from callee representation into caller representation and symbolically evaluate predicate P into new predicate. - INFO is inline_summary of function we are adding predicate into, - CALLEE_INFO is summary of function predicate P is from. OPERAND_MAP is - array giving callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is - clausule of all callee conditions that may be true in caller context. - TOPLEV_PREDICATE is predicate under which callee is executed. */ + INFO is inline_summary of function we are adding predicate into, CALLEE_INFO + is summary of function predicate P is from. OPERAND_MAP is array giving + callee formal IDs the caller formal IDs. POSSSIBLE_TRUTHS is clausule of all + callee conditions that may be true in caller context. TOPLEV_PREDICATE is + predicate under which callee is executed. OFFSET_MAP is an array of of + offsets that need to be added to conditions, negative offset means that + conditions relying on values passed by reference have to be discarded + because they might not be preserved (and should be considered offset zero + for other purposes). */ static struct predicate remap_predicate (struct inline_summary *info, struct inline_summary *callee_info, struct predicate *p, VEC (int, heap) *operand_map, + VEC (int, heap) *offset_map, clause_t possible_truths, struct predicate *toplev_predicate) { @@ -2431,19 +2683,40 @@ remap_predicate (struct inline_summary *info, { struct condition *c; - c = VEC_index (condition, callee_info->conds, - cond - predicate_first_dynamic_condition); + c = &VEC_index (condition, callee_info->conds, + cond - predicate_first_dynamic_condition); /* See if we can remap condition operand to caller's operand. Otherwise give up. */ if (!operand_map || (int)VEC_length (int, operand_map) <= c->operand_num - || VEC_index (int, operand_map, c->operand_num) == -1) + || VEC_index (int, operand_map, c->operand_num) == -1 + || (!c->agg_contents + && VEC_index (int, offset_map, c->operand_num) != 0) + || (c->agg_contents && c->by_ref + && VEC_index (int, offset_map, c->operand_num) < 0)) cond_predicate = true_predicate (); else - cond_predicate = add_condition (info, - VEC_index (int, operand_map, - c->operand_num), - c->code, c->val); + { + struct agg_position_info ap; + HOST_WIDE_INT offset_delta = VEC_index (int, offset_map, + c->operand_num); + if (offset_delta < 0) + { + gcc_checking_assert (!c->agg_contents || !c->by_ref); + offset_delta = 0; + } + gcc_assert (!c->agg_contents + || c->by_ref + || offset_delta == 0); + ap.offset = c->offset + offset_delta; + ap.agg_contents = c->agg_contents; + ap.by_ref = c->by_ref; + cond_predicate = add_condition (info, + VEC_index (int, + operand_map, + c->operand_num), + &ap, c->code, c->val); + } } /* Fixed conditions remains same, construct single condition predicate. */ @@ -2520,10 +2793,10 @@ remap_edge_change_prob (struct cgraph_edge *inlined_edge, { int jf_formal_id = ipa_get_jf_pass_through_formal_id (jfunc); int prob1 = VEC_index (inline_param_summary_t, - es->param, i)->change_prob; + es->param, i).change_prob; int prob2 = VEC_index (inline_param_summary_t, - inlined_es->param, jf_formal_id)->change_prob; + inlined_es->param, jf_formal_id).change_prob; int prob = ((prob1 * prob2 + REG_BR_PROB_BASE / 2) / REG_BR_PROB_BASE); @@ -2531,7 +2804,7 @@ remap_edge_change_prob (struct cgraph_edge *inlined_edge, prob = 1; VEC_index (inline_param_summary_t, - es->param, i)->change_prob = prob; + es->param, i).change_prob = prob; } } } @@ -2550,6 +2823,7 @@ remap_edge_summaries (struct cgraph_edge *inlined_edge, struct inline_summary *info, struct inline_summary *callee_info, VEC (int, heap) *operand_map, + VEC (int, heap) *offset_map, clause_t possible_truths, struct predicate *toplev_predicate) { @@ -2566,7 +2840,8 @@ remap_edge_summaries (struct cgraph_edge *inlined_edge, if (es->predicate) { p = remap_predicate (info, callee_info, - es->predicate, operand_map, possible_truths, + es->predicate, operand_map, offset_map, + possible_truths, toplev_predicate); edge_set_predicate (e, &p); /* TODO: We should remove the edge for code that will be @@ -2583,7 +2858,8 @@ remap_edge_summaries (struct cgraph_edge *inlined_edge, } else remap_edge_summaries (inlined_edge, e->callee, info, callee_info, - operand_map, possible_truths, toplev_predicate); + operand_map, offset_map, possible_truths, + toplev_predicate); } for (e = node->indirect_calls; e; e = e->next_callee) { @@ -2594,8 +2870,8 @@ remap_edge_summaries (struct cgraph_edge *inlined_edge, if (es->predicate) { p = remap_predicate (info, callee_info, - es->predicate, operand_map, possible_truths, - toplev_predicate); + es->predicate, operand_map, offset_map, + possible_truths, toplev_predicate); edge_set_predicate (e, &p); /* TODO: We should remove the edge for code that will be optimized out, but we need to keep verifiers and tree-inline happy. @@ -2624,6 +2900,7 @@ inline_merge_summary (struct cgraph_edge *edge) clause_t clause = 0; /* not_inline is known to be false. */ size_time_entry *e; VEC (int, heap) *operand_map = NULL; + VEC (int, heap) *offset_map = NULL; int i; struct predicate toplev_predicate; struct predicate true_p = true_predicate (); @@ -2640,17 +2917,36 @@ inline_merge_summary (struct cgraph_edge *edge) int count = ipa_get_cs_argument_count (args); int i; - evaluate_properties_for_edge (edge, true, &clause, NULL, NULL); + evaluate_properties_for_edge (edge, true, &clause, NULL, NULL, NULL); if (count) - VEC_safe_grow_cleared (int, heap, operand_map, count); + { + VEC_safe_grow_cleared (int, heap, operand_map, count); + VEC_safe_grow_cleared (int, heap, offset_map, count); + } for (i = 0; i < count; i++) { struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, i); int map = -1; + /* TODO: handle non-NOPs when merging. */ - if (jfunc->type == IPA_JF_PASS_THROUGH - && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) - map = ipa_get_jf_pass_through_formal_id (jfunc); + if (jfunc->type == IPA_JF_PASS_THROUGH) + { + if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) + map = ipa_get_jf_pass_through_formal_id (jfunc); + if (!ipa_get_jf_pass_through_agg_preserved (jfunc)) + VEC_replace (int, offset_map, i, -1); + } + else if (jfunc->type == IPA_JF_ANCESTOR) + { + HOST_WIDE_INT offset = ipa_get_jf_ancestor_offset (jfunc); + if (offset >= 0 && offset < INT_MAX) + { + map = ipa_get_jf_ancestor_formal_id (jfunc); + if (!ipa_get_jf_ancestor_agg_preserved (jfunc)) + offset = -1; + VEC_replace (int, offset_map, i, offset); + } + } VEC_replace (int, operand_map, i, map); gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to))); } @@ -2658,7 +2954,8 @@ inline_merge_summary (struct cgraph_edge *edge) for (i = 0; VEC_iterate (size_time_entry, callee_info->entry, i, e); i++) { struct predicate p = remap_predicate (info, callee_info, - &e->predicate, operand_map, clause, + &e->predicate, operand_map, + offset_map, clause, &toplev_predicate); if (!false_predicate_p (&p)) { @@ -2680,14 +2977,29 @@ inline_merge_summary (struct cgraph_edge *edge) } } remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map, - clause, &toplev_predicate); - info->size = 0; - info->time = 0; - for (i = 0; VEC_iterate (size_time_entry, info->entry, i, e); i++) - info->size += e->size, info->time += e->time; - estimate_calls_size_and_time (to, &info->size, &info->time, - ~(clause_t)(1 << predicate_false_condition), - NULL, NULL); + offset_map, clause, &toplev_predicate); + if (callee_info->loop_iterations) + { + predicate p = remap_predicate (info, callee_info, + callee_info->loop_iterations, + operand_map, offset_map, + clause, + &toplev_predicate); + if (!false_predicate_p (&p) + && !true_predicate_p (&p)) + { + if (!info->loop_iterations) + { + info->loop_iterations + = (struct predicate *)pool_alloc (edge_predicate_pool); + *info->loop_iterations = p; + } + else + *info->loop_iterations = and_predicates (info->conds, + info->loop_iterations, + &p); + } + } inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); @@ -2697,12 +3009,30 @@ inline_merge_summary (struct cgraph_edge *edge) /* Similarly remove param summaries. */ VEC_free (inline_param_summary_t, heap, es->param); VEC_free (int, heap, operand_map); + VEC_free (int, heap, offset_map); +} + +/* For performance reasons inline_merge_summary is not updating overall size + and time. Recompute it. */ + +void +inline_update_overall_summary (struct cgraph_node *node) +{ + struct inline_summary *info = inline_summary (node); + size_time_entry *e; + int i; + info->size = 0; + info->time = 0; + for (i = 0; VEC_iterate (size_time_entry, info->entry, i, e); i++) + info->size += e->size, info->time += e->time; + estimate_calls_size_and_time (node, &info->size, &info->time, NULL, + ~(clause_t)(1 << predicate_false_condition), + NULL, NULL, NULL); info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; info->size = (info->size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; } - /* Estimate the time cost for the caller when inlining EDGE. Only to be called via estimate_edge_time, that handles the caching mechanism. @@ -2715,22 +3045,26 @@ do_estimate_edge_time (struct cgraph_edge *edge) { int time; int size; + inline_hints hints; gcov_type ret; struct cgraph_node *callee; clause_t clause; VEC (tree, heap) *known_vals; VEC (tree, heap) *known_binfos; + VEC (ipa_agg_jump_function_p, heap) *known_aggs; struct inline_edge_summary *es = inline_edge_summary (edge); callee = cgraph_function_or_thunk_node (edge->callee, NULL); gcc_checking_assert (edge->inline_failed); evaluate_properties_for_edge (edge, true, - &clause, &known_vals, &known_binfos); + &clause, &known_vals, &known_binfos, + &known_aggs); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, - &size, &time, es->param); + known_aggs, &size, &time, &hints, es->param); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); + VEC_free (ipa_agg_jump_function_p, heap, known_aggs); ret = (((gcov_type)time - es->call_stmt_time) * edge->frequency @@ -2744,13 +3078,15 @@ do_estimate_edge_time (struct cgraph_edge *edge) <= edge->uid) VEC_safe_grow_cleared (edge_growth_cache_entry, heap, edge_growth_cache, cgraph_edge_max_uid); - VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid)->time + VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).time = ret + (ret >= 0); ret_size = size - es->call_stmt_size; gcc_checking_assert (es->call_stmt_size); - VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid)->size + VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).size = ret_size + (ret_size >= 0); + VEC_index (edge_growth_cache_entry, edge_growth_cache, edge->uid).hints + = hints + 1; } return ret; } @@ -2767,6 +3103,7 @@ do_estimate_edge_growth (struct cgraph_edge *edge) clause_t clause; VEC (tree, heap) *known_vals; VEC (tree, heap) *known_binfos; + VEC (ipa_agg_jump_function_p, heap) *known_aggs; /* When we do caching, use do_estimate_edge_time to populate the entry. */ @@ -2775,7 +3112,7 @@ do_estimate_edge_growth (struct cgraph_edge *edge) do_estimate_edge_time (edge); size = VEC_index (edge_growth_cache_entry, edge_growth_cache, - edge->uid)->size; + edge->uid).size; gcc_checking_assert (size); return size - (size > 0); } @@ -2785,16 +3122,59 @@ do_estimate_edge_growth (struct cgraph_edge *edge) /* Early inliner runs without caching, go ahead and do the dirty work. */ gcc_checking_assert (edge->inline_failed); evaluate_properties_for_edge (edge, true, - &clause, &known_vals, &known_binfos); + &clause, &known_vals, &known_binfos, + &known_aggs); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, - &size, NULL, NULL); + known_aggs, &size, NULL, NULL, NULL); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); + VEC_free (ipa_agg_jump_function_p, heap, known_aggs); gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size); return size - inline_edge_summary (edge)->call_stmt_size; } +/* Estimate the growth of the caller when inlining EDGE. + Only to be called via estimate_edge_size. */ + +inline_hints +do_estimate_edge_hints (struct cgraph_edge *edge) +{ + inline_hints hints; + struct cgraph_node *callee; + clause_t clause; + VEC (tree, heap) *known_vals; + VEC (tree, heap) *known_binfos; + VEC (ipa_agg_jump_function_p, heap) *known_aggs; + + /* When we do caching, use do_estimate_edge_time to populate the entry. */ + + if (edge_growth_cache) + { + do_estimate_edge_time (edge); + hints = VEC_index (edge_growth_cache_entry, + edge_growth_cache, + edge->uid).hints; + gcc_checking_assert (hints); + return hints - 1; + } + + callee = cgraph_function_or_thunk_node (edge->callee, NULL); + + /* Early inliner runs without caching, go ahead and do the dirty work. */ + gcc_checking_assert (edge->inline_failed); + evaluate_properties_for_edge (edge, true, + &clause, &known_vals, &known_binfos, + &known_aggs); + estimate_node_size_and_time (callee, clause, known_vals, known_binfos, + known_aggs, NULL, NULL, &hints, NULL); + VEC_free (tree, heap, known_vals); + VEC_free (tree, heap, known_binfos); + VEC_free (ipa_agg_jump_function_p, heap, known_aggs); + return hints; +} + + /* Estimate self time of the function NODE after inlining EDGE. */ int @@ -3010,7 +3390,7 @@ read_inline_edge_summary (struct lto_input_block *ib, struct cgraph_edge *e) { VEC_safe_grow_cleared (inline_param_summary_t, heap, es->param, length); for (i = 0; i < length; i++) - VEC_index (inline_param_summary_t, es->param, i)->change_prob + VEC_index (inline_param_summary_t, es->param, i).change_prob = streamer_read_uhwi (ib); } } @@ -3044,13 +3424,14 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data, unsigned int index; struct cgraph_node *node; struct inline_summary *info; - lto_cgraph_encoder_t encoder; + lto_symtab_encoder_t encoder; struct bitpack_d bp; struct cgraph_edge *e; + predicate p; index = streamer_read_uhwi (&ib); - encoder = file_data->cgraph_node_encoder; - node = lto_cgraph_encoder_deref (encoder, index); + encoder = file_data->symtab_node_encoder; + node = cgraph (lto_symtab_encoder_deref (encoder, index)); info = inline_summary (node); info->estimated_stack_size @@ -3069,6 +3450,11 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data, c.operand_num = streamer_read_uhwi (&ib); c.code = (enum tree_code) streamer_read_uhwi (&ib); c.val = stream_read_tree (&ib, data_in); + bp = streamer_read_bitpack (&ib); + c.agg_contents = bp_unpack_value (&bp, 1); + c.by_ref = bp_unpack_value (&bp, 1); + if (c.agg_contents) + c.offset = streamer_read_uhwi (&ib); VEC_safe_push (condition, gc, info->conds, &c); } count2 = streamer_read_uhwi (&ib); @@ -3083,6 +3469,13 @@ inline_read_section (struct lto_file_decl_data *file_data, const char *data, VEC_safe_push (size_time_entry, gc, info->entry, &e); } + + p = read_predicate (&ib); + if (!true_predicate_p (&p)) + { + info->loop_iterations = (struct predicate *)pool_alloc (edge_predicate_pool); + *info->loop_iterations = p; + } for (e = node->callees; e; e = e->next_callee) read_inline_edge_summary (&ib, e); for (e = node->indirect_calls; e; e = e->next_callee) @@ -3164,7 +3557,7 @@ write_inline_edge_summary (struct output_block *ob, struct cgraph_edge *e) streamer_write_uhwi (ob, VEC_length (inline_param_summary_t, es->param)); for (i = 0; i < (int)VEC_length (inline_param_summary_t, es->param); i++) streamer_write_uhwi (ob, VEC_index (inline_param_summary_t, - es->param, i)->change_prob); + es->param, i).change_prob); } @@ -3173,24 +3566,25 @@ write_inline_edge_summary (struct output_block *ob, struct cgraph_edge *e) active, we don't need to write them twice. */ void -inline_write_summary (cgraph_node_set set, - varpool_node_set vset ATTRIBUTE_UNUSED) +inline_write_summary (void) { struct cgraph_node *node; + symtab_node snode; struct output_block *ob = create_output_block (LTO_section_inline_summary); - lto_cgraph_encoder_t encoder = ob->decl_state->cgraph_node_encoder; + lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder; unsigned int count = 0; int i; - for (i = 0; i < lto_cgraph_encoder_size (encoder); i++) - if (lto_cgraph_encoder_deref (encoder, i)->analyzed) + for (i = 0; i < lto_symtab_encoder_size (encoder); i++) + if (symtab_function_p (snode = lto_symtab_encoder_deref (encoder, i)) + && cgraph (snode)->analyzed) count++; streamer_write_uhwi (ob, count); - for (i = 0; i < lto_cgraph_encoder_size (encoder); i++) + for (i = 0; i < lto_symtab_encoder_size (encoder); i++) { - node = lto_cgraph_encoder_deref (encoder, i); - if (node->analyzed) + if (symtab_function_p (snode = lto_symtab_encoder_deref (encoder, i)) + && (node = cgraph (snode))->analyzed) { struct inline_summary *info = inline_summary (node); struct bitpack_d bp; @@ -3199,7 +3593,7 @@ inline_write_summary (cgraph_node_set set, size_time_entry *e; struct condition *c; - streamer_write_uhwi (ob, lto_cgraph_encoder_encode (encoder, node)); + streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, (symtab_node)node)); streamer_write_hwi (ob, info->estimated_self_stack_size); streamer_write_hwi (ob, info->self_size); streamer_write_hwi (ob, info->self_time); @@ -3212,6 +3606,12 @@ inline_write_summary (cgraph_node_set set, streamer_write_uhwi (ob, c->operand_num); streamer_write_uhwi (ob, c->code); stream_write_tree (ob, c->val, true); + bp = bitpack_create (ob->main_stream); + bp_pack_value (&bp, c->agg_contents, 1); + bp_pack_value (&bp, c->by_ref, 1); + streamer_write_bitpack (&bp); + if (c->agg_contents) + streamer_write_uhwi (ob, c->offset); } streamer_write_uhwi (ob, VEC_length (size_time_entry, info->entry)); for (i = 0; @@ -3222,6 +3622,7 @@ inline_write_summary (cgraph_node_set set, streamer_write_uhwi (ob, e->time); write_predicate (ob, &e->predicate); } + write_predicate (ob, info->loop_iterations); for (edge = node->callees; edge; edge = edge->next_callee) write_inline_edge_summary (ob, edge); for (edge = node->indirect_calls; edge; edge = edge->next_callee) @@ -3233,7 +3634,7 @@ inline_write_summary (cgraph_node_set set, destroy_output_block (ob); if (optimize && !flag_ipa_cp) - ipa_prop_write_jump_functions (set); + ipa_prop_write_jump_functions (); } @@ -3243,6 +3644,8 @@ void inline_free_summary (void) { struct cgraph_node *node; + if (inline_edge_summary_vec == NULL) + return; FOR_EACH_DEFINED_FUNCTION (node) reset_inline_summary (node); if (function_insertion_hook_holder) |