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