+2006-01-11 Jan Hubicka <jh@suse.cz>
+
+ * cgraph.c (cgraph_insert_node_to_hashtable): New function.
+ * cgraph.h (cgraph_node): Add inline_decl.
+ (cgraph_insert_node_to_hashtable): Declare.
+ (save_inline_function_body): Declare.
+ * cgraphunit.c (verify_cgraph_node): Inline edges might point to inline
+ clones.
+ (cgraph_preserve_function_body_p): Do not presrve when dump is enabled.
+ (cgraph_function_versioning): Update call of tree_function_versioning.
+ (save_inline_function_body): New function.
+ * function.h (struct function): Kill saved_eh, saved_cfg, saved_args,
+ saved_static_chain_decl, saved_blocks and saved-unexpanded_var_list.
+ * ipa-inline.c (cgraph_mark_inline_edge): Look for inline clones.
+ (cgraph_default_inline_p): Likewise.
+ (cgraph_decide_inlining_incrementally): Likewise.
+ * tree-inline.c (inline_data): Kill saving_p add update_clones_p.
+ (copy_bb): Kill saving; do updating of clones.
+ (copy_cfg_body): Kill saving.
+ (initialize_inlined-parameters): Likewise.
+ (expand_call_inline): Likewise.
+ (save_body): Kill.
+ (tree_function_versioning): New parameter "update_clones".
+ (inlining_p): Kill saving.
+ * tree-inline.h (tree_function_versioning): Update prototype.
+ * tree-optimize.c (tree_rest_of_compilation): Use clonning instead of
+ saving.
+
2006-01-11 Ian Lance Taylor <ian@airs.com>
* combine.c (struct undo): Remove is_int. Enumify types of undos.
return node;
}
+/* Insert already constructed node into hashtable. */
+
+void
+cgraph_insert_node_to_hashtable (struct cgraph_node *node)
+{
+ struct cgraph_node **slot;
+
+ slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, node, INSERT);
+
+ gcc_assert (!*slot);
+ *slot = node;
+}
+
/* Compare ASMNAME with the DECL_ASSEMBLER_NAME of DECL. */
static bool
bool externally_visible;
/* Set for aliases once they got through assemble_alias. */
bool alias;
+
+ /* In non-unit-at-a-time mode the function body of inline candidates is saved
+ into clone before compiling so the function in original form can be
+ inlined later. This pointer points to the clone. */
+ tree inline_decl;
};
struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller")))
/* In cgraph.c */
void dump_cgraph (FILE *);
void dump_cgraph_node (FILE *, struct cgraph_node *);
+void cgraph_insert_node_to_hashtable (struct cgraph_node *node);
void dump_varpool (FILE *);
void dump_cgraph_varpool_node (FILE *, struct cgraph_varpool_node *);
void cgraph_remove_edge (struct cgraph_edge *);
void init_cgraph (void);
struct cgraph_node *cgraph_function_versioning (struct cgraph_node *,
varray_type, varray_type);
+struct cgraph_node *save_inline_function_body (struct cgraph_node *);
/* In ipa.c */
bool cgraph_remove_unreachable_nodes (bool, FILE *);
debug_generic_stmt (stmt);
error_found = true;
}
- if (e->callee->decl != cgraph_node (decl)->decl)
+ if (e->callee->decl != cgraph_node (decl)->decl
+ && e->inline_failed)
{
error ("edge points to wrong declaration:");
debug_tree (e->callee->decl);
cgraph_preserve_function_body_p (tree decl)
{
struct cgraph_node *node;
- /* Keep the body; we're going to dump it. */
- if (dump_enabled_p (TDI_tree_all))
- return true;
if (!cgraph_global_info_ready)
return (DECL_INLINE (decl) && !flag_really_no_inline);
/* Look if there is any clone around. */
redirect_callers);
/* Copy the OLD_VERSION_NODE function tree to the new version. */
- tree_function_versioning (old_decl, new_decl, tree_map);
+ tree_function_versioning (old_decl, new_decl, tree_map, false);
/* Update the call_expr on the edges to call the new version node. */
update_call_expr (new_version_node);
new_version_node->lowered = true;
return new_version_node;
}
+
+/* Produce separate function body for inline clones so the offline copy can be
+ modified without affecting them. */
+struct cgraph_node *
+save_inline_function_body (struct cgraph_node *node)
+{
+ struct cgraph_node *first_clone;
+
+ gcc_assert (node == cgraph_node (node->decl));
+
+ cgraph_lower_function (node);
+
+ /* In non-unit-at-a-time we construct full fledged clone we never output to
+ assembly file. This clone is pointed out by inline_decl of orginal function
+ and inlining infrastructure knows how to deal with this. */
+ if (!flag_unit_at_a_time)
+ {
+ struct cgraph_edge *e;
+
+ first_clone = cgraph_clone_node (node, node->count, 0, false);
+ first_clone->needed = 0;
+ first_clone->reachable = 1;
+ /* Recursively clone all bodies. */
+ for (e = first_clone->callees; e; e = e->next_callee)
+ if (!e->inline_failed)
+ cgraph_clone_inlined_nodes (e, true, false);
+ }
+ else
+ first_clone = node->next_clone;
+
+ first_clone->decl = copy_node (node->decl);
+ node->next_clone = NULL;
+ if (!flag_unit_at_a_time)
+ node->inline_decl = first_clone->decl;
+ first_clone->prev_clone = NULL;
+ cgraph_insert_node_to_hashtable (first_clone);
+ gcc_assert (first_clone == cgraph_node (first_clone->decl));
+
+ /* Copy the OLD_VERSION_NODE function tree to the new version. */
+ tree_function_versioning (node->decl, first_clone->decl, NULL, true);
+
+ DECL_EXTERNAL (first_clone->decl) = 0;
+ DECL_ONE_ONLY (first_clone->decl) = 0;
+ TREE_PUBLIC (first_clone->decl) = 0;
+ DECL_COMDAT (first_clone->decl) = 0;
+
+ for (node = first_clone->next_clone; node; node = node->next_clone)
+ node->decl = first_clone->decl;
+#ifdef ENABLE_CHECKING
+ verify_cgraph_node (first_clone);
+#endif
+ return first_clone;
+}
+
struct function GTY(())
{
struct eh_status *eh;
- struct eh_status *saved_eh;
struct expr_status *expr;
struct emit_status *emit;
struct varasm_status *varasm;
/* The control flow graph for this function. */
struct control_flow_graph *cfg;
- struct control_flow_graph *saved_cfg;
bool after_inlining;
- /* For tree-optimize.c. */
-
- /* Saved tree and arguments during tree optimization. Used later for
- inlining */
- tree saved_args;
- tree saved_static_chain_decl;
- tree saved_blocks;
- tree saved_unexpanded_var_list;
-
/* For function.c. */
/* Points to the FUNCTION_DECL of this function. */
int old_insns = 0, new_insns = 0;
struct cgraph_node *to = NULL, *what;
+ if (e->callee->inline_decl)
+ cgraph_redirect_edge_callee (e, cgraph_node (e->callee->inline_decl));
+
gcc_assert (e->inline_failed);
e->inline_failed = NULL;
bool
cgraph_default_inline_p (struct cgraph_node *n, const char **reason)
{
- if (!DECL_INLINE (n->decl))
+ tree decl = n->decl;
+
+ if (n->inline_decl)
+ decl = n->inline_decl;
+ if (!DECL_INLINE (decl))
{
if (reason)
*reason = N_("function not inlinable");
return false;
}
- if (!DECL_SAVED_TREE (n->decl))
+ if (!DECL_STRUCT_FUNCTION (decl)->cfg)
{
if (reason)
*reason = N_("function body not available");
return false;
}
- if (DECL_DECLARED_INLINE_P (n->decl))
+ if (DECL_DECLARED_INLINE_P (decl))
{
if (n->global.insns >= MAX_INLINE_INSNS_SINGLE)
{
&& !cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed)
/* ??? It is possible that renaming variable removed the function body
in duplicate_decls. See gcc.c-torture/compile/20011119-2.c */
- && DECL_SAVED_TREE (e->callee->decl))
+ && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
{
if (dump_file && early)
{
|| (cgraph_estimate_size_after_inlining (1, e->caller, node)
<= e->caller->global.insns))
&& cgraph_check_inline_limits (node, e->callee, &e->inline_failed)
- && DECL_SAVED_TREE (e->callee->decl))
+ && (DECL_SAVED_TREE (e->callee->decl) || e->callee->inline_decl))
{
if (cgraph_default_inline_p (e->callee, &failed_reason))
{
tree callee;
/* FUNCTION_DECL for function being inlined into. */
tree caller;
- /* struct function for function being inlined. Usually this is the same
- as DECL_STRUCT_FUNCTION (callee), but can be different if saved_cfg
- and saved_eh are in use. */
+ /* struct function for function being inlined. */
struct function *callee_cfun;
/* The VAR_DECL for the return value. */
tree retvar;
distinguish between those two situations. This flag is true if
we are cloning, rather than inlining. */
bool cloning_p;
- /* Similarly for saving function body. */
- bool saving_p;
/* Versioning function is slightly different from inlining. */
bool versioning_p;
+ /* If set, the call_stmt of edges in clones of caller functions will
+ be updated. */
+ bool update_clones_p;
/* Callgraph node of function we are inlining into. */
struct cgraph_node *node;
/* Callgraph node of currently inlined function. */
callgraph edges and update or duplicate them. */
if (call && (decl = get_callee_fndecl (call)))
{
- if (id->saving_p)
- {
- struct cgraph_node *node;
- struct cgraph_edge *edge;
-
- /* We're saving a copy of the body, so we'll update the
- callgraph nodes in place. Note that we avoid
- altering the original callgraph node; we begin with
- the first clone. */
- for (node = id->node->next_clone;
- node;
- node = node->next_clone)
- {
- edge = cgraph_edge (node, orig_stmt);
- gcc_assert (edge);
- edge->call_stmt = stmt;
- }
- }
- else
+ if (!id->versioning_p)
{
struct cgraph_edge *edge;
/* We're cloning or inlining this body; duplicate the
associate callgraph nodes. */
- if (!id->versioning_p)
- {
- edge = cgraph_edge (id->current_node, orig_stmt);
- if (edge)
- cgraph_clone_edge (edge, id->node, stmt,
- REG_BR_PROB_BASE, 1, true);
- }
+ edge = cgraph_edge (id->current_node, orig_stmt);
+ if (edge)
+ cgraph_clone_edge (edge, id->node, stmt,
+ REG_BR_PROB_BASE, 1, true);
}
- if (id->versioning_p)
+ else
{
/* Update the call_expr on the edges from the new version
to its callees. */
struct cgraph_edge *edge;
edge = cgraph_edge (id->node, orig_stmt);
if (edge)
- edge->call_stmt = stmt;
+ {
+ edge->call_stmt = stmt;
+ if (id->update_clones_p)
+ {
+ struct cgraph_node *n;
+ for (n = id->node->next_clone; n; n = n->next_clone)
+ cgraph_edge (n, orig_stmt)->call_stmt = stmt;
+ }
+ }
}
}
/* If you think we can abort here, you are wrong.
(struct function *) ggc_alloc_cleared (sizeof (struct function));
basic_block bb;
tree new_fndecl = NULL;
- bool saving_or_cloning;
+ bool versioning_or_cloning;
int count_scale, frequency_scale;
if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count)
*cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl);
- /* If there is a saved_cfg+saved_args lurking in the
- struct function, a copy of the callee body was saved there, and
- the 'struct cgraph edge' nodes have been fudged to point into the
- saved body. Accordingly, we want to copy that saved body so the
- callgraph edges will be recognized and cloned properly. */
- if (cfun_to_copy->saved_cfg)
- {
- cfun_to_copy->cfg = cfun_to_copy->saved_cfg;
- cfun_to_copy->eh = cfun_to_copy->saved_eh;
- }
id->callee_cfun = cfun_to_copy;
/* If saving or cloning a function body, create new basic_block_info
and label_to_block_maps. Otherwise, we're duplicating a function
body for inlining; insert our new blocks and labels into the
existing varrays. */
- saving_or_cloning = (id->saving_p || id->cloning_p || id->versioning_p);
- if (saving_or_cloning)
+ versioning_or_cloning = (id->cloning_p || id->versioning_p);
+ if (versioning_or_cloning)
{
new_cfun =
(struct function *) ggc_alloc_cleared (sizeof (struct function));
/* Duplicate any exception-handling regions. */
if (cfun->eh)
{
- if (saving_or_cloning)
+ if (versioning_or_cloning)
init_eh_for_function ();
id->eh_region_offset = duplicate_eh_regions (cfun_to_copy,
remap_decl_1,
FOR_ALL_BB_FN (bb, cfun_to_copy)
bb->aux = NULL;
- if (saving_or_cloning)
+ if (versioning_or_cloning)
pop_cfun ();
return new_fndecl;
/* Figure out what the parameters are. */
parms = DECL_ARGUMENTS (fn);
- if (fn == current_function_decl)
- parms = cfun->saved_args;
/* Loop through the parameter declarations, replacing each with an
equivalent VAR_DECL, appropriately initialized. */
/* Initialize the static chain. */
p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
- if (fn == current_function_decl)
- p = DECL_STRUCT_FUNCTION (fn)->saved_static_chain_decl;
+ gcc_assert (fn != current_function_decl);
if (p)
{
/* No static chain? Seems like a bug in tree-nested.c. */
}
goto egress;
}
+ fn = cg_edge->callee->decl;
#ifdef ENABLE_CHECKING
if (cg_edge->callee->decl != id->node->decl)
/* Record the function we are about to inline. */
id->callee = fn;
- if (DECL_STRUCT_FUNCTION (fn)->saved_blocks)
- add_lexical_block (id->block, remap_blocks (DECL_STRUCT_FUNCTION (fn)->saved_blocks, id));
- else if (DECL_INITIAL (fn))
+ if (DECL_INITIAL (fn))
add_lexical_block (id->block, remap_blocks (DECL_INITIAL (fn), id));
/* Return statements in the function body will be replaced by jumps
/* Add local vars in this inlined callee to caller. */
t_step = id->callee_cfun->unexpanded_var_list;
- if (id->callee_cfun->saved_unexpanded_var_list)
- t_step = id->callee_cfun->saved_unexpanded_var_list;
for (; t_step; t_step = TREE_CHAIN (t_step))
{
var = TREE_VALUE (t_step);
append_to_statement_list_force (copy_generic_body (&id), &DECL_SAVED_TREE (clone));
}
-/* Save duplicate body in FN. MAP is used to pass around splay tree
- used to update arguments in restore_body. */
-
-/* Make and return duplicate of body in FN. Put copies of DECL_ARGUMENTS
- in *arg_copy and of the static chain, if any, in *sc_copy. */
-
-void
-save_body (tree fn, tree *arg_copy, tree *sc_copy)
-{
- inline_data id;
- tree newdecl, *parg;
- basic_block fn_entry_block;
- tree t_step;
-
- memset (&id, 0, sizeof (id));
- id.callee = fn;
- id.callee_cfun = DECL_STRUCT_FUNCTION (fn);
- id.caller = fn;
- id.node = cgraph_node (fn);
- id.saving_p = true;
- id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
- *arg_copy = DECL_ARGUMENTS (fn);
-
- for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg))
- {
- tree new = copy_node (*parg);
-
- lang_hooks.dup_lang_specific_decl (new);
- DECL_ABSTRACT_ORIGIN (new) = DECL_ORIGIN (*parg);
- insert_decl_map (&id, *parg, new);
- TREE_CHAIN (new) = TREE_CHAIN (*parg);
- *parg = new;
- }
-
- *sc_copy = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
- if (*sc_copy)
- {
- tree new = copy_node (*sc_copy);
-
- lang_hooks.dup_lang_specific_decl (new);
- DECL_ABSTRACT_ORIGIN (new) = DECL_ORIGIN (*sc_copy);
- insert_decl_map (&id, *sc_copy, new);
- TREE_CHAIN (new) = TREE_CHAIN (*sc_copy);
- *sc_copy = new;
- }
-
- /* We're not inside any EH region. */
- id.eh_region = -1;
-
- insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn));
-
- DECL_STRUCT_FUNCTION (fn)->saved_blocks
- = remap_blocks (DECL_INITIAL (fn), &id);
- for (t_step = id.callee_cfun->unexpanded_var_list;
- t_step;
- t_step = TREE_CHAIN (t_step))
- {
- tree var = TREE_VALUE (t_step);
- if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
- cfun->saved_unexpanded_var_list
- = tree_cons (NULL_TREE, var, cfun->saved_unexpanded_var_list);
- else
- cfun->saved_unexpanded_var_list
- = tree_cons (NULL_TREE, remap_decl (var, &id),
- cfun->saved_unexpanded_var_list);
- }
-
- /* Actually copy the body, including a new (struct function *) and CFG.
- EH info is also duplicated so its labels point into the copied
- CFG, not the original. */
- fn_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fn));
- newdecl = copy_body (&id, fn_entry_block->count, fn_entry_block->frequency,
- NULL, NULL);
- DECL_STRUCT_FUNCTION (fn)->saved_cfg = DECL_STRUCT_FUNCTION (newdecl)->cfg;
- DECL_STRUCT_FUNCTION (fn)->saved_eh = DECL_STRUCT_FUNCTION (newdecl)->eh;
-
- /* Clean up. */
- splay_tree_delete (id.decl_map);
-}
-
/* Passed to walk_tree. Copies the node pointed to, if appropriate. */
tree
respectively. In case we want to replace a DECL
tree with another tree while duplicating the function's
body, TREE_MAP represents the mapping between these
- trees. */
+ trees. If UPDATE_CLONES is set, the call_stmt fields
+ of edges of clones of the function will be updated. */
void
-tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map)
+tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map,
+ bool update_clones)
{
struct cgraph_node *old_version_node;
struct cgraph_node *new_version_node;
DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
/* Generate a new name for the new version. */
- DECL_NAME (new_decl) =
- create_tmp_var_name (NULL);
+ if (!update_clones)
+ DECL_NAME (new_decl) =
+ create_tmp_var_name (NULL);
/* Create a new SYMBOL_REF rtx for the new name. */
if (DECL_RTL (old_decl) != NULL)
{
id.current_node = cgraph_node (old_decl);
id.versioning_p = true;
+ id.update_clones_p = update_clones;
id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
id.caller = new_decl;
id.callee = old_decl;
static inline bool
inlining_p (inline_data * id)
{
- return (!id->saving_p && !id->cloning_p && !id->versioning_p);
+ return (!id->cloning_p && !id->versioning_p);
}
/* Duplicate a type, fields and all. */
void pop_cfun (void);
int estimate_num_insns (tree expr);
bool tree_versionable_function_p (tree);
-void tree_function_versioning (tree, tree, varray_type);
+void tree_function_versioning (tree, tree, varray_type, bool);
/* Copy a declaration when one function is substituted inline into
another. It is used also for versioning. */
tree_rest_of_compilation (tree fndecl)
{
location_t saved_loc;
- struct cgraph_node *saved_node = NULL, *node;
+ struct cgraph_node *node;
timevar_push (TV_EXPAND);
gcc_assert (!flag_unit_at_a_time || cgraph_global_info_ready);
+ node = cgraph_node (fndecl);
+
+ /* We might need the body of this function so that we can expand
+ it inline somewhere else. */
+ if (cgraph_preserve_function_body_p (fndecl))
+ save_inline_function_body (node);
+
/* Initialize the RTL code for the function. */
current_function_decl = fndecl;
saved_loc = input_location;
cfun->x_dont_save_pending_sizes_p = 1;
cfun->after_inlining = true;
- node = cgraph_node (fndecl);
-
- /* We might need the body of this function so that we can expand
- it inline somewhere else. This means not lowering some constructs
- such as exception handling. */
- if (cgraph_preserve_function_body_p (fndecl))
- {
- if (!flag_unit_at_a_time)
- {
- struct cgraph_edge *e;
-
- saved_node = cgraph_clone_node (node, node->count, 1, false);
- for (e = saved_node->callees; e; e = e->next_callee)
- if (!e->inline_failed)
- cgraph_clone_inlined_nodes (e, true, false);
- }
- cfun->saved_static_chain_decl = cfun->static_chain_decl;
- save_body (fndecl, &cfun->saved_args, &cfun->saved_static_chain_decl);
- }
-
if (flag_inline_trees)
{
struct cgraph_edge *e;
/* Release the default bitmap obstack. */
bitmap_obstack_release (NULL);
- /* Restore original body if still needed. */
- if (cfun->saved_cfg)
- {
- DECL_ARGUMENTS (fndecl) = cfun->saved_args;
- cfun->cfg = cfun->saved_cfg;
- cfun->eh = cfun->saved_eh;
- DECL_INITIAL (fndecl) = cfun->saved_blocks;
- cfun->unexpanded_var_list = cfun->saved_unexpanded_var_list;
- cfun->saved_cfg = NULL;
- cfun->saved_eh = NULL;
- cfun->saved_args = NULL_TREE;
- cfun->saved_blocks = NULL_TREE;
- cfun->saved_unexpanded_var_list = NULL_TREE;
- cfun->static_chain_decl = cfun->saved_static_chain_decl;
- cfun->saved_static_chain_decl = NULL;
- /* When not in unit-at-a-time mode, we must preserve out of line copy
- representing node before inlining. Restore original outgoing edges
- using clone we created earlier. */
- if (!flag_unit_at_a_time)
- {
- struct cgraph_edge *e;
-
- node = cgraph_node (current_function_decl);
- cgraph_node_remove_callees (node);
- node->callees = saved_node->callees;
- saved_node->callees = NULL;
- update_inlined_to_pointers (node, node);
- for (e = node->callees; e; e = e->next_callee)
- e->caller = node;
- cgraph_remove_node (saved_node);
- }
- }
- else
- DECL_SAVED_TREE (fndecl) = NULL;
+ DECL_SAVED_TREE (fndecl) = NULL;
cfun = 0;
/* If requested, warn about function definitions where the function will