cgraph.c (cgraph_create_indirect_edge): Update call of get_polymorphic_call_info.

* 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.

From-SVN: r212304
This commit is contained in:
Jan Hubicka 2014-07-05 19:22:44 +02:00 committed by Jan Hubicka
parent aed773a2c8
commit 058d0a9059
10 changed files with 266 additions and 47 deletions

View File

@ -1,3 +1,24 @@
2014-07-05 Jan Hubicka <hubicka@ucw.cz>
* 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.
2014-07-05 Charles Baylis <charles.baylis@linaro.org>
PR target/49423

View File

@ -967,7 +967,7 @@ cgraph_create_indirect_edge (struct cgraph_node *caller, gimple call_stmt,
get_polymorphic_call_info (caller->decl,
target,
&otr_type, &otr_token,
&context);
&context, call_stmt);
/* Only record types can have virtual calls. */
gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE);

View File

@ -376,7 +376,7 @@ fold_gimple_assign (gimple_stmt_iterator *si)
{
bool final;
vec <cgraph_node *>targets
= possible_polymorphic_call_targets (val, &final);
= possible_polymorphic_call_targets (val, stmt, &final);
if (final && targets.length () <= 1 && dbg_cnt (devirt))
{
tree fndecl;
@ -1125,7 +1125,7 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
{
bool final;
vec <cgraph_node *>targets
= possible_polymorphic_call_targets (callee, &final);
= possible_polymorphic_call_targets (callee, stmt, &final);
if (final && targets.length () <= 1 && dbg_cnt (devirt))
{
tree lhs = gimple_call_lhs (stmt);

View File

@ -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

View File

@ -62,6 +62,7 @@ along with GCC; see the file COPYING3. If not see
#include "dbgcnt.h"
#include "domwalk.h"
#include "builtins.h"
#include "calls.h"
/* Intermediate information that we get from alias analysis about a particular
parameter in a particular basic_block. When a parameter or the memory it
@ -552,7 +553,11 @@ ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
tree
ipa_binfo_from_known_type_jfunc (struct ipa_jump_func *jfunc)
{
if (!RECORD_OR_UNION_TYPE_P (jfunc->value.known_type.base_type))
return NULL_TREE;
tree base_binfo = TYPE_BINFO (jfunc->value.known_type.base_type);
if (!base_binfo)
return NULL_TREE;
return get_binfo_at_offset (base_binfo,
@ -731,18 +736,84 @@ check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data)
return false;
}
/* See if ARG is PARAM_DECl describing instance passed by pointer
or reference in FUNCTION. Return false if the dynamic type may change
in between beggining of the function until CALL is invoked.
Generally functions are not allowed to change type of such instances,
but they call destructors. We assume that methods can not destroy the THIS
pointer. Also as a special cases, constructor and destructors may change
type of the THIS pointer. */
static bool
param_type_may_change_p (tree function, tree arg, gimple call)
{
/* Pure functions can not do any changes on the dynamic type;
that require writting to memory. */
if (flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
return false;
/* We need to check if we are within inlined consturctor
or destructor (ideally we would have way to check that the
inline cdtor is actually working on ARG, but we don't have
easy tie on this, so punt on all non-pure cdtors.
We may also record the types of cdtors and once we know type
of the instance match them.
Also code unification optimizations may merge calls from
different blocks making return values unreliable. So
do nothing during late optimization. */
if (DECL_STRUCT_FUNCTION (function)->after_inlining)
return true;
if (TREE_CODE (arg) == SSA_NAME
&& SSA_NAME_IS_DEFAULT_DEF (arg)
&& TREE_CODE (SSA_NAME_VAR (arg)) == PARM_DECL)
{
/* Normal (non-THIS) argument. */
if ((SSA_NAME_VAR (arg) != DECL_ARGUMENTS (function)
|| TREE_CODE (TREE_TYPE (function)) != METHOD_TYPE)
/* THIS pointer of an method - here we we want to watch constructors
and destructors as those definitely may change the dynamic
type. */
|| (TREE_CODE (TREE_TYPE (function)) == METHOD_TYPE
&& !DECL_CXX_CONSTRUCTOR_P (function)
&& !DECL_CXX_DESTRUCTOR_P (function)
&& (SSA_NAME_VAR (arg) == DECL_ARGUMENTS (function))))
{
/* Walk the inline stack and watch out for ctors/dtors. */
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 (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
continue;
if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
&& (DECL_CXX_CONSTRUCTOR_P (fn)
|| DECL_CXX_DESTRUCTOR_P (fn)))
return true;
}
return false;
}
}
return true;
}
/* Detect whether the dynamic type of ARG of COMP_TYPE has changed (before
callsite CALL) by looking for assignments to its virtual table pointer. If
it is, return true and fill in the jump function JFUNC with relevant type
information or set it to unknown. ARG is the object itself (not a pointer
to it, unless dereferenced). BASE is the base of the memory access as
returned by get_ref_base_and_extent, as is the offset. */
returned by get_ref_base_and_extent, as is the offset.
This is helper function for detect_type_change and detect_type_change_ssa
that does the heavy work which is usually unnecesary. */
static bool
detect_type_change (tree arg, tree base, tree comp_type, gimple call,
struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
detect_type_change_from_memory_writes (tree arg, tree base, tree comp_type,
gimple call, struct ipa_jump_func *jfunc,
HOST_WIDE_INT offset)
{
struct type_change_info tci;
ao_ref ao;
@ -753,25 +824,6 @@ detect_type_change (tree arg, tree base, tree comp_type, gimple call,
comp_type = TYPE_MAIN_VARIANT (comp_type);
if (!flag_devirtualize)
return false;
/* C++ methods are not allowed to change THIS pointer unless they
are constructors or destructors. */
if (TREE_CODE (base) == MEM_REF
&& TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME
&& SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (base, 0))
&& TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (base, 0))) == PARM_DECL
&& TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
&& !DECL_CXX_CONSTRUCTOR_P (current_function_decl)
&& !DECL_CXX_DESTRUCTOR_P (current_function_decl)
&& (SSA_NAME_VAR (TREE_OPERAND (base, 0))
== DECL_ARGUMENTS (current_function_decl)))
{
gcc_assert (comp_type);
return false;
}
/* Const calls cannot call virtual methods through VMT and so type changes do
not matter. */
if (!flag_devirtualize || !gimple_vuse (call)
@ -809,6 +861,28 @@ detect_type_change (tree arg, tree base, tree comp_type, gimple call,
return true;
}
/* Detect whether the dynamic type of ARG of COMP_TYPE may have changed.
If it is, return true and fill in the jump function JFUNC with relevant type
information or set it to unknown. ARG is the object itself (not a pointer
to it, unless dereferenced). BASE is the base of the memory access as
returned by get_ref_base_and_extent, as is the offset. */
static bool
detect_type_change (tree arg, tree base, tree comp_type, gimple call,
struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
{
if (!flag_devirtualize)
return false;
if (TREE_CODE (base) == MEM_REF
&& !param_type_may_change_p (current_function_decl,
TREE_OPERAND (base, 0),
call))
return false;
return detect_type_change_from_memory_writes (arg, base, comp_type,
call, jfunc, offset);
}
/* Like detect_type_change but ARG is supposed to be a non-dereferenced pointer
SSA name (its dereference will become the base and the offset is assumed to
be zero). */
@ -822,10 +896,14 @@ detect_type_change_ssa (tree arg, tree comp_type,
|| !POINTER_TYPE_P (TREE_TYPE (arg)))
return false;
if (!param_type_may_change_p (current_function_decl, arg, call))
return false;
arg = build2 (MEM_REF, ptr_type_node, arg,
build_int_cst (ptr_type_node, 0));
return detect_type_change (arg, arg, comp_type, call, jfunc, 0);
return detect_type_change_from_memory_writes (arg, arg, comp_type,
call, jfunc, 0);
}
/* Callback of walk_aliased_vdefs. Flags that it has been invoked to the
@ -1433,11 +1511,15 @@ compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc,
if (!DECL_P (base)
|| max_size == -1
|| max_size != size
|| !contains_polymorphic_type_p (TREE_TYPE (base))
|| is_global_var (base))
|| !contains_polymorphic_type_p (TREE_TYPE (base)))
return;
if (detect_type_change (op, base, expected_type, call, jfunc, offset))
if (decl_maybe_in_construction_p (base, TREE_TYPE (base),
call, current_function_decl)
/* Even if the var seems to be in construction by inline call stack,
we may work out the actual type by walking memory writes. */
&& (!is_global_var (base)
&& detect_type_change (op, base, expected_type, call, jfunc, offset)))
return;
ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base),

View File

@ -87,9 +87,11 @@ bool possible_polymorphic_call_target_p (tree, HOST_WIDE_INT,
tree method_class_type (const_tree);
tree get_polymorphic_call_info (tree, tree, tree *,
HOST_WIDE_INT *,
ipa_polymorphic_call_context *);
ipa_polymorphic_call_context *,
gimple call = NULL);
bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *,
tree, tree, HOST_WIDE_INT);
bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
tree vtable_pointer_value_to_binfo (const_tree);
bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
bool contains_polymorphic_type_p (const_tree);
@ -125,7 +127,8 @@ possible_polymorphic_call_targets (struct cgraph_edge *e,
/* Same as above but taking OBJ_TYPE_REF as an parameter. */
inline vec <cgraph_node *>
possible_polymorphic_call_targets (tree call,
possible_polymorphic_call_targets (tree ref,
gimple call,
bool *final = NULL,
void **cache_token = NULL)
{
@ -134,11 +137,11 @@ possible_polymorphic_call_targets (tree call,
ipa_polymorphic_call_context context;
get_polymorphic_call_info (current_function_decl,
call,
&otr_type, &otr_token, &context);
return possible_polymorphic_call_targets (obj_type_ref_class (call),
ref,
&otr_type, &otr_token, &context, call);
return possible_polymorphic_call_targets (obj_type_ref_class (ref),
tree_to_uhwi
(OBJ_TYPE_REF_TOKEN (call)),
(OBJ_TYPE_REF_TOKEN (ref)),
context,
final, cache_token);
}

View File

@ -1,3 +1,9 @@
2014-07-05 Jan Hubicka <hubicka@ucw.cz>
* 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.
2014-07-04 Tobias Burnus <burnus@net-b.de>
* gfortran.dg/coarray/coindexed_3.f90: New.

View File

@ -1,5 +1,5 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-devirt-details" } */
/* { dg-options "-O3 -fdump-tree-ccp1" } */
struct A
{
int a;
@ -23,7 +23,6 @@ int test(void)
return d->foo()+b->foo();
}
/* The call to b->foo() is perfectly devirtualizable because C can not be in construction
when &c was used, but we can not analyze that so far. Test that we at least speculate
that type is in the construction. */
/* { dg-final { scan-ipa-dump "speculatively devirtualizing" "devirt" } } */
/* { dg-final { cleanup-ipa-dump "devirt" } } */
when &c was used. */
/* { dg-final { scan-tree-dump-not "OBJ_TYPE_REF" "ccp1" } } */
/* { dg-final { cleanup-tree-dump "ccp1" } } */

View File

@ -1,7 +1,7 @@
/* Verify that virtual calls are folded even early inlining puts them into one
function with the definition. */
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-fre1-details" } */
/* { dg-options "-O2 -fdump-tree-einline" } */
extern "C" void abort (void);
@ -58,5 +58,10 @@ int main (int argc, char *argv[])
return 0;
}
/* { dg-final { scan-tree-dump "converting indirect call to function virtual int B::foo" "fre1" } } */
/* { dg-final { cleanup-tree-dump "fre1" } } */
/* middleman_2 gets early inlined and the virtual call should get turned to
a direct call. */
/* { dg-final { scan-tree-dump "Inlining int middleman_1" "einline" } } */
/* { dg-final { scan-tree-dump "Inlining int middleman_2" "einline" } } */
/* { dg-final { scan-tree-dump "B::foo (" "einline" } } */
/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 2 "einline" } } */
/* { dg-final { cleanup-tree-dump "einline" } } */

View File

@ -1,7 +1,7 @@
/* Verify that virtual calls are folded even early inlining puts them into one
function with the definition. */
/* { dg-do run } */
/* { dg-options "-O2 -fdump-tree-fre1-details" } */
/* { dg-options "-O2 -fdump-tree-einline" } */
extern "C" void abort (void);
@ -91,5 +91,6 @@ int main (int argc, char *argv[])
return 0;
}
/* { dg-final { scan-tree-dump "converting indirect call to function" "fre1" } } */
/* { dg-final { cleanup-tree-dump "fre1" } } */
/* We fold into thunk of C. Eventually we should inline the thunk. */
/* { dg-final { scan-tree-dump "C::_ZThn24_N1C3fooEi (" "einline" } } */
/* { dg-final { cleanup-tree-dump "einline" } } */