diff options
-rw-r--r-- | gcc/ChangeLog | 37 | ||||
-rw-r--r-- | gcc/ipa-cp.c | 31 | ||||
-rw-r--r-- | gcc/ipa-inline-analysis.c | 432 | ||||
-rw-r--r-- | gcc/ipa-inline.h | 11 | ||||
-rw-r--r-- | gcc/ipa-prop.h | 5 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/testsuite/gfortran.dg/pr48636.f90 | 37 |
7 files changed, 425 insertions, 133 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6eb422b0ca8..8a4bcb56487 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,40 @@ +2012-08-11 Martin Jambor <mjambor@suse.cz> + + PR fortran/48636 + * ipa-inline.h (condition): New fields offset, agg_contents and by_ref. + * ipa-inline-analysis.c (agg_position_info): New type. + (add_condition): New parameter aggpos, also store agg_contents, by_ref + and offset. + (dump_condition): Also dump aggregate conditions. + (evaluate_conditions_for_known_args): Also handle aggregate + conditions. New parameter known_aggs. + (evaluate_properties_for_edge): Gather known aggregate contents. + (inline_node_duplication_hook): Pass NULL known_aggs to + evaluate_conditions_for_known_args. + (unmodified_parm): Split into unmodified_parm and unmodified_parm_1. + (unmodified_parm_or_parm_agg_item): New function. + (set_cond_stmt_execution_predicate): Handle values passed in + aggregates. + (set_switch_stmt_execution_predicate): Likewise. + (will_be_nonconstant_predicate): Likewise. + (estimate_edge_devirt_benefit): Pass new parameter known_aggs to + ipa_get_indirect_edge_target. + (estimate_calls_size_and_time): New parameter known_aggs, pass it + recrsively to itself and to estimate_edge_devirt_benefit. + (estimate_node_size_and_time): New vector known_aggs, pass it o + functions which need it. + (remap_predicate): New parameter offset_map, use it to remap aggregate + conditions. + (remap_edge_summaries): New parameter offset_map, pass it recursively + to itself and to remap_predicate. + (inline_merge_summary): Also create and populate vector offset_map. + (do_estimate_edge_time): New vector of known aggregate contents, + passed to functions which need it. + (inline_read_section): Stream new fields of condition. + (inline_write_summary): Likewise. + * ipa-cp.c (ipa_get_indirect_edge_target): Also examine the aggregate + contents. Let all local callers pass NULL for known_aggs. + 2012-08-11 Jan Hubicka <jh@suse.cz> * lto-cgraph.c (output_cgraph): Rename to ... diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index b9184702aed..1f2ea92fb50 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1084,7 +1084,8 @@ propagate_constants_accross_call (struct cgraph_edge *cs) tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, VEC (tree, heap) *known_vals, - VEC (tree, heap) *known_binfos) + VEC (tree, heap) *known_binfos, + VEC (ipa_agg_jump_function_p, heap) *known_aggs) { int param_index = ie->indirect_info->param_index; HOST_WIDE_INT token, anc_offset; @@ -1096,8 +1097,26 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie, if (!ie->indirect_info->polymorphic) { - tree t = (VEC_length (tree, known_vals) > (unsigned int) param_index - ? VEC_index (tree, known_vals, param_index) : NULL); + tree t; + + if (ie->indirect_info->agg_contents) + { + if (VEC_length (ipa_agg_jump_function_p, known_aggs) + > (unsigned int) param_index) + { + struct ipa_agg_jump_function *agg; + agg = VEC_index (ipa_agg_jump_function_p, known_aggs, + param_index); + t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset, + ie->indirect_info->by_ref); + } + else + t = NULL; + } + else + t = (VEC_length (tree, known_vals) > (unsigned int) param_index + ? VEC_index (tree, known_vals, param_index) : NULL); + if (t && TREE_CODE (t) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL) @@ -1106,6 +1125,7 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie, return NULL_TREE; } + gcc_assert (!ie->indirect_info->agg_contents); token = ie->indirect_info->otr_token; anc_offset = ie->indirect_info->offset; otr_type = ie->indirect_info->otr_type; @@ -1156,7 +1176,8 @@ devirtualization_time_bonus (struct cgraph_node *node, struct inline_summary *isummary; tree target; - target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos); + target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos, + NULL); if (!target) continue; @@ -1673,7 +1694,7 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node, tree target; next_ie = ie->next_callee; - target = ipa_get_indirect_edge_target (ie, known_vals, NULL); + target = ipa_get_indirect_edge_target (ie, known_vals, NULL, NULL); if (target) ipa_make_edge_direct_to_target (ie, target); } diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 474f2a9af19..1a3afc45699 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -203,22 +203,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. */ @@ -228,6 +260,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); } @@ -519,6 +554,9 @@ dump_condition (FILE *f, conditions conditions, int cond) 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"); @@ -659,15 +697,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); @@ -679,16 +719,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 (val == error_mark_node && c->code != CHANGED) - val = NULL; + 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 (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) { @@ -711,13 +780,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; @@ -742,13 +813,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) @@ -761,17 +835,26 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p, es->param, 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); } @@ -917,8 +1000,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); @@ -1262,11 +1345,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 @@ -1285,13 +1368,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. @@ -1422,13 +1559,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 @@ -1440,12 +1576,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, @@ -1453,8 +1585,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)); @@ -1475,28 +1606,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; } @@ -1514,22 +1638,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) @@ -1554,18 +1674,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 @@ -1659,13 +1773,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 @@ -1681,23 +1796,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. */ @@ -1716,23 +1826,24 @@ will_be_nonconstant_predicate (struct ipa_node_params *info, 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)); @@ -2194,7 +2305,8 @@ static void 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; @@ -2202,7 +2314,8 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie, if (!known_vals && !known_binfos) return; - 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; @@ -2259,7 +2372,8 @@ static void estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, 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) @@ -2276,7 +2390,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size, int *time, else estimate_calls_size_and_time (e->callee, size, time, possible_truths, - known_vals, known_binfos); + known_vals, known_binfos, known_aggs); } } for (e = node->indirect_calls; e; e = e->next_callee) @@ -2286,7 +2400,7 @@ 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); estimate_edge_devirt_benefit (e, size, time, REG_BR_PROB_BASE, - known_vals, known_binfos); + known_vals, known_binfos, known_aggs); } } } @@ -2301,6 +2415,7 @@ 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, VEC (inline_param_summary_t, heap) *inline_param_summary) @@ -2352,7 +2467,7 @@ estimate_node_size_and_time (struct cgraph_node *node, time = MAX_TIME * INLINE_TIME_SCALE; estimate_calls_size_and_time (node, &size, &time, possible_truths, - known_vals, known_binfos); + known_vals, known_binfos, known_aggs); time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; @@ -2381,27 +2496,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, + 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); } - /* 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) { @@ -2436,13 +2555,34 @@ remap_predicate (struct inline_summary *info, 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. */ @@ -2549,6 +2689,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) { @@ -2565,7 +2706,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 @@ -2582,7 +2724,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) { @@ -2593,8 +2736,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. @@ -2623,6 +2766,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 (); @@ -2639,17 +2783,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))); } @@ -2657,7 +2820,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)) { @@ -2679,7 +2843,7 @@ inline_merge_summary (struct cgraph_edge *edge) } } remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map, - clause, &toplev_predicate); + offset_map, clause, &toplev_predicate); inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); @@ -2689,6 +2853,7 @@ 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 @@ -2707,7 +2872,7 @@ inline_update_overall_summary (struct cgraph_node *node) info->size += e->size, info->time += e->time; estimate_calls_size_and_time (node, &info->size, &info->time, ~(clause_t)(1 << predicate_false_condition), - NULL, NULL); + 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; } @@ -2729,17 +2894,20 @@ do_estimate_edge_time (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; 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, 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 @@ -2776,6 +2944,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. */ @@ -2794,11 +2963,13 @@ 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); 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; } @@ -3078,6 +3249,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); @@ -3223,6 +3399,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; diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h index fbd0b9982b7..db3f8d4e56b 100644 --- a/gcc/ipa-inline.h +++ b/gcc/ipa-inline.h @@ -28,9 +28,18 @@ along with GCC; see the file COPYING3. If not see typedef struct GTY(()) condition { + /* If agg_contents is set, this is the offset from which the used data was + loaded. */ + HOST_WIDE_INT offset; tree val; int operand_num; - enum tree_code code; + ENUM_BITFIELD(tree_code) code : 16; + /* Set if the used data were loaded from an aggregate parameter or from + data received by reference. */ + unsigned agg_contents : 1; + /* If agg_contents is set, this differentiates between loads from data + passed by reference and by value. */ + unsigned by_ref : 1; } condition; DEF_VEC_O (condition); diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 489e5d859b8..2d2a54be0cf 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -494,8 +494,9 @@ bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs, /* Indirect edge and binfo processing. */ tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, - VEC (tree, heap) *known_csts, - VEC (tree, heap) *known_binfs); + VEC (tree, heap) *, + VEC (tree, heap) *, + VEC (ipa_agg_jump_function_p, heap) *); struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree); /* Functions related to both. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 50c97413c15..5c5b9b52567 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2012-08-11 Martin Jambor <mjambor@suse.cz> + + PR fortran/48636 + * gfortran.dg/pr48636.f90: New test. + 2012-08-10 Jakub Jelinek <jakub@redhat.com> * gcc.dg/torture/vector-shuffle1.c (f): Pass vectors indirectly diff --git a/gcc/testsuite/gfortran.dg/pr48636.f90 b/gcc/testsuite/gfortran.dg/pr48636.f90 new file mode 100644 index 00000000000..44515ae9ad9 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr48636.f90 @@ -0,0 +1,37 @@ +! { dg-do compile } +! { dg-options "-O3 -fdump-ipa-inline" } + +module foo + implicit none +contains + subroutine bar(a,x) + real, dimension(:,:), intent(in) :: a + real, intent(out) :: x + integer :: i,j + + x = 0 + do j=1,ubound(a,2) + do i=1,ubound(a,1) + x = x + a(i,j)**2 + end do + end do + end subroutine bar +end module foo + +program main + use foo + implicit none + real, dimension(2,3) :: a + real :: x + integer :: i + + data a /1.0, 2.0, 3.0, -1.0, -2.0, -3.0/ + + do i=1,2000000 + call bar(a,x) + end do + print *,x +end program main + +! { dg-final { scan-ipa-dump "bar\[^\\n\]*inline copy in MAIN" "inline" } } +! { dg-final { cleanup-ipa-dump "inline" } } |