Hi, current mainline (as well as release branch) dies by ICE in cgraph_mark_address_taken because we attempt to create reference to inline clone. This happens becuase of ltrans propagation for pretty special case from devirtualization missed by WPA but taken by constant propagation happening as part of clone creating.
In this case we need to re-create symbol node for the virtual call that we fail t do so. It is the same case as in ipa_make_edge_direct_to_target where I added the necessary code. This patch is not aesthetically pleasing because I plan to solve the problem for 4.9 by moving cgraph clones out of symbol table hash, but it is not trivial to do so and I hope this form of patch is fine for release branch. Bootstrapped/regtested x86_64-linux, I will commit it to mainline. Does it seem OK for release branch? (it is technically a regression even if I do not have better testcase than Mozilla version that does not build with 4.7. It depends heavily on partitioning decisions making it hard to reduce). Honza * cgraph.h (cgraph_get_create_real_symbol_node): Declare. * cgraph.c (cgraph_get_create_real_symbol_node): New function. * cgrpahbuild.c: Use cgraph_get_create_real_symbol_node instead of cgraph_get_create_node. * ipa-prop.c (ipa_make_edge_direct_to_target): Likewise. Index: cgraph.h =================================================================== --- cgraph.h (revision 196601) +++ cgraph.h (working copy) @@ -575,6 +575,7 @@ struct cgraph_node * cgraph_create_node (tree); struct cgraph_node * cgraph_create_empty_node (void); struct cgraph_node * cgraph_get_create_node (tree); +struct cgraph_node * cgraph_get_create_real_symbol_node (tree); struct cgraph_node * cgraph_same_body_alias (struct cgraph_node *, tree, tree); struct cgraph_node * cgraph_add_thunk (struct cgraph_node *, tree, tree, bool, HOST_WIDE_INT, HOST_WIDE_INT, tree, tree); Index: cgraph.c =================================================================== --- cgraph.c (revision 196601) +++ cgraph.c (working copy) @@ -2595,4 +2597,47 @@ FOR_EACH_FUNCTION (node) verify_cgraph_node (node); } + +/* Create external decl node for DECL. + The difference i nbetween cgraph_get_create_node and + cgraph_get_create_real_symbol_node is that cgraph_get_create_node + may return inline clone, while cgraph_get_create_real_symbol_node + will create a new node in this case. + FIXME: This function should be removed once clones are put out of decl + hash. */ + +struct cgraph_node * +cgraph_get_create_real_symbol_node (tree decl) +{ + struct cgraph_node *first_clone = cgraph_get_node (decl); + struct cgraph_node *node; + /* create symbol table node. even if inline clone exists, we can not take + it as a target of non-inlined call. */ + node = cgraph_get_node (decl); + if (node && !node->global.inlined_to) + return node; + + node = cgraph_create_node (decl); + + /* ok, we previously inlined the function, then removed the offline copy and + now we want it back for external call. this can happen when devirtualizing + while inlining function called once that happens after extern inlined and + virtuals are already removed. in this case introduce the external node + and make it available for call. */ + if (first_clone) + { + first_clone->clone_of = node; + node->clones = first_clone; + symtab_prevail_in_asm_name_hash ((symtab_node) node); + symtab_insert_node_to_hashtable ((symtab_node) node); + if (dump_file) + fprintf (dump_file, "Introduced new external node " + "(%s/%i) and turned into root of the clone tree.\n", + xstrdup (cgraph_node_name (node)), node->uid); + } + else if (dump_file) + fprintf (dump_file, "Introduced new external node " + "(%s/%i).\n", xstrdup (cgraph_node_name (node)), node->uid); + return node; +} #include "gt-cgraph.h" Index: cgraphbuild.c =================================================================== --- cgraphbuild.c (revision 196601) +++ cgraphbuild.c (working copy) @@ -73,7 +73,7 @@ decl = get_base_var (*tp); if (TREE_CODE (decl) == FUNCTION_DECL) { - struct cgraph_node *node = cgraph_get_create_node (decl); + struct cgraph_node *node = cgraph_get_create_real_symbol_node (decl); if (!ctx->only_vars) cgraph_mark_address_taken_node (node); ipa_record_reference ((symtab_node)ctx->varpool_node, @@ -143,7 +143,7 @@ { struct cgraph_node *per_node; - per_node = cgraph_get_create_node (DECL_FUNCTION_PERSONALITY (node->symbol.decl)); + per_node = cgraph_get_create_real_symbol_node (DECL_FUNCTION_PERSONALITY (node->symbol.decl)); ipa_record_reference ((symtab_node)node, (symtab_node)per_node, IPA_REF_ADDR, NULL); cgraph_mark_address_taken_node (per_node); } @@ -223,7 +223,7 @@ addr = get_base_address (addr); if (TREE_CODE (addr) == FUNCTION_DECL) { - struct cgraph_node *node = cgraph_get_create_node (addr); + struct cgraph_node *node = cgraph_get_create_real_symbol_node (addr); cgraph_mark_address_taken_node (node); ipa_record_reference ((symtab_node)data, (symtab_node)node, @@ -252,7 +252,7 @@ { /* ??? This can happen on platforms with descriptors when these are directly manipulated in the code. Pretend that it's an address. */ - struct cgraph_node *node = cgraph_get_create_node (t); + struct cgraph_node *node = cgraph_get_create_real_symbol_node (t); cgraph_mark_address_taken_node (node); ipa_record_reference ((symtab_node)data, (symtab_node)node, @@ -330,7 +330,7 @@ { tree fn = gimple_omp_parallel_child_fn (stmt); ipa_record_reference ((symtab_node)node, - (symtab_node)cgraph_get_create_node (fn), + (symtab_node)cgraph_get_create_real_symbol_node (fn), IPA_REF_ADDR, stmt); } if (gimple_code (stmt) == GIMPLE_OMP_TASK) @@ -338,12 +338,12 @@ tree fn = gimple_omp_task_child_fn (stmt); if (fn) ipa_record_reference ((symtab_node)node, - (symtab_node) cgraph_get_create_node (fn), + (symtab_node) cgraph_get_create_real_symbol_node (fn), IPA_REF_ADDR, stmt); fn = gimple_omp_task_copy_fn (stmt); if (fn) ipa_record_reference ((symtab_node)node, - (symtab_node)cgraph_get_create_node (fn), + (symtab_node)cgraph_get_create_real_symbol_node (fn), IPA_REF_ADDR, stmt); } } Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 196601) +++ ipa-prop.c (working copy) @@ -2117,7 +2117,6 @@ we may create the first reference to the object in the unit. */ if (!callee || callee->global.inlined_to) { - struct cgraph_node *first_clone = callee; /* We are better to ensure we can refer to it. In the case of static functions we are out of luck, since we already @@ -2133,31 +2132,7 @@ xstrdup (cgraph_node_name (ie->callee)), ie->callee->uid); return NULL; } - - /* Create symbol table node. Even if inline clone exists, we can not take - it as a target of non-inlined call. */ - callee = cgraph_create_node (target); - - /* OK, we previously inlined the function, then removed the offline copy and - now we want it back for external call. This can happen when devirtualizing - while inlining function called once that happens after extern inlined and - virtuals are already removed. In this case introduce the external node - and make it available for call. */ - if (first_clone) - { - first_clone->clone_of = callee; - callee->clones = first_clone; - symtab_prevail_in_asm_name_hash ((symtab_node)callee); - symtab_insert_node_to_hashtable ((symtab_node)callee); - if (dump_file) - fprintf (dump_file, "ipa-prop: Introduced new external node " - "(%s/%i) and turned into root of the clone tree.\n", - xstrdup (cgraph_node_name (callee)), callee->uid); - } - else if (dump_file) - fprintf (dump_file, "ipa-prop: Introduced new external node " - "(%s/%i).\n", - xstrdup (cgraph_node_name (callee)), callee->uid); + callee = cgraph_get_create_real_symbol_node (target); } ipa_check_create_node_params ();