On 07/24/2014 07:36 PM, Jan Hubicka wrote:
ipa-deivrt has code to do the tracking for you. All is needed is to cal
get_odr_type
for all polymorphic type that we care about. build_type_inheritance_graph just
walks all virtual methods to register all polymorphic types that matters (i.e.
have methods associated with them) into the datastructure. I think within C++
FE we can just reigster all polymoprhic types we produce.
Makes sense.
I see that during the reachability walk new types may be instantiated. This may
extend target lists earlier visited, I guess we need to add some way to make
to track this?
True.
And more problematic, doing this in a single TU isn't good enough; there
are cases where LTO can devirtualize a call to a target that isn't
declared in the TU where the call occurs. That is,
foo.h:
struct A { virtual ~A() {} };
void g(A*);
bar.h:
#include "foo.h"
struct B: A { virtual void f(); };
one.C:
#include "foo.h"
void g(A* p) { delete p; }
two.C:
#include "bar.h"
int main() { g(new B); }
three.C:
#include "bar.h"
void B::f() {}
$ gcc -fpic -shared -fvisibility-inlines-hidden three.C -o libfoo.so
$ gcc -O2 -flto -fvisibility-inlines-hidden one.C two.C libfoo.so
/home/jason/gtt/foo/one.C:2: undefined reference to `B::~B()'
To deal with this I guess we can either keep -fuse-all-virtuals or not
devirtualize in LTO to something that is hidden and not defined. Thoughts?
commit ac04d4b08190593299c8c94431d5e43384514096
Author: Jason Merrill <ja...@redhat.com>
Date: Fri Jul 25 09:15:02 2014 -0400
mark_virtual_overrides
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index c318cad..75711a5 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1272,10 +1272,6 @@ funsigned-char
C ObjC C++ ObjC++ LTO Var(flag_signed_char, 0)
Make \"char\" unsigned by default
-fuse-all-virtuals
-C++ ObjC++ Var(flag_use_all_virtuals) Init(1)
-Treat all virtual functions as odr-used
-
fuse-cxa-atexit
C++ ObjC++ Var(flag_use_cxa_atexit) Init(DEFAULT_USE_CXA_ATEXIT)
Use __cxa_atexit to register destructors
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4d37c65..969e730 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -7364,6 +7364,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
ba_any, NULL, complain);
gcc_assert (binfo && binfo != error_mark_node);
+ note_fn_called_virtually (fn);
+
/* Warn about deprecated virtual functions now, since we're about
to throw away the decl. */
if (TREE_DEPRECATED (fn))
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 0f611e1..1401069 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -817,6 +817,9 @@ get_vtable_decl (tree type, int complete)
decl = build_vtable (type, get_vtable_name (type), vtbl_type_node);
CLASSTYPE_VTABLES (type) = decl;
+ /* Make the vtable visible to build_type_inheritance_graph. */
+ varpool_node::get_create (decl);
+
if (complete)
{
DECL_EXTERNAL (decl) = 1;
@@ -6408,7 +6411,7 @@ finish_struct_1 (tree t)
in every translation unit where the class definition appears. If
we're devirtualizing, we can look into the vtable even if we
aren't emitting it. */
- if (CLASSTYPE_KEY_METHOD (t) == NULL_TREE || flag_use_all_virtuals)
+ if (CLASSTYPE_KEY_METHOD (t) == NULL_TREE)
keyed_classes = tree_cons (NULL_TREE, t, keyed_classes);
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0c0d804..c9f248a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -109,6 +109,7 @@ c-common.h, not after.
BIND_EXPR_BODY_BLOCK (in BIND_EXPR)
DECL_NON_TRIVIALLY_INITIALIZED_P (in VAR_DECL)
CALL_EXPR_LIST_INIT_P (in CALL_EXPR, AGGR_INIT_EXPR)
+ FNDECL_CALLED_VIRTUALLY (in FUNCTION_DECL)
4: TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
or FIELD_DECL).
IDENTIFIER_TYPENAME_P (in IDENTIFIER_NODE)
@@ -3222,6 +3223,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
#define FNDECL_USED_AUTO(NODE) \
TREE_LANG_FLAG_2 (FUNCTION_DECL_CHECK (NODE))
+/* True if NODE was called through the vtable; used to avoid duplicates in
+ fns_called_virtually. */
+#define FNDECL_CALLED_VIRTUALLY(NODE) \
+ TREE_LANG_FLAG_3 (FUNCTION_DECL_CHECK (NODE))
+
/* Nonzero if NODE is a DECL which we know about but which has not
been explicitly declared, such as a built-in function or a friend
declared inside a class. In the latter case DECL_HIDDEN_FRIEND_P
@@ -5381,6 +5387,7 @@ extern tree get_tls_wrapper_fn (tree);
extern void mark_needed (tree);
extern bool decl_needed_p (tree);
extern void note_vague_linkage_fn (tree);
+extern void note_fn_called_virtually (tree);
extern tree build_artificial_parm (tree, tree);
extern bool possibly_inlined_p (tree);
extern int parm_index (tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 8fa3145..a448103 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -47,6 +47,8 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-common.h"
#include "c-family/c-objc.h"
#include "cgraph.h"
+/* For build_type_inheritance_graph and possible_polymorphic_call_targets. */
+#include "ipa-utils.h"
#include "tree-inline.h"
#include "c-family/c-pragma.h"
#include "dumpfile.h"
@@ -103,6 +105,10 @@ static GTY(()) vec<tree, va_gc> *deferred_fns;
sure are defined. */
static GTY(()) vec<tree, va_gc> *no_linkage_decls;
+/* A list of functions called through the vtable, so we can mark their
+ overriders as used. */
+static GTY(()) vec<tree, va_gc> *fns_called_virtually;
+
/* Nonzero if we're done parsing and into end-of-file activities. */
int at_eof;
@@ -791,6 +797,19 @@ note_vague_linkage_fn (tree decl)
vec_safe_push (deferred_fns, decl);
}
+/* DECL is a function being called through the vtable. Remember it so that
+ at the end of the translation unit we can mark as used any functions
+ that override it, for devirtualization. */
+
+void
+note_fn_called_virtually (tree decl)
+{
+ if (FNDECL_CALLED_VIRTUALLY (decl))
+ return;
+ FNDECL_CALLED_VIRTUALLY (decl) = true;
+ vec_safe_push (fns_called_virtually, decl);
+}
+
/* We have just processed the DECL, which is a static data member.
The other parameters are as for cp_finish_decl. */
@@ -4281,6 +4300,39 @@ dump_tu (void)
}
}
+/* Now that we've seen all the types in the translation unit, go back over
+ the list of functions called through the vtable and mark any overriders
+ as used so they're available for devirtualization. */
+
+static bool
+mark_virtual_overrides (void)
+{
+ if (!fns_called_virtually)
+ return false;
+
+ build_type_inheritance_graph ();
+
+ bool reconsider = false;
+ size_t i; tree fn;
+ FOR_EACH_VEC_SAFE_ELT (fns_called_virtually, i, fn)
+ {
+ vec<cgraph_node*> targets
+ = possible_polymorphic_call_targets (fn);
+
+ size_t j; cgraph_node *n;
+ FOR_EACH_VEC_ELT (targets, j, n)
+ {
+ if (!DECL_ODR_USED (n->decl))
+ reconsider = true;
+ mark_used (n->decl);
+ }
+ }
+
+ release_tree_vector (fns_called_virtually);
+ fns_called_virtually = NULL;
+ return reconsider;
+}
+
/* This routine is called at the end of compilation.
Its job is to create all the code needed to initialize and
destroy the global aggregates. We do the destruction
@@ -4349,8 +4401,6 @@ cp_write_global_declarations (void)
instantiated, etc., etc. */
emit_support_tinfos ();
- int errs = errorcount + sorrycount;
- bool explained_devirt = false;
do
{
@@ -4396,6 +4446,9 @@ cp_write_global_declarations (void)
}
}
+ if (mark_virtual_overrides ())
+ reconsider = true;
+
/* Write out needed type info variables. We have to be careful
looping through unemitted decls, because emit_tinfo_decl may
cause other variables to be needed. New elements will be
@@ -4583,27 +4636,6 @@ cp_write_global_declarations (void)
pending_statics->length ()))
reconsider = true;
- if (flag_use_all_virtuals)
- {
- if (!reconsider && !mark_all_virtuals)
- {
- mark_all_virtuals = true;
- reconsider = true;
- errs = errorcount + sorrycount;
- }
- else if (mark_all_virtuals
- && !explained_devirt
- && (errorcount + sorrycount > errs))
- {
- inform (global_dc->last_location, "this error is seen due to "
- "instantiation of all virtual functions, which the C++ "
- "standard says are always considered used; this is done "
- "to support devirtualization optimizations, but can be "
- "disabled with -fno-use-all-virtuals");
- explained_devirt = true;
- }
- }
-
retries++;
}
while (reconsider);
diff --git a/gcc/ipa-devirt.c b/gcc/ipa-devirt.c
index 7c4151a..2bb6461 100644
--- a/gcc/ipa-devirt.c
+++ b/gcc/ipa-devirt.c
@@ -2592,6 +2592,17 @@ possible_polymorphic_call_targets (tree otr_type,
return nodes;
}
+/* Return vector containing possible targets of polymorphic call to
+ function FN. */
+
+vec <cgraph_node *>
+possible_polymorphic_call_targets (tree fn)
+{
+ return possible_polymorphic_call_targets
+ (DECL_CONTEXT (fn), tree_to_uhwi (DECL_VINDEX (fn)),
+ ipa_dummy_polymorphic_call_context);
+}
+
/* Dump all possible targets of a polymorphic call. */
void
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index bb2e0d5..1023794 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -78,6 +78,7 @@ possible_polymorphic_call_targets (tree, HOST_WIDE_INT,
bool *final = NULL,
void **cache_token = NULL,
int *nonconstruction_targets = NULL);
+vec <cgraph_node *> possible_polymorphic_call_targets (tree fn);
odr_type get_odr_type (tree, bool insert = false);
void dump_possible_polymorphic_call_targets (FILE *, tree, HOST_WIDE_INT,
const ipa_polymorphic_call_context &);