From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11241 invoked by alias); 3 Aug 2012 15:50:59 -0000 Received: (qmail 11193 invoked by uid 22791); 3 Aug 2012 15:50:49 -0000 X-SWARE-Spam-Status: No, hits=-5.3 required=5.0 tests=AWL,BAYES_00,KHOP_RCVD_UNTRUST,RCVD_IN_DNSWL_HI,TW_TM X-Spam-Check-By: sourceware.org Received: from cantor2.suse.de (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 03 Aug 2012 15:50:21 +0000 Received: from relay2.suse.de (unknown [195.135.220.254]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx2.suse.de (Postfix) with ESMTP id 155A399430; Fri, 3 Aug 2012 17:50:20 +0200 (CEST) Date: Fri, 03 Aug 2012 15:50:00 -0000 From: Martin Jambor To: GCC Patches , Jan Hubicka Subject: Re: [PATCH 2/3] Incorporate aggregate jump functions into inlining analysis Message-ID: <20120803155016.GA29154@virgil.arch.suse.de> Mail-Followup-To: GCC Patches , Jan Hubicka References: <20120802192811.GC20202@virgil.arch.suse.de> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <20120802192811.GC20202@virgil.arch.suse.de> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org X-SW-Source: 2012-08/txt/msg00184.txt.bz2 On Thu, Aug 02, 2012 at 09:28:11PM +0200, Martin Jambor wrote: > Hi, > > this patch uses the aggregate jump functions created by the previous > patch in the series to determine benefits of inlining a particular > call graph edge. It has not changed much since the last time I posted > it, except for the presence of by_ref flags and removal of checks > required by TBAA which we now do not use. > > The patch works in fairly straightforward way. It ads two flags to > struct condition to specify it actually refers to an aggregate passed > by value or something passed by reference, in both cases at a > particular offset, also newly stored in the structures. Functions > which build the predicates specifying under which conditions CFG edges > will be taken or individual statements are actually executed then > simply also look whether a value comes from an aggregate passed to us > in a parameter (either by value or reference) and if so, create > appropriate conditions. Later on, predicates are evaluated as before, > we only also look at aggregate contents of the jump functions of the > edge we are considering to inline when evaluating the predicates, and > also remap the offsets of the jump functions when remapping over an > ancestor jump function. > > This patch alone makes us inline the function bar in testcase of PR > 48636 in comment #4. It also passes bootstrap and testing on > x86_64-linux. I successfully LTO-built Firefox with it too. > And this version even passes (C, C++ and Fortran) LTO bootstrap because it does not stream bits with undefined values (condition->by_ref). Thanks for all comments and suggestions, Martin 2012-07-31 Martin Jambor 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. * testsuite/gfortran.dg/pr48636.f90: New test. Index: src/gcc/ipa-inline.h =================================================================== *** src.orig/gcc/ipa-inline.h --- src/gcc/ipa-inline.h *************** along with GCC; see the file COPYING3. *** 28,36 **** --- 28,45 ---- 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; + /* 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); Index: src/gcc/ipa-inline-analysis.c =================================================================== *** src.orig/gcc/ipa-inline-analysis.c --- src/gcc/ipa-inline-analysis.c *************** not_inlined_predicate (void) *** 203,224 **** return single_cond_predicate (predicate_not_inlined_condition); } ! /* Add condition to condition list CONDS. */ static struct predicate add_condition (struct inline_summary *summary, int operand_num, enum tree_code code, tree val) { int i; struct condition *c; struct condition new_cond; for (i = 0; VEC_iterate (condition, summary->conds, i, c); i++) { if (c->operand_num == operand_num && c->code == code ! && c->val == val) return single_cond_predicate (i + predicate_first_dynamic_condition); } /* Too many conditions. Give up and return constant true. */ --- 203,256 ---- 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. */ + + 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->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. */ *************** add_condition (struct inline_summary *su *** 228,233 **** --- 260,268 ---- 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); } *************** dump_condition (FILE *f, conditions cond *** 519,524 **** --- 554,561 ---- c = VEC_index (condition, conditions, cond - predicate_first_dynamic_condition); fprintf (f, "op%i", c->operand_num); + if (c->agg_contents) + fprintf (f, "[offset: " HOST_WIDE_INT_PRINT_DEC "]", c->offset); if (c->code == IS_NOT_CONSTANT) { fprintf (f, " not constant"); *************** edge_set_predicate (struct cgraph_edge * *** 659,673 **** /* 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. 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) { clause_t clause = inline_p ? 0 : 1 << predicate_not_inlined_condition; struct inline_summary *info = inline_summary (node); --- 696,712 ---- /* KNOWN_VALS is partial mapping of parameters of NODE to constant values. ! 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, ! 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); *************** evaluate_conditions_for_known_args (stru *** 679,694 **** 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; ! if (val == error_mark_node && c->code != CHANGED) ! val = NULL; if (!val) { --- 718,761 ---- 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 (we assume ! known_aggs vector, if non-NULL, has the same length as ! known_vals). */ ! gcc_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 (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) { *************** evaluate_conditions_for_known_args (stru *** 711,723 **** 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) { 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; if (clause_ptr) *clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition; --- 778,792 ---- 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, ! 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; *************** evaluate_properties_for_edge (struct cgr *** 742,754 **** if (count && (info->conds || known_vals_ptr)) VEC_safe_grow_cleared (tree, heap, known_vals, 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)); if (cst) { if (known_vals && TREE_CODE (cst) != TREE_BINFO) --- 811,826 ---- 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++) { ! 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) *************** evaluate_properties_for_edge (struct cgr *** 761,777 **** es->param, i)->change_prob) VEC_replace (tree, known_vals, i, error_mark_node); } } if (clause_ptr) *clause_ptr = evaluate_conditions_for_known_args (callee, inline_p, ! known_vals); if (known_vals_ptr) *known_vals_ptr = known_vals; else VEC_free (tree, heap, known_vals); } --- 833,857 ---- es->param, i)->change_prob) VEC_replace (tree, known_vals, i, error_mark_node); + /* TODO: When IPA-CP starts 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_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); } *************** inline_node_duplication_hook (struct cgr *** 917,924 **** } } } ! possible_truths = evaluate_conditions_for_known_args (dst, ! false, known_vals); VEC_free (tree, heap, known_vals); account_size_time (info, 0, 0, &true_pred); --- 997,1004 ---- } } } ! 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); *************** mark_modified (ao_ref *ao ATTRIBUTE_UNUS *** 1262,1272 **** return true; } ! /* If OP reffers to value of function parameter, return ! the corresponding parameter. */ static tree ! unmodified_parm (gimple stmt, tree op) { /* SSA_NAME referring to parm default def? */ if (TREE_CODE (op) == SSA_NAME --- 1342,1352 ---- return true; } ! /* If OP refers to value of function parameter, return the corresponding ! parameter. */ static tree ! unmodified_parm_1 (gimple stmt, tree op) { /* SSA_NAME referring to parm default def? */ if (TREE_CODE (op) == SSA_NAME *************** unmodified_parm (gimple stmt, tree op) *** 1285,1297 **** if (!modified) return op; } ! /* Assignment from a parameter? */ 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; } /* See if statement might disappear after inlining. --- 1365,1431 ---- if (!modified) return op; } ! 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_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. *************** set_cond_stmt_execution_predicate (struc *** 1422,1427 **** --- 1556,1562 ---- gimple last; tree op; int index; + struct agg_position_info aggpos; enum tree_code code, inverted_code; edge e; edge_iterator ei; *************** set_cond_stmt_execution_predicate (struc *** 1440,1451 **** /* TODO: handle conditionals like var = op0 < 4; if (var != 0). */ ! parm = unmodified_parm (last, op); ! if (parm) { - index = ipa_get_param_decl_index (info, parm); - if (index == -1) - return; code = gimple_cond_code (last); inverted_code = invert_tree_comparison (code, --- 1575,1582 ---- /* TODO: handle conditionals like var = op0 < 4; if (var != 0). */ ! if (unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos)) { code = gimple_cond_code (last); inverted_code = invert_tree_comparison (code, *************** set_cond_stmt_execution_predicate (struc *** 1453,1460 **** FOR_EACH_EDGE (e, ei, bb->succs) { ! struct predicate p = add_condition (summary, ! index, e->flags & EDGE_TRUE_VALUE ? code : inverted_code, gimple_cond_rhs (last)); --- 1584,1590 ---- FOR_EACH_EDGE (e, ei, bb->succs) { ! struct predicate p = add_condition (summary, index, &aggpos, e->flags & EDGE_TRUE_VALUE ? code : inverted_code, gimple_cond_rhs (last)); *************** set_cond_stmt_execution_predicate (struc *** 1480,1485 **** --- 1610,1616 ---- || gimple_call_num_args (set_stmt) != 1) return; op2 = gimple_call_arg (set_stmt, 0); + /* TODO: Use unmodified_parm_or_parm_agg_item also here. */ base = get_base_address (op2); parm = unmodified_parm (set_stmt, base ? base : op2); if (!parm) *************** set_cond_stmt_execution_predicate (struc *** 1493,1502 **** FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE) { ! struct predicate p = add_condition (summary, ! index, ! IS_NOT_CONSTANT, ! NULL); e->aux = pool_alloc (edge_predicate_pool); *(struct predicate *)e->aux = p; } --- 1624,1631 ---- FOR_EACH_EDGE (e, ei, bb->succs) if (e->flags & EDGE_FALSE_VALUE) { ! struct predicate p = add_condition (summary, index, NULL, ! IS_NOT_CONSTANT, NULL_TREE); e->aux = pool_alloc (edge_predicate_pool); *(struct predicate *)e->aux = p; } *************** set_switch_stmt_execution_predicate (str *** 1514,1535 **** gimple last; tree op; int index; 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) return; FOR_EACH_EDGE (e, ei, bb->succs) --- 1643,1660 ---- gimple last; tree op; int index; + struct agg_position_info aggpos; edge e; edge_iterator ei; size_t n; size_t case_idx; last = last_stmt (bb); if (!last || gimple_code (last) != GIMPLE_SWITCH) return; op = gimple_switch_index (last); ! if (!unmodified_parm_or_parm_agg_item (info, last, op, &index, &aggpos)) return; FOR_EACH_EDGE (e, ei, bb->succs) *************** set_switch_stmt_execution_predicate (str *** 1554,1571 **** if (!min && !max) p = true_predicate (); else if (!max) ! p = add_condition (summary, index, ! EQ_EXPR, ! min); else { struct predicate p1, p2; ! p1 = add_condition (summary, index, ! GE_EXPR, ! min); ! p2 = add_condition (summary, index, ! LE_EXPR, ! max); p = and_predicates (summary->conds, &p1, &p2); } *(struct predicate *)e->aux --- 1679,1690 ---- if (!min && !max) p = true_predicate (); else if (!max) ! p = add_condition (summary, index, &aggpos, EQ_EXPR, min); else { struct predicate p1, p2; ! 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 *************** will_be_nonconstant_predicate (struct ip *** 1659,1671 **** 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; /* What statments might be optimized away when their arguments are constant --- 1778,1791 ---- 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 *************** will_be_nonconstant_predicate (struct ip *** 1681,1703 **** 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; ! 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) return p; } /* See if we understand all operands before we start adding conditionals. */ --- 1801,1818 ---- return p; is_load = gimple_vuse (stmt) != NULL; /* Loads can be optimized when the value is known. */ if (is_load) { ! tree op; gcc_assert (gimple_assign_single_p (stmt)); ! 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. */ *************** will_be_nonconstant_predicate (struct ip *** 1716,1738 **** 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); ! } 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); else p = *VEC_index (predicate_t, nonconstant_names, SSA_NAME_VERSION (use)); --- 1831,1854 ---- continue; return p; } ! if (is_load) ! 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); ! 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)); *************** static void *** 2194,2200 **** estimate_edge_devirt_benefit (struct cgraph_edge *ie, int *size, int *time, int prob, VEC (tree, heap) *known_vals, ! VEC (tree, heap) *known_binfos) { tree target; int time_diff, size_diff; --- 2310,2317 ---- 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; *************** estimate_edge_devirt_benefit (struct cgr *** 2202,2208 **** if (!known_vals && !known_binfos) return; ! target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos); if (!target) return; --- 2319,2326 ---- if (!known_vals && !known_binfos) return; ! target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos, ! known_aggs); if (!target) return; *************** static void *** 2259,2265 **** 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) { struct cgraph_edge *e; for (e = node->callees; e; e = e->next_callee) --- 2377,2384 ---- 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 (ipa_agg_jump_function_p, heap) *known_aggs) { struct cgraph_edge *e; for (e = node->callees; e; e = e->next_callee) *************** estimate_calls_size_and_time (struct cgr *** 2276,2282 **** else estimate_calls_size_and_time (e->callee, size, time, possible_truths, ! known_vals, known_binfos); } } for (e = node->indirect_calls; e; e = e->next_callee) --- 2395,2401 ---- else estimate_calls_size_and_time (e->callee, size, time, possible_truths, ! known_vals, known_binfos, known_aggs); } } for (e = node->indirect_calls; e; e = e->next_callee) *************** estimate_calls_size_and_time (struct cgr *** 2286,2292 **** { 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); } } } --- 2405,2411 ---- { 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_aggs); } } } *************** estimate_node_size_and_time (struct cgra *** 2301,2306 **** --- 2420,2426 ---- 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) *************** estimate_node_size_and_time (struct cgra *** 2352,2358 **** time = MAX_TIME * INLINE_TIME_SCALE; estimate_calls_size_and_time (node, &size, &time, possible_truths, ! known_vals, known_binfos); time = (time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; size = (size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; --- 2472,2478 ---- time = MAX_TIME * INLINE_TIME_SCALE; estimate_calls_size_and_time (node, &size, &time, 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; *************** estimate_ipcp_clone_size_and_time (struc *** 2381,2407 **** { 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, 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. */ static struct predicate remap_predicate (struct inline_summary *info, struct inline_summary *callee_info, struct predicate *p, VEC (int, heap) *operand_map, clause_t possible_truths, struct predicate *toplev_predicate) { --- 2501,2531 ---- { clause_t clause; ! 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. 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) { *************** remap_predicate (struct inline_summary * *** 2436,2448 **** Otherwise give up. */ if (!operand_map || (int)VEC_length (int, operand_map) <= c->operand_num ! || VEC_index (int, operand_map, c->operand_num) == -1) cond_predicate = true_predicate (); else ! cond_predicate = add_condition (info, ! VEC_index (int, operand_map, ! c->operand_num), ! c->code, c->val); } /* Fixed conditions remains same, construct single condition predicate. */ --- 2560,2593 ---- Otherwise give up. */ if (!operand_map || (int)VEC_length (int, operand_map) <= c->operand_num ! || 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 ! { ! 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. */ *************** remap_edge_summaries (struct cgraph_edg *** 2549,2554 **** --- 2694,2700 ---- 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) { *************** remap_edge_summaries (struct cgraph_edg *** 2565,2571 **** if (es->predicate) { p = remap_predicate (info, callee_info, ! es->predicate, operand_map, possible_truths, toplev_predicate); edge_set_predicate (e, &p); /* TODO: We should remove the edge for code that will be --- 2711,2718 ---- if (es->predicate) { p = remap_predicate (info, callee_info, ! 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 *************** remap_edge_summaries (struct cgraph_edg *** 2582,2588 **** } else remap_edge_summaries (inlined_edge, e->callee, info, callee_info, ! operand_map, possible_truths, toplev_predicate); } for (e = node->indirect_calls; e; e = e->next_callee) { --- 2729,2736 ---- } else remap_edge_summaries (inlined_edge, e->callee, info, callee_info, ! operand_map, offset_map, possible_truths, ! toplev_predicate); } for (e = node->indirect_calls; e; e = e->next_callee) { *************** remap_edge_summaries (struct cgraph_edg *** 2593,2600 **** if (es->predicate) { p = remap_predicate (info, callee_info, ! es->predicate, operand_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. --- 2741,2748 ---- if (es->predicate) { p = remap_predicate (info, callee_info, ! 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. *************** inline_merge_summary (struct cgraph_edge *** 2623,2628 **** --- 2771,2777 ---- 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 (); *************** inline_merge_summary (struct cgraph_edge *** 2639,2655 **** int count = ipa_get_cs_argument_count (args); int i; ! evaluate_properties_for_edge (edge, true, &clause, NULL, NULL); if (count) ! VEC_safe_grow_cleared (int, heap, operand_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); VEC_replace (int, operand_map, i, map); gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to))); } --- 2788,2822 ---- int count = ipa_get_cs_argument_count (args); int i; ! 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, 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) ! { ! 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, operand_map, i, map); gcc_assert (map < ipa_get_param_count (IPA_NODE_REF (to))); } *************** inline_merge_summary (struct cgraph_edge *** 2657,2663 **** 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, &toplev_predicate); if (!false_predicate_p (&p)) { --- 2824,2831 ---- 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, ! offset_map, clause, &toplev_predicate); if (!false_predicate_p (&p)) { *************** inline_merge_summary (struct cgraph_edge *** 2679,2692 **** } } 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); inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); --- 2847,2860 ---- } } remap_edge_summaries (edge, edge->callee, info, callee_info, operand_map, ! offset_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, NULL); inline_update_callee_summaries (edge->callee, inline_edge_summary (edge)->loop_depth); *************** inline_merge_summary (struct cgraph_edge *** 2696,2701 **** --- 2864,2870 ---- /* 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); info->time = (info->time + INLINE_TIME_SCALE / 2) / INLINE_TIME_SCALE; info->size = (info->size + INLINE_SIZE_SCALE / 2) / INLINE_SIZE_SCALE; *************** do_estimate_edge_time (struct cgraph_edg *** 2719,2735 **** clause_t clause; VEC (tree, heap) *known_vals; VEC (tree, heap) *known_binfos; 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); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, ! &size, &time, es->param); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); ret = (((gcov_type)time - es->call_stmt_time) * edge->frequency --- 2888,2907 ---- 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, ! &known_aggs); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, ! 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 *************** do_estimate_edge_growth (struct cgraph_e *** 2766,2771 **** --- 2938,2944 ---- 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. */ *************** do_estimate_edge_growth (struct cgraph_e *** 2784,2794 **** /* 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); estimate_node_size_and_time (callee, clause, known_vals, known_binfos, ! &size, NULL, NULL); VEC_free (tree, heap, known_vals); VEC_free (tree, heap, known_binfos); gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size); return size - inline_edge_summary (edge)->call_stmt_size; } --- 2957,2969 ---- /* 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, &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; } *************** inline_read_section (struct lto_file_dec *** 3068,3073 **** --- 3243,3253 ---- 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); *************** inline_write_summary (cgraph_node_set se *** 3211,3216 **** --- 3391,3402 ---- 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; Index: src/gcc/ipa-cp.c =================================================================== *** src.orig/gcc/ipa-cp.c --- src/gcc/ipa-cp.c *************** propagate_constants_accross_call (struct *** 1084,1090 **** tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, VEC (tree, heap) *known_vals, ! VEC (tree, heap) *known_binfos) { int param_index = ie->indirect_info->param_index; HOST_WIDE_INT token, anc_offset; --- 1084,1091 ---- tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, VEC (tree, heap) *known_vals, ! 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; *************** ipa_get_indirect_edge_target (struct cgr *** 1096,1103 **** if (!ie->indirect_info->polymorphic) { ! tree 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) --- 1097,1122 ---- if (!ie->indirect_info->polymorphic) { ! 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) *************** ipa_get_indirect_edge_target (struct cgr *** 1106,1111 **** --- 1125,1131 ---- 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; *************** devirtualization_time_bonus (struct cgra *** 1156,1162 **** struct inline_summary *isummary; tree target; ! target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos); if (!target) continue; --- 1176,1183 ---- struct inline_summary *isummary; tree target; ! target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos, ! NULL); if (!target) continue; *************** ipcp_discover_new_direct_edges (struct c *** 1673,1679 **** tree target; next_ie = ie->next_callee; ! target = ipa_get_indirect_edge_target (ie, known_vals, NULL); if (target) ipa_make_edge_direct_to_target (ie, target); } --- 1694,1700 ---- tree target; 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); } Index: src/gcc/ipa-prop.h =================================================================== *** src.orig/gcc/ipa-prop.h --- src/gcc/ipa-prop.h *************** bool ipa_propagate_indirect_call_infos ( *** 494,501 **** /* 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); struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree); /* Functions related to both. */ --- 494,502 ---- /* Indirect edge and binfo processing. */ tree ipa_get_indirect_edge_target (struct cgraph_edge *ie, ! 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. */ Index: src/gcc/testsuite/gfortran.dg/pr48636.f90 =================================================================== *** /dev/null --- src/gcc/testsuite/gfortran.dg/pr48636.f90 *************** *** 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" } }