aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Jambor <mjambor@suse.cz>2012-08-09 18:05:46 +0200
committerMartin Jambor <jamborm@gcc.gnu.org>2012-08-09 18:05:46 +0200
commit8b7773a4ee4833570eb306cbd64884c288f89d57 (patch)
treeb4b8ef818a3ba89f292cf70e40875c201119ffe6
parentb67b3838e94c69f500c84ff58ffa44720a0ec79c (diff)
downloadgcc-8b7773a4ee4833570eb306cbd64884c288f89d57.zip
gcc-8b7773a4ee4833570eb306cbd64884c288f89d57.tar.gz
gcc-8b7773a4ee4833570eb306cbd64884c288f89d57.tar.bz2
cgraph.h (cgraph_indirect_call_info): Field anc_offse renamd to offset, updated all users.
2012-08-09 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. From-SVN: r190260
-rw-r--r--gcc/ChangeLog66
-rw-r--r--gcc/cgraph.h14
-rw-r--r--gcc/ipa-cp.c2
-rw-r--r--gcc/ipa-prop.c1007
-rw-r--r--gcc/ipa-prop.h83
-rw-r--r--gcc/testsuite/ChangeLog9
-rw-r--r--gcc/testsuite/gcc.dg/ipa/iinline-4.c221
-rw-r--r--gcc/testsuite/gcc.dg/ipa/iinline-5.c124
-rw-r--r--gcc/testsuite/gcc.dg/ipa/iinline-6.c72
-rw-r--r--gcc/testsuite/gcc.dg/ipa/iinline-7.c157
-rw-r--r--gcc/testsuite/gcc.dg/lto/20120723_0.c52
-rw-r--r--gcc/testsuite/gcc.dg/lto/20120723_1.c39
12 files changed, 1519 insertions, 327 deletions
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 <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.
+
2012-08-09 Oleg Endo <olegendo@gcc.gnu.org>
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 (*<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
@@ -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 <mjambor@suse.cz>
+
+ * 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 <olegendo@gcc.gnu.org>
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);
+}