This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [1/3] Build aggregate jump functions
- From: Richard Guenther <rguenther at suse dot de>
- To: Martin Jambor <mjambor at suse dot cz>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, Jan Hubicka <hubicka at ucw dot cz>
- Date: Tue, 7 Aug 2012 15:06:02 +0200 (CEST)
- Subject: Re: [1/3] Build aggregate jump functions
- References: <20120802164558.GB20202@virgil.arch.suse.de>
On Thu, 2 Aug 2012, Martin Jambor wrote:
> Hi,
>
> this is the third version of aggregate jump function patch. There are
> three major differences from the previous one:
>
> 1. Because using TBAA when computing pass-through jump functions made
> difference only in less than 2% of those and required complex
> type checking when doing IPA propagation, I removed it.
>
> 2. In order to create aggregate jump functions even in cases like
> the PR 53787 testcase, I also compute aggregate jump functions
> for pointers to non-aggregates. This makes the name aggregate
> jump function a bit misleading but I could not think of a better
> one :-)
>
> 3. The previous two changes require that in each aggregate jump
> function I store whether it describes values passed by reference
> or by value. I added an LTO testcase which breaks if I don't
> (gcc.dg/lto/20120723_[01].c) but it is invalid C because the
> prototypes of corresponding functions in each file do not match.
> However, current trunk does not "miscompile" it and I'm afraid
> code like this can be found in thee wild so I do not want to
> either. On the other hand, I'm not sure whether we want an
> essentially invalid testcase in the testsuite, should I remove
> it?
Just keep it.
> In addition to that, a small improvement of ipa_load_from_parm_agg_1
> allows us to catch cases when pointers to aggregates are not gimple
> registers (as e.g. in hiphip7 and hip7 in gcc.dg/ipa/iinline-4.c).
>
> Alias analysis is used in three places when doing intra-procedural
> analysis:
>
> 1. In determine_known_aggregate_parts where we look at what data are
> passed to a callee by value in an aggregate actual argument or by
> reference through a pointer. It just scans a BB back from the
> call statement, uses stmt_may_clobber_ref_p_1 to figure out
> whether a statement can clobber the passed value, building the
> jump function along the way and stopping at the first potential
> clobber that it cannot build a jump function from or safely
> ignore. I use ao_ref_init and thus TBAA for stuff passed by
> value (because I'm looking at a load in the call statement) and
> ao_ref_init_from_ptr_and_size and therefore no TBAA for pointers
> (because we are not).
>
> 2. In parm_ref_data_pass_through_p where we try to figure out
> whether passing a pointer from a formal argument to an actual
> argument of a callee also passes the data it points to or whether
> that data might be clobbered. ao_ref_init_from_ptr_and_size and
> thus no TBAA is used.
>
> In order to cap complexity, if the referred data by a pointer is
> detected to be clobbered at one call site, it is subsequently
> considered clobbered at all investigated call sites. We also
> keep a bitmap of vdefs visited by walk_aliased_vdefs. The worst
> case complexity therefore is that we walk each vdef for each
> formal parameter of the current function.
>
> 3. In ipa_load_from_parm_agg_1, where we are looking at a load and
> need to figure out whether we could later on replace that load
> from a value from a jump function (if we are lucky at IPA stage).
> Thus it needs to find out whether it is a load from a formal
> parameter or data pointed to by a formal parameter and whether
> that data might have been clobbered since function started
> executing. Because we always have a load at hand, we use
> ao_ref_init in parm_ref_data_preserved_p and
> parm_preserved_before_stmt_p to initialize the ao_ref.
>
> In order not to do too much vdef walking, once we determine that
> data is clobbered, all such subsequent checks do not invoke AA
> and pessimistically consider the data clobbered too. Because the
> references may be very different, we cannot cache visited
> statements in a bitmap and thus in the the theoretical worst case
> parm_ref_data_preserved_p can always walk all vdefs in the
> current function. (Eventually, parm_ref_data_preserved_p1 will
> be called for all loads through parameters, in this patch it is
> called only once per an indirect call.) On the other hand, I
> assume that big functions generally clobber memory and we would
> thus soon fall back on returning false without performing any AA.
>
> 4. In parm_preserved_before_stmt_p is used whenever we want to find
> out whether a load from a non-gimple-register PARM_DECL loads
> data that has not been modified in this function.
>
> It is already in trunk, though its name is now
> is_parm_modified_before_stmt and it is used for address taken
> scalar parameters. I have renamed the function for the sake of
> consistency and added two new uses: When we pass an aggregate
> PARM_DECL to a callee and want to determine whether it is
> unchanged, and ipa_load_from_parm_agg_1 (described above) when it
> looks at a load from an aggregate PARM_DECL. In all cases it
> looks at a load, no pointers are involved and so I use
> ao_ref_init and its TBAA.
>
> This function too falls back to "return false for this PARM_DECL"
> mode once the decl is considered clobbered. For naked PARM_DECL
> loads it also caches visited statements, it cannot do that for
> other references. This bitmap is different from the one from
> point 2, function hiphip7 from gcc.dg/ipa/iinline-4.c is an
> example where both are used together but differently to construct
> an aggregate pass-through function (for data pointed to by an
> address taken parameter).
>
> I'm currently looking into how to best use the return value of
> walk_aliased_vdefs in order not to cause grave compile-time issues
> even in the unlikely cases.
>
> The other half of the patch is manipulation with IPA structures,
> mainly ipa_jump_func but also cgraph_indirect_call_info. I'll be
> happy to explain any of it in detail too but I guess this email is
> already too long and I should get the functions above (and AA usage in
> general) approved first and then ask Honza to look at the rest.
>
> The patch of course passes bootstrap and testing on x86_64-linux. I
> also LTO-built Firefox with its slightly older version which behaved
> just as what I got when I non-LTO-built it with pristine trunk
> (i.e. it crashed on an assert after a while, but that is probably a
> problem of the particular Firefox checkout, I don't know yet).
>
> Thanks for any comments,
The patch looks good to me. Thus it is ok for trunk.
Thanks,
Richard.
> Martin
>
>
>
> 2012-07-30 Martin Jambor <mjambor@suse.cz>
>
> * cgraph.h (cgraph_indirect_call_info): Field anc_offse renamd to
> offset, updated all users. New field agg_contents.
>
> * ipa-prop.h (jump_func_type): Removed IPA_JF_CONST_MEMBER_PTR.
> (ipa_pass_through_data): New field agg_preserved.
> (ipa_ancestor_jf_data): Likewise.
> (ipa_member_ptr_cst): Removed.
> (ipa_agg_jf_item): New type.
> (ipa_agg_jump_function): Likewise.
> (ipa_jump_func): New field agg. Removed field member_cst.
> (ipa_get_jf_pass_through_agg_preserved): New function.
> (ipa_get_jf_ancestor_agg_preserved): Likewise.
> (ipa_get_jf_member_ptr_pfn): Removed.
> (ipa_find_agg_cst_for_param): Declare.
> (ipa_load_from_parm_agg): Likewise.
>
> * ipa-prop.c (param_analysis_info): Fields modified and
> visited_statements rename to parm_modified and parm_visited_statements
> respectively, added fields ref_modified, ref_visited_statements,
> pt_modified and pt_visited_statements.
> (ipa_print_node_jump_functions_for_edge): Do not dump const member
> functions. Dump agg_preserved flags and aggregate jump functions.
> (ipa_set_jf_simple_pass_through): Set also agg_preserved.
> (ipa_set_ancestor_jf): Likewise.
> (ipa_set_jf_arith_pass_through): Clear agg_preserved.
> (ipa_set_jf_member_ptr_cst): Removed.
> (is_parm_modified_before_stmt): Logic reversed, renamed to
> parm_preserved_before_stmt_p. Cache visited bitmap only for
> naked DECL parameters. All callers updated.
> (load_from_unmodified_param): Allow NULL parms_ainfo.
> (parm_ref_data_preserved_p): New function.
> (parm_ref_data_pass_through_p): Likewise.
> (ipa_load_from_parm_agg_1): Likewise.
> (ipa_load_from_parm_agg): Likewise.
> (compute_complex_assign_jump_func): Check if aggregate contents are
> preserved.
> (compute_complex_ancestor_jump_func): Likewise.
> (compute_scalar_jump_functions): Removed.
> (type_like_member_ptr_p): Also check field position are known and
> sane.
> (compute_pass_through_member_ptrs): Removed.
> (determine_cst_member_ptr): Likewise.
> (ipa_known_agg_contents_list): New type.
> (determine_known_aggregate_parts): New function.
> (compute_cst_member_ptr_arguments): Removed.
> (ipa_compute_jump_functions_for_edge): Compute all kinds of jump
> functions (scalar, aggregate and member pointer).
> (ipa_get_member_ptr_load_param): Incorporate into
> ipa_get_stmt_member_ptr_load_param, also pass back an offset.
> (ipa_note_param_call): Clear agg_contents.
> (ipa_analyze_indirect_call_uses): Also look for simple pointers loaded
> from aggregates. In such cases, store offset of the called field.
> (ipa_analyze_node): Initialize new fields of param_analysis_info.
> (update_jump_functions_after_inlining): Handle aggregate contents.
> (ipa_find_agg_cst_for_param): New function.
> (try_make_edge_direct_simple_call): Handle called aggregate values.
> (update_indirect_edges_after_inlining): Make sure aggregate preserving
> jump functions comply with type compatibility requirements.
> (ipa_edge_duplication_hook): Copy also aggregate jump functions.
> (ipa_write_jump_function): Stream agg_preserved flags and aggregate
> jump functions. 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.
> * testsuite/gcc.dg/ipa/iinline-5.c: Likewise.
> * testsuite/gcc.dg/ipa/iinline-6.c: Likewise.
> * testsuite/gcc.dg/ipa/iinline-7.c: Likewise.
> * testsuite/gcc.dg/lto/20120723_0.c: Likewise.
> * testsuite/gcc.dg/lto/20120723_1.c: Likewise.
>
> Index: src/gcc/ipa-prop.h
> ===================================================================
> *** src.orig/gcc/ipa-prop.h
> --- src/gcc/ipa-prop.h
> *************** along with GCC; see the file COPYING3.
> *** 44,53 ****
> 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
> --- 44,49 ----
> *************** enum jump_func_type
> *** 74,80 ****
> 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 */
> };
> --- 70,75 ----
> *************** struct GTY(()) ipa_pass_through_data
> *** 104,109 ****
> --- 99,111 ----
> arithmetic operation where the caller's parameter is the first operand and
> operand field from this structure is the second one. */
> enum tree_code operation;
> + /* When the passed value is a pointer, it is set to true only when we are
> + certain that no write to the object it points to has occurred since the
> + caller functions started execution, except for changes noted in the
> + aggregate part of the jump function (see description of
> + ipa_agg_jump_function). The flag is used only when the operation is
> + NOP_EXPR. */
> + bool agg_preserved;
> };
>
> /* Structure holding data required to describe an ancestor pass-through
> *************** struct GTY(()) ipa_ancestor_jf_data
> *** 117,137 ****
> tree type;
> /* Number of the caller's formal parameter being passed. */
> int formal_id;
> };
>
> ! /* 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;
> };
>
> /* A jump function for a callsite represents the values passed as actual
> arguments of the callsite. See enum jump_func_type for the various
> types of jump functions supported. */
> typedef struct GTY (()) ipa_jump_func
> {
> enum jump_func_type type;
> /* Represents a value of a jump function. pass_through is used only in jump
> function context. constant represents the actual constant in constant jump
> --- 119,174 ----
> tree type;
> /* Number of the caller's formal parameter being passed. */
> int formal_id;
> + /* Flag with the same meaning like agg_preserve in ipa_pass_through_data. */
> + bool agg_preserved;
> };
>
> ! /* An element in an aggegate part of a jump function describing a known value
> ! at a given offset. When it is part of a pass-through jump function with
> ! agg_preserved set or an ancestor jump function with agg_preserved set, all
> ! unlisted positions are assumed to be preserved but the value can be a type
> ! node, which means that the particular piece (starting at offset and having
> ! the size of the type) is clobbered with an unknown value. When
> ! agg_preserved is false or the type of the containing jump function is
> ! different, all unlisted parts are assumed to be unknown and all values must
> ! fullfill is_gimple_ip_invariant. */
> !
> ! typedef struct GTY(()) ipa_agg_jf_item
> {
> ! /* The offset at which the known value is located within the aggregate. */
> ! HOST_WIDE_INT offset;
> !
> ! /* The known constant or type if this is a clobber. */
> ! tree value;
> ! } ipa_agg_jf_item_t;
> !
> ! DEF_VEC_O (ipa_agg_jf_item_t);
> ! DEF_VEC_ALLOC_O (ipa_agg_jf_item_t, gc);
> !
> ! /* Aggregate jump function - i.e. description of contents of aggregates passed
> ! either by reference or value. */
> !
> ! struct GTY(()) ipa_agg_jump_function
> ! {
> ! /* Description of the individual items. */
> ! VEC (ipa_agg_jf_item_t, gc) *items;
> ! /* True if the data was passed by reference (as opposed to by value). */
> ! bool by_ref;
> };
>
> + typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
> + DEF_VEC_P (ipa_agg_jump_function_p);
> + DEF_VEC_ALLOC_P (ipa_agg_jump_function_p, heap);
> +
> /* A jump function for a callsite represents the values passed as actual
> arguments of the callsite. See enum jump_func_type for the various
> types of jump functions supported. */
> typedef struct GTY (()) ipa_jump_func
> {
> + /* Aggregate contants description. See struct ipa_agg_jump_function and its
> + description. */
> + struct ipa_agg_jump_function agg;
> +
> enum jump_func_type type;
> /* Represents a value of a jump function. pass_through is used only in jump
> function context. constant represents the actual constant in constant jump
> *************** typedef struct GTY (()) ipa_jump_func
> *** 140,146 ****
> {
> 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;
> --- 177,182 ----
> *************** ipa_get_jf_pass_through_operation (struc
> *** 214,219 ****
> --- 250,264 ----
> return jfunc->value.pass_through.operation;
> }
>
> + /* Return the agg_preserved flag of a pass through jump functin JFUNC. */
> +
> + static inline bool
> + ipa_get_jf_pass_through_agg_preserved (struct ipa_jump_func *jfunc)
> + {
> + gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
> + return jfunc->value.pass_through.agg_preserved;
> + }
> +
> /* Return the offset of an ancestor jump function JFUNC. */
>
> static inline HOST_WIDE_INT
> *************** ipa_get_jf_ancestor_formal_id (struct ip
> *** 242,254 ****
> return jfunc->value.ancestor.formal_id;
> }
>
> ! /* Return the pfn part of a member pointer constant jump function JFUNC. */
>
> ! 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. */
> --- 287,299 ----
> return jfunc->value.ancestor.formal_id;
> }
>
> ! /* Return the agg_preserved flag of an ancestor jump functin JFUNC. */
>
> ! static inline bool
> ! ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
> {
> ! gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
> ! return jfunc->value.ancestor.agg_preserved;
> }
>
> /* Summary describing a single formal parameter. */
> *************** struct cgraph_edge *ipa_make_edge_direct
> *** 456,461 ****
> --- 501,512 ----
> /* Functions related to both. */
> void ipa_analyze_node (struct cgraph_node *);
>
> + /* Aggregate jump function related functions. */
> + tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT,
> + bool);
> + bool ipa_load_from_parm_agg (struct ipa_node_params *, gimple, tree, int *,
> + HOST_WIDE_INT *, bool *);
> +
> /* Debugging interface. */
> void ipa_print_node_params (FILE *, struct cgraph_node *node);
> void ipa_print_all_params (FILE *);
> Index: src/gcc/ipa-prop.c
> ===================================================================
> *** src.orig/gcc/ipa-prop.c
> --- src/gcc/ipa-prop.c
> *************** along with GCC; see the file COPYING3.
> *** 44,51 ****
>
> struct param_analysis_info
> {
> ! bool modified;
> ! bitmap visited_statements;
> };
>
> /* Vector where the parameter infos are actually stored. */
> --- 44,51 ----
>
> struct param_analysis_info
> {
> ! bool parm_modified, ref_modified, pt_modified;
> ! bitmap parm_visited_statements, pt_visited_statements;
> };
>
> /* Vector where the parameter infos are actually stored. */
> *************** ipa_print_node_jump_functions_for_edge (
> *** 178,201 ****
> }
> 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: ");
> ! fprintf (f, "%d, op %s ",
> jump_func->value.pass_through.formal_id,
> tree_code_name[(int)
> jump_func->value.pass_through.operation]);
> if (jump_func->value.pass_through.operation != NOP_EXPR)
> ! print_generic_expr (f,
> ! jump_func->value.pass_through.operand, 0);
> fprintf (f, "\n");
> }
> else if (type == IPA_JF_ANCESTOR)
> --- 178,198 ----
> }
> fprintf (f, "\n");
> }
> else if (type == IPA_JF_PASS_THROUGH)
> {
> fprintf (f, "PASS THROUGH: ");
> ! fprintf (f, "%d, op %s",
> jump_func->value.pass_through.formal_id,
> tree_code_name[(int)
> jump_func->value.pass_through.operation]);
> if (jump_func->value.pass_through.operation != NOP_EXPR)
> ! {
> ! fprintf (f, " ");
> ! print_generic_expr (f,
> ! jump_func->value.pass_through.operand, 0);
> ! }
> ! if (jump_func->value.pass_through.agg_preserved)
> ! fprintf (f, ", agg_preserved");
> fprintf (f, "\n");
> }
> else if (type == IPA_JF_ANCESTOR)
> *************** ipa_print_node_jump_functions_for_edge (
> *** 205,212 ****
> --- 202,235 ----
> jump_func->value.ancestor.formal_id,
> jump_func->value.ancestor.offset);
> print_generic_expr (f, jump_func->value.ancestor.type, 0);
> + if (jump_func->value.ancestor.agg_preserved)
> + fprintf (f, ", agg_preserved");
> fprintf (f, "\n");
> }
> +
> + if (jump_func->agg.items)
> + {
> + struct ipa_agg_jf_item *item;
> + int j;
> +
> + fprintf (f, " Aggregate passed by %s:\n",
> + jump_func->agg.by_ref ? "reference" : "value");
> + FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, jump_func->agg.items,
> + j, item)
> + {
> + fprintf (f, " offset: " HOST_WIDE_INT_PRINT_DEC ", ",
> + item->offset);
> + if (TYPE_P (item->value))
> + fprintf (f, "clobber of " HOST_WIDE_INT_PRINT_DEC " bits",
> + tree_low_cst (TYPE_SIZE (item->value), 1));
> + else
> + {
> + fprintf (f, "cst: ");
> + print_generic_expr (f, item->value, 0);
> + }
> + fprintf (f, "\n");
> + }
> + }
> }
> }
>
> *************** ipa_set_jf_constant (struct ipa_jump_fun
> *** 286,297 ****
>
> /* Set JFUNC to be a simple pass-through jump function. */
> static void
> ! ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id)
> {
> jfunc->type = IPA_JF_PASS_THROUGH;
> jfunc->value.pass_through.operand = NULL_TREE;
> jfunc->value.pass_through.formal_id = formal_id;
> jfunc->value.pass_through.operation = NOP_EXPR;
> }
>
> /* Set JFUNC to be an arithmetic pass through jump function. */
> --- 309,322 ----
>
> /* Set JFUNC to be a simple pass-through jump function. */
> static void
> ! ipa_set_jf_simple_pass_through (struct ipa_jump_func *jfunc, int formal_id,
> ! bool agg_preserved)
> {
> jfunc->type = IPA_JF_PASS_THROUGH;
> jfunc->value.pass_through.operand = NULL_TREE;
> jfunc->value.pass_through.formal_id = formal_id;
> jfunc->value.pass_through.operation = NOP_EXPR;
> + jfunc->value.pass_through.agg_preserved = agg_preserved;
> }
>
> /* Set JFUNC to be an arithmetic pass through jump function. */
> *************** ipa_set_jf_arith_pass_through (struct ip
> *** 304,333 ****
> jfunc->value.pass_through.operand = operand;
> jfunc->value.pass_through.formal_id = formal_id;
> jfunc->value.pass_through.operation = operation;
> }
>
> /* Set JFUNC to be an ancestor jump function. */
>
> static void
> ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
> ! tree type, int formal_id)
> {
> jfunc->type = IPA_JF_ANCESTOR;
> jfunc->value.ancestor.formal_id = formal_id;
> jfunc->value.ancestor.offset = offset;
> jfunc->value.ancestor.type = type;
> ! }
> !
> ! /* 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
> --- 329,348 ----
> jfunc->value.pass_through.operand = operand;
> jfunc->value.pass_through.formal_id = formal_id;
> jfunc->value.pass_through.operation = operation;
> + jfunc->value.pass_through.agg_preserved = false;
> }
>
> /* Set JFUNC to be an ancestor jump function. */
>
> static void
> ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
> ! tree type, int formal_id, bool agg_preserved)
> {
> jfunc->type = IPA_JF_ANCESTOR;
> jfunc->value.ancestor.formal_id = formal_id;
> jfunc->value.ancestor.offset = offset;
> jfunc->value.ancestor.type = type;
> ! jfunc->value.ancestor.agg_preserved = agg_preserved;
> }
>
> /* Structure to be passed in between detect_type_change and
> *************** mark_modified (ao_ref *ao ATTRIBUTE_UNUS
> *** 581,610 ****
> return true;
> }
>
> ! /* Return true if the formal parameter PARM might have been modified in this
> ! function before reaching the statement STMT. PARM_AINFO is a pointer to a
> ! structure containing temporary information about PARM. */
>
> static bool
> ! is_parm_modified_before_stmt (struct param_analysis_info *parm_ainfo,
> ! gimple stmt, tree parm)
> {
> bool modified = false;
> ao_ref refd;
>
> ! if (parm_ainfo->modified)
> ! return true;
>
> gcc_checking_assert (gimple_vuse (stmt) != NULL_TREE);
> ! ao_ref_init (&refd, parm);
> ! walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified,
> ! &modified, &parm_ainfo->visited_statements);
> ! if (modified)
> ! {
> ! parm_ainfo->modified = true;
> ! return true;
> ! }
> ! return false;
> }
>
> /* If STMT is an assignment that loads a value from an parameter declaration,
> --- 596,630 ----
> return true;
> }
>
> ! /* Return true if a load from a formal parameter PARM_LOAD is known to retreive
> ! a value known not to be modified in this function before reaching the
> ! statement STMT. PARM_AINFO is a pointer to a structure containing temporary
> ! information about the parameter. */
>
> static bool
> ! parm_preserved_before_stmt_p (struct param_analysis_info *parm_ainfo,
> ! gimple stmt, tree parm_load)
> {
> bool modified = false;
> + bitmap *visited_stmts;
> ao_ref refd;
>
> ! if (parm_ainfo && parm_ainfo->parm_modified)
> ! return false;
>
> gcc_checking_assert (gimple_vuse (stmt) != NULL_TREE);
> ! ao_ref_init (&refd, parm_load);
> ! /* We can cache visited statements only when parm_ainfo is available and when
> ! we are looking at a naked load of the whole parameter. */
> ! if (!parm_ainfo || TREE_CODE (parm_load) != PARM_DECL)
> ! visited_stmts = NULL;
> ! else
> ! visited_stmts = &parm_ainfo->parm_visited_statements;
> ! walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
> ! visited_stmts);
> ! if (parm_ainfo && modified)
> ! parm_ainfo->parm_modified = true;
> ! return !modified;
> }
>
> /* If STMT is an assignment that loads a value from an parameter declaration,
> *************** load_from_unmodified_param (struct ipa_n
> *** 628,639 ****
>
> index = ipa_get_param_decl_index (info, op1);
> if (index < 0
> ! || is_parm_modified_before_stmt (&parms_ainfo[index], stmt, op1))
> return -1;
>
> return index;
> }
>
> /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
> of an assignment statement STMT, try to determine whether we are actually
> handling any of the following cases and construct an appropriate jump
> --- 648,805 ----
>
> index = ipa_get_param_decl_index (info, op1);
> if (index < 0
> ! || !parm_preserved_before_stmt_p (parms_ainfo ? &parms_ainfo[index]
> ! : NULL, stmt, op1))
> return -1;
>
> return index;
> }
>
> + /* Return true if memory reference REF loads data that are known to be
> + unmodified in this function before reaching statement STMT. PARM_AINFO, if
> + non-NULL, is a pointer to a structure containing temporary information about
> + PARM. */
> +
> + static bool
> + parm_ref_data_preserved_p (struct param_analysis_info *parm_ainfo,
> + gimple stmt, tree ref)
> + {
> + bool modified = false;
> + ao_ref refd;
> +
> + gcc_checking_assert (gimple_vuse (stmt));
> + if (parm_ainfo && parm_ainfo->ref_modified)
> + return false;
> +
> + ao_ref_init (&refd, ref);
> + walk_aliased_vdefs (&refd, gimple_vuse (stmt), mark_modified, &modified,
> + NULL);
> + if (parm_ainfo && modified)
> + parm_ainfo->ref_modified = true;
> + return !modified;
> + }
> +
> + /* Return true if the data pointed to by PARM is known to be unmodified in this
> + function before reaching call statement CALL into which it is passed.
> + PARM_AINFO is a pointer to a structure containing temporary information
> + about PARM. */
> +
> + static bool
> + parm_ref_data_pass_through_p (struct param_analysis_info *parm_ainfo,
> + gimple call, tree parm)
> + {
> + bool modified = false;
> + ao_ref refd;
> +
> + /* It's unnecessary to calculate anything about memory contnets for a const
> + function because it is not goin to use it. But do not cache the result
> + either. Also, no such calculations for non-pointers. */
> + if (!gimple_vuse (call)
> + || !POINTER_TYPE_P (TREE_TYPE (parm)))
> + return false;
> +
> + if (parm_ainfo->pt_modified)
> + return false;
> +
> + ao_ref_init_from_ptr_and_size (&refd, parm, NULL_TREE);
> + walk_aliased_vdefs (&refd, gimple_vuse (call), mark_modified, &modified,
> + parm_ainfo ? &parm_ainfo->pt_visited_statements : NULL);
> + if (modified)
> + parm_ainfo->pt_modified = true;
> + return !modified;
> + }
> +
> + /* Return true if we can prove that OP is a memory reference loading unmodified
> + data from an aggregate passed as a parameter and if the aggregate is passed
> + by reference, that the alias type of the load corresponds to the type of the
> + formal parameter (so that we can rely on this type for TBAA in callers).
> + INFO and PARMS_AINFO describe parameters of the current function (but the
> + latter can be NULL), STMT is the load statement. If function returns true,
> + *INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset
> + within the aggregate and whether it is a load from a value passed by
> + reference respectively. */
> +
> + static bool
> + ipa_load_from_parm_agg_1 (struct ipa_node_params *info,
> + struct param_analysis_info *parms_ainfo, gimple stmt,
> + tree op, int *index_p, HOST_WIDE_INT *offset_p,
> + bool *by_ref_p)
> + {
> + int index;
> + HOST_WIDE_INT size, max_size;
> + tree base = get_ref_base_and_extent (op, offset_p, &size, &max_size);
> +
> + if (max_size == -1 || max_size != size || *offset_p < 0)
> + return false;
> +
> + if (DECL_P (base))
> + {
> + int index = ipa_get_param_decl_index (info, base);
> + if (index >= 0
> + && parm_preserved_before_stmt_p (parms_ainfo ? &parms_ainfo[index]
> + : NULL, stmt, op))
> + {
> + *index_p = index;
> + *by_ref_p = false;
> + return true;
> + }
> + return false;
> + }
> +
> + if (TREE_CODE (base) != MEM_REF
> + || TREE_CODE (TREE_OPERAND (base, 0)) != SSA_NAME
> + || !integer_zerop (TREE_OPERAND (base, 1)))
> + return false;
> +
> + if (SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0)))
> + {
> + tree parm = SSA_NAME_VAR (TREE_OPERAND (base, 0));
> + index = ipa_get_param_decl_index (info, parm);
> + }
> + else
> + {
> + /* This branch catches situations where a pointer parameter is not a
> + gimple register, for example:
> +
> + void hip7(S*) (struct S * p)
> + {
> + void (*<T2e4>) (struct S *) D.1867;
> + struct S * p.1;
> +
> + <bb 2>:
> + p.1_1 = p;
> + D.1867_2 = p.1_1->f;
> + D.1867_2 ();
> + gdp = &p;
> + */
> +
> + gimple def = SSA_NAME_DEF_STMT (TREE_OPERAND (base, 0));
> + index = load_from_unmodified_param (info, parms_ainfo, def);
> + }
> +
> + if (index >= 0
> + && parm_ref_data_preserved_p (parms_ainfo ? &parms_ainfo[index] : NULL,
> + stmt, op))
> + {
> + *index_p = index;
> + *by_ref_p = true;
> + return true;
> + }
> + return false;
> + }
> +
> + /* Just like the previous function, just without the param_analysis_info
> + pointer, for users outside of this file. */
> +
> + bool
> + ipa_load_from_parm_agg (struct ipa_node_params *info, gimple stmt,
> + tree op, int *index_p, HOST_WIDE_INT *offset_p,
> + bool *by_ref_p)
> + {
> + return ipa_load_from_parm_agg_1 (info, NULL, stmt, op, index_p, offset_p,
> + by_ref_p);
> + }
> +
> /* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
> of an assignment statement STMT, try to determine whether we are actually
> handling any of the following cases and construct an appropriate jump
> *************** compute_complex_assign_jump_func (struct
> *** 731,737 ****
> }
> else if (gimple_assign_single_p (stmt)
> && !detect_type_change_ssa (tc_ssa, call, jfunc))
> ! ipa_set_jf_simple_pass_through (jfunc, index);
> return;
> }
>
> --- 897,907 ----
> }
> else if (gimple_assign_single_p (stmt)
> && !detect_type_change_ssa (tc_ssa, call, jfunc))
> ! {
> ! bool agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! call, tc_ssa);
> ! ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
> ! }
> return;
> }
>
> *************** compute_complex_assign_jump_func (struct
> *** 757,763 ****
> index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
> if (index >= 0
> && !detect_type_change (op1, base, call, jfunc, offset))
> ! ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index);
> }
>
> /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> --- 927,935 ----
> index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
> if (index >= 0
> && !detect_type_change (op1, base, call, jfunc, offset))
> ! ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index,
> ! parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! call, ssa));
> }
>
> /* Extract the base, offset and MEM_REF expression from a statement ASSIGN if
> *************** get_ancestor_addr_info (gimple assign, t
> *** 828,833 ****
> --- 1000,1006 ----
>
> static void
> compute_complex_ancestor_jump_func (struct ipa_node_params *info,
> + struct param_analysis_info *parms_ainfo,
> struct ipa_jump_func *jfunc,
> gimple call, gimple phi)
> {
> *************** compute_complex_ancestor_jump_func (stru
> *** 881,887 ****
> }
>
> if (!detect_type_change (obj, expr, call, jfunc, offset))
> ! ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index);
> }
>
> /* Given OP which is passed as an actual argument to a called function,
> --- 1054,1062 ----
> }
>
> if (!detect_type_change (obj, expr, call, jfunc, offset))
> ! ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index,
> ! parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! call, parm));
> }
>
> /* Given OP which is passed as an actual argument to a called function,
> *************** compute_known_type_jump_func (tree op, s
> *** 916,970 ****
> ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base), TREE_TYPE (op));
> }
>
> -
> - /* Determine the jump functions of scalar arguments. Scalar means SSA names
> - and constants of a number of selected types. INFO is the ipa_node_params
> - structure associated with the caller, PARMS_AINFO describes state of
> - analysis with respect to individual formal parameters. ARGS is the
> - ipa_edge_args structure describing the callsite CALL which is the call
> - statement being examined.*/
> -
> - static void
> - compute_scalar_jump_functions (struct ipa_node_params *info,
> - struct param_analysis_info *parms_ainfo,
> - struct ipa_edge_args *args,
> - gimple call)
> - {
> - tree arg;
> - unsigned num = 0;
> -
> - for (num = 0; num < gimple_call_num_args (call); num++)
> - {
> - struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, num);
> - arg = gimple_call_arg (call, num);
> -
> - if (is_gimple_ip_invariant (arg))
> - ipa_set_jf_constant (jfunc, arg);
> - else if (TREE_CODE (arg) == SSA_NAME)
> - {
> - if (SSA_NAME_IS_DEFAULT_DEF (arg))
> - {
> - int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
> -
> - if (index >= 0
> - && !detect_type_change_ssa (arg, call, jfunc))
> - ipa_set_jf_simple_pass_through (jfunc, index);
> - }
> - else
> - {
> - gimple stmt = SSA_NAME_DEF_STMT (arg);
> - if (is_gimple_assign (stmt))
> - compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
> - call, stmt, arg);
> - else if (gimple_code (stmt) == GIMPLE_PHI)
> - compute_complex_ancestor_jump_func (info, jfunc, call, stmt);
> - }
> - }
> - else
> - compute_known_type_jump_func (arg, jfunc, call);
> - }
> - }
> -
> /* Inspect the given TYPE and return true iff it has the same structure (the
> same number of fields of the same types) as a C++ member pointer. If
> METHOD_PTR and DELTA are non-NULL, store the trees representing the
> --- 1091,1096 ----
> *************** type_like_member_ptr_p (tree type, tree
> *** 980,993 ****
>
> fld = TYPE_FIELDS (type);
> if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
> ! || TREE_CODE (TREE_TYPE (TREE_TYPE (fld))) != METHOD_TYPE)
> return false;
>
> if (method_ptr)
> *method_ptr = fld;
>
> fld = DECL_CHAIN (fld);
> ! if (!fld || INTEGRAL_TYPE_P (fld))
> return false;
> if (delta)
> *delta = fld;
> --- 1106,1121 ----
>
> fld = TYPE_FIELDS (type);
> if (!fld || !POINTER_TYPE_P (TREE_TYPE (fld))
> ! || 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)
> ! || !host_integerp (DECL_FIELD_OFFSET (fld), 1))
> return false;
> if (delta)
> *delta = fld;
> *************** type_like_member_ptr_p (tree type, tree
> *** 998,1051 ****
> return true;
> }
>
> - /* Go through arguments of the CALL and for every one that looks like a member
> - pointer, check whether it can be safely declared pass-through and if so,
> - mark that to the corresponding item of jump FUNCTIONS. Return true iff
> - there are non-pass-through member pointers within the arguments. INFO
> - describes formal parameters of the caller. PARMS_INFO is a pointer to a
> - vector containing intermediate information about each formal parameter. */
> -
> - static bool
> - compute_pass_through_member_ptrs (struct ipa_node_params *info,
> - struct param_analysis_info *parms_ainfo,
> - struct ipa_edge_args *args,
> - gimple call)
> - {
> - bool undecided_members = false;
> - unsigned num;
> - tree arg;
> -
> - for (num = 0; num < gimple_call_num_args (call); num++)
> - {
> - arg = gimple_call_arg (call, num);
> -
> - if (type_like_member_ptr_p (TREE_TYPE (arg), NULL, NULL))
> - {
> - if (TREE_CODE (arg) == PARM_DECL)
> - {
> - int index = ipa_get_param_decl_index (info, arg);
> -
> - gcc_assert (index >=0);
> - if (!is_parm_modified_before_stmt (&parms_ainfo[index], call,
> - arg))
> - {
> - struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args,
> - num);
> - ipa_set_jf_simple_pass_through (jfunc, index);
> - }
> - else
> - undecided_members = true;
> - }
> - else
> - undecided_members = true;
> - }
> - }
> -
> - return undecided_members;
> - }
> -
> /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
> ! return the rhs of its defining statement. */
>
> static inline tree
> get_ssa_def_if_simple_copy (tree rhs)
> --- 1126,1134 ----
> return true;
> }
>
> /* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
> ! return the rhs of its defining statement. Otherwise return RHS as it
> ! is. */
>
> static inline tree
> get_ssa_def_if_simple_copy (tree rhs)
> *************** get_ssa_def_if_simple_copy (tree rhs)
> *** 1062,1165 ****
> 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;
> - }
>
> ! /* Go through the arguments of the CALL and for every member pointer within
> ! tries determine whether it is a constant. If it is, create a corresponding
> ! constant jump function in FUNCTIONS which is an array of jump functions
> ! associated with the call. */
>
> ! static void
> ! compute_cst_member_ptr_arguments (struct ipa_edge_args *args,
> ! gimple call)
> ! {
> ! unsigned num;
> ! tree arg, method_field, delta_field;
>
> ! for (num = 0; num < gimple_call_num_args (call); num++)
> {
> ! struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, num);
> ! arg = gimple_call_arg (call, num);
> !
> ! if (jfunc->type == IPA_JF_UNKNOWN
> ! && type_like_member_ptr_p (TREE_TYPE (arg), &method_field,
> ! &delta_field))
> ! determine_cst_member_ptr (call, arg, method_field, delta_field, jfunc);
> }
> }
>
> --- 1145,1357 ----
> return rhs;
> }
>
> ! /* TODO: Turn this into a PARAM. */
> ! #define IPA_MAX_AFF_JF_ITEMS 16
>
> ! /* Simple linked list, describing known contents of an aggregate beforere
> ! call. */
> !
> ! struct ipa_known_agg_contents_list
> {
> + /* Offset and size of the described part of the aggregate. */
> + HOST_WIDE_INT offset, size;
> + /* Known constant value or NULL if the contents is known to be unknown. */
> + tree constant;
> + /* Pointer to the next structure in the list. */
> + struct ipa_known_agg_contents_list *next;
> + };
> +
> + /* Traverse statements from CALL backwards, scanning whether an aggregate given
> + in ARG is filled in with constant values. ARG can either be an aggregate
> + expression or a pointer to an aggregate. JFUNC is the jump function into
> + which the constants are subsequently stored. */
> +
> + static void
> + determine_known_aggregate_parts (gimple call, tree arg,
> + struct ipa_jump_func *jfunc)
> + {
> + struct ipa_known_agg_contents_list *list = NULL;
> + int item_count = 0, const_count = 0;
> + HOST_WIDE_INT arg_offset, arg_size;
> gimple_stmt_iterator gsi;
> ! tree arg_base;
> ! bool check_ref, by_ref;
> ! ao_ref r;
> !
> ! /* The function operates in three stages. First, we prepare check_ref, r,
> ! arg_base and arg_offset based on what is actually passed as an actual
> ! argument. */
> !
> ! if (POINTER_TYPE_P (TREE_TYPE (arg)))
> ! {
> ! by_ref = true;
> ! if (TREE_CODE (arg) == SSA_NAME)
> ! {
> ! tree type_size;
> ! if (!host_integerp (TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg))), 1))
> ! return;
> ! check_ref = true;
> ! arg_base = arg;
> ! arg_offset = 0;
> ! type_size = TYPE_SIZE (TREE_TYPE (TREE_TYPE (arg)));
> ! arg_size = tree_low_cst (type_size, 1);
> ! ao_ref_init_from_ptr_and_size (&r, arg_base, NULL_TREE);
> ! }
> ! else if (TREE_CODE (arg) == ADDR_EXPR)
> ! {
> ! HOST_WIDE_INT arg_max_size;
> !
> ! arg = TREE_OPERAND (arg, 0);
> ! arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
> ! &arg_max_size);
> ! if (arg_max_size == -1
> ! || arg_max_size != arg_size
> ! || arg_offset < 0)
> ! return;
> ! if (DECL_P (arg_base))
> ! {
> ! tree size;
> ! check_ref = false;
> ! size = build_int_cst (integer_type_node, arg_size);
> ! ao_ref_init_from_ptr_and_size (&r, arg_base, size);
> ! }
> ! else
> ! return;
> ! }
> ! else
> ! return;
> ! }
> ! else
> ! {
> ! HOST_WIDE_INT arg_max_size;
>
> ! gcc_checking_assert (AGGREGATE_TYPE_P (TREE_TYPE (arg)));
>
> + by_ref = false;
> + check_ref = false;
> + arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
> + &arg_max_size);
> + if (arg_max_size == -1
> + || arg_max_size != arg_size
> + || arg_offset < 0)
> + return;
> +
> + ao_ref_init (&r, arg);
> + }
> +
> + /* Second stage walks back the BB, looks at individual statements and as long
> + as it is confident of how the statements affect contents of the
> + aggregates, it builds a sorted linked list of ipa_agg_jf_list structures
> + describing it. */
> + gsi = gsi_for_stmt (call);
> gsi_prev (&gsi);
> for (; !gsi_end_p (gsi); gsi_prev (&gsi))
> {
> + struct ipa_known_agg_contents_list *n, **p;
> gimple stmt = gsi_stmt (gsi);
> ! HOST_WIDE_INT lhs_offset, lhs_size, lhs_max_size;
> ! tree lhs, rhs, lhs_base;
> ! bool partial_overlap;
>
> ! if (!stmt_may_clobber_ref_p_1 (stmt, &r))
> continue;
> if (!gimple_assign_single_p (stmt))
> ! break;
>
> lhs = gimple_assign_lhs (stmt);
> rhs = gimple_assign_rhs1 (stmt);
> + if (!is_gimple_reg_type (rhs))
> + break;
>
> ! lhs_base = get_ref_base_and_extent (lhs, &lhs_offset, &lhs_size,
> ! &lhs_max_size);
> ! if (lhs_max_size == -1
> ! || lhs_max_size != lhs_size
> ! || (lhs_offset < arg_offset
> ! && lhs_offset + lhs_size > arg_offset)
> ! || (lhs_offset < arg_offset + arg_size
> ! && lhs_offset + lhs_size > arg_offset + arg_size))
> ! break;
>
> ! if (check_ref)
> {
> ! if (TREE_CODE (lhs_base) != MEM_REF
> ! || TREE_OPERAND (lhs_base, 0) != arg_base
> ! || !integer_zerop (TREE_OPERAND (lhs_base, 1)))
> ! break;
> }
> + else if (lhs_base != arg_base)
> + break;
> +
> + if (lhs_offset + lhs_size < arg_offset
> + || lhs_offset >= (arg_offset + arg_size))
> + continue;
>
> ! partial_overlap = false;
> ! p = &list;
> ! while (*p && (*p)->offset < lhs_offset)
> {
> ! if ((*p)->offset + (*p)->size > lhs_offset)
> {
> ! partial_overlap = true;
> ! break;
> }
> + p = &(*p)->next;
> + }
> + if (partial_overlap)
> + break;
> + if (*p && (*p)->offset < lhs_offset + lhs_size)
> + {
> + if ((*p)->offset == lhs_offset && (*p)->size == lhs_size)
> + /* We already know this value is subsequently overwritten with
> + something else. */
> + continue;
> else
> ! /* Otherwise this is a partial overlap which we cannot
> ! represent. */
> ! break;
> }
>
> ! rhs = get_ssa_def_if_simple_copy (rhs);
> ! n = XALLOCA (struct ipa_known_agg_contents_list);
> ! n->size = lhs_size;
> ! n->offset = lhs_offset;
> ! if (is_gimple_ip_invariant (rhs))
> ! {
> ! n->constant = rhs;
> ! const_count++;
> ! }
> ! else
> ! n->constant = NULL_TREE;
> ! n->next = *p;
> ! *p = n;
> !
> ! item_count++;
> ! if (const_count == IPA_MAX_AFF_JF_ITEMS
> ! || item_count == 2 * IPA_MAX_AFF_JF_ITEMS)
> ! break;
> ! }
>
> ! /* Third stage just goes over the list and creates an appropriate vector of
> ! ipa_agg_jf_item structures out of it, of sourse only if there are
> ! any known constants to begin with. */
>
> ! if (const_count)
> {
> ! jfunc->agg.by_ref = by_ref;
> ! jfunc->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, const_count);
> ! while (list)
> ! {
> ! if (list->constant)
> ! {
> ! struct ipa_agg_jf_item *item;
> ! item = VEC_quick_push (ipa_agg_jf_item_t,
> ! jfunc->agg.items, NULL);
> ! item->offset = list->offset - arg_offset;
> ! item->value = list->constant;
> ! }
> ! list = list->next;
> ! }
> }
> }
>
> *************** ipa_compute_jump_functions_for_edge (str
> *** 1174,1196 ****
> struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
> struct ipa_edge_args *args = IPA_EDGE_REF (cs);
> gimple call = cs->call_stmt;
> ! int arg_num = gimple_call_num_args (call);
>
> if (arg_num == 0 || args->jump_functions)
> return;
> VEC_safe_grow_cleared (ipa_jump_func_t, gc, args->jump_functions, arg_num);
>
> ! /* We will deal with constants and SSA scalars first: */
> ! compute_scalar_jump_functions (info, parms_ainfo, args, call);
>
> ! /* Let's check whether there are any potential member pointers and if so,
> ! whether we can determine their functions as pass_through. */
> ! if (!compute_pass_through_member_ptrs (info, parms_ainfo, args, call))
> ! return;
>
> ! /* Finally, let's check whether we actually pass a new constant member
> ! pointer here... */
> ! compute_cst_member_ptr_arguments (args, call);
> }
>
> /* Compute jump functions for all edges - both direct and indirect - outgoing
> --- 1366,1435 ----
> struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
> struct ipa_edge_args *args = IPA_EDGE_REF (cs);
> gimple call = cs->call_stmt;
> ! int n, arg_num = gimple_call_num_args (call);
>
> if (arg_num == 0 || args->jump_functions)
> return;
> VEC_safe_grow_cleared (ipa_jump_func_t, gc, args->jump_functions, arg_num);
>
> ! for (n = 0; n < arg_num; n++)
> ! {
> ! struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, n);
> ! tree arg = gimple_call_arg (call, n);
>
> ! if (is_gimple_ip_invariant (arg))
> ! ipa_set_jf_constant (jfunc, arg);
> ! else if (!is_gimple_reg_type (TREE_TYPE (arg))
> ! && TREE_CODE (arg) == PARM_DECL)
> ! {
> ! int index = ipa_get_param_decl_index (info, arg);
> !
> ! gcc_assert (index >=0);
> ! /* Aggregate passed by value, check for pass-through, otherwise we
> ! will attempt to fill in aggregate contents later in this
> ! for cycle. */
> ! if (parm_preserved_before_stmt_p (&parms_ainfo[index], call, arg))
> ! {
> ! ipa_set_jf_simple_pass_through (jfunc, index, false);
> ! continue;
> ! }
> ! }
> ! else if (TREE_CODE (arg) == SSA_NAME)
> ! {
> ! if (SSA_NAME_IS_DEFAULT_DEF (arg))
> ! {
> ! int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
> ! if (index >= 0
> ! && !detect_type_change_ssa (arg, call, jfunc))
> ! {
> ! bool agg_p;
> ! agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
> ! call, arg);
> ! ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
> ! }
> ! }
> ! else
> ! {
> ! gimple stmt = SSA_NAME_DEF_STMT (arg);
> ! if (is_gimple_assign (stmt))
> ! compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
> ! call, stmt, arg);
> ! else if (gimple_code (stmt) == GIMPLE_PHI)
> ! compute_complex_ancestor_jump_func (info, parms_ainfo, jfunc,
> ! call, stmt);
> ! }
> ! }
> ! else
> ! compute_known_type_jump_func (arg, jfunc, call);
>
> ! if ((jfunc->type != IPA_JF_PASS_THROUGH
> ! || !ipa_get_jf_pass_through_agg_preserved (jfunc))
> ! && (jfunc->type != IPA_JF_ANCESTOR
> ! || !ipa_get_jf_ancestor_agg_preserved (jfunc))
> ! && (AGGREGATE_TYPE_P (TREE_TYPE (arg))
> ! || (POINTER_TYPE_P (TREE_TYPE (arg)))))
> ! determine_known_aggregate_parts (call, arg, jfunc);
> ! }
> }
>
> /* Compute jump functions for all edges - both direct and indirect - outgoing
> *************** ipa_compute_jump_functions (struct cgrap
> *** 1217,1232 ****
> 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. */
>
> static tree
> ! ipa_get_member_ptr_load_param (tree rhs, bool use_delta)
> {
> ! tree rec, ref_field, ref_offset, fld, fld_offset, ptr_field, delta_field;
>
> if (TREE_CODE (rhs) == COMPONENT_REF)
> {
> ref_field = TREE_OPERAND (rhs, 1);
> --- 1456,1477 ----
> ipa_compute_jump_functions_for_edge (parms_ainfo, cs);
> }
>
> ! /* 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_stmt_member_ptr_load_param (gimple stmt, bool use_delta,
> ! HOST_WIDE_INT *offset_p)
> {
> ! 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);
> *************** ipa_get_member_ptr_load_param (tree rhs,
> *** 1243,1285 ****
> 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 (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);
> }
>
> /* Returns true iff T is an SSA_NAME defined by a statement. */
> --- 1488,1511 ----
> 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;
> return ref_field == fld ? rec : NULL_TREE;
> }
> else
> ! 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. */
> *************** ipa_note_param_call (struct cgraph_node
> *** 1305,1312 ****
>
> cs = cgraph_edge (node, stmt);
> cs->indirect_info->param_index = param_index;
> ! cs->indirect_info->anc_offset = 0;
> cs->indirect_info->polymorphic = 0;
> return cs;
> }
>
> --- 1531,1539 ----
>
> cs = cgraph_edge (node, stmt);
> cs->indirect_info->param_index = param_index;
> ! cs->indirect_info->offset = 0;
> cs->indirect_info->polymorphic = 0;
> + cs->indirect_info->agg_contents = 0;
> return cs;
> }
>
> *************** ipa_note_param_call (struct cgraph_node
> *** 1365,1371 ****
>
> return (S.*f)(4);
> }
> ! */
>
> static void
> ipa_analyze_indirect_call_uses (struct cgraph_node *node,
> --- 1592,1600 ----
>
> 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,
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1380,1385 ****
> --- 1609,1616 ----
> gimple branch;
> int index;
> basic_block bb, virt_bb, join;
> + HOST_WIDE_INT offset;
> + bool by_ref;
>
> if (SSA_NAME_IS_DEFAULT_DEF (target))
> {
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1390,1409 ****
> return;
> }
>
> /* Now we need to try to match the complex pattern of calling a member
> pointer. */
> !
> ! if (!POINTER_TYPE_P (TREE_TYPE (target))
> || TREE_CODE (TREE_TYPE (TREE_TYPE (target))) != METHOD_TYPE)
> return;
>
> - def = SSA_NAME_DEF_STMT (target);
> - if (gimple_code (def) != GIMPLE_PHI)
> - return;
> -
> - if (gimple_phi_num_args (def) != 2)
> - return;
> -
> /* First, we need to check whether one of these is a load from a member
> pointer that is a parameter to this function. */
> n1 = PHI_ARG_DEF (def, 0);
> --- 1621,1647 ----
> return;
> }
>
> + def = SSA_NAME_DEF_STMT (target);
> + if (gimple_assign_single_p (def)
> + && ipa_load_from_parm_agg_1 (info, parms_ainfo, def,
> + gimple_assign_rhs1 (def), &index, &offset,
> + &by_ref))
> + {
> + struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
> + cs->indirect_info->offset = offset;
> + cs->indirect_info->agg_contents = 1;
> + cs->indirect_info->by_ref = by_ref;
> + return;
> + }
> +
> /* 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
> pointer that is a parameter to this function. */
> n1 = PHI_ARG_DEF (def, 0);
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1414,1428 ****
> d2 = SSA_NAME_DEF_STMT (n2);
>
> join = gimple_bb (def);
> ! if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false)))
> {
> ! if (ipa_get_stmt_member_ptr_load_param (d2, false))
> return;
>
> bb = EDGE_PRED (join, 0)->src;
> virt_bb = gimple_bb (d2);
> }
> ! else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false)))
> {
> bb = EDGE_PRED (join, 1)->src;
> virt_bb = gimple_bb (d1);
> --- 1652,1666 ----
> d2 = SSA_NAME_DEF_STMT (n2);
>
> join = gimple_bb (def);
> ! if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false, &offset)))
> {
> ! 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, &offset)))
> {
> bb = EDGE_PRED (join, 1)->src;
> virt_bb = gimple_bb (d1);
> *************** ipa_analyze_indirect_call_uses (struct c
> *** 1477,1491 ****
>
> rec2 = ipa_get_stmt_member_ptr_load_param (def,
> (TARGET_PTRMEMFUNC_VBIT_LOCATION
> ! == ptrmemfunc_vbit_in_delta));
> !
> if (rec != rec2)
> return;
>
> index = ipa_get_param_decl_index (info, rec);
> ! if (index >= 0 && !is_parm_modified_before_stmt (&parms_ainfo[index],
> ! call, rec))
> ! ipa_note_param_call (node, index, call);
>
> return;
> }
> --- 1715,1733 ----
>
> rec2 = ipa_get_stmt_member_ptr_load_param (def,
> (TARGET_PTRMEMFUNC_VBIT_LOCATION
> ! == ptrmemfunc_vbit_in_delta),
> ! NULL);
> if (rec != rec2)
> return;
>
> index = ipa_get_param_decl_index (info, rec);
> ! if (index >= 0
> ! && parm_preserved_before_stmt_p (&parms_ainfo[index], call, rec))
> ! {
> ! struct cgraph_edge *cs = ipa_note_param_call (node, index, call);
> ! cs->indirect_info->offset = offset;
> ! cs->indirect_info->agg_contents = 1;
> ! }
>
> return;
> }
> *************** ipa_analyze_virtual_call_uses (struct cg
> *** 1540,1546 ****
>
> cs = ipa_note_param_call (node, index, call);
> ii = cs->indirect_info;
> ! ii->anc_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;
> --- 1782,1788 ----
>
> cs = ipa_note_param_call (node, index, call);
> ii = cs->indirect_info;
> ! 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;
> *************** ipa_analyze_node (struct cgraph_node *no
> *** 1682,1689 ****
> ipa_compute_jump_functions (node, parms_ainfo);
>
> for (i = 0; i < param_count; i++)
> ! if (parms_ainfo[i].visited_statements)
> ! BITMAP_FREE (parms_ainfo[i].visited_statements);
>
> current_function_decl = NULL;
> pop_cfun ();
> --- 1924,1935 ----
> ipa_compute_jump_functions (node, parms_ainfo);
>
> for (i = 0; i < param_count; i++)
> ! {
> ! if (parms_ainfo[i].parm_visited_statements)
> ! BITMAP_FREE (parms_ainfo[i].parm_visited_statements);
> ! if (parms_ainfo[i].pt_visited_statements)
> ! BITMAP_FREE (parms_ainfo[i].pt_visited_statements);
> ! }
>
> current_function_decl = NULL;
> pop_cfun ();
> *************** update_jump_functions_after_inlining (st
> *** 1730,1755 ****
> if (dst->type == IPA_JF_ANCESTOR)
> {
> struct ipa_jump_func *src;
>
> /* Variable number of arguments can cause havoc if we try to access
> one that does not exist in the inlined edge. So make sure we
> don't. */
> ! if (dst->value.ancestor.formal_id >= ipa_get_cs_argument_count (top))
> {
> dst->type = IPA_JF_UNKNOWN;
> continue;
> }
>
> ! src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
> if (src->type == IPA_JF_KNOWN_TYPE)
> combine_known_type_and_ancestor_jfs (src, dst);
> else if (src->type == IPA_JF_PASS_THROUGH
> && src->value.pass_through.operation == NOP_EXPR)
> ! dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
> else if (src->type == IPA_JF_ANCESTOR)
> {
> dst->value.ancestor.formal_id = src->value.ancestor.formal_id;
> dst->value.ancestor.offset += src->value.ancestor.offset;
> }
> else
> dst->type = IPA_JF_UNKNOWN;
> --- 1976,2025 ----
> if (dst->type == IPA_JF_ANCESTOR)
> {
> struct ipa_jump_func *src;
> + int dst_fid = dst->value.ancestor.formal_id;
>
> /* Variable number of arguments can cause havoc if we try to access
> one that does not exist in the inlined edge. So make sure we
> don't. */
> ! if (dst_fid >= ipa_get_cs_argument_count (top))
> {
> dst->type = IPA_JF_UNKNOWN;
> continue;
> }
>
> ! src = ipa_get_ith_jump_func (top, dst_fid);
> !
> ! if (src->agg.items
> ! && (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
> ! {
> ! struct ipa_agg_jf_item *item;
> ! int j;
> !
> ! /* Currently we do not produce clobber aggregate jump functions,
> ! replace with merging when we do. */
> ! gcc_assert (!dst->agg.items);
> !
> ! dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc, src->agg.items);
> ! dst->agg.by_ref = src->agg.by_ref;
> ! FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, dst->agg.items, j, item)
> ! item->offset -= dst->value.ancestor.offset;
> ! }
> !
> if (src->type == IPA_JF_KNOWN_TYPE)
> combine_known_type_and_ancestor_jfs (src, dst);
> else if (src->type == IPA_JF_PASS_THROUGH
> && src->value.pass_through.operation == NOP_EXPR)
> ! {
> ! dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
> ! dst->value.ancestor.agg_preserved &=
> ! src->value.pass_through.agg_preserved;
> ! }
> else if (src->type == IPA_JF_ANCESTOR)
> {
> dst->value.ancestor.formal_id = src->value.ancestor.formal_id;
> dst->value.ancestor.offset += src->value.ancestor.offset;
> + dst->value.ancestor.agg_preserved &=
> + src->value.ancestor.agg_preserved;
> }
> else
> dst->type = IPA_JF_UNKNOWN;
> *************** update_jump_functions_after_inlining (st
> *** 1763,1771 ****
> && (dst->value.pass_through.formal_id
> < ipa_get_cs_argument_count (top)))
> {
> ! src = ipa_get_ith_jump_func (top,
> ! dst->value.pass_through.formal_id);
> ! *dst = *src;
> }
> else
> dst->type = IPA_JF_UNKNOWN;
> --- 2033,2065 ----
> && (dst->value.pass_through.formal_id
> < ipa_get_cs_argument_count (top)))
> {
> ! bool agg_p;
> ! int dst_fid = dst->value.pass_through.formal_id;
> ! src = ipa_get_ith_jump_func (top, dst_fid);
> ! agg_p = dst->value.pass_through.agg_preserved;
> !
> ! dst->type = src->type;
> ! dst->value = src->value;
> !
> ! if (src->agg.items
> ! && (agg_p || !src->agg.by_ref))
> ! {
> ! /* Currently we do not produce clobber aggregate jump
> ! functions, replace with merging when we do. */
> ! gcc_assert (!dst->agg.items);
> !
> ! dst->agg.by_ref = src->agg.by_ref;
> ! dst->agg.items = VEC_copy (ipa_agg_jf_item_t, gc,
> ! src->agg.items);
> ! }
> !
> ! if (!agg_p)
> ! {
> ! if (dst->type == IPA_JF_PASS_THROUGH)
> ! dst->value.pass_through.agg_preserved = false;
> ! else if (dst->type == IPA_JF_ANCESTOR)
> ! dst->value.ancestor.agg_preserved = false;
> ! }
> }
> else
> dst->type = IPA_JF_UNKNOWN;
> *************** ipa_make_edge_direct_to_target (struct c
> *** 1812,1817 ****
> --- 2106,2140 ----
> return ie;
> }
>
> + /* Retrieve value from aggregate jump function AGG for the given OFFSET or
> + return NULL if there is not any. BY_REF specifies whether the value has to
> + be passed by reference or by value. */
> +
> + tree
> + ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
> + HOST_WIDE_INT offset, bool by_ref)
> + {
> + struct ipa_agg_jf_item *item;
> + int i;
> +
> + if (by_ref != agg->by_ref)
> + 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 for 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
> *************** try_make_edge_direct_simple_call (struct
> *** 1823,1835 ****
> {
> 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);
> else
> ! return NULL;
> !
> return ipa_make_edge_direct_to_target (ie, target);
> }
>
> --- 2146,2165 ----
> {
> tree target;
>
> ! if (ie->indirect_info->agg_contents)
> ! {
> ! target = ipa_find_agg_cst_for_param (&jfunc->agg,
> ! ie->indirect_info->offset,
> ! ie->indirect_info->by_ref);
> ! if (!target)
> ! return NULL;
> ! }
> else
> ! {
> ! if (jfunc->type != IPA_JF_CONST)
> ! return NULL;
> ! target = ipa_get_jf_constant (jfunc);
> ! }
> return ipa_make_edge_direct_to_target (ie, target);
> }
>
> *************** try_make_edge_direct_virtual_call (struc
> *** 1850,1856 ****
> 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->otr_type);
> if (binfo)
> target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
> --- 2180,2186 ----
> 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->offset,
> ie->indirect_info->otr_type);
> if (binfo)
> target = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
> *************** update_indirect_edges_after_inlining (st
> *** 1886,1891 ****
> --- 2216,2222 ----
> {
> struct cgraph_indirect_call_info *ici = ie->indirect_info;
> struct ipa_jump_func *jfunc;
> + int param_index;
>
> next_ie = ie->next_callee;
>
> *************** update_indirect_edges_after_inlining (st
> *** 1899,1912 ****
> continue;
> }
>
> ! jfunc = ipa_get_ith_jump_func (top, ici->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);
> }
> else
> /* Either we can find a destination for this edge now or never. */
> --- 2230,2256 ----
> continue;
> }
>
> ! 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)
> ! {
> ! if (ici->agg_contents
> ! && !ipa_get_jf_pass_through_agg_preserved (jfunc))
> ! ici->param_index = -1;
> ! else
> ! ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
> ! }
> else if (jfunc->type == IPA_JF_ANCESTOR)
> {
> ! if (ici->agg_contents
> ! && !ipa_get_jf_ancestor_agg_preserved (jfunc))
> ! ici->param_index = -1;
> ! else
> ! {
> ! ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
> ! ici->offset += ipa_get_jf_ancestor_offset (jfunc);
> ! }
> }
> else
> /* Either we can find a destination for this edge now or never. */
> *************** ipa_node_removal_hook (struct cgraph_nod
> *** 2074,2086 ****
> ipa_free_node_params_substructures (IPA_NODE_REF (node));
> }
>
> ! /* Hook that is called by cgraph.c when a node is duplicated. */
>
> static void
> ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
> __attribute__((unused)) void *data)
> {
> struct ipa_edge_args *old_args, *new_args;
>
> ipa_check_create_edge_args ();
>
> --- 2418,2431 ----
> ipa_free_node_params_substructures (IPA_NODE_REF (node));
> }
>
> ! /* Hook that is called by cgraph.c when an edge is duplicated. */
>
> static void
> ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
> __attribute__((unused)) void *data)
> {
> struct ipa_edge_args *old_args, *new_args;
> + unsigned int i;
>
> ipa_check_create_edge_args ();
>
> *************** ipa_edge_duplication_hook (struct cgraph
> *** 2089,2094 ****
> --- 2434,2445 ----
>
> new_args->jump_functions = VEC_copy (ipa_jump_func_t, gc,
> old_args->jump_functions);
> +
> + for (i = 0; i < VEC_length (ipa_jump_func_t, old_args->jump_functions); i++)
> + VEC_index (ipa_jump_func_t, new_args->jump_functions, i)->agg.items
> + = VEC_copy (ipa_agg_jf_item_t, gc,
> + VEC_index (ipa_jump_func_t,
> + old_args->jump_functions, i)->agg.items);
> }
>
> /* Hook that is called by cgraph.c when a node is duplicated. */
> *************** static void
> *** 2787,2794 ****
> ipa_write_jump_function (struct output_block *ob,
> struct ipa_jump_func *jump_func)
> {
> ! streamer_write_uhwi (ob, jump_func->type);
>
> switch (jump_func->type)
> {
> case IPA_JF_UNKNOWN:
> --- 3138,3148 ----
> ipa_write_jump_function (struct output_block *ob,
> struct ipa_jump_func *jump_func)
> {
> ! struct ipa_agg_jf_item *item;
> ! struct bitpack_d bp;
> ! int i, count;
>
> + streamer_write_uhwi (ob, jump_func->type);
> switch (jump_func->type)
> {
> case IPA_JF_UNKNOWN:
> *************** ipa_write_jump_function (struct output_b
> *** 2805,2820 ****
> stream_write_tree (ob, jump_func->value.pass_through.operand, true);
> streamer_write_uhwi (ob, jump_func->value.pass_through.formal_id);
> streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
> break;
> case IPA_JF_ANCESTOR:
> streamer_write_uhwi (ob, jump_func->value.ancestor.offset);
> stream_write_tree (ob, jump_func->value.ancestor.type, true);
> streamer_write_uhwi (ob, jump_func->value.ancestor.formal_id);
> 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;
> }
> }
>
> --- 3159,3191 ----
> stream_write_tree (ob, jump_func->value.pass_through.operand, true);
> streamer_write_uhwi (ob, jump_func->value.pass_through.formal_id);
> streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
> + bp = bitpack_create (ob->main_stream);
> + bp_pack_value (&bp, jump_func->value.pass_through.agg_preserved, 1);
> + streamer_write_bitpack (&bp);
> break;
> case IPA_JF_ANCESTOR:
> streamer_write_uhwi (ob, jump_func->value.ancestor.offset);
> stream_write_tree (ob, jump_func->value.ancestor.type, true);
> streamer_write_uhwi (ob, jump_func->value.ancestor.formal_id);
> + bp = bitpack_create (ob->main_stream);
> + bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
> + streamer_write_bitpack (&bp);
> break;
> ! }
> !
> ! count = VEC_length (ipa_agg_jf_item_t, jump_func->agg.items);
> ! streamer_write_uhwi (ob, count);
> ! if (count)
> ! {
> ! bp = bitpack_create (ob->main_stream);
> ! bp_pack_value (&bp, jump_func->agg.by_ref, 1);
> ! streamer_write_bitpack (&bp);
> ! }
> !
> ! FOR_EACH_VEC_ELT (ipa_agg_jf_item_t, jump_func->agg.items, i, item)
> ! {
> ! streamer_write_uhwi (ob, item->offset);
> ! stream_write_tree (ob, item->value, true);
> }
> }
>
> *************** ipa_read_jump_function (struct lto_input
> *** 2825,2832 ****
> struct ipa_jump_func *jump_func,
> struct data_in *data_in)
> {
> ! jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib);
>
> switch (jump_func->type)
> {
> case IPA_JF_UNKNOWN:
> --- 3196,3205 ----
> struct ipa_jump_func *jump_func,
> struct data_in *data_in)
> {
> ! struct bitpack_d bp;
> ! int i, count;
>
> + jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib);
> switch (jump_func->type)
> {
> case IPA_JF_UNKNOWN:
> *************** ipa_read_jump_function (struct lto_input
> *** 2845,2860 ****
> jump_func->value.pass_through.formal_id = streamer_read_uhwi (ib);
> jump_func->value.pass_through.operation
> = (enum tree_code) streamer_read_uhwi (ib);
> break;
> case IPA_JF_ANCESTOR:
> jump_func->value.ancestor.offset = streamer_read_uhwi (ib);
> jump_func->value.ancestor.type = stream_read_tree (ib, data_in);
> jump_func->value.ancestor.formal_id = streamer_read_uhwi (ib);
> 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;
> }
> }
>
> --- 3218,3249 ----
> jump_func->value.pass_through.formal_id = streamer_read_uhwi (ib);
> jump_func->value.pass_through.operation
> = (enum tree_code) streamer_read_uhwi (ib);
> + bp = streamer_read_bitpack (ib);
> + jump_func->value.pass_through.agg_preserved = bp_unpack_value (&bp, 1);
> break;
> case IPA_JF_ANCESTOR:
> jump_func->value.ancestor.offset = streamer_read_uhwi (ib);
> jump_func->value.ancestor.type = stream_read_tree (ib, data_in);
> jump_func->value.ancestor.formal_id = streamer_read_uhwi (ib);
> + bp = streamer_read_bitpack (ib);
> + jump_func->value.ancestor.agg_preserved = bp_unpack_value (&bp, 1);
> break;
> ! }
> !
> ! count = streamer_read_uhwi (ib);
> ! jump_func->agg.items = VEC_alloc (ipa_agg_jf_item_t, gc, count);
> ! if (count)
> ! {
> ! bp = streamer_read_bitpack (ib);
> ! jump_func->agg.by_ref = bp_unpack_value (&bp, 1);
> ! }
> ! for (i = 0; i < count; i++)
> ! {
> ! struct ipa_agg_jf_item *item = VEC_quick_push (ipa_agg_jf_item_t,
> ! jump_func->agg.items, NULL);
> !
> ! item->offset = streamer_read_uhwi (ib);
> ! item->value = stream_read_tree (ib, data_in);
> }
> }
>
> *************** ipa_write_indirect_edge_info (struct out
> *** 2869,2877 ****
> struct bitpack_d bp;
>
> streamer_write_hwi (ob, ii->param_index);
> ! streamer_write_hwi (ob, ii->anc_offset);
> bp = bitpack_create (ob->main_stream);
> bp_pack_value (&bp, ii->polymorphic, 1);
> streamer_write_bitpack (&bp);
>
> if (ii->polymorphic)
> --- 3258,3268 ----
> struct bitpack_d bp;
>
> streamer_write_hwi (ob, ii->param_index);
> ! 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);
> + bp_pack_value (&bp, ii->by_ref, 1);
> streamer_write_bitpack (&bp);
>
> if (ii->polymorphic)
> *************** ipa_read_indirect_edge_info (struct lto_
> *** 2893,2901 ****
> struct bitpack_d bp;
>
> ii->param_index = (int) streamer_read_hwi (ib);
> ! ii->anc_offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
> bp = streamer_read_bitpack (ib);
> ii->polymorphic = bp_unpack_value (&bp, 1);
> if (ii->polymorphic)
> {
> ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
> --- 3284,3294 ----
> struct bitpack_d bp;
>
> ii->param_index = (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);
> + ii->by_ref = bp_unpack_value (&bp, 1);
> if (ii->polymorphic)
> {
> ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib);
> Index: src/gcc/cgraph.h
> ===================================================================
> *** src.orig/gcc/cgraph.h
> --- src/gcc/cgraph.h
> *************** typedef enum cgraph_inline_failed_enum {
> *** 338,346 ****
>
> struct GTY(()) cgraph_indirect_call_info
> {
> ! /* Offset accumulated from ancestor jump functions of inlined call graph
> ! edges. */
> ! HOST_WIDE_INT anc_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. */
> --- 338,348 ----
>
> struct GTY(()) cgraph_indirect_call_info
> {
> ! /* 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. */
> *************** struct GTY(()) cgraph_indirect_call_info
> *** 353,358 ****
> --- 355,366 ----
> /* 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;
> + /* When the previous bit is set, this one determines whether the destination
> + is loaded from a parameter passed by reference. */
> + unsigned by_ref : 1;
> };
>
> struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
> Index: src/gcc/ipa-cp.c
> ===================================================================
> *** src.orig/gcc/ipa-cp.c
> --- src/gcc/ipa-cp.c
> *************** ipa_get_indirect_edge_target (struct cgr
> *** 1107,1113 ****
> }
>
> token = ie->indirect_info->otr_token;
> ! anc_offset = ie->indirect_info->anc_offset;
> otr_type = ie->indirect_info->otr_type;
>
> t = VEC_index (tree, known_vals, param_index);
> --- 1107,1113 ----
> }
>
> token = ie->indirect_info->otr_token;
> ! 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 ****
> --- 1,221 ----
> + /* Verify that simple indirect calls are inlined even without early
> + inlining.. */
> + /* { dg-do compile } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + unsigned u;
> + };
> +
> + struct U
> + {
> + struct U *next;
> + struct S s;
> + short a[8];
> + };
> +
> + extern void non_existent(struct S *p, int);
> +
> + /* ----- 1 ----- */
> +
> + 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.u = 1001;
> + hiphip1 (&s);
> + return 0;
> + }
> +
> + /* ----- 2 ----- */
> +
> + struct S *gp;
> +
> + 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->u = 1002;
> + hiphip2 (p);
> + return 0;
> + }
> +
> + /* ----- 3 ----- */
> +
> + static void hooray3 (struct S *p)
> + {
> + non_existent (p, 2);
> + }
> +
> + static void hip3 (struct S *p)
> + {
> + p->f (p);
> + }
> +
> + static __attribute__ ((flatten)) void hiphip3 (struct S *p)
> + {
> + hip3 (p);
> + }
> +
> + int test3 (void)
> + {
> + struct S *p = gp;
> + p->i = 2341;
> + p->f = hooray3;
> + p->u = 1003;
> + hiphip3 (p);
> + return 0;
> + }
> +
> + /* ----- 4 ----- */
> +
> + static void hooray4 (struct S *p)
> + {
> + non_existent (p, 3);
> + }
> +
> + static void hiphip4 (struct S s)
> + {
> + s.f (&s);
> + }
> +
> + int test4(void)
> + {
> + struct S s;
> + s.i = 3412;
> + s.f = hooray4;
> + s.u = 1004;
> + hiphip4 (s);
> + return 0;
> + }
> +
> + /* ----- 5 ----- */
> +
> + struct U *gu;
> +
> + static void hooray5 (struct S *p)
> + {
> + non_existent (p, 5);
> + }
> +
> + static void hip5 (struct S *p)
> + {
> + p->f (p);
> + }
> +
> + static void hiphip5 (struct U *u)
> + {
> + hip5 (&u->s);
> + }
> +
> + int test5 (void)
> + {
> + struct U *u = gu;
> + u->next = u;
> + u->s.i = 9876;
> + u->s.f = hooray5;
> + u->s.u = 1005;
> + hiphip5 (u);
> + return 0;
> + }
> +
> + /* ----- 6 ----- */
> +
> + static void hooray6 (struct S *p)
> + {
> + non_existent (p, 6);
> + }
> +
> + static void hip6 (struct S *p)
> + {
> + p->f (p);
> + }
> +
> + static __attribute__ ((flatten)) void hiphip6 (struct U *u)
> + {
> + hip6 (&u->s);
> + }
> +
> + int test6 (void)
> + {
> + struct U *u = gu;
> + u->next = u;
> + u->s.i = 9876;
> + u->s.f = hooray6;
> + u->s.u = 1006;
> + hiphip6 (u);
> + return 0;
> + }
> +
> + /* ----- 7 ----- */
> +
> + struct S **gdp;
> +
> +
> + static void hooray7 (struct S *p)
> + {
> + non_existent (p, 7);
> + }
> +
> + static void hip7 (struct S *p)
> + {
> + p->f (p);
> + gdp = &p;
> + }
> +
> + static void hiphip7 (struct S *p)
> + {
> + hip7 (p);
> + gdp = &p;
> + }
> +
> + int test7 (void)
> + {
> + struct S *p = gp;
> + p->i = 7341;
> + p->f = hooray7;
> + p->u = 1007;
> + hiphip7 (p);
> + 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 "hooray3\[^\\n\]*inline copy in test3" "inline" } } */
> + /* { dg-final { scan-ipa-dump "hooray4\[^\\n\]*inline copy in test4" "inline" } } */
> + /* { dg-final { scan-ipa-dump "hooray5\[^\\n\]*inline copy in test5" "inline" } } */
> + /* { dg-final { scan-ipa-dump "hooray6\[^\\n\]*inline copy in test6" "inline" } } */
> + /* { dg-final { scan-ipa-dump "hooray7\[^\\n\]*inline copy in test7" "inline" } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-5.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-5.c
> ***************
> *** 0 ****
> --- 1,124 ----
> + /* Verify that simple indirect calls are inlined even without early
> + inlining.. */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */
> +
> + extern void abort (void);
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + int j,k,l;
> + };
> +
> + struct Z
> + {
> + unsigned u;
> + void (*f)(struct Z *, int);
> + struct Z *next;
> + };
> +
> + static struct Z *gz;
> + static struct S *gs;
> + static int gr = 111;
> + char gc[1024];
> +
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> + return (struct S *) &gc;
> + }
> +
> +
> + static void wrong_target_1 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void wrong_target_2 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void wrong_target_3 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void good_target (struct Z *z, int i)
> + {
> + gr = 0;
> + }
> +
> + static void good_target_3 (struct S *s)
> + {
> + gr = 0;
> + }
> +
> + static void g1 (struct S *s)
> + {
> + struct Z *z = (struct Z*) s;
> + z->f (z, 8);
> + }
> +
> + static void f1 (struct S *s)
> + {
> + gz->f = good_target;
> + g1 (s);
> + }
> +
> + static void g2 (struct Z *z)
> + {
> + z->f (z, 8);
> + }
> +
> + static void f2 (struct S *s)
> + {
> + gz->f = good_target;
> + g2 ((struct Z*) s);
> + }
> +
> + static void g3 (struct S *s)
> + {
> + s->f (s);
> + }
> +
> + static void h3 (struct Z *z)
> + {
> + gs->f = good_target_3;
> + g3 ((struct S *) z);
> + }
> +
> + static void f3 (struct S *s)
> + {
> + h3 ((struct Z*) s);
> + }
> +
> + int main (int argc, char **argv)
> + {
> + struct S *s = get_s();
> + s->i = 5678;
> + s->f = wrong_target_1;
> + s->j = 1234;
> + gz = (struct Z *) s;
> + f1 (s);
> +
> + s = get_s();
> + gz = (struct Z *) s;
> + s->i = 9999;
> + s->f = wrong_target_1;
> + f2 (s);
> +
> + s = get_s();
> + gs = s;
> + s->i = 9999;
> + s->f = wrong_target_3;
> + f3 (s);
> +
> + return gr;
> + }
> +
> +
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline" } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-6.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-6.c
> ***************
> *** 0 ****
> --- 1,72 ----
> + /* Verify that simple indirect calls are inlined even without early
> + inlining.. */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */
> +
> + extern void abort (void);
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + int j,k,l;
> + };
> +
> + struct Z
> + {
> + unsigned u;
> + void (*f)(struct Z *, int);
> + struct Z *next;
> + };
> +
> + static struct S *gs;
> + static int gr = 111;
> + char gc[1024];
> +
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> + return (struct S *) &gc;
> + }
> +
> + static void wrong_target (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void good_target (struct S *s)
> + {
> + gr = 0;
> + }
> +
> + static void g1 (struct S *s)
> + {
> + s->f (s);
> + }
> +
> + static void f2 (struct Z *z)
> + {
> + gs->f = good_target;
> + g1 ((struct S *) z);
> + }
> +
> + static inline __attribute__ ((flatten)) void f1 (struct S *s)
> + {
> + f2 ((struct Z *) s);
> + }
> +
> + int main (int argc, char **argv)
> + {
> + struct S *s = get_s();
> + s->i = 5678;
> + s->f = wrong_target;
> + s->j = 1234;
> + gs = s;
> + f1 (s);
> +
> + return gr;
> + }
> +
> +
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline" } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/ipa/iinline-7.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/ipa/iinline-7.c
> ***************
> *** 0 ****
> --- 1,157 ----
> + /* Verify that simple indirect calls are inlined even without early
> + inlining.. */
> + /* { dg-do run } */
> + /* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */
> +
> + extern void abort (void);
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + int j,k,l;
> + };
> +
> + struct U
> + {
> + struct U *next;
> + struct S s;
> + short a[8];
> + };
> +
> + struct Z
> + {
> + unsigned u;
> + void (*f)(struct Z *, int);
> + struct Z *next;
> + };
> +
> + static struct Z *gz;
> + static struct U *gu;
> + static int gr = 111;
> + char gc[1024];
> +
> + static __attribute__ ((noinline, noclone)) struct U *
> + get_u (void)
> + {
> + return (struct U *) &gc;
> + }
> +
> + static void wrong_target_1 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void wrong_target_2 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void wrong_target_3 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void wrong_target_4 (struct S *s)
> + {
> + abort ();
> + }
> +
> + static void good_target (struct Z *z, int i)
> + {
> + gr = 0;
> + }
> +
> + static void good_target_4 (struct S *s)
> + {
> + gr = 0;
> + }
> +
> + static void g1 (struct S *s)
> + {
> + struct Z *z = (struct Z*) s;
> + z->f (z, 8);
> + }
> +
> + static void f1 (struct U *u)
> + {
> + gz->f = good_target;
> + g1 (&u->s);
> + }
> +
> + static void g2 (struct Z *z)
> + {
> + z->f (z, 8);
> + }
> +
> + static void f2 (struct U *u)
> + {
> + gz->f = good_target;
> + g2 ((struct Z*) &u->s);
> + }
> +
> + static void h3 (struct Z *z)
> + {
> + z->f (z, 8);
> + }
> +
> + static void g3 (struct S *s)
> + {
> + h3 ((struct Z*) s);
> + }
> +
> + static void f3 (struct U *u)
> + {
> + gz->f = good_target;
> + g3 (&u->s);
> + }
> +
> + static void h4 (struct S *s)
> + {
> + s->f (s);
> + }
> +
> + static void g4 (struct U *u)
> + {
> + h4 (&u->s);
> + }
> +
> + static inline __attribute__ ((flatten)) void f4 (struct Z *z)
> + {
> + gu->s.f = good_target_4;
> + g4 ((struct U *) z);
> + }
> +
> + int main (int argc, char **argv)
> + {
> + struct U *u = get_u ();
> + u->next = u;
> + u->s.i = 5678;
> + u->s.f = wrong_target_1;
> + u->s.j = 1234;
> + gz = (struct Z *) &u->s;
> + f1 (u);
> +
> + u = get_u();
> + u->s.i = 9999;
> + u->s.f = wrong_target_2;
> + gz = (struct Z *) &u->s;
> + f2 (u);
> +
> + u = get_u();
> + u->s.i = 9998;
> + u->s.f = wrong_target_3;
> + gz = (struct Z *) &u->s;
> + f3 (u);
> +
> + u = get_u();
> + u->s.i = 9998;
> + u->s.f = wrong_target_4;
> + gu = u;
> + f4 ((struct Z *) u);
> + return gr;
> + }
> +
> +
> + /* { dg-final { scan-ipa-dump-not "wrong_target\[^\\n\]*inline copy in" "inline" } } */
> + /* { dg-final { cleanup-ipa-dump "inline" } } */
> Index: src/gcc/testsuite/gcc.dg/lto/20120723_0.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/lto/20120723_0.c
> ***************
> *** 0 ****
> --- 1,52 ----
> + /* Make sure that by reference and by value aggregate jump functions do not get
> + mixed up. */
> + /* { dg-lto-do run } */
> + /* { dg-lto-options {{-O3 -fno-early-inlining -flto}} } */
> +
> + extern void abort (void);
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + int j;
> + };
> +
> + struct E
> + {
> + struct S *p;
> + };
> +
> + struct S *gs;
> + int gr = 111;
> + char gc[1024];
> +
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> + return (struct S *) &gc;
> + }
> +
> + static void wrong_target (struct S *s)
> + {
> + abort ();
> + }
> +
> + void bar (struct S *s)
> + {
> + s->f (s);
> + }
> +
> + extern void foo (struct S *s);
> +
> + int main (int argc, char **argv)
> + {
> + struct S *s = get_s();
> + gs = s;
> + s->i = 5678;
> + s->f = wrong_target;
> + s->j = 1234;
> + foo (s);
> +
> + return gr;
> + }
> Index: src/gcc/testsuite/gcc.dg/lto/20120723_1.c
> ===================================================================
> *** /dev/null
> --- src/gcc/testsuite/gcc.dg/lto/20120723_1.c
> ***************
> *** 0 ****
> --- 1,39 ----
> + /* Make sure that by reference and by value aggregate jump functions do not get
> + mixed up. */
> +
> + extern void abort (void);
> +
> + struct S
> + {
> + int i;
> + void (*f)(struct S *);
> + int j;
> + };
> +
> + struct E
> + {
> + struct S *p;
> + };
> +
> + extern struct S *gs;
> + extern int gr;
> + extern char gc[1024];
> +
> + static __attribute__ ((noinline, noclone)) struct S *
> + get_s (void)
> + {
> + return (struct S *) &gc;
> + }
> +
> + static void good_target (struct S *s)
> + {
> + gr = 0;
> + }
> +
> + extern void bar (struct E e);
> +
> + void foo (struct E e)
> + {
> + gs->f = good_target;
> + bar (e);
> + }
>
>
--
Richard Guenther <rguenther@suse.de>
SUSE / SUSE Labs
SUSE LINUX Products GmbH - Nuernberg - AG Nuernberg - HRB 16746
GF: Jeff Hawn, Jennifer Guild, Felix Imend