From patchwork Fri Jun 1 00:02:50 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 162259 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 3AB53B6FD3 for ; Fri, 1 Jun 2012 11:25:13 +1000 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1339118713; h=Comment: DomainKey-Signature:Received:Received:Received:Received:Received: Received:Resent-From:Resent-Date:Resent-Message-ID:Resent-To: Message-Id:User-Agent:Date:From:To:Cc:Subject:References: Content-Disposition:Mailing-List:Precedence:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:Sender: Delivered-To; bh=099WtDghosTBQZwZl34sLY+wnwU=; b=usE4+++zsMqpi7l JoAIOk5LmDOyPgAsuf44jIBlgOx9Va+UpZSQgGCAJyryeRdADu+NS4NzecS8x8YI IgPECiBFZKfnPJ7S9zG+Y6T5SDixA4Y45VTvCFUkEfMzEGxRJwdg/mSSSx4N0tPR mfdytXE0r/U2o/HOXPn80ouO3JTo= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:Received:Received:Resent-From:Resent-Date:Resent-Message-ID:Resent-To:Message-Id:User-Agent:Date:From:To:Cc:Subject:References:Content-Disposition:X-detected-operating-system:X-Received-From:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=QvY5IZSdAVz4d2CgGBzouTySXznFsLE2sgJlgop2GAXMXKDbID1s8l0cqX8b6O q+c7pi3DZQIIVI3T/1CjYGqN3gjoX0at3IDg7YIePvIMcVYqAXcmU3v3YQ/+Nwth /jQm9hgaQuqiu4Ob38NlbRU+Gv8tRDs2tgBzXm0jC16NI=; Received: (qmail 10980 invoked by alias); 1 Jun 2012 01:25:01 -0000 Received: (qmail 10458 invoked by uid 22791); 1 Jun 2012 01:24:48 -0000 X-SWARE-Spam-Status: No, hits=-6.1 required=5.0 tests=AWL, BAYES_00, KHOP_RCVD_UNTRUST, KHOP_THREADED, RCVD_IN_DNSWL_HI, RCVD_IN_HOSTKARMA_W, TW_JF, TW_TM X-Spam-Check-By: sourceware.org Received: from eggs.gnu.org (HELO eggs.gnu.org) (208.118.235.92) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 01 Jun 2012 01:24:26 +0000 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SaGbE-0003Ui-Mf for gcc-patches@gcc.gnu.org; Thu, 31 May 2012 21:24:25 -0400 Received: from cantor2.suse.de ([195.135.220.15]:60547 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SaGbE-0003Lq-1q for gcc-patches@gcc.gnu.org; Thu, 31 May 2012 21:24:20 -0400 Received: from relay1.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 8551A9A3F6 for ; Fri, 1 Jun 2012 03:23:47 +0200 (CEST) Resent-From: Martin Jambor Resent-Date: Fri, 1 Jun 2012 03:23:47 +0200 Resent-Message-ID: <20120601012347.GD8689@virgil.arch.suse.de> Resent-To: GCC Patches Message-Id: <20120601000319.850117646@virgil.suse.cz> User-Agent: quilt/0.48-20.3.1 Date: Fri, 01 Jun 2012 02:02:50 +0200 From: Martin Jambor To: GCC Patches Cc: Jan Hubicka Subject: [PATCH 3/5] Use aggregate jump functions in indirect inlining References: <20120601000247.795996744@virgil.suse.cz> Content-Disposition: inline; filename=remove_member_ptr_jfuncs.diff X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 X-Received-From: 195.135.220.15 X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, this patch uses the aggregate jump functions collected by the previous one to do indirect inlining. It can do stuff we could not do before (see the testcase) and also removes the special constant jump functions representing C++ function member pointers and represents that information with ordinary aggregate jump functions and is also able to indirectly inline through them. I hoped I'd be able to get rid of type_like_member_ptr_p altogether but so far I did not and still use it when examining indirect calls which are from member pointers (we need to match slightly complex pattern, see ipa_analyze_indirect_call_uses). The reason is that there are architectures that store the bit deciding whether the pointer represents a normal or virtual function in delta, not in the function pointer field itself and thus I'd need to check values of two aggregate fields to make sure we do not inline wrong function. For now, I needed to moved on and so kept the type check as it is. It is however ugly and I plan to return to this later. The patch passed bootstrap and testing on x86_64-linux. Thanks, Martin 2012-05-30 Martin Jambor * cgraph.h (cgraph_indirect_call_info): Field anc_offset renamed to offset. New field agg_contents. * ipa-prop.h (jump_func_type): Removed IPA_JF_CONST_MEMBER_PTR. (ipa_member_ptr_cst): Removed. (ipa_jump_func): Removed field member_cst. (ipa_find_agg_cst_for_param): Declare. (ipa_get_jf_member_ptr_pfn): Removed. * ipa-prop.c (ipa_print_node_jump_functions_for_edge): Do not dumo const member functions. (ipa_set_jf_member_ptr_cst): Removed. (type_like_member_ptr_p): Also check field position are known and sane. (determine_cst_member_ptr): Removed. (ipa_compute_jump_functions_for_edge): Remove creation of member pointer jump functions. Simplify. (ipa_get_member_ptr_load_param): Incorporate into ipa_get_stmt_member_ptr_load_param, also return offset of the required field. Update the remaining caller. (ipa_note_param_call): Initialize new fields of cgraph_indirect_call_info. (ipa_analyze_indirect_call_uses): Also look for simple pointers loaded from aggregates. In such cases, store offset of the called field. (ipa_find_agg_cst_for_param): New function. (try_make_edge_direct_simple_call): Handle called aggregate values. (update_indirect_edges_after_inlining): Update after various identifier and parameter changes. (ipa_write_jump_function): Do not stream member pointer constant jump functions. (ipa_read_jump_function): Likewise. (ipa_write_indirect_edge_info): Stream new cgraph_indirect_call_info fields. (ipa_read_indirect_edge_info): Likewise. * testsuite/gcc.dg/ipa/iinline-4.c: New test. Index: src/gcc/cgraph.h =================================================================== --- src.orig/gcc/cgraph.h +++ src/gcc/cgraph.h @@ -338,9 +338,11 @@ typedef enum cgraph_inline_failed_enum { struct GTY(()) cgraph_indirect_call_info { - /* Offset accumulated from ancestor jump functions of inlined call graph - edges. */ - HOST_WIDE_INT anc_offset; + /* When polymorphic is set, this field contains offset where the object which + was actually used in the polymorphic resides within a larger structure. + If agg_contents is set, the field contains the offset within the aggregate + from which the address to call was loaded. */ + HOST_WIDE_INT offset; /* OBJ_TYPE_REF_TOKEN of a polymorphic call (if polymorphic is set). */ HOST_WIDE_INT otr_token; /* Type of the object from OBJ_TYPE_REF_OBJECT. */ @@ -353,6 +355,9 @@ struct GTY(()) cgraph_indirect_call_info /* Set when the call is a virtual call with the parameter being the associated object pointer rather than a simple direct call. */ unsigned polymorphic : 1; + /* Set when the call is a call of a pointer loaded from contents of an + aggregate at offset. */ + unsigned agg_contents : 1; }; struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge { Index: src/gcc/ipa-prop.c =================================================================== --- src.orig/gcc/ipa-prop.c +++ src/gcc/ipa-prop.c @@ -182,14 +182,6 @@ ipa_print_node_jump_functions_for_edge ( } fprintf (f, "\n"); } - else if (type == IPA_JF_CONST_MEMBER_PTR) - { - fprintf (f, "CONST MEMBER PTR: "); - print_generic_expr (f, jump_func->value.member_cst.pfn, 0); - fprintf (f, ", "); - print_generic_expr (f, jump_func->value.member_cst.delta, 0); - fprintf (f, "\n"); - } else if (type == IPA_JF_PASS_THROUGH) { fprintf (f, "PASS THROUGH: "); @@ -356,18 +348,6 @@ ipa_set_ancestor_jf (struct ipa_jump_fun jfunc->value.ancestor.agg_preserved = agg_preserved; } -/* Simple function filling in a member pointer constant jump function (with PFN - and DELTA as the constant value) into JFUNC. */ - -static void -ipa_set_jf_member_ptr_cst (struct ipa_jump_func *jfunc, - tree pfn, tree delta) -{ - jfunc->type = IPA_JF_CONST_MEMBER_PTR; - jfunc->value.member_cst.pfn = pfn; - jfunc->value.member_cst.delta = delta; -} - /* Structure to be passed in between detect_type_change and check_stmt_for_type_change. */ @@ -1013,14 +993,16 @@ type_like_member_ptr_p (tree type, tree fld = TYPE_FIELDS (type); if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld)) - || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE) + || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE + || !host_integerp (DECL_FIELD_OFFSET (fld), 1)) return false; if (method_ptr) *method_ptr = fld; fld = DECL_CHAIN (fld); - if (!fld || INTEGRAL_TYPE_P (fld)) + if (!fld || INTEGRAL_TYPE_P (fld) + || !host_integerp (DECL_FIELD_OFFSET (fld), 1)) return false; if (delta) *delta = fld; @@ -1050,83 +1032,6 @@ get_ssa_def_if_simple_copy (tree rhs) return rhs; } -/* Traverse statements from CALL backwards, scanning whether the argument ARG - which is a member pointer is filled in with constant values. If it is, fill - the jump function JFUNC in appropriately. METHOD_FIELD and DELTA_FIELD are - fields of the record type of the member pointer. To give an example, we - look for a pattern looking like the following: - - D.2515.__pfn ={v} printStuff; - D.2515.__delta ={v} 0; - i_1 = doprinting (D.2515); */ - -static void -determine_cst_member_ptr (gimple call, tree arg, tree method_field, - tree delta_field, struct ipa_jump_func *jfunc) -{ - gimple_stmt_iterator gsi; - tree method = NULL_TREE; - tree delta = NULL_TREE; - - gsi = gsi_for_stmt (call); - - gsi_prev (&gsi); - for (; !gsi_end_p (gsi); gsi_prev (&gsi)) - { - gimple stmt = gsi_stmt (gsi); - tree lhs, rhs, fld; - - if (!stmt_may_clobber_ref_p (stmt, arg)) - continue; - if (!gimple_assign_single_p (stmt)) - return; - - lhs = gimple_assign_lhs (stmt); - rhs = gimple_assign_rhs1 (stmt); - - if (TREE_CODE (lhs) != COMPONENT_REF - || TREE_OPERAND (lhs, 0) != arg) - return; - - fld = TREE_OPERAND (lhs, 1); - if (!method && fld == method_field) - { - rhs = get_ssa_def_if_simple_copy (rhs); - if (TREE_CODE (rhs) == ADDR_EXPR - && TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL - && TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE) - { - method = TREE_OPERAND (rhs, 0); - if (delta) - { - ipa_set_jf_member_ptr_cst (jfunc, rhs, delta); - return; - } - } - else - return; - } - - if (!delta && fld == delta_field) - { - rhs = get_ssa_def_if_simple_copy (rhs); - if (TREE_CODE (rhs) == INTEGER_CST) - { - delta = rhs; - if (method) - { - ipa_set_jf_member_ptr_cst (jfunc, rhs, delta); - return; - } - } - else - return; - } - } - - return; -} - /* Helper for determine_known_aggregate_parts, initializes *R for an aggregate passed by reference based on BASE and with the given TYPE. */ @@ -1383,32 +1288,20 @@ ipa_compute_jump_functions_for_edge (str if (is_gimple_ip_invariant (arg)) ipa_set_jf_constant (jfunc, arg); - else if (!is_gimple_reg_type (TREE_TYPE (arg))) + else if (!is_gimple_reg_type (TREE_TYPE (arg)) + && TREE_CODE (arg) == PARM_DECL) { - tree method_field, delta_field; - - /* Aggregate passed by value, check for pass-through, otherwise fill - in aggregate contents. */ + int index = ipa_get_param_decl_index (info, arg); - if (TREE_CODE (arg) == PARM_DECL) + gcc_assert (index >=0); + /* Aggregate passed by value, check for pass-through, otherwise we + will attempt to fill in aggregate contents later in this + function. */ + if (is_parm_preserved_before_stmt (&parms_ainfo[index], call, arg)) { - int index = ipa_get_param_decl_index (info, arg); - gcc_assert (index >=0); - if (is_parm_preserved_before_stmt (&parms_ainfo[index], call, - arg)) - { - ipa_set_jf_simple_pass_through (jfunc, index, true); - continue; - } + ipa_set_jf_simple_pass_through (jfunc, index, true); + continue; } - - /* TODO: The call to determine_cst_member_ptr will be removed by a - subsequent patch which will do away with IPA_JF_CONST_MEMBER_PTR - altogether. */ - if (type_like_member_ptr_p (TREE_TYPE (arg), &method_field, - &delta_field)) - determine_cst_member_ptr (call, arg, method_field, delta_field, - jfunc); } else if (TREE_CODE (arg) == SSA_NAME) { @@ -1442,7 +1335,6 @@ ipa_compute_jump_functions_for_edge (str || !ipa_get_jf_pass_through_agg_preserved (jfunc)) && (jfunc->type != IPA_JF_ANCESTOR || !ipa_get_jf_ancestor_agg_preserved (jfunc)) - && jfunc->type != IPA_JF_CONST_MEMBER_PTR && (AGGREGATE_TYPE_P (TREE_TYPE (arg)) || (POINTER_TYPE_P (TREE_TYPE (arg)) && AGGREGATE_TYPE_P (TREE_TYPE (TREE_TYPE (arg)))))) @@ -1474,16 +1366,22 @@ ipa_compute_jump_functions (struct cgrap ipa_compute_jump_functions_for_edge (parms_ainfo, cs); } -/* If RHS looks like a rhs of a statement loading pfn from a member - pointer formal parameter, return the parameter, otherwise return - NULL. If USE_DELTA, then we look for a use of the delta field - rather than the pfn. */ +/* If STMT looks like a statement loading a value from a member pointer formal + parameter, return that parameter and store the offset of the field to + *OFFSET_P, if it is non-NULL. Otherwise return NULL (but *OFFSET_P still + might be clobbered). If USE_DELTA, then we look for a use of the delta + field rather than the pfn. */ static tree -ipa_get_member_ptr_load_param (tree rhs, bool use_delta) +ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta, + HOST_WIDE_INT *offset_p) { - tree rec, ref_field, ref_offset, fld, fld_offset, ptr_field, delta_field; + tree rhs, rec, ref_field, ref_offset, fld, ptr_field, delta_field; + + if (!gimple_assign_single_p (stmt)) + return NULL_TREE; + rhs = gimple_assign_rhs1 (stmt); if (TREE_CODE (rhs) == COMPONENT_REF) { ref_field = TREE_OPERAND (rhs, 1); @@ -1500,43 +1398,24 @@ ipa_get_member_ptr_load_param (tree rhs, if (TREE_CODE (rec) != PARM_DECL || !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, &delta_field)) return NULL_TREE; - ref_offset = TREE_OPERAND (rhs, 1); + if (use_delta) + fld = delta_field; + else + fld = ptr_field; + if (offset_p) + *offset_p = int_bit_position (fld); + if (ref_field) { if (integer_nonzerop (ref_offset)) return NULL_TREE; - - if (use_delta) - fld = delta_field; - else - fld = ptr_field; - return ref_field == fld ? rec : NULL_TREE; } - - if (use_delta) - fld_offset = byte_position (delta_field); else - fld_offset = byte_position (ptr_field); - - return tree_int_cst_equal (ref_offset, fld_offset) ? rec : NULL_TREE; -} - -/* If STMT looks like a statement loading a value from a member pointer formal - parameter, this function returns that parameter. */ - -static tree -ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta) -{ - tree rhs; - - if (!gimple_assign_single_p (stmt)) - return NULL_TREE; - - rhs = gimple_assign_rhs1 (stmt); - return ipa_get_member_ptr_load_param (rhs, use_delta); + return tree_int_cst_equal (byte_position (fld), ref_offset) ? rec + : NULL_TREE; } /* Returns true iff T is an SSA_NAME defined by a statement. */ @@ -1562,8 +1441,9 @@ ipa_note_param_call (struct cgraph_node cs = cgraph_edge (node, stmt); cs->indirect_info->param_index = param_index; - cs->indirect_info->anc_offset = 0; + cs->indirect_info->offset = 0; cs->indirect_info->polymorphic = 0; + cs->indirect_info->agg_contents = 0; return cs; } @@ -1622,7 +1502,9 @@ ipa_note_param_call (struct cgraph_node return (S.*f)(4); } -*/ + + Moreover, the function also looks for called pointers loaded from aggregates + passed by value or reference. */ static void ipa_analyze_indirect_call_uses (struct cgraph_node *node, @@ -1637,6 +1519,7 @@ ipa_analyze_indirect_call_uses (struct c gimple branch; int index; basic_block bb, virt_bb, join; + HOST_WIDE_INT offset; if (SSA_NAME_IS_DEFAULT_DEF (target)) { @@ -1647,18 +1530,52 @@ ipa_analyze_indirect_call_uses (struct c return; } - /* Now we need to try to match the complex pattern of calling a member - pointer. */ + def = SSA_NAME_DEF_STMT (target); + if (gimple_assign_single_p (def)) + { + struct cgraph_edge *cs; + HOST_WIDE_INT size, max_size; + tree base = get_ref_base_and_extent (gimple_assign_rhs1 (def), + &offset, &size, &max_size); + if (max_size == -1 || max_size != size || offset < 0) + return; - if (!POINTER_TYPE_P (TREE_TYPE (target)) - || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE) - return; + if (DECL_P (base)) + { + index = ipa_get_param_decl_index (info, base); + if (index < 0 || !is_parm_preserved_before_stmt (&parms_ainfo[index], + call, base)) + return; + } + else if (TREE_CODE (base) == MEM_REF + && TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME + && SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0)) + && integer_zerop (TREE_OPERAND (base, 1))) + { - def = SSA_NAME_DEF_STMT (target); - if (gimple_code (def) != GIMPLE_PHI) - return; + index = ipa_get_param_decl_index (info, + SSA_NAME_VAR (TREE_OPERAND (base, + 0))); + if (index < 0 || !is_parm_ref_data_preserved (&parms_ainfo[index], + call, + TREE_OPERAND (base, 0))) + return; + } + else + return; + + cs = ipa_note_param_call (node, index, call); + cs->indirect_info->offset = offset; + cs->indirect_info->agg_contents = 1; + return; + } - if (gimple_phi_num_args (def) != 2) + /* Now we need to try to match the complex pattern of calling a member + pointer. */ + if (gimple_code (def) != GIMPLE_PHI + || gimple_phi_num_args (def) != 2 + || !POINTER_TYPE_P (TREE_TYPE (target)) + || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE) return; /* First, we need to check whether one of these is a load from a member @@ -1671,15 +1588,15 @@ ipa_analyze_indirect_call_uses (struct c d2 = SSA_NAME_DEF_STMT (n2); join = gimple_bb (def); - if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false))) + if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false, &offset))) { - if (ipa_get_stmt_member_ptr_load_param (d2, false)) + if (ipa_get_stmt_member_ptr_load_param (d2, false, NULL)) return; bb = EDGE_PRED (join, 0)->src; virt_bb = gimple_bb (d2); } - else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false))) + else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false, &offset))) { bb = EDGE_PRED (join, 1)->src; virt_bb = gimple_bb (d1); @@ -1734,15 +1651,19 @@ ipa_analyze_indirect_call_uses (struct c rec2 = ipa_get_stmt_member_ptr_load_param (def, (TARGET_PTRMEMFUNC_VBIT_LOCATION - == ptrmemfunc_vbit_in_delta)); - + == ptrmemfunc_vbit_in_delta), + NULL); if (rec != rec2) return; index = ipa_get_param_decl_index (info, rec); if (index >= 0 && is_parm_preserved_before_stmt (&parms_ainfo[index], call, rec)) - ipa_note_param_call (node, index, call); + { + struct cgraph_edge *cs = ipa_note_param_call (node, index, call); + cs->indirect_info->offset = offset; + cs->indirect_info->agg_contents = 1; + } return; } @@ -1797,7 +1718,7 @@ ipa_analyze_virtual_call_uses (struct cg cs = ipa_note_param_call (node, index, call); ii = cs->indirect_info; - ii->anc_offset = anc_offset; + ii->offset = anc_offset; ii->otr_token = tree_low_cst (OBJ_TYPE_REF_TOKEN (target), 1); ii->otr_type = TREE_TYPE (TREE_TYPE (OBJ_TYPE_REF_OBJECT (target))); ii->polymorphic = 1; @@ -2191,6 +2112,41 @@ ipa_make_edge_direct_to_target (struct c return ie; } +/* Provided that values from AGG can be propagated to parameter of NODE with + index PARAM_INDEX so that is_parm_ref_data_preserved results are reliable + and that there is a known constant value in AGG the given OFFSET, return + the nown constant value. */ + +tree +ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, + HOST_WIDE_INT offset, + struct cgraph_node *node, + int param_index) +{ + struct ipa_node_params *info = IPA_NODE_REF (node); + tree t = TREE_TYPE (ipa_get_param (info, param_index)); + struct ipa_agg_jf_item *item; + int i; + + if (!agg->items + || !ipa_agg_types_propagatable_p (agg, t, 0)) + return NULL; + + FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, agg->items, i, item) + { + if (item->offset == offset) + { + /* CUrrently we do not have clobber values, return NULL fro them once + we do. */ + gcc_checking_assert (is_gimple_ip_invariant (item->value)); + return item->value; + } + else if (item->offset > offset) + return NULL; + } + return NULL; +} + /* Try to find a destination for indirect edge IE that corresponds to a simple call or a call of a member function pointer and where the destination is a pointer formal parameter described by jump function JFUNC. If it can be @@ -2198,17 +2154,24 @@ ipa_make_edge_direct_to_target (struct c static struct cgraph_edge * try_make_edge_direct_simple_call (struct cgraph_edge *ie, - struct ipa_jump_func *jfunc) + struct ipa_jump_func *jfunc, int param_index) { tree target; - if (jfunc->type == IPA_JF_CONST) - target = ipa_get_jf_constant (jfunc); - else if (jfunc->type == IPA_JF_CONST_MEMBER_PTR) - target = ipa_get_jf_member_ptr_pfn (jfunc); + if (ie->indirect_info->agg_contents) + { + target = ipa_find_agg_cst_for_param (&jfunc->agg, + ie->indirect_info->offset, + ie->caller, param_index); + if (!target) + return NULL; + } else - return NULL; - + { + if (jfunc->type != IPA_JF_CONST) + return NULL; + target = ipa_get_jf_constant (jfunc); + } return ipa_make_edge_direct_to_target (ie, target); } @@ -2229,7 +2192,7 @@ try_make_edge_direct_virtual_call (struc binfo = TYPE_BINFO (ipa_get_jf_known_type_base_type (jfunc)); gcc_checking_assert (binfo); binfo = get_binfo_at_offset (binfo, ipa_get_jf_known_type_offset (jfunc) - + ie->indirect_info->anc_offset, + + ie->indirect_info->offset, ie->indirect_info->otr_type); if (binfo) target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token, @@ -2265,6 +2228,7 @@ update_indirect_edges_after_inlining (st { struct cgraph_indirect_call_info *ici = ie->indirect_info; struct ipa_jump_func *jfunc; + int param_index; next_ie = ie->next_callee; @@ -2278,14 +2242,15 @@ update_indirect_edges_after_inlining (st continue; } - jfunc = ipa_get_ith_jump_func (top, ici->param_index); + param_index = ici->param_index; + jfunc = ipa_get_ith_jump_func (top, param_index); if (jfunc->type == IPA_JF_PASS_THROUGH && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR) ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc); else if (jfunc->type == IPA_JF_ANCESTOR) { ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc); - ici->anc_offset += ipa_get_jf_ancestor_offset (jfunc); + ici->offset += ipa_get_jf_ancestor_offset (jfunc); } else /* Either we can find a destination for this edge now or never. */ @@ -2297,7 +2262,8 @@ update_indirect_edges_after_inlining (st if (ici->polymorphic) new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc); else - new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc); + new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc, + param_index); if (new_direct_edge) { @@ -3207,10 +3173,6 @@ ipa_write_jump_function (struct output_b bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1); streamer_write_bitpack (&bp); break; - case IPA_JF_CONST_MEMBER_PTR: - stream_write_tree (ob, jump_func->value.member_cst.pfn, true); - stream_write_tree (ob, jump_func->value.member_cst.delta, false); - break; } count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items); @@ -3263,10 +3225,6 @@ ipa_read_jump_function (struct lto_input bp = streamer_read_bitpack (ib); jump_func->value.ancestor.agg_preserved = bp_unpack_value (&bp, 1); break; - case IPA_JF_CONST_MEMBER_PTR: - jump_func->value.member_cst.pfn = stream_read_tree (ib, data_in); - jump_func->value.member_cst.delta = stream_read_tree (ib, data_in); - break; } count = streamer_read_uhwi (ib); @@ -3294,9 +3252,10 @@ ipa_write_indirect_edge_info (struct out struct bitpack_d bp; streamer_write_hwi (ob, ii->param_index); - streamer_write_hwi (ob, ii->anc_offset); + streamer_write_hwi (ob, ii->offset); bp = bitpack_create (ob->main_stream); bp_pack_value (&bp, ii->polymorphic, 1); + bp_pack_value (&bp, ii->agg_contents, 1); streamer_write_bitpack (&bp); if (ii->polymorphic) @@ -3318,9 +3277,10 @@ ipa_read_indirect_edge_info (struct lto_ struct bitpack_d bp; ii->param_index = (int) streamer_read_hwi (ib); - ii->anc_offset = (HOST_WIDE_INT) streamer_read_hwi (ib); + ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib); bp = streamer_read_bitpack (ib); ii->polymorphic = bp_unpack_value (&bp, 1); + ii->agg_contents = bp_unpack_value (&bp, 1); if (ii->polymorphic) { ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib); Index: src/gcc/ipa-prop.h =================================================================== --- src.orig/gcc/ipa-prop.h +++ src/gcc/ipa-prop.h @@ -44,10 +44,6 @@ along with GCC; see the file COPYING3. argument. Unknown - neither of the above. - IPA_JF_CONST_MEMBER_PTR stands for C++ member pointers, it is a special - constant in this regard because it is in fact a structure consisting of two - values. Other constants are represented with IPA_JF_CONST. - IPA_JF_ANCESTOR is a special pass-through jump function, which means that the result is an address of a part of the object pointed to by the formal parameter to which the function refers. It is mainly intended to represent @@ -74,7 +70,6 @@ enum jump_func_type IPA_JF_UNKNOWN = 0, /* newly allocated and zeroed jump functions default */ IPA_JF_KNOWN_TYPE, /* represented by field known_type */ IPA_JF_CONST, /* represented by field costant */ - IPA_JF_CONST_MEMBER_PTR, /* represented by field member_cst */ IPA_JF_PASS_THROUGH, /* represented by field pass_through */ IPA_JF_ANCESTOR /* represented by field ancestor */ }; @@ -128,14 +123,6 @@ struct GTY(()) ipa_ancestor_jf_data bool agg_preserved; }; -/* Structure holding a C++ member pointer constant. Holds a pointer to the - method and delta offset. */ -struct GTY(()) ipa_member_ptr_cst -{ - tree pfn; - tree delta; -}; - /* An element in an aggegate part of a jump function describing a known value at a given offset. When the this is part of a pass-through jump function with agg_preserved set or an ancestor jump function with agg_preserved set, @@ -191,7 +178,6 @@ typedef struct GTY (()) ipa_jump_func { struct ipa_known_type_data GTY ((tag ("IPA_JF_KNOWN_TYPE"))) known_type; tree GTY ((tag ("IPA_JF_CONST"))) constant; - struct ipa_member_ptr_cst GTY ((tag ("IPA_JF_CONST_MEMBER_PTR"))) member_cst; struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through; struct ipa_ancestor_jf_data GTY ((tag ("IPA_JF_ANCESTOR"))) ancestor; } GTY ((desc ("%1.type"))) value; @@ -311,14 +297,10 @@ ipa_get_jf_ancestor_agg_preserved (struc return jfunc->value.ancestor.agg_preserved; } -/* Return the pfn part of a member pointer constant jump function JFUNC. */ +/* Aggregate jump function related functions. */ +tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT, + struct cgraph_node *, int); -static inline tree -ipa_get_jf_member_ptr_pfn (struct ipa_jump_func *jfunc) -{ - gcc_checking_assert (jfunc->type == IPA_JF_CONST_MEMBER_PTR); - return jfunc->value.member_cst.pfn; -} /* Summary describing a single formal parameter. */ Index: src/gcc/ipa-cp.c =================================================================== --- src.orig/gcc/ipa-cp.c +++ src/gcc/ipa-cp.c @@ -1110,7 +1110,7 @@ ipa_get_indirect_edge_target (struct cgr } token = ie->indirect_info->otr_token; - anc_offset = ie->indirect_info->anc_offset; + anc_offset = ie->indirect_info->offset; otr_type = ie->indirect_info->otr_type; t = VEC_index (tree, known_vals, param_index); Index: src/gcc/testsuite/gcc.dg/ipa/iinline-4.c =================================================================== --- /dev/null +++ src/gcc/testsuite/gcc.dg/ipa/iinline-4.c @@ -0,0 +1,87 @@ +/* Verify that simple indirect calls are inlined even without early + inlining.. */ +/* { dg-do compile } */ +/* { dg-options "-O3 -c -fdump-ipa-inline -fno-early-inlining" } */ + +struct S +{ + int i; + void (*f)(struct S *); + char c; +}; + +extern void non_existent(struct S *p, int); + +static void hooray1 (struct S *p) +{ + non_existent (p, 1); +} + +static void hiphip1 (struct S *p) +{ + p->f (p); +} + +int test1 (void) +{ + struct S s; + s.i = 1234; + s.f = hooray1; + s.c = 'c'; + hiphip1 (&s); + return 0; +} + +struct S gs; +struct S *gp = &gs; + +static void hooray2 (struct S *p) +{ + non_existent (p, 2); +} + +static void hip2 (struct S *p) +{ + p->f (p); +} + +static void hiphip2 (struct S *p) +{ + hip2 (p); +} + +int test2 (void) +{ + struct S *p = gp; + p->i = 2341; + p->f = hooray2; + p->c = 'c'; + hiphip2 (p); + return 0; +} + +static void hooray3 (struct S *p) +{ + non_existent (p, 3); +} + +static void hiphip3 (struct S s) +{ + s.f (&s); +} + +int test3(void) +{ + struct S s; + s.i = 3412; + s.f = hooray3; + s.c = 'c'; + hiphip3 (s); + return 0; +} + + +/* { dg-final { scan-ipa-dump "hooray1\[^\\n\]*inline copy in test1" "inline" } } */ +/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2" "inline" } } */ +/* { dg-final { scan-ipa-dump "hooray2\[^\\n\]*inline copy in test2" "inline" } } */ +/* { dg-final { cleanup-ipa-dump "inline" } } */