+ return (!lookup_attribute ("noclone", DECL_ATTRIBUTES (fndecl))
+ && copy_forbidden (DECL_STRUCT_FUNCTION (fndecl), fndecl) == NULL);
+}
+
+/* Delete all unreachable basic blocks and update callgraph.
+ Doing so is somewhat nontrivial because we need to update all clones and
+ remove inline function that become unreachable. */
+
+static bool
+delete_unreachable_blocks_update_callgraph (copy_body_data *id)
+{
+ bool changed = false;
+ basic_block b, next_bb;
+
+ find_unreachable_blocks ();
+
+ /* Delete all unreachable basic blocks. */
+
+ for (b = ENTRY_BLOCK_PTR->next_bb; b != EXIT_BLOCK_PTR; b = next_bb)
+ {
+ next_bb = b->next_bb;
+
+ if (!(b->flags & BB_REACHABLE))
+ {
+ gimple_stmt_iterator bsi;
+
+ for (bsi = gsi_start_bb (b); !gsi_end_p (bsi); gsi_next (&bsi))
+ if (gimple_code (gsi_stmt (bsi)) == GIMPLE_CALL)
+ {
+ struct cgraph_edge *e;
+ struct cgraph_node *node;
+
+ if ((e = cgraph_edge (id->dst_node, gsi_stmt (bsi))) != NULL)
+ {
+ if (!e->inline_failed)
+ cgraph_remove_node_and_inline_clones (e->callee);
+ else
+ cgraph_remove_edge (e);
+ }
+ if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES
+ && id->dst_node->clones)
+ for (node = id->dst_node->clones; node != id->dst_node;)
+ {
+ if ((e = cgraph_edge (node, gsi_stmt (bsi))) != NULL)
+ {
+ if (!e->inline_failed)
+ cgraph_remove_node_and_inline_clones (e->callee);
+ else
+ cgraph_remove_edge (e);
+ }
+
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != id->dst_node && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != id->dst_node)
+ node = node->next_sibling_clone;
+ }
+ }
+ }
+ delete_basic_block (b);
+ changed = true;
+ }
+ }
+
+ if (changed)
+ tidy_fallthru_edges ();
+#ifdef ENABLE_CHECKING0
+ verify_cgraph_node (id->dst_node);
+ if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES
+ && id->dst_node->clones)
+ {
+ struct cgraph_node *node;
+ for (node = id->dst_node->clones; node != id->dst_node;)
+ {
+ verify_cgraph_node (node);
+
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != id->dst_node && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != id->dst_node)
+ node = node->next_sibling_clone;
+ }
+ }
+ }
+#endif
+ return changed;
+}
+
+/* Update clone info after duplication. */
+
+static void
+update_clone_info (copy_body_data * id)
+{
+ struct cgraph_node *node;
+ if (!id->dst_node->clones)
+ return;
+ for (node = id->dst_node->clones; node != id->dst_node;)
+ {
+ /* First update replace maps to match the new body. */
+ if (node->clone.tree_map)
+ {
+ unsigned int i;
+ for (i = 0; i < VEC_length (ipa_replace_map_p, node->clone.tree_map); i++)
+ {
+ struct ipa_replace_map *replace_info;
+ replace_info = VEC_index (ipa_replace_map_p, node->clone.tree_map, i);
+ walk_tree (&replace_info->old_tree, copy_tree_body_r, id, NULL);
+ walk_tree (&replace_info->new_tree, copy_tree_body_r, id, NULL);
+ }
+ }
+ if (node->clones)
+ node = node->clones;
+ else if (node->next_sibling_clone)
+ node = node->next_sibling_clone;
+ else
+ {
+ while (node != id->dst_node && !node->next_sibling_clone)
+ node = node->clone_of;
+ if (node != id->dst_node)
+ node = node->next_sibling_clone;
+ }
+ }