From: Martin Jambor Date: Thu, 9 Aug 2012 16:05:46 +0000 (+0200) Subject: cgraph.h (cgraph_indirect_call_info): Field anc_offse renamd to offset, updated all... X-Git-Tag: upstream/12.2.0~74855 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8b7773a4ee4833570eb306cbd64884c288f89d57;p=platform%2Fupstream%2Fgcc.git cgraph.h (cgraph_indirect_call_info): Field anc_offse renamd to offset, updated all users. 2012-08-09 Martin Jambor * 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. From-SVN: r190260 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dc96a3b..0fe531c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,69 @@ +2012-08-09 Martin Jambor + + * 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. + 2012-08-09 Oleg Endo PR target/39423 diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 7b9098b..24ede64 100644 --- a/gcc/cgraph.h +++ b/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,12 @@ 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; + /* 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 { diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 7ecd9e2..b918470 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1107,7 +1107,7 @@ ipa_get_indirect_edge_target (struct cgraph_edge *ie, } 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); diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 09e96d5..7f90984 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -44,8 +44,8 @@ along with GCC; see the file COPYING3. If not see struct param_analysis_info { - bool modified; - bitmap visited_statements; + bool parm_modified, ref_modified, pt_modified; + bitmap parm_visited_statements, pt_visited_statements; }; /* Vector where the parameter infos are actually stored. */ @@ -178,24 +178,21 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs) } 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 ", + 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, " "); + 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) @@ -205,8 +202,34 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs) 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"); + } + } } } @@ -286,12 +309,14 @@ ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant) /* 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) +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. */ @@ -304,30 +329,20 @@ ipa_set_jf_arith_pass_through (struct ipa_jump_func *jfunc, int formal_id, 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) + 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; -} - -/* 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; + jfunc->value.ancestor.agg_preserved = agg_preserved; } /* Structure to be passed in between detect_type_change and @@ -581,30 +596,35 @@ mark_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED, 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. */ +/* 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 -is_parm_modified_before_stmt (struct param_analysis_info *parm_ainfo, - gimple stmt, tree parm) +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->modified) - return true; + if (parm_ainfo && parm_ainfo->parm_modified) + return false; 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; + 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, @@ -628,12 +648,158 @@ load_from_unmodified_param (struct ipa_node_params *info, index = ipa_get_param_decl_index (info, op1); if (index < 0 - || is_parm_modified_before_stmt (&parms_ainfo[index], stmt, op1)) + || !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 (*) (struct S *) D.1867; + struct S * p.1; + + : + 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 @@ -731,7 +897,11 @@ compute_complex_assign_jump_func (struct ipa_node_params *info, } else if (gimple_assign_single_p (stmt) && !detect_type_change_ssa (tc_ssa, call, jfunc)) - ipa_set_jf_simple_pass_through (jfunc, index); + { + 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; } @@ -757,7 +927,9 @@ compute_complex_assign_jump_func (struct ipa_node_params *info, 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); + 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 @@ -828,6 +1000,7 @@ get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset) 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) { @@ -881,7 +1054,9 @@ compute_complex_ancestor_jump_func (struct ipa_node_params *info, } if (!detect_type_change (obj, expr, call, jfunc, offset)) - ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index); + 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, @@ -916,55 +1091,6 @@ compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc, 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 @@ -980,14 +1106,16 @@ type_like_member_ptr_p (tree type, tree *method_ptr, tree *delta) 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; @@ -998,54 +1126,9 @@ type_like_member_ptr_p (tree type, tree *method_ptr, tree *delta) 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. */ + return the rhs of its defining statement. Otherwise return RHS as it + is. */ static inline tree get_ssa_def_if_simple_copy (tree rhs) @@ -1062,104 +1145,213 @@ 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: +/* TODO: Turn this into a PARAM. */ +#define IPA_MAX_AFF_JF_ITEMS 16 + +/* Simple linked list, describing known contents of an aggregate beforere + call. */ - D.2515.__pfn ={v} printStuff; - D.2515.__delta ={v} 0; - i_1 = doprinting (D.2515); */ +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_cst_member_ptr (gimple call, tree arg, tree method_field, - tree delta_field, struct ipa_jump_func *jfunc) +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 method = NULL_TREE; - tree delta = NULL_TREE; + tree arg_base; + bool check_ref, by_ref; + ao_ref r; - gsi = gsi_for_stmt (call); + /* 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); - tree lhs, rhs, fld; + HOST_WIDE_INT lhs_offset, lhs_size, lhs_max_size; + tree lhs, rhs, lhs_base; + bool partial_overlap; - if (!stmt_may_clobber_ref_p (stmt, arg)) + if (!stmt_may_clobber_ref_p_1 (stmt, &r)) continue; if (!gimple_assign_single_p (stmt)) - return; + break; lhs = gimple_assign_lhs (stmt); rhs = gimple_assign_rhs1 (stmt); + if (!is_gimple_reg_type (rhs)) + break; - if (TREE_CODE (lhs) != COMPONENT_REF - || TREE_OPERAND (lhs, 0) != arg) - return; + 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; - fld = TREE_OPERAND (lhs, 1); - if (!method && fld == method_field) + if (check_ref) { - 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 (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 (!delta && fld == delta_field) + 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) { - rhs = get_ssa_def_if_simple_copy (rhs); - if (TREE_CODE (rhs) == INTEGER_CST) + if ((*p)->offset + (*p)->size > lhs_offset) { - delta = rhs; - if (method) - { - ipa_set_jf_member_ptr_cst (jfunc, rhs, delta); - return; - } + 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 - return; + /* Otherwise this is a partial overlap which we cannot + represent. */ + break; } - } - return; -} + 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; -/* 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. */ + item_count++; + if (const_count == IPA_MAX_AFF_JF_ITEMS + || item_count == 2 * IPA_MAX_AFF_JF_ITEMS) + break; + } -static void -compute_cst_member_ptr_arguments (struct ipa_edge_args *args, - gimple call) -{ - unsigned num; - tree arg, method_field, delta_field; + /* 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. */ - for (num = 0; num < gimple_call_num_args (call); num++) + if (const_count) { - 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); + 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; + } } } @@ -1174,23 +1366,70 @@ ipa_compute_jump_functions_for_edge (struct param_analysis_info *parms_ainfo, 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); + 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); - /* We will deal with constants and SSA scalars first: */ - compute_scalar_jump_functions (info, parms_ainfo, args, call); + 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); - /* 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; + 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); - /* Finally, let's check whether we actually pass a new constant member - pointer here... */ - compute_cst_member_ptr_arguments (args, call); + 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 @@ -1217,16 +1456,22 @@ ipa_compute_jump_functions (struct cgraph_node *node, 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); @@ -1243,43 +1488,24 @@ ipa_get_member_ptr_load_param (tree rhs, bool use_delta) 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. */ @@ -1305,8 +1531,9 @@ ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt) 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; } @@ -1365,7 +1592,9 @@ ipa_note_param_call (struct cgraph_node *node, int param_index, gimple stmt) 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, @@ -1380,6 +1609,8 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, gimple branch; int index; basic_block bb, virt_bb, join; + HOST_WIDE_INT offset; + bool by_ref; if (SSA_NAME_IS_DEFAULT_DEF (target)) { @@ -1390,20 +1621,27 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, 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 (!POINTER_TYPE_P (TREE_TYPE (target)) + 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; - 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); @@ -1414,15 +1652,15 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, 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); @@ -1477,15 +1715,19 @@ ipa_analyze_indirect_call_uses (struct cgraph_node *node, 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_modified_before_stmt (&parms_ainfo[index], - call, rec)) - ipa_note_param_call (node, index, call); + 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; } @@ -1540,7 +1782,7 @@ ipa_analyze_virtual_call_uses (struct cgraph_node *node, 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; @@ -1685,8 +1927,12 @@ ipa_analyze_node (struct cgraph_node *node) 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); + { + 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 (); @@ -1733,26 +1979,50 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, 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->value.ancestor.formal_id >= ipa_get_cs_argument_count (top)) + if (dst_fid >= ipa_get_cs_argument_count (top)) { dst->type = IPA_JF_UNKNOWN; continue; } - src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id); + 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.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; @@ -1766,9 +2036,33 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, && (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; + 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; @@ -1815,6 +2109,35 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target) 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 @@ -1826,13 +2149,20 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, { 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->indirect_info->by_ref); + 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); } @@ -1853,7 +2183,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, 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, @@ -1889,6 +2219,7 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs, { struct cgraph_indirect_call_info *ici = ie->indirect_info; struct ipa_jump_func *jfunc; + int param_index; next_ie = ie->next_callee; @@ -1902,14 +2233,27 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs, 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); + { + 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) { - ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc); - ici->anc_offset += ipa_get_jf_ancestor_offset (jfunc); + 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. */ @@ -2077,13 +2421,14 @@ ipa_node_removal_hook (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) ipa_free_node_params_substructures (IPA_NODE_REF (node)); } -/* Hook that is called by cgraph.c when a node is duplicated. */ +/* 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 (); @@ -2092,6 +2437,12 @@ ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst, 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. */ @@ -2788,8 +3139,11 @@ static void ipa_write_jump_function (struct output_block *ob, struct ipa_jump_func *jump_func) { - streamer_write_uhwi (ob, jump_func->type); + 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: @@ -2806,16 +3160,33 @@ ipa_write_jump_function (struct output_block *ob, 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; - 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); + 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); } } @@ -2826,8 +3197,10 @@ ipa_read_jump_function (struct lto_input_block *ib, struct ipa_jump_func *jump_func, struct data_in *data_in) { - jump_func->type = (enum jump_func_type) streamer_read_uhwi (ib); + 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: @@ -2846,16 +3219,32 @@ ipa_read_jump_function (struct lto_input_block *ib, 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; - 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); + 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); } } @@ -2870,9 +3259,11 @@ ipa_write_indirect_edge_info (struct output_block *ob, 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); + bp_pack_value (&bp, ii->by_ref, 1); streamer_write_bitpack (&bp); if (ii->polymorphic) @@ -2894,9 +3285,11 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib, 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); + ii->by_ref = bp_unpack_value (&bp, 1); if (ii->polymorphic) { ii->otr_token = (HOST_WIDE_INT) streamer_read_hwi (ib); diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 7faa3e1..489e5d8 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -44,10 +44,6 @@ along with GCC; see the file COPYING3. If not see 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 */ }; @@ -104,6 +99,13 @@ struct GTY(()) ipa_pass_through_data 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 @@ -117,21 +119,56 @@ struct GTY(()) ipa_ancestor_jf_data 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; }; -/* Structure holding a C++ member pointer constant. Holds a pointer to the - method and delta offset. */ -struct GTY(()) ipa_member_ptr_cst +/* 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 { - tree pfn; - tree delta; + /* 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 @@ -140,7 +177,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; @@ -214,6 +250,15 @@ ipa_get_jf_pass_through_operation (struct ipa_jump_func *jfunc) 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 @@ -242,13 +287,13 @@ ipa_get_jf_ancestor_formal_id (struct ipa_jump_func *jfunc) return jfunc->value.ancestor.formal_id; } -/* Return the pfn part of a member pointer constant jump function JFUNC. */ +/* Return the agg_preserved flag of an ancestor jump functin JFUNC. */ -static inline tree -ipa_get_jf_member_ptr_pfn (struct ipa_jump_func *jfunc) +static inline bool +ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc) { - gcc_checking_assert (jfunc->type == IPA_JF_CONST_MEMBER_PTR); - return jfunc->value.member_cst.pfn; + gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR); + return jfunc->value.ancestor.agg_preserved; } /* Summary describing a single formal parameter. */ @@ -456,6 +501,12 @@ struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree); /* 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 *); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 790921f..eddc103 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2012-08-09 Martin Jambor + + * gcc.dg/ipa/iinline-4.c: New test. + * gcc.dg/ipa/iinline-5.c: Likewise. + * gcc.dg/ipa/iinline-6.c: Likewise. + * gcc.dg/ipa/iinline-7.c: Likewise. + * gcc.dg/lto/20120723_0.c: Likewise. + * gcc.dg/lto/20120723_1.c: Likewise. + 2012-08-09 Oleg Endo PR target/39423 diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-4.c b/gcc/testsuite/gcc.dg/ipa/iinline-4.c new file mode 100644 index 0000000..4de90d6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-4.c @@ -0,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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-5.c b/gcc/testsuite/gcc.dg/ipa/iinline-5.c new file mode 100644 index 0000000..8fb47ca --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-5.c @@ -0,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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-6.c b/gcc/testsuite/gcc.dg/ipa/iinline-6.c new file mode 100644 index 0000000..5a9b759 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-6.c @@ -0,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" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-7.c b/gcc/testsuite/gcc.dg/ipa/iinline-7.c new file mode 100644 index 0000000..c95d374 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-7.c @@ -0,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" } } */ diff --git a/gcc/testsuite/gcc.dg/lto/20120723_0.c b/gcc/testsuite/gcc.dg/lto/20120723_0.c new file mode 100644 index 0000000..ea08a00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/20120723_0.c @@ -0,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; +} diff --git a/gcc/testsuite/gcc.dg/lto/20120723_1.c b/gcc/testsuite/gcc.dg/lto/20120723_1.c new file mode 100644 index 0000000..5c50861 --- /dev/null +++ b/gcc/testsuite/gcc.dg/lto/20120723_1.c @@ -0,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); +}