This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH, PR 60640] When creating virtual clones, clone thunks too
- From: Martin Jambor <mjambor at suse dot cz>
- To: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Cc: Jan Hubicka <hubicka at ucw dot cz>
- Date: Fri, 28 Mar 2014 18:28:25 +0100
- Subject: [PATCH, PR 60640] When creating virtual clones, clone thunks too
- Authentication-results: sourceware.org; auth=none
Hi,
this patch fixes PR 60640 by creating thunks to clones when that is
necessary to properly redirect edges to them. I mostly does what
cgraph_add_thunk does and what analyze_function does to thunks. It
fixes the testcases on trunk (it does not apply to 4.8, I have not
looked how easily fixable that it) and passes bootstrap and testing on
x86_64-linux.
OK for trunk?
Thanks,
Martin
2014-03-26 Martin Jambor <mjambor@suse.cz>
* cgraph.h (cgraph_clone_node): New parameter added to declaration.
Adjust all callers.
* cgraphclones.c (build_function_type_skip_args): Moved upwards in the
file.
(build_function_decl_skip_args): Likewise.
(duplicate_thunk_for_node): New function.
(redirect_edge_duplicating_thunks): Likewise.
(cgraph_clone_node): New parameter args_to_skip, pass it to
redirect_edge_duplicating_thunks which is called instead of
cgraph_redirect_edge_callee.
(cgraph_create_virtual_clone): Pass args_to_skip to cgraph_clone_node.
testsuite/
* g++.dg/ipa/pr60640-1.C: New test.
* g++.dg/ipa/pr60640-2.C: Likewise.
Index: src/gcc/cgraph.h
===================================================================
--- src.orig/gcc/cgraph.h
+++ src/gcc/cgraph.h
@@ -890,7 +890,7 @@ struct cgraph_edge * cgraph_clone_edge (
unsigned, gcov_type, int, bool);
struct cgraph_node * cgraph_clone_node (struct cgraph_node *, tree, gcov_type,
int, bool, vec<cgraph_edge_p>,
- bool, struct cgraph_node *);
+ bool, struct cgraph_node *, bitmap);
tree clone_function_name (tree decl, const char *);
struct cgraph_node * cgraph_create_virtual_clone (struct cgraph_node *old_node,
vec<cgraph_edge_p>,
Index: src/gcc/cgraphclones.c
===================================================================
--- src.orig/gcc/cgraphclones.c
+++ src/gcc/cgraphclones.c
@@ -168,6 +168,183 @@ cgraph_clone_edge (struct cgraph_edge *e
return new_edge;
}
+/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
+ return value if SKIP_RETURN is true. */
+
+static tree
+build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
+ bool skip_return)
+{
+ tree new_type = NULL;
+ tree args, new_args = NULL, t;
+ tree new_reversed;
+ int i = 0;
+
+ for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node;
+ args = TREE_CHAIN (args), i++)
+ if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
+ new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args);
+
+ new_reversed = nreverse (new_args);
+ if (args)
+ {
+ if (new_reversed)
+ TREE_CHAIN (new_args) = void_list_node;
+ else
+ new_reversed = void_list_node;
+ }
+
+ /* Use copy_node to preserve as much as possible from original type
+ (debug info, attribute lists etc.)
+ Exception is METHOD_TYPEs must have THIS argument.
+ When we are asked to remove it, we need to build new FUNCTION_TYPE
+ instead. */
+ if (TREE_CODE (orig_type) != METHOD_TYPE
+ || !args_to_skip
+ || !bitmap_bit_p (args_to_skip, 0))
+ {
+ new_type = build_distinct_type_copy (orig_type);
+ TYPE_ARG_TYPES (new_type) = new_reversed;
+ }
+ else
+ {
+ new_type
+ = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
+ new_reversed));
+ TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
+ }
+
+ if (skip_return)
+ TREE_TYPE (new_type) = void_type_node;
+
+ /* This is a new type, not a copy of an old type. Need to reassociate
+ variants. We can handle everything except the main variant lazily. */
+ t = TYPE_MAIN_VARIANT (orig_type);
+ if (t != orig_type)
+ {
+ t = build_function_type_skip_args (t, args_to_skip, skip_return);
+ TYPE_MAIN_VARIANT (new_type) = t;
+ TYPE_NEXT_VARIANT (new_type) = TYPE_NEXT_VARIANT (t);
+ TYPE_NEXT_VARIANT (t) = new_type;
+ }
+ else
+ {
+ TYPE_MAIN_VARIANT (new_type) = new_type;
+ TYPE_NEXT_VARIANT (new_type) = NULL;
+ }
+
+ return new_type;
+}
+
+/* Build variant of function decl ORIG_DECL skipping ARGS_TO_SKIP and the
+ return value if SKIP_RETURN is true.
+
+ Arguments from DECL_ARGUMENTS list can't be removed now, since they are
+ linked by TREE_CHAIN directly. The caller is responsible for eliminating
+ them when they are being duplicated (i.e. copy_arguments_for_versioning). */
+
+static tree
+build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip,
+ bool skip_return)
+{
+ tree new_decl = copy_node (orig_decl);
+ tree new_type;
+
+ new_type = TREE_TYPE (orig_decl);
+ if (prototype_p (new_type)
+ || (skip_return && !VOID_TYPE_P (TREE_TYPE (new_type))))
+ new_type
+ = build_function_type_skip_args (new_type, args_to_skip, skip_return);
+ TREE_TYPE (new_decl) = new_type;
+
+ /* For declarations setting DECL_VINDEX (i.e. methods)
+ we expect first argument to be THIS pointer. */
+ if (args_to_skip && bitmap_bit_p (args_to_skip, 0))
+ DECL_VINDEX (new_decl) = NULL_TREE;
+
+ /* When signature changes, we need to clear builtin info. */
+ if (DECL_BUILT_IN (new_decl)
+ && args_to_skip
+ && !bitmap_empty_p (args_to_skip))
+ {
+ DECL_BUILT_IN_CLASS (new_decl) = NOT_BUILT_IN;
+ DECL_FUNCTION_CODE (new_decl) = (enum built_in_function) 0;
+ }
+ /* The FE might have information and assumptions about the other
+ arguments. */
+ DECL_LANG_SPECIFIC (new_decl) = NULL;
+ return new_decl;
+}
+
+/* Duplicate thunk THUNK but make it to refer to NODE. ARGS_TO_SKIP, if
+ non-NULL, determines which parameters should be omitted. */
+
+static cgraph_node *
+duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node,
+ bitmap args_to_skip)
+{
+ cgraph_node *new_thunk, *thunk_of;
+ thunk_of = cgraph_function_or_thunk_node (thunk->callees->callee);
+
+ if (thunk_of->thunk.thunk_p)
+ node = duplicate_thunk_for_node (thunk_of, node, args_to_skip);
+
+ tree new_decl;
+ if (!args_to_skip)
+ new_decl = copy_node (thunk->decl);
+ else
+ new_decl = build_function_decl_skip_args (thunk->decl, args_to_skip, false);
+
+ gcc_checking_assert (!DECL_STRUCT_FUNCTION (new_decl));
+ gcc_checking_assert (!DECL_INITIAL (new_decl));
+ gcc_checking_assert (!DECL_RESULT (new_decl));
+ gcc_checking_assert (!DECL_RTL_SET_P (new_decl));
+
+ DECL_NAME (new_decl) = clone_function_name (thunk->decl, "artificial_thunk");
+ SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
+ DECL_EXTERNAL (new_decl) = 0;
+ DECL_SECTION_NAME (new_decl) = NULL;
+ DECL_COMDAT_GROUP (new_decl) = 0;
+ TREE_PUBLIC (new_decl) = 0;
+ DECL_COMDAT (new_decl) = 0;
+ DECL_WEAK (new_decl) = 0;
+ DECL_VIRTUAL_P (new_decl) = 0;
+ DECL_STATIC_CONSTRUCTOR (new_decl) = 0;
+ DECL_STATIC_DESTRUCTOR (new_decl) = 0;
+
+ new_thunk = cgraph_create_node (new_decl);
+ new_thunk->definition = true;
+ new_thunk->thunk = thunk->thunk;
+ new_thunk->unique_name = in_lto_p;
+ new_thunk->externally_visible = 0;
+ new_thunk->local.local = 1;
+ new_thunk->lowered = true;
+ new_thunk->former_clone_of = thunk->decl;
+
+ struct cgraph_edge *e = cgraph_create_edge (new_thunk, node, NULL, 0,
+ CGRAPH_FREQ_BASE);
+ e->call_stmt_cannot_inline_p = true;
+ cgraph_call_edge_duplication_hooks (thunk->callees, e);
+ if (!expand_thunk (new_thunk, false))
+ new_thunk->analyzed = true;
+ cgraph_call_node_duplication_hooks (thunk, new_thunk);
+ return new_thunk;
+}
+
+/* If E does not lead to a thunk, simply redirect it to N. Otherwise create
+ one or more equivalent thunks for N and redirect E to the first in the
+ chain. */
+
+void
+redirect_edge_duplicating_thunks (struct cgraph_edge *e, struct cgraph_node *n,
+ bitmap args_to_skip)
+{
+ cgraph_node *orig_to = cgraph_function_or_thunk_node (e->callee);
+ if (orig_to->thunk.thunk_p)
+ n = duplicate_thunk_for_node (orig_to, n, args_to_skip);
+
+ cgraph_redirect_edge_callee (e, n);
+}
/* Create node representing clone of N executed COUNT times. Decrease
the execution counts from original node too.
@@ -190,7 +367,8 @@ cgraph_clone_node (struct cgraph_node *n
bool update_original,
vec<cgraph_edge_p> redirect_callers,
bool call_duplication_hook,
- struct cgraph_node *new_inlined_to)
+ struct cgraph_node *new_inlined_to,
+ bitmap args_to_skip)
{
struct cgraph_node *new_node = cgraph_create_empty_node ();
struct cgraph_edge *e;
@@ -239,7 +417,7 @@ cgraph_clone_node (struct cgraph_node *n
{
/* Redirect calls to the old version node to point to its new
version. */
- cgraph_redirect_edge_callee (e, new_node);
+ redirect_edge_duplicating_thunks (e, new_node, args_to_skip);
}
@@ -288,114 +466,6 @@ clone_function_name (tree decl, const ch
return get_identifier (tmp_name);
}
-/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
- return value if SKIP_RETURN is true. */
-
-static tree
-build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
- bool skip_return)
-{
- tree new_type = NULL;
- tree args, new_args = NULL, t;
- tree new_reversed;
- int i = 0;
-
- for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node;
- args = TREE_CHAIN (args), i++)
- if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
- new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args);
-
- new_reversed = nreverse (new_args);
- if (args)
- {
- if (new_reversed)
- TREE_CHAIN (new_args) = void_list_node;
- else
- new_reversed = void_list_node;
- }
-
- /* Use copy_node to preserve as much as possible from original type
- (debug info, attribute lists etc.)
- Exception is METHOD_TYPEs must have THIS argument.
- When we are asked to remove it, we need to build new FUNCTION_TYPE
- instead. */
- if (TREE_CODE (orig_type) != METHOD_TYPE
- || !args_to_skip
- || !bitmap_bit_p (args_to_skip, 0))
- {
- new_type = build_distinct_type_copy (orig_type);
- TYPE_ARG_TYPES (new_type) = new_reversed;
- }
- else
- {
- new_type
- = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
- new_reversed));
- TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
- }
-
- if (skip_return)
- TREE_TYPE (new_type) = void_type_node;
-
- /* This is a new type, not a copy of an old type. Need to reassociate
- variants. We can handle everything except the main variant lazily. */
- t = TYPE_MAIN_VARIANT (orig_type);
- if (t != orig_type)
- {
- t = build_function_type_skip_args (t, args_to_skip, skip_return);
- TYPE_MAIN_VARIANT (new_type) = t;
- TYPE_NEXT_VARIANT (new_type) = TYPE_NEXT_VARIANT (t);
- TYPE_NEXT_VARIANT (t) = new_type;
- }
- else
- {
- TYPE_MAIN_VARIANT (new_type) = new_type;
- TYPE_NEXT_VARIANT (new_type) = NULL;
- }
-
- return new_type;
-}
-
-/* Build variant of function decl ORIG_DECL skipping ARGS_TO_SKIP and the
- return value if SKIP_RETURN is true.
-
- Arguments from DECL_ARGUMENTS list can't be removed now, since they are
- linked by TREE_CHAIN directly. The caller is responsible for eliminating
- them when they are being duplicated (i.e. copy_arguments_for_versioning). */
-
-static tree
-build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip,
- bool skip_return)
-{
- tree new_decl = copy_node (orig_decl);
- tree new_type;
-
- new_type = TREE_TYPE (orig_decl);
- if (prototype_p (new_type)
- || (skip_return && !VOID_TYPE_P (TREE_TYPE (new_type))))
- new_type
- = build_function_type_skip_args (new_type, args_to_skip, skip_return);
- TREE_TYPE (new_decl) = new_type;
-
- /* For declarations setting DECL_VINDEX (i.e. methods)
- we expect first argument to be THIS pointer. */
- if (args_to_skip && bitmap_bit_p (args_to_skip, 0))
- DECL_VINDEX (new_decl) = NULL_TREE;
-
- /* When signature changes, we need to clear builtin info. */
- if (DECL_BUILT_IN (new_decl)
- && args_to_skip
- && !bitmap_empty_p (args_to_skip))
- {
- DECL_BUILT_IN_CLASS (new_decl) = NOT_BUILT_IN;
- DECL_FUNCTION_CODE (new_decl) = (enum built_in_function) 0;
- }
- /* The FE might have information and assumptions about the other
- arguments. */
- DECL_LANG_SPECIFIC (new_decl) = NULL;
- return new_decl;
-}
-
/* Create callgraph node clone with new declaration. The actual body will
be copied later at compilation stage.
@@ -449,7 +519,7 @@ cgraph_create_virtual_clone (struct cgra
new_node = cgraph_clone_node (old_node, new_decl, old_node->count,
CGRAPH_FREQ_BASE, false,
- redirect_callers, false, NULL);
+ redirect_callers, false, NULL, args_to_skip);
/* Update the properties.
Make clone visible only within this translation unit. Make sure
that is not weak also.
Index: src/gcc/ipa-inline-transform.c
===================================================================
--- src.orig/gcc/ipa-inline-transform.c
+++ src/gcc/ipa-inline-transform.c
@@ -184,7 +184,7 @@ clone_inlined_nodes (struct cgraph_edge
freq_scale = e->frequency;
n = cgraph_clone_node (e->callee, e->callee->decl,
e->count, freq_scale, update_original,
- vNULL, true, inlining_into);
+ vNULL, true, inlining_into, NULL);
cgraph_redirect_edge_callee (e, n);
}
}
Index: src/gcc/ipa-inline.c
===================================================================
--- src.orig/gcc/ipa-inline.c
+++ src/gcc/ipa-inline.c
@@ -1388,7 +1388,7 @@ recursive_inlining (struct cgraph_edge *
/* We need original clone to copy around. */
master_clone = cgraph_clone_node (node, node->decl,
node->count, CGRAPH_FREQ_BASE,
- false, vNULL, true, NULL);
+ false, vNULL, true, NULL, NULL);
for (e = master_clone->callees; e; e = e->next_callee)
if (!e->inline_failed)
clone_inlined_nodes (e, true, false, NULL, CGRAPH_FREQ_BASE);
Index: src/gcc/lto-cgraph.c
===================================================================
--- src.orig/gcc/lto-cgraph.c
+++ src/gcc/lto-cgraph.c
@@ -1042,7 +1042,7 @@ input_node (struct lto_file_decl_data *f
{
node = cgraph_clone_node (cgraph (nodes[clone_ref]), fn_decl,
0, CGRAPH_FREQ_BASE, false,
- vNULL, false, NULL);
+ vNULL, false, NULL, NULL);
}
else
{
Index: src/gcc/testsuite/g++.dg/ipa/pr60640-1.C
===================================================================
--- /dev/null
+++ src/gcc/testsuite/g++.dg/ipa/pr60640-1.C
@@ -0,0 +1,50 @@
+// { dg-do compile }
+// { dg-options "-O3" }
+
+class ASN1Object
+{
+public:
+ virtual ~ASN1Object ();
+};
+class A
+{
+ virtual unsigned m_fn1 () const;
+};
+class B
+{
+public:
+ ASN1Object Element;
+ virtual unsigned m_fn1 (bool) const;
+};
+template <class BASE> class C : public BASE
+{
+};
+
+class D : ASN1Object, public B
+{
+};
+class G : public D
+{
+ unsigned m_fn1 (bool) const {}
+};
+class F : A
+{
+public:
+ F (A);
+ unsigned m_fn1 () const
+ {
+ int a;
+ a = m_fn2 ().m_fn1 (0);
+ return a;
+ }
+ const B &m_fn2 () const { return m_groupParameters; }
+ C<G> m_groupParameters;
+};
+template <class D> void BenchMarkKeyAgreement (int *, int *, int)
+{
+ A f;
+ D d (f);
+}
+
+void BenchmarkAll2 () { BenchMarkKeyAgreement<F>(0, 0, 0); }
+