summaryrefslogtreecommitdiff
path: root/gcc/ipa-devirt.c
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2014-07-05 17:22:44 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2014-07-05 17:22:44 +0000
commit1b613a0a58dc17898dd6313de1ed9b2ff26f20c9 (patch)
treea316c1d813e71ce225a70f30c867801face3d5aa /gcc/ipa-devirt.c
parente3f4ccee578cd80d9117d50fcec2ab80372b61ed (diff)
downloadgcc-1b613a0a58dc17898dd6313de1ed9b2ff26f20c9.tar.gz
* cgraph.c (cgraph_create_indirect_edge): Update call of
get_polymorphic_call_info. * ipa-utils.h (get_polymorphic_call_info): Add parameter CALL. (possible_polymorphic_call_targets): Add parameter call. (decl_maybe_in_construction_p): New predicate. (get_polymorphic_call_info): Add parameter call; use decl_maybe_in_construction_p. * gimple-fold.c (fold_gimple_assign): Update use of possible_polymorphic_call_targets. (gimple_fold_call): Likewise. * ipa-prop.c: Inlcude calls.h (ipa_binfo_from_known_type_jfunc): Check that known type is record. (param_type_may_change_p): New predicate. (detect_type_change_from_memory_writes): Break out from ... (detect_type_change): ... this one; use param_type_may_change_p. (detect_type_change_ssa): Use param_type_may_change_p. (compute_known_type_jump_func): Use decl_maybe_in_construction_p. * g++.dg/ipa/devirt-26.C: Update testcase. * g++.dg/ipa/imm-devirt-1.C: Update testcase. * g++.dg/ipa/imm-devirt-2.C: Update testcase. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@212304 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-devirt.c')
-rw-r--r--gcc/ipa-devirt.c104
1 files changed, 103 insertions, 1 deletions
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index f7418f175f6..da8dfcfee71 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -1438,6 +1438,99 @@ vtable_pointer_value_to_binfo (const_tree t)
offset, vtable);
}
+/* We know that the instance is stored in variable or parameter
+ (not dynamically allocated) and we want to disprove the fact
+ that it may be in construction at invocation of CALL.
+
+ For the variable to be in construction we actually need to
+ be in constructor of corresponding global variable or
+ the inline stack of CALL must contain the constructor.
+ Check this condition. This check works safely only before
+ IPA passes, because inline stacks may become out of date
+ later. */
+
+bool
+decl_maybe_in_construction_p (tree base, tree outer_type,
+ gimple call, tree function)
+{
+ outer_type = TYPE_MAIN_VARIANT (outer_type);
+ gcc_assert (DECL_P (base));
+
+ /* After inlining the code unification optimizations may invalidate
+ inline stacks. Also we need to give up on global variables after
+ IPA, because addresses of these may have been propagated to their
+ constructors. */
+ if (DECL_STRUCT_FUNCTION (function)->after_inlining)
+ return true;
+
+ /* Pure functions can not do any changes on the dynamic type;
+ that require writting to memory. */
+ if (!auto_var_in_fn_p (base, function)
+ && flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
+ return false;
+
+ for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
+ block = BLOCK_SUPERCONTEXT (block))
+ if (BLOCK_ABSTRACT_ORIGIN (block)
+ && TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
+ {
+ tree fn = BLOCK_ABSTRACT_ORIGIN (block);
+
+ if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
+ || (!DECL_CXX_CONSTRUCTOR_P (fn)
+ || !DECL_CXX_DESTRUCTOR_P (fn)))
+ {
+ /* Watch for clones where we constant propagated the first
+ argument (pointer to the instance). */
+ fn = DECL_ABSTRACT_ORIGIN (fn);
+ if (!fn
+ || !is_global_var (base)
+ || TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
+ || (!DECL_CXX_CONSTRUCTOR_P (fn)
+ || !DECL_CXX_DESTRUCTOR_P (fn)))
+ continue;
+ }
+ if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
+ continue;
+
+ /* FIXME: this can go away once we have ODR types equivalency on
+ LTO level. */
+ if (in_lto_p && !polymorphic_type_binfo_p (TYPE_BINFO (outer_type)))
+ return true;
+ tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (fn)));
+ if (types_same_for_odr (type, outer_type))
+ return true;
+ }
+
+ if (TREE_CODE (base) == VAR_DECL
+ && is_global_var (base))
+ {
+ if (TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
+ || (!DECL_CXX_CONSTRUCTOR_P (function)
+ || !DECL_CXX_DESTRUCTOR_P (function)))
+ {
+ if (!DECL_ABSTRACT_ORIGIN (function))
+ return false;
+ /* Watch for clones where we constant propagated the first
+ argument (pointer to the instance). */
+ function = DECL_ABSTRACT_ORIGIN (function);
+ if (!function
+ || TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE
+ || (!DECL_CXX_CONSTRUCTOR_P (function)
+ || !DECL_CXX_DESTRUCTOR_P (function)))
+ return false;
+ }
+ /* FIXME: this can go away once we have ODR types equivalency on
+ LTO level. */
+ if (in_lto_p && !polymorphic_type_binfo_p (TYPE_BINFO (outer_type)))
+ return true;
+ tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (function)));
+ if (types_same_for_odr (type, outer_type))
+ return true;
+ }
+ return false;
+}
+
/* Proudce polymorphic call context for call method of instance
that is located within BASE (that is assumed to be a decl) at OFFSET. */
@@ -1490,6 +1583,8 @@ get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *context,
/* Given REF call in FNDECL, determine class of the polymorphic
call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT.
+ CALL is optional argument giving the actual statement (usually call) where
+ the context is used.
Return pointer to object described by the context */
tree
@@ -1497,7 +1592,8 @@ get_polymorphic_call_info (tree fndecl,
tree ref,
tree *otr_type,
HOST_WIDE_INT *otr_token,
- ipa_polymorphic_call_context *context)
+ ipa_polymorphic_call_context *context,
+ gimple call)
{
tree base_pointer;
*otr_type = obj_type_ref_class (ref);
@@ -1561,6 +1657,12 @@ get_polymorphic_call_info (tree fndecl,
}
get_polymorphic_call_info_for_decl (context, base,
context->offset + offset2);
+ if (context->maybe_in_construction && call)
+ context->maybe_in_construction
+ = decl_maybe_in_construction_p (base,
+ context->outer_type,
+ call,
+ current_function_decl);
return NULL;
}
else