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 <mjam...@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); } +