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:
parent
aed773a2c8
commit
058d0a9059
10 changed files with 266 additions and 47 deletions
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
104
gcc/ipa-devirt.c
104
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
|
||||
|
|
134
gcc/ipa-prop.c
134
gcc/ipa-prop.c
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" } } */
|
||||
|
|
|
@ -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" } } */
|
||||
|
|
|
@ -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" } } */
|
||||
|
|
Loading…
Add table
Reference in a new issue