aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-inline.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-inline.c')
-rw-r--r--gcc/tree-inline.c181
1 files changed, 145 insertions, 36 deletions
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 1d410af..d9aeefe 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -40,6 +40,7 @@ Boston, MA 02111-1307, USA. */
#include "cgraph.h"
#include "intl.h"
#include "diagnostic.h"
+#include "function.h"
/* This should be eventually be generalized to other languages, but
this would require a shared function-as-trees infrastructure. */
@@ -100,12 +101,15 @@ typedef struct inline_data
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;
/* Hash table used to prevent walk_tree from visiting the same node
umpteen million times. */
htab_t tree_pruner;
- /* Decl of function we are inlining into. */
- tree decl;
- tree current_decl;
+ /* Callgraph node of function we are inlining into. */
+ struct cgraph_node *node;
+ /* Callgraph node of currently inlined function. */
+ struct cgraph_node *current_node;
} inline_data;
/* Prototypes. */
@@ -537,9 +541,9 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
/* If this is a RETURN_STMT, change it into an EXPR_STMT and a
GOTO_STMT with the RET_LABEL as its target. */
#ifndef INLINER_FOR_JAVA
- if (TREE_CODE (*tp) == RETURN_STMT && id->ret_label)
+ if (TREE_CODE (*tp) == RETURN_STMT && id->ret_label && !id->saving_p)
#else /* INLINER_FOR_JAVA */
- if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label)
+ if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label && !id->saving_p)
#endif /* INLINER_FOR_JAVA */
{
tree return_stmt = *tp;
@@ -646,6 +650,8 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
knows not to copy VAR_DECLs, etc., so this is safe. */
else
{
+ tree old_node = *tp;
+
if (TREE_CODE (*tp) == MODIFY_EXPR
&& TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1)
&& (lang_hooks.tree_inlining.auto_var_in_fn_p
@@ -693,6 +699,32 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
copy_tree_r (tp, walk_subtrees, NULL);
+ if (TREE_CODE (*tp) == CALL_EXPR && id->node && get_callee_fndecl (*tp))
+ {
+ if (id->saving_p)
+ {
+ struct cgraph_node *node;
+ struct cgraph_edge *edge;
+
+ for (node = id->node->next_clone; node; node = node->next_clone)
+ {
+ edge = cgraph_edge (node, old_node);
+ if (edge)
+ edge->call_expr = *tp;
+ else
+ abort ();
+ }
+ }
+ else if (!id->cloning_p)
+ {
+ struct cgraph_edge *edge;
+
+ edge = cgraph_edge (id->current_node, old_node);
+ if (edge)
+ cgraph_clone_edge (edge, id->node, *tp);
+ }
+ }
+
TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
/* The copied TARGET_EXPR has never been expanded, even if the
@@ -715,8 +747,13 @@ static tree
copy_body (inline_data *id)
{
tree body;
+ tree fndecl = VARRAY_TOP_TREE (id->fns);
- body = DECL_SAVED_TREE (VARRAY_TOP_TREE (id->fns));
+ if (fndecl == current_function_decl
+ && cfun->saved_tree)
+ body = cfun->saved_tree;
+ else
+ body = DECL_SAVED_TREE (fndecl);
walk_tree (&body, copy_body_r, id, NULL);
return body;
@@ -742,8 +779,9 @@ initialize_inlined_parameters (inline_data *id, tree args, tree fn, tree block)
int argnum = 0;
/* Figure out what the parameters are. */
- parms =
-DECL_ARGUMENTS (fn);
+ parms = DECL_ARGUMENTS (fn);
+ if (fn == current_function_decl && cfun->saved_args)
+ parms = cfun->saved_args;
/* Start with no initializations whatsoever. */
init_stmts = NULL_TREE;
@@ -1254,6 +1292,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
splay_tree st;
tree args;
tree return_slot_addr;
+ struct cgraph_edge *edge;
const char *reason;
/* See what we've got. */
@@ -1333,9 +1372,30 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
&& DECL_SAVED_TREE (DECL_ABSTRACT_ORIGIN (fn)))
fn = DECL_ABSTRACT_ORIGIN (fn);
+ /* Objective C and fortran still calls tree_rest_of_compilation directly.
+ Kill this check once this is fixed. */
+ if (!id->current_node->analyzed)
+ return NULL_TREE;
+
+ edge = cgraph_edge (id->current_node, t);
+
+ /* Constant propagation on argument done during previous inlining
+ may create new direct call. Produce an edge for it. */
+ if (!edge)
+ {
+ struct cgraph_node *dest = cgraph_node (fn);
+
+ /* FN must have address taken so it can be passed as argument. */
+ if (!dest->needed)
+ abort ();
+ cgraph_create_edge (id->node, dest, t)->inline_failed
+ = N_("originally indirect function call not considered for inlining");
+ return NULL_TREE;
+ }
+
/* Don't try to inline functions that are not well-suited to
inlining. */
- if (!cgraph_inline_p (id->current_decl, fn, &reason))
+ if (!cgraph_inline_p (edge, &reason))
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
@@ -1352,6 +1412,11 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
return NULL_TREE;
}
+#ifdef ENABLE_CHECKING
+ if (edge->callee->decl != id->node->decl)
+ verify_cgraph_node (edge->callee);
+#endif
+
if (! lang_hooks.tree_inlining.start_inlining (fn))
return NULL_TREE;
@@ -1487,23 +1552,29 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
/* After we've initialized the parameters, we insert the body of the
function itself. */
+ {
+ struct cgraph_node *old_node = id->current_node;
+
+ id->current_node = edge->callee;
#ifndef INLINER_FOR_JAVA
- inlined_body = &COMPOUND_BODY (stmt);
- while (*inlined_body)
- inlined_body = &TREE_CHAIN (*inlined_body);
- *inlined_body = copy_body (id);
+ inlined_body = &COMPOUND_BODY (stmt);
+ while (*inlined_body)
+ inlined_body = &TREE_CHAIN (*inlined_body);
+ *inlined_body = copy_body (id);
#else /* INLINER_FOR_JAVA */
- {
- tree new_body;
- java_inlining_map_static_initializers (fn, id->decl_map);
- new_body = copy_body (id);
- TREE_TYPE (new_body) = TREE_TYPE (TREE_TYPE (fn));
- BLOCK_EXPR_BODY (expr)
- = add_stmt_to_compound (BLOCK_EXPR_BODY (expr),
- TREE_TYPE (new_body), new_body);
- inlined_body = &BLOCK_EXPR_BODY (expr);
- }
+ {
+ tree new_body;
+ java_inlining_map_static_initializers (fn, id->decl_map);
+ new_body = copy_body (id);
+ TREE_TYPE (new_body) = TREE_TYPE (TREE_TYPE (fn));
+ BLOCK_EXPR_BODY (expr)
+ = add_stmt_to_compound (BLOCK_EXPR_BODY (expr),
+ TREE_TYPE (new_body), new_body);
+ inlined_body = &BLOCK_EXPR_BODY (expr);
+ }
#endif /* INLINER_FOR_JAVA */
+ id->current_node = old_node;
+ }
/* After the body of the function comes the RET_LABEL. This must come
before we evaluate the returned value below, because that evaluation
@@ -1574,19 +1645,10 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data)
TREE_USED (*tp) = 1;
/* Update callgraph if needed. */
- if (id->decl)
- {
- cgraph_remove_call (id->decl, fn);
- cgraph_create_edges (id->decl, *inlined_body);
- }
+ cgraph_remove_node (edge->callee);
/* Recurse into the body of the just inlined function. */
- {
- tree old_decl = id->current_decl;
- id->current_decl = fn;
- expand_calls_inline (inlined_body, id);
- id->current_decl = old_decl;
- }
+ expand_calls_inline (inlined_body, id);
VARRAY_POP (id->fns);
/* Don't walk into subtrees. We've already handled them above. */
@@ -1629,8 +1691,7 @@ optimize_inline_calls (tree fn)
/* Clear out ID. */
memset (&id, 0, sizeof (id));
- id.decl = fn;
- id.current_decl = fn;
+ id.current_node = id.node = cgraph_node (fn);
/* Don't allow recursion into FN. */
VARRAY_TREE_INIT (id.fns, 32, "fns");
VARRAY_PUSH_TREE (id.fns, fn);
@@ -1669,6 +1730,18 @@ optimize_inline_calls (tree fn)
VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree));
DECL_INLINED_FNS (fn) = ifn;
}
+#ifdef ENABLE_CHECKING
+ {
+ struct cgraph_edge *e;
+
+ verify_cgraph_node (id.node);
+
+ /* Double check that we inlined everything we are supposed to inline. */
+ for (e = id.node->callees; e; e = e->next_callee)
+ if (!e->inline_failed)
+ abort ();
+ }
+#endif
}
/* FN is a function that has a complete body, and CLONE is a function
@@ -1698,6 +1771,42 @@ clone_body (tree clone, tree fn, void *arg_map)
TREE_CHAIN (DECL_SAVED_TREE (clone)) = copy_body (&id);
}
+/* Save duplicate of body in FN. MAP is used to pass around splay tree
+ used to update arguments in restore_body. */
+tree
+save_body (tree fn, tree *arg_copy)
+{
+ inline_data id;
+ tree body, *parg;
+
+ memset (&id, 0, sizeof (id));
+ VARRAY_TREE_INIT (id.fns, 1, "fns");
+ VARRAY_PUSH_TREE (id.fns, 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;
+ }
+ insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn));
+
+ /* Actually copy the body. */
+ body = copy_body (&id);
+ if (lang_hooks.update_decl_after_saving)
+ lang_hooks.update_decl_after_saving (fn, id.decl_map);
+
+ /* Clean up. */
+ splay_tree_delete (id.decl_map);
+ return body;
+}
+
/* Apply FUNC to all the sub-trees of TP in a pre-order traversal.
FUNC is called with the DATA and the address of each sub-tree. If
FUNC returns a non-NULL value, the traversal is aborted, and the