devirt-11.C: Update template.

* testsuite/g++.dg/ipa/devirt-11.C: Update template.
	* testsuite/g++.dg/ipa/devirt-16.C: New testcase.
	* testsuite/g++.dg/ipa/devirt-17.C: New testcase.
	* testsuite/g++.dg/ipa/devirt-18.C: New testcase.

	* cgraphunit.c (walk_polymorphic_call_targets): Permit 0 possible
	targets and devirtualize to BUILT_IN_UNREACHABLE.
	* timevar.def (TV_IPA_UNREACHABLE): New timevar.
	* ipa.c (walk_polymorphic_call_targets): New function.
	(symtab_remove_unreachable_nodes): Use it; do not keep all virtual
	functions; use the new timevar.
	* ipa-devirt.c (maybe_record_node): Do not insert static nodes that
	was removed from the program.
	(record_binfo): If BINFO corresponds to an anonymous namespace, we may
	not consider it in the walk when its vtable is dead.
	(possible_polymorphic_call_targets_1): Pass anonymous flag to
	record_binfo.
	(devirt_variable_node_removal_hook): New function.
	(possible_polymorphic_call_targets): Also register
	devirt_variable_node_removal_hook.
	(ipa_devirt): Do not do non-speculative devirtualization.
	(gate_ipa_devirt): One execute if devirtualizing speculatively.

From-SVN: r202368
This commit is contained in:
Jan Hubicka 2013-09-08 18:42:21 +02:00 committed by Jan Hubicka
parent 26e5b0fd28
commit 3462aa02a6
10 changed files with 297 additions and 38 deletions

View File

@ -1,3 +1,23 @@
2013-09-08 Jan Hubicka <jh@suse.cz>
* cgraphunit.c (walk_polymorphic_call_targets): Permit 0 possible
targets and devirtualize to BUILT_IN_UNREACHABLE.
* timevar.def (TV_IPA_UNREACHABLE): New timevar.
* ipa.c (walk_polymorphic_call_targets): New function.
(symtab_remove_unreachable_nodes): Use it; do not keep all virtual
functions; use the new timevar.
* ipa-devirt.c (maybe_record_node): Do not insert static nodes that
was removed from the program.
(record_binfo): If BINFO corresponds to an anonymous namespace, we may
not consider it in the walk when its vtable is dead.
(possible_polymorphic_call_targets_1): Pass anonymous flag to
record_binfo.
(devirt_variable_node_removal_hook): New function.
(possible_polymorphic_call_targets): Also register
devirt_variable_node_removal_hook.
(ipa_devirt): Do not do non-speculative devirtualization.
(gate_ipa_devirt): One execute if devirtualizing speculatively.
2013-09-08 Jan Hubicka <jh@suse.cz>
* cgraph.h (varpool_node_hook, varpool_node_hook_list,

View File

@ -866,9 +866,15 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
make the edge direct. */
if (final)
{
gcc_assert (targets.length());
if (targets.length() == 1)
if (targets.length() <= 1)
{
cgraph_node *target;
if (targets.length () == 1)
target = targets[0];
else
target = cgraph_get_create_node
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
if (cgraph_dump_file)
{
fprintf (cgraph_dump_file,
@ -877,7 +883,7 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
edge->call_stmt, 0,
TDF_SLIM);
}
cgraph_make_edge_direct (edge, targets[0]);
cgraph_make_edge_direct (edge, target);
cgraph_redirect_edge_call_stmt_to_callee (edge);
if (cgraph_dump_file)
{
@ -1092,7 +1098,7 @@ analyze_functions (void)
mangling and same body alias creation before we free DECL_ARGUMENTS
used by it. */
if (!seen_error ())
symtab_initialize_asm_name_hash ();
symtab_initialize_asm_name_hash ();
}
/* Translate the ugly representation of aliases as alias pairs into nice

View File

@ -570,6 +570,8 @@ maybe_record_node (vec <cgraph_node *> &nodes,
&& fcode != BUILT_IN_TRAP
&& !pointer_set_insert (inserted, target)
&& (target_node = cgraph_get_node (target)) != NULL
&& (TREE_PUBLIC (target)
|| target_node->symbol.definition)
&& symtab_real_symbol_p ((symtab_node)target_node))
{
pointer_set_insert (cached_polymorphic_call_targets,
@ -591,6 +593,8 @@ maybe_record_node (vec <cgraph_node *> &nodes,
MATCHED_VTABLES tracks virtual tables we already did lookup
for virtual function in.
ANONYMOUS is true if BINFO is part of anonymous namespace.
*/
static void
@ -600,7 +604,8 @@ record_binfo (vec <cgraph_node *> &nodes,
tree type_binfo,
HOST_WIDE_INT otr_token,
pointer_set_t *inserted,
pointer_set_t *matched_vtables)
pointer_set_t *matched_vtables,
bool anonymous)
{
tree type = BINFO_TYPE (binfo);
int i;
@ -611,6 +616,19 @@ record_binfo (vec <cgraph_node *> &nodes,
if (types_same_for_odr (type, otr_type)
&& !pointer_set_insert (matched_vtables, BINFO_VTABLE (type_binfo)))
{
/* For types in anonymous namespace first check if the respective vtable
is alive. If not, we know the type can't be called. */
if (!flag_ltrans && anonymous)
{
tree vtable = BINFO_VTABLE (type_binfo);
struct varpool_node *vnode;
if (TREE_CODE (vtable) == POINTER_PLUS_EXPR)
vtable = TREE_OPERAND (TREE_OPERAND (vtable, 0), 0);
vnode = varpool_get_node (vtable);
if (!vnode || !vnode->symbol.definition)
return;
}
tree target = gimple_get_virt_method_for_binfo (otr_token, type_binfo);
if (target)
maybe_record_node (nodes, target, inserted);
@ -626,7 +644,7 @@ record_binfo (vec <cgraph_node *> &nodes,
is shared with the outer type. */
BINFO_VTABLE (base_binfo) ? base_binfo : type_binfo,
otr_token, inserted,
matched_vtables);
matched_vtables, anonymous);
}
/* Lookup virtual methods matching OTR_TYPE (with OFFSET and OTR_TOKEN)
@ -646,7 +664,7 @@ possible_polymorphic_call_targets_1 (vec <cgraph_node *> &nodes,
unsigned int i;
record_binfo (nodes, binfo, otr_type, binfo, otr_token, inserted,
matched_vtables);
matched_vtables, type->anonymous_namespace);
for (i = 0; i < type->derived_types.length(); i++)
possible_polymorphic_call_targets_1 (nodes, inserted,
matched_vtables,
@ -735,6 +753,18 @@ devirt_node_removal_hook (struct cgraph_node *n, void *d ATTRIBUTE_UNUSED)
free_polymorphic_call_targets_hash ();
}
/* When virtual table is removed, we may need to flush the cache. */
static void
devirt_variable_node_removal_hook (struct varpool_node *n,
void *d ATTRIBUTE_UNUSED)
{
if (cached_polymorphic_call_targets
&& DECL_VIRTUAL_P (n->symbol.decl)
&& type_in_anonymous_namespace_p (DECL_CONTEXT (n->symbol.decl)))
free_polymorphic_call_targets_hash ();
}
/* Return vector containing possible targets of polymorphic call of type
OTR_TYPE caling method OTR_TOKEN with OFFSET. If FINALp is non-NULL,
store true if the list is complette.
@ -782,8 +812,12 @@ possible_polymorphic_call_targets (tree otr_type,
cached_polymorphic_call_targets = pointer_set_create ();
polymorphic_call_target_hash.create (23);
if (!node_removal_hook_holder)
node_removal_hook_holder =
cgraph_add_node_removal_hook (&devirt_node_removal_hook, NULL);
{
node_removal_hook_holder =
cgraph_add_node_removal_hook (&devirt_node_removal_hook, NULL);
varpool_add_node_removal_hook (&devirt_variable_node_removal_hook,
NULL);
}
}
/* Lookup cached answer. */
@ -928,11 +962,8 @@ likely_target_p (struct cgraph_node *n)
}
/* The ipa-devirt pass.
This performs very trivial devirtualization:
1) when polymorphic call is known to have precisely one target,
turn it into direct call
2) when polymorphic call has only one likely target in the unit,
turn it into speculative call. */
When polymorphic call has only one likely target in the unit,
turn it into speculative call. */
static unsigned int
ipa_devirt (void)
@ -965,26 +996,9 @@ ipa_devirt (void)
if (dump_file)
dump_possible_polymorphic_call_targets
(dump_file, e);
npolymorphic++;
if (final)
{
gcc_assert (targets.length());
if (targets.length() == 1)
{
if (dump_file)
fprintf (dump_file,
"Devirtualizing call in %s/%i to %s/%i\n",
cgraph_node_name (n), n->symbol.order,
cgraph_node_name (targets[0]), targets[0]->symbol.order);
cgraph_make_edge_direct (e, targets[0]);
ndevirtualized++;
update = true;
continue;
}
}
if (!flag_devirtualize_speculatively)
continue;
if (!cgraph_maybe_hot_edge_p (e))
{
if (dump_file)
@ -1114,7 +1128,7 @@ ipa_devirt (void)
static bool
gate_ipa_devirt (void)
{
return flag_devirtualize && !in_lto_p && optimize;
return flag_devirtualize_speculatively && !in_lto_p && optimize;
}
namespace {

100
gcc/ipa.c
View File

@ -149,6 +149,84 @@ process_references (struct ipa_ref_list *list,
}
}
/* EDGE is an polymorphic call. If BEFORE_INLINING_P is set, mark
all its potential targets as reachable to permit later inlining if
devirtualization happens. After inlining still keep their declarations
around, so we can devirtualize to a direct call.
Also try to make trivial devirutalization when no or only one target is
possible. */
static void
walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets,
struct cgraph_edge *edge,
symtab_node *first,
pointer_set_t *reachable, bool before_inlining_p)
{
unsigned int i;
void *cache_token;
bool final;
vec <cgraph_node *>targets
= possible_polymorphic_call_targets
(edge, &final, &cache_token);
if (!pointer_set_insert (reachable_call_targets,
cache_token))
{
for (i = 0; i < targets.length(); i++)
{
struct cgraph_node *n = targets[i];
/* Do not bother to mark virtual methods in anonymous namespace;
either we will find use of virtual table defining it, or it is
unused. */
if (TREE_CODE (TREE_TYPE (n->symbol.decl)) == METHOD_TYPE
&& type_in_anonymous_namespace_p
(method_class_type (TREE_TYPE (n->symbol.decl))))
continue;
/* Prior inlining, keep alive bodies of possible targets for
devirtualization. */
if (n->symbol.definition
&& before_inlining_p)
pointer_set_insert (reachable, n);
/* Even after inlining we want to keep the possible targets in the
boundary, so late passes can still produce direct call even if
the chance for inlining is lost. */
enqueue_node ((symtab_node) n, first, reachable);
}
}
/* Very trivial devirtualization; when the type is
final or anonymous (so we know all its derivation)
and there is only one possible virtual call target,
make the edge direct. */
if (final)
{
if (targets.length() <= 1)
{
cgraph_node *target;
if (targets.length () == 1)
target = targets[0];
else
target = cgraph_get_create_node
(builtin_decl_implicit (BUILT_IN_UNREACHABLE));
if (dump_file)
fprintf (dump_file,
"Devirtualizing call in %s/%i to %s/%i\n",
cgraph_node_name (edge->caller),
edge->caller->symbol.order,
cgraph_node_name (target), target->symbol.order);
edge = cgraph_make_edge_direct (edge, target);
if (cgraph_state != CGRAPH_STATE_IPA_SSA)
cgraph_redirect_edge_call_stmt_to_callee (edge);
else
inline_update_overall_summary (edge->caller);
}
}
}
/* Perform reachability analysis and reclaim all unreachable nodes.
@ -214,7 +292,9 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
bool changed = false;
struct pointer_set_t *reachable = pointer_set_create ();
struct pointer_set_t *body_needed_for_clonning = pointer_set_create ();
struct pointer_set_t *reachable_call_targets = pointer_set_create ();
timevar_push (TV_IPA_UNREACHABLE);
#ifdef ENABLE_CHECKING
verify_symtab ();
#endif
@ -238,10 +318,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
if (node->symbol.definition
&& !node->global.inlined_to
&& !node->symbol.in_other_partition
&& (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node)
/* Keep around virtual functions for possible devirtualization. */
|| (before_inlining_p
&& DECL_VIRTUAL_P (node->symbol.decl))))
&& !cgraph_can_remove_if_no_direct_calls_and_refs_p (node))
{
gcc_assert (!node->global.inlined_to);
pointer_set_insert (reachable, node);
@ -304,6 +381,19 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
if (!in_boundary_p)
{
struct cgraph_edge *e;
/* Keep alive possible targets for devirtualization. */
if (optimize && flag_devirtualize)
{
struct cgraph_edge *next;
for (e = cnode->indirect_calls; e; e = next)
{
next = e->next_callee;
if (e->indirect_info->polymorphic)
walk_polymorphic_call_targets (reachable_call_targets,
e, &first, reachable,
before_inlining_p);
}
}
for (e = cnode->callees; e; e = e->next_callee)
{
if (e->callee->symbol.definition
@ -449,6 +539,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
pointer_set_destroy (reachable);
pointer_set_destroy (body_needed_for_clonning);
pointer_set_destroy (reachable_call_targets);
/* Now update address_taken flags and try to promote functions to be local. */
if (file)
@ -483,6 +574,7 @@ symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file)
FOR_EACH_DEFINED_FUNCTION (node)
ipa_propagate_frequency (node);
timevar_pop (TV_IPA_UNREACHABLE);
return changed;
}

View File

@ -1,3 +1,10 @@
2013-09-08 Jan Hubicka <jh@suse.cz>
* testsuite/g++.dg/ipa/devirt-11.C: Update template.
* testsuite/g++.dg/ipa/devirt-16.C: New testcase.
* testsuite/g++.dg/ipa/devirt-17.C: New testcase.
* testsuite/g++.dg/ipa/devirt-18.C: New testcase.
2013-09-08 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/54941

View File

@ -46,5 +46,4 @@ bar ()
and two to fn3. While doing so the new symbol for fn2 needs to be
introduced. */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 3 "inline" } } */
/* { dg-final { scan-ipa-dump-times "and turned into root of the clone tree" 1 "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */

View File

@ -0,0 +1,39 @@
/* We shall devirtualize to unreachable. No anonymous type method should surivve
reachability. */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-whole-program" } */
namespace {
class B {
public:
virtual int foo(void)
{
return 0;
}
};
class A : public B {
public:
virtual int foo(void)
{
return 1;
}
};
}
class B *b;
main()
{
int c;
if (c)
{
class A a;
a.foo();
class B b;
b.foo();
}
return b->foo();
}
/* { dg-final { scan-ipa-dump "Devirtualizing" "whole-program"} } */
/* { dg-final { scan-ipa-dump "builtin_unreachable" "whole-program"} } */
/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
/* { dg-final { cleanup-ipa-dump "whole-program" } } */

View File

@ -0,0 +1,44 @@
/* We shall devirtualize to B::foo since it is the only live candidate of an
anonymous type. */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-whole-program" } */
namespace {
class B {
public:
virtual int foo(void)
{
return 0;
}
};
class A : public B {
public:
virtual int foo(void)
{
return 1;
}
};
}
class B *b;
void get_me_lost (void *);
main()
{
int c;
if (c)
{
class A a;
a.foo();
}
else
{
b = new (class B);
b->foo();
get_me_lost ((void *)&b);
}
return b->foo();
}
/* { dg-final { scan-ipa-dump "Devirtualizing" "whole-program"} } */
/* { dg-final { scan-ipa-dump-not "builtin_unreachable" "whole-program"} } */
/* { dg-final { scan-ipa-dump "B::foo" "whole-program"} } */
/* { dg-final { scan-ipa-dump-not "A::foo" "whole-program"} } */
/* { dg-final { cleanup-ipa-dump "whole-program" } } */

View File

@ -0,0 +1,37 @@
/* We shall devirtualize to unreachable. No anonymous type method should surivve
reachability. */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-ssa" } */
namespace {
class B {
public:
virtual int foo(void)
{
return 0;
}
};
class A : public B {
public:
virtual int foo(void)
{
return 1;
}
};
}
class B *b;
main()
{
if (0)
{
class A a;
a.foo();
class B b;
b.foo();
}
return b->foo();
}
/* { dg-final { scan-tree-dump-not "A::foo" "ssa"} } */
/* { dg-final { scan-tree-dump-not "B::foo" "ssa"} } */
/* { dg-final { scan-tree-dump "builtin_unreachable" "ssa"} } */
/* { dg-final { cleanup-tree-dump "ssa" } } */

View File

@ -64,6 +64,7 @@ DEFTIMEVAR (TV_PCH_CPP_RESTORE , "PCH preprocessor state restore")
DEFTIMEVAR (TV_CGRAPH , "callgraph construction")
DEFTIMEVAR (TV_CGRAPHOPT , "callgraph optimization")
DEFTIMEVAR (TV_IPA_UNREACHABLE , "ipa dead code removal")
DEFTIMEVAR (TV_IPA_INHERITANCE , "ipa inheritance graph")
DEFTIMEVAR (TV_IPA_VIRTUAL_CALL , "ipa virtual call target")
DEFTIMEVAR (TV_IPA_DEVIRT , "ipa devirtualization")