/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "insn-config.h"
#include "varray.h"
#include "hashtab.h"
-#include "splay-tree.h"
#include "langhooks.h"
#include "basic-block.h"
#include "tree-iterator.h"
#include "except.h"
#include "debug.h"
#include "pointer-set.h"
-#include "integrate.h"
+#include "ipa-prop.h"
/* I'm not real happy about this, but we need to handle gimple and
non-gimple trees. */
#include "tree-gimple.h"
-/* Inlining, Saving, Cloning
+/* Inlining, Cloning, Versioning, Parallelization
Inlining: a function body is duplicated, but the PARM_DECLs are
remapped into VAR_DECLs, and non-void RETURN_EXPRs become
to the info for the caller; the eh_region info in copied throwing
statements and RESX_EXPRs is adjusted accordingly.
- Saving: make a semantically-identical copy of the function body.
- Necessary when we want to generate code for the body (a destructive
- operation), but we expect to need this body in the future (e.g. for
- inlining into another function).
-
Cloning: (only in C++) We have one body for a con/de/structor, and
multiple function decls, each with a unique parameter list.
Duplicate the body, using the given splay tree; some parameters
will become constants (like 0 or 1).
+ Versioning: a function body is duplicated and the result is a new
+ function rather than into blocks of an existing function as with
+ inlining. Some parameters will become constants.
+
+ Parallelization: a region of a function is duplicated resulting in
+ a new function. Variables may be replaced with complex expressions
+ to enable shared variable semantics.
+
All of these will simultaneously lookup any callgraph edges. If
we're going to inline the duplicated function body, and the given
function has some cloned callgraph nodes (one for each place this
function will be inlined) those callgraph edges will be duplicated.
- If we're saving or cloning the body, those callgraph edges will be
+ If we're cloning the body, those callgraph edges will be
updated to point into the new body. (Note that the original
callgraph node and edge list will not be altered.)
o Provide heuristics to clamp inlining of recursive template
calls? */
-/* Data required for function inlining. */
-
-typedef struct inline_data
-{
- /* FUNCTION_DECL for function being inlined. */
- 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 *callee_cfun;
- /* The VAR_DECL for the return value. */
- tree retvar;
- /* The map from local declarations in the inlined function to
- equivalents in the function into which it is being inlined. */
- splay_tree decl_map;
- /* We use the same mechanism to build clones that we do to perform
- inlining. However, there are a few places where we need to
- 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;
- /* Callgraph node of function we are inlining into. */
- struct cgraph_node *node;
- /* Callgraph node of currently inlined function. */
- struct cgraph_node *current_node;
- /* Current BLOCK. */
- tree block;
- /* Exception region the inlined call lie in. */
- int eh_region;
- /* Take region number in the function being copied, add this value and
- get eh region number of the duplicate in the function we inline into. */
- int eh_region_offset;
-} inline_data;
-
/* Prototypes. */
-static tree declare_return_variable (inline_data *, tree, tree, tree *);
-static tree copy_body_r (tree *, int *, void *);
-static tree copy_generic_body (inline_data *);
+static tree declare_return_variable (copy_body_data *, tree, tree, tree *);
+static tree copy_generic_body (copy_body_data *);
static bool inlinable_function_p (tree);
-static tree remap_decl (tree, inline_data *);
-static tree remap_type (tree, inline_data *);
-static void remap_block (tree *, inline_data *);
-static tree remap_decl (tree, inline_data *);
-static tree remap_decls (tree, inline_data *);
-static void copy_bind_expr (tree *, int *, inline_data *);
+static void remap_block (tree *, copy_body_data *);
+static tree remap_decls (tree, copy_body_data *);
+static void copy_bind_expr (tree *, int *, copy_body_data *);
static tree mark_local_for_remap_r (tree *, int *, void *);
static void unsave_expr_1 (tree);
static tree unsave_r (tree *, int *, void *);
static void declare_inline_vars (tree, tree);
static void remap_save_expr (tree *, void *, int *);
-
-static inline bool inlining_p (inline_data *id);
+static void add_lexical_block (tree current_block, tree new_block);
+static tree copy_decl_to_var (tree, copy_body_data *);
+static tree copy_decl_no_change (tree, copy_body_data *);
+static tree copy_decl_maybe_to_var (tree, copy_body_data *);
/* Insert a tree->tree mapping for ID. Despite the name suggests
that the trees should be variables, it is used for more than that. */
-static void
-insert_decl_map (inline_data *id, tree key, tree value)
+void
+insert_decl_map (copy_body_data *id, tree key, tree value)
{
splay_tree_insert (id->decl_map, (splay_tree_key) key,
(splay_tree_value) value);
/* Remap DECL during the copying of the BLOCK tree for the function. */
-static tree
-remap_decl (tree decl, inline_data *id)
+tree
+remap_decl (tree decl, copy_body_data *id)
{
splay_tree_node n;
tree fn;
/* We only remap local variables in the current function. */
- fn = id->callee;
+ fn = id->src_fn;
/* See if we have remapped this declaration. */
if (!n)
{
/* Make a copy of the variable or label. */
- tree t;
- t = copy_decl_for_inlining (decl, fn, id->caller);
-
+ tree t = id->copy_decl (decl, id);
+
/* Remember it, so that if we encounter this local entity again
we can reuse this copy. Do this early because remap_type may
need this decl for TYPE_STUB_DECL. */
insert_decl_map (id, decl, t);
+ if (!DECL_P (t))
+ return t;
+
/* Remap types, if necessary. */
TREE_TYPE (t) = remap_type (TREE_TYPE (t), id);
if (TREE_CODE (t) == TYPE_DECL)
DECL_ORIGINAL_TYPE (t) = remap_type (DECL_ORIGINAL_TYPE (t), id);
- else if (TREE_CODE (t) == PARM_DECL)
- DECL_ARG_TYPE_AS_WRITTEN (t)
- = remap_type (DECL_ARG_TYPE_AS_WRITTEN (t), id);
/* Remap sizes as necessary. */
walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL);
walk_tree (&DECL_QUALIFIER (t), copy_body_r, id, NULL);
}
-#if 0
- /* FIXME handle anon aggrs. */
- if (! DECL_NAME (t) && TREE_TYPE (t)
- && lang_hooks.tree_inlining.anon_aggr_type_p (TREE_TYPE (t)))
- {
- /* For a VAR_DECL of anonymous type, we must also copy the
- member VAR_DECLS here and rechain the DECL_ANON_UNION_ELEMS. */
- tree members = NULL;
- tree src;
-
- for (src = DECL_ANON_UNION_ELEMS (t); src;
- src = TREE_CHAIN (src))
- {
- tree member = remap_decl (TREE_VALUE (src), id);
-
- gcc_assert (!TREE_PURPOSE (src));
- members = tree_cons (NULL, member, members);
- }
- DECL_ANON_UNION_ELEMS (t) = nreverse (members);
- }
-#endif
-
- /* If we are inlining and this is a variable (not a label), declare the
- remapped variable in the callers' body. */
- if (inlining_p (id)
- && (TREE_CODE (t) == VAR_DECL
- || TREE_CODE (t) == PARM_DECL))
- declare_inline_vars (id->block, t);
-
- /* Remember it, so that if we encounter this local entity
- again we can reuse this copy. */
- insert_decl_map (id, decl, t);
return t;
}
}
static tree
-remap_type (tree type, inline_data *id)
+remap_type_1 (tree type, copy_body_data *id)
{
splay_tree_node node;
tree new, t;
return (tree) node->value;
/* The type only needs remapping if it's variably modified. */
- if (! variably_modified_type_p (type, id->callee))
+ if (! variably_modified_type_p (type, id->src_fn))
{
insert_decl_map (id, type, type);
return type;
case REAL_TYPE:
case ENUMERAL_TYPE:
case BOOLEAN_TYPE:
- case CHAR_TYPE:
t = TYPE_MIN_VALUE (new);
if (t && TREE_CODE (t) != INTEGER_CST)
walk_tree (&TYPE_MIN_VALUE (new), copy_body_r, id, NULL);
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
- walk_tree (&TYPE_FIELDS (new), copy_body_r, id, NULL);
+ {
+ tree f, nf = NULL;
+
+ for (f = TYPE_FIELDS (new); f ; f = TREE_CHAIN (f))
+ {
+ t = remap_decl (f, id);
+ DECL_CONTEXT (t) = new;
+ TREE_CHAIN (t) = nf;
+ nf = t;
+ }
+ TYPE_FIELDS (new) = nreverse (nf);
+ }
break;
case OFFSET_TYPE:
return new;
}
+tree
+remap_type (tree type, copy_body_data *id)
+{
+ splay_tree_node node;
+
+ if (type == NULL)
+ return type;
+
+ /* See if we have remapped this type. */
+ node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
+ if (node)
+ return (tree) node->value;
+
+ /* The type only needs remapping if it's variably modified. */
+ if (! variably_modified_type_p (type, id->src_fn))
+ {
+ insert_decl_map (id, type, type);
+ return type;
+ }
+
+ return remap_type_1 (type, id);
+}
+
static tree
-remap_decls (tree decls, inline_data *id)
+remap_decls (tree decls, copy_body_data *id)
{
tree old_var;
tree new_decls = NULL_TREE;
{
tree new_var;
+ /* We can not chain the local static declarations into the unexpanded_var_list
+ as we can't duplicate them or break one decl rule. Go ahead and link
+ them into unexpanded_var_list. */
+ if (!lang_hooks.tree_inlining.auto_var_in_fn_p (old_var, id->src_fn)
+ && !DECL_EXTERNAL (old_var))
+ {
+ cfun->unexpanded_var_list = tree_cons (NULL_TREE, old_var,
+ cfun->unexpanded_var_list);
+ continue;
+ }
+
/* Remap the variable. */
new_var = remap_decl (old_var, id);
therein. And hook the new block into the block-tree. */
static void
-remap_block (tree *block, inline_data *id)
+remap_block (tree *block, copy_body_data *id)
{
tree old_block;
tree new_block;
new_block = make_node (BLOCK);
TREE_USED (new_block) = TREE_USED (old_block);
BLOCK_ABSTRACT_ORIGIN (new_block) = old_block;
+ BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block);
*block = new_block;
/* Remap its variables. */
BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id);
- fn = id->caller;
-#if 1
- /* FIXME! It shouldn't be so hard to manage blocks. Rebuilding them in
- rest_of_compilation is a good start. */
- if (id->cloning_p)
- /* We're building a clone; DECL_INITIAL is still
- error_mark_node, and current_binding_level is the parm
- binding level. */
+ fn = id->dst_fn;
+
+ if (id->transform_lang_insert_block)
lang_hooks.decls.insert_block (new_block);
- else
- {
- /* Attach this new block after the DECL_INITIAL block for the
- function into which this block is being inlined. In
- rest_of_compilation we will straighten out the BLOCK tree. */
- tree *first_block;
- if (DECL_INITIAL (fn))
- first_block = &BLOCK_CHAIN (DECL_INITIAL (fn));
- else
- first_block = &DECL_INITIAL (fn);
- BLOCK_CHAIN (new_block) = *first_block;
- *first_block = new_block;
- }
-#endif
+
/* Remember the remapped block. */
insert_decl_map (id, old_block, new_block);
}
+/* Copy the whole block tree and root it in id->block. */
+static tree
+remap_blocks (tree block, copy_body_data *id)
+{
+ tree t;
+ tree new = block;
+
+ if (!block)
+ return NULL;
+
+ remap_block (&new, id);
+ gcc_assert (new != block);
+ for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t))
+ add_lexical_block (new, remap_blocks (t, id));
+ return new;
+}
+
static void
copy_statement_list (tree *tp)
{
}
static void
-copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id)
+copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id)
{
tree block = BIND_EXPR_BLOCK (*tp);
/* Copy (and replace) the statement. */
}
/* Called from copy_body_id via walk_tree. DATA is really an
- `inline_data *'. */
+ `copy_body_data *'. */
-static tree
+tree
copy_body_r (tree *tp, int *walk_subtrees, void *data)
{
- inline_data *id = (inline_data *) data;
- tree fn = id->callee;
+ copy_body_data *id = (copy_body_data *) data;
+ tree fn = id->src_fn;
+ tree new_block;
/* Begin by recognizing trees that we'll completely rewrite for the
inlining context. Our output for these trees is completely
into an edge). Further down, we'll handle trees that get
duplicated and/or tweaked. */
- /* If this is a RETURN_STMT, change it into an EXPR_STMT and a
- GOTO_STMT with the RET_LABEL as its target. */
- if (TREE_CODE (*tp) == RETURN_EXPR && inlining_p (id))
+ /* When requested, RETURN_EXPRs should be transformed to just the
+ contained MODIFY_EXPR. The branch semantics of the return will
+ be handled elsewhere by manipulating the CFG rather than a statement. */
+ if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify)
{
tree assignment = TREE_OPERAND (*tp, 0);
else /* Else the RETURN_EXPR returns no value. */
{
*tp = NULL;
- return (void *)1;
+ return (tree) (void *)1;
}
}
copy_statement_list (tp);
else if (TREE_CODE (*tp) == SAVE_EXPR)
remap_save_expr (tp, id->decl_map, walk_subtrees);
- else if (TREE_CODE (*tp) == LABEL_DECL)
+ else if (TREE_CODE (*tp) == LABEL_DECL
+ && (! DECL_CONTEXT (*tp)
+ || decl_function_context (*tp) == id->src_fn))
/* These may need to be remapped for EH handling. */
- remap_decl (*tp, id);
+ *tp = remap_decl (*tp, id);
else if (TREE_CODE (*tp) == BIND_EXPR)
copy_bind_expr (tp, walk_subtrees, id);
/* Types may need remapping as well. */
n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
if (n)
{
+ tree new;
+ tree old;
/* If we happen to get an ADDR_EXPR in n->value, strip
it manually here as we'll eventually get ADDR_EXPRs
which lie about their types pointed to. In this case
build_fold_indirect_ref wouldn't strip the INDIRECT_REF,
- but we absolutely rely on that. */
- if (TREE_CODE ((tree)n->value) == ADDR_EXPR)
- *tp = TREE_OPERAND ((tree)n->value, 0);
- else
- *tp = build1 (INDIRECT_REF,
- TREE_TYPE (TREE_TYPE ((tree)n->value)),
- (tree)n->value);
+ but we absolutely rely on that. As fold_indirect_ref
+ does other useful transformations, try that first, though. */
+ tree type = TREE_TYPE (TREE_TYPE ((tree)n->value));
+ new = unshare_expr ((tree)n->value);
+ old = *tp;
+ *tp = fold_indirect_ref_1 (type, new);
+ if (! *tp)
+ {
+ if (TREE_CODE (new) == ADDR_EXPR)
+ *tp = TREE_OPERAND (new, 0);
+ else
+ {
+ *tp = build1 (INDIRECT_REF, type, new);
+ TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old);
+ }
+ }
*walk_subtrees = 0;
return NULL;
}
/* Here is the "usual case". Copy this tree node, and then
tweak some special cases. */
copy_tree_r (tp, walk_subtrees, NULL);
- if (id->block
- && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (*tp))))
- TREE_BLOCK (*tp) = id->block;
+
+ /* If EXPR has block defined, map it to newly constructed block.
+ When inlining we want EXPRs without block appear in the block
+ of function call. */
+ if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (*tp))))
+ {
+ new_block = id->block;
+ if (TREE_BLOCK (*tp))
+ {
+ splay_tree_node n;
+ n = splay_tree_lookup (id->decl_map,
+ (splay_tree_key) TREE_BLOCK (*tp));
+ gcc_assert (n);
+ new_block = (tree) n->value;
+ }
+ TREE_BLOCK (*tp) = new_block;
+ }
if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset)
TREE_OPERAND (*tp, 0) =
else if (TREE_CODE (*tp) == ADDR_EXPR)
{
walk_tree (&TREE_OPERAND (*tp, 0), copy_body_r, id, NULL);
- recompute_tree_invarant_for_addr_expr (*tp);
+ /* Handle the case where we substituted an INDIRECT_REF
+ into the operand of the ADDR_EXPR. */
+ if (TREE_CODE (TREE_OPERAND (*tp, 0)) == INDIRECT_REF)
+ *tp = TREE_OPERAND (TREE_OPERAND (*tp, 0), 0);
+ else
+ recompute_tree_invariant_for_addr_expr (*tp);
*walk_subtrees = 0;
}
}
later */
static basic_block
-copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
+copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, int count_scale)
{
block_stmt_iterator bsi, copy_bsi;
basic_block copy_basic_block;
/* create_basic_block() will append every new block to
basic_block_info automatically. */
- copy_basic_block = create_basic_block (NULL, (void *) 0, bb->prev_bb->aux);
+ copy_basic_block = create_basic_block (NULL, (void *) 0,
+ (basic_block) bb->prev_bb->aux);
copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE;
copy_basic_block->frequency = (bb->frequency
* frequency_scale / REG_BR_PROB_BASE);
if (stmt)
{
tree call, decl;
+
+ /* With return slot optimization we can end up with
+ non-gimple (foo *)&this->m, fix that here. */
+ if (TREE_CODE (stmt) == MODIFY_EXPR
+ && TREE_CODE (TREE_OPERAND (stmt, 1)) == NOP_EXPR
+ && !is_gimple_val (TREE_OPERAND (TREE_OPERAND (stmt, 1), 0)))
+ gimplify_stmt (&stmt);
+
bsi_insert_after (©_bsi, stmt, BSI_NEW_STMT);
call = get_call_expr_in (stmt);
/* We're duplicating a CALL_EXPR. Find any corresponding
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;
+
+ switch (id->transform_call_graph_edges)
{
- 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;
+ case CB_CGE_DUPLICATE:
+ edge = cgraph_edge (id->src_node, orig_stmt);
+ if (edge)
+ cgraph_clone_edge (edge, id->dst_node, stmt,
+ REG_BR_PROB_BASE, 1, true);
+ break;
+
+ case CB_CGE_MOVE_CLONES:
+ for (node = id->dst_node->next_clone;
node;
node = node->next_clone)
{
gcc_assert (edge);
edge->call_stmt = stmt;
}
- }
- else
- {
- struct cgraph_edge *edge;
+ /* FALLTHRU */
- /* We're cloning or inlining this body; duplicate the
- associate callgraph nodes. */
- edge = cgraph_edge (id->current_node, orig_stmt);
+ case CB_CGE_MOVE:
+ edge = cgraph_edge (id->dst_node, orig_stmt);
if (edge)
- cgraph_clone_edge (edge, id->node, stmt,
- REG_BR_PROB_BASE, 1);
+ edge->call_stmt = stmt;
+ break;
+
+ default:
+ gcc_unreachable ();
}
}
/* If you think we can abort here, you are wrong.
There is no region 0 in tree land. */
- gcc_assert (lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt)
+ gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt)
!= 0);
if (tree_could_throw_p (stmt))
{
- int region = lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt);
+ int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt);
/* Add an entry for the copied tree in the EH hashtable.
- When saving or cloning or versioning, use the hashtable in
+ When cloning or versioning, use the hashtable in
cfun, and just copy the EH number. When inlining, use the
hashtable in the caller, and adjust the region number. */
if (region > 0)
and there is a "current region,"
then associate this tree with the current region
and add edges associated with this region. */
- if ((lookup_stmt_eh_region_fn (id->callee_cfun,
+ if ((lookup_stmt_eh_region_fn (id->src_cfun,
orig_stmt) <= 0
&& id->eh_region > 0)
&& tree_could_throw_p (stmt))
static void
copy_edges_for_bb (basic_block bb, int count_scale)
{
- basic_block new_bb = bb->aux;
+ basic_block new_bb = (basic_block) bb->aux;
edge_iterator ei;
edge old_edge;
block_stmt_iterator bsi;
if (old_edge->dest->index == EXIT_BLOCK && !old_edge->flags
&& old_edge->dest->aux != EXIT_BLOCK_PTR)
flags |= EDGE_FALLTHRU;
- new = make_edge (new_bb, old_edge->dest->aux, flags);
+ new = make_edge (new_bb, (basic_block) old_edge->dest->aux, flags);
new->count = old_edge->count * count_scale / REG_BR_PROB_BASE;
new->probability = old_edge->probability;
}
static tree
remap_decl_1 (tree decl, void *data)
{
- return remap_decl (decl, data);
+ return remap_decl (decl, (copy_body_data *) data);
}
/* Make a copy of the body of FN so that it can be inserted inline in
another function. Walks FN via CFG, returns new fndecl. */
static tree
-copy_cfg_body (inline_data * id, gcov_type count, int frequency,
+copy_cfg_body (copy_body_data * id, gcov_type count, int frequency,
basic_block entry_block_map, basic_block exit_block_map)
{
- tree callee_fndecl = id->callee;
+ tree callee_fndecl = id->src_fn;
/* Original cfun for the callee, doesn't change. */
- struct function *callee_cfun = DECL_STRUCT_FUNCTION (callee_fndecl);
+ struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl);
/* Copy, built by this function. */
struct function *new_cfun;
/* Place to copy from; when a copy of the function was saved off earlier,
(struct function *) ggc_alloc_cleared (sizeof (struct function));
basic_block bb;
tree new_fndecl = NULL;
- bool saving_or_cloning;
int count_scale, frequency_scale;
- if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count)
+ if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count)
count_scale = (REG_BR_PROB_BASE * count
- / ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count);
+ / ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count);
else
count_scale = 1;
- if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency)
+ if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency)
frequency_scale = (REG_BR_PROB_BASE * frequency
/
- ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency);
+ ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency);
else
frequency_scale = count_scale;
*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);
- if (saving_or_cloning)
+ id->src_cfun = cfun_to_copy;
+
+ /* If requested, create new basic_block_info and label_to_block_maps.
+ Otherwise, insert our new blocks and labels into the existing cfg. */
+ if (id->transform_new_cfg)
{
new_cfun =
(struct function *) ggc_alloc_cleared (sizeof (struct function));
*new_cfun = *DECL_STRUCT_FUNCTION (callee_fndecl);
new_cfun->cfg = NULL;
new_cfun->decl = new_fndecl = copy_node (callee_fndecl);
- new_cfun->ib_boundaries_block = (varray_type) 0;
+ new_cfun->ib_boundaries_block = NULL;
DECL_STRUCT_FUNCTION (new_fndecl) = new_cfun;
push_cfun (new_cfun);
init_empty_tree_cfg ();
ENTRY_BLOCK_PTR->count =
- (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale /
+ (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale /
REG_BR_PROB_BASE);
ENTRY_BLOCK_PTR->frequency =
- (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+ (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency *
frequency_scale / REG_BR_PROB_BASE);
EXIT_BLOCK_PTR->count =
- (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale /
+ (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale /
REG_BR_PROB_BASE);
EXIT_BLOCK_PTR->frequency =
- (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+ (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency *
frequency_scale / REG_BR_PROB_BASE);
entry_block_map = ENTRY_BLOCK_PTR;
ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = entry_block_map;
EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = exit_block_map;
-
/* Duplicate any exception-handling regions. */
if (cfun->eh)
{
- if (saving_or_cloning)
+ if (id->transform_new_cfg)
init_eh_for_function ();
- id->eh_region_offset = duplicate_eh_regions (cfun_to_copy,
- remap_decl_1,
- id, id->eh_region);
- gcc_assert (inlining_p (id) || !id->eh_region_offset);
+ id->eh_region_offset
+ = duplicate_eh_regions (cfun_to_copy, remap_decl_1, id,
+ 0, id->eh_region);
}
/* Use aux pointers to map the original blocks to copy. */
FOR_EACH_BB_FN (bb, cfun_to_copy)
FOR_ALL_BB_FN (bb, cfun_to_copy)
bb->aux = NULL;
- if (saving_or_cloning)
+ if (id->transform_new_cfg)
pop_cfun ();
return new_fndecl;
another function. */
static tree
-copy_generic_body (inline_data *id)
+copy_generic_body (copy_body_data *id)
{
tree body;
- tree fndecl = id->callee;
+ tree fndecl = id->src_fn;
body = DECL_SAVED_TREE (fndecl);
walk_tree (&body, copy_body_r, id, NULL);
}
static tree
-copy_body (inline_data *id, gcov_type count, int frequency,
+copy_body (copy_body_data *id, gcov_type count, int frequency,
basic_block entry_block_map, basic_block exit_block_map)
{
- tree fndecl = id->callee;
+ tree fndecl = id->src_fn;
tree body;
/* If this body has a CFG, walk CFG and copy. */
}
static void
-setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
+setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
basic_block bb, tree *vars)
{
tree init_stmt;
/* Make an equivalent VAR_DECL. Note that we must NOT remap the type
here since the type of this decl must be visible to the calling
function. */
- var = copy_decl_for_inlining (p, fn, id->caller);
+ var = copy_decl_to_var (p, id);
/* See if the frontend wants to pass this by invisible reference. If
so, our new VAR_DECL will have REFERENCE_TYPE, and we need to
if (rhs == error_mark_node)
return;
+
+ STRIP_USELESS_TYPE_CONVERSION (rhs);
/* We want to use MODIFY_EXPR, not INIT_EXPR here so that we
keep our trees in gimple form. */
- init_stmt = build (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
+ init_stmt = build2 (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
/* If we did not create a gimple value and we did not create a gimple
cast of a gimple value, then we will need to gimplify INIT_STMTS
&& (!is_gimple_cast (rhs)
|| !is_gimple_val (TREE_OPERAND (rhs, 0))))
gimplify_stmt (&init_stmt);
- bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT);
+
+ /* If VAR represents a zero-sized variable, it's possible that the
+ assignment statment may result in no gimple statements. */
+ if (init_stmt)
+ bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT);
}
}
top of the stack in ID from the ARGS (presented as a TREE_LIST). */
static void
-initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
+initialize_inlined_parameters (copy_body_data *id, tree args, tree static_chain,
tree fn, basic_block bb)
{
tree parms;
/* 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. */
holds the result as seen by the caller. */
static tree
-declare_return_variable (inline_data *id, tree return_slot_addr,
+declare_return_variable (copy_body_data *id, tree return_slot_addr,
tree modify_dest, tree *use_p)
{
- tree callee = id->callee;
- tree caller = id->caller;
+ tree callee = id->src_fn;
+ tree caller = id->dst_fn;
tree result = DECL_RESULT (callee);
tree callee_type = TREE_TYPE (result);
tree caller_type = TREE_TYPE (TREE_TYPE (callee));
var = return_slot_addr;
else
var = build_fold_indirect_ref (return_slot_addr);
+ if (TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE
+ && !DECL_COMPLEX_GIMPLE_REG_P (result)
+ && DECL_P (var))
+ DECL_COMPLEX_GIMPLE_REG_P (var) = 0;
use = NULL;
goto done;
}
/* If the callee cannot possibly modify MODIFY_DEST, then we can
reuse it as the result of the call directly. Don't do this if
it would promote MODIFY_DEST to addressable. */
- else if (!TREE_STATIC (modify_dest)
- && !TREE_ADDRESSABLE (modify_dest)
- && !TREE_ADDRESSABLE (result))
- use_it = true;
+ else if (TREE_ADDRESSABLE (result))
+ use_it = false;
+ else
+ {
+ tree base_m = get_base_address (modify_dest);
+
+ /* If the base isn't a decl, then it's a pointer, and we don't
+ know where that's going to go. */
+ if (!DECL_P (base_m))
+ use_it = false;
+ else if (is_global_var (base_m))
+ use_it = false;
+ else if (TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE
+ && !DECL_COMPLEX_GIMPLE_REG_P (result)
+ && DECL_COMPLEX_GIMPLE_REG_P (base_m))
+ use_it = false;
+ else if (!TREE_ADDRESSABLE (base_m))
+ use_it = true;
+ }
if (use_it)
{
gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
- var = copy_decl_for_inlining (result, callee, caller);
+ var = copy_decl_to_var (result, id);
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
use = var;
if (!lang_hooks.types_compatible_p (TREE_TYPE (var), caller_type))
use = fold_convert (caller_type, var);
+
+ STRIP_USELESS_TYPE_CONVERSION (use);
done:
/* Register the VAR_DECL as the equivalent for the RESULT_DECL; that
&& !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because it uses "
+ = G_("function %q+F can never be inlined because it uses "
"alloca (override using the always_inline attribute)");
return node;
}
if (setjmp_call_p (t))
{
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because it uses setjmp");
+ = G_("function %q+F can never be inlined because it uses setjmp");
return node;
}
case BUILT_IN_NEXT_ARG:
case BUILT_IN_VA_END:
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because it "
+ = G_("function %q+F can never be inlined because it "
"uses variable argument lists");
return node;
function calling __builtin_longjmp to be inlined into the
function calling __builtin_setjmp, Things will Go Awry. */
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because "
+ = G_("function %q+F can never be inlined because "
"it uses setjmp-longjmp exception handling");
return node;
case BUILT_IN_NONLOCAL_GOTO:
/* Similarly. */
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because "
+ = G_("function %q+F can never be inlined because "
"it uses non-local goto");
return node;
been inlined into. Similarly __builtin_return would
return from the function the inline has been inlined into. */
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined because "
+ = G_("function %q+F can never be inlined because "
"it uses __builtin_return or __builtin_apply_args");
return node;
if (TREE_CODE (t) != LABEL_DECL)
{
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined "
+ = G_("function %q+F can never be inlined "
"because it contains a computed goto");
return node;
}
because we cannot remap the destination label used in the
function that is performing the non-local goto. */
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined "
+ = G_("function %q+F can never be inlined "
"because it receives a non-local goto");
return node;
}
if (variably_modified_type_p (TREE_TYPE (t), NULL))
{
inline_forbidden_reason
- = N_("%Jfunction %qF can never be inlined "
+ = G_("function %q+F can never be inlined "
"because it uses variable sized variables");
return node;
}
&& !DECL_IN_SYSTEM_HEADER (fn));
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
- sorry (inline_forbidden_reason, fn, fn);
+ sorry (inline_forbidden_reason, fn);
else if (do_warning)
- warning (0, inline_forbidden_reason, fn, fn);
+ warning (OPT_Winline, inline_forbidden_reason, fn);
inlinable = false;
}
static tree
estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
{
- int *count = data;
+ int *count = (int *) data;
tree x = *tp;
if (IS_TYPE_OR_DECL_P (x))
case LOOP_EXPR:
case PHI_NODE:
case WITH_SIZE_EXPR:
+ case OMP_CLAUSE:
+ case OMP_RETURN:
+ case OMP_CONTINUE:
break;
/* We don't account constants for now. Assume that the cost is amortized
case RSHIFT_EXPR:
case LROTATE_EXPR:
case RROTATE_EXPR:
+ case VEC_LSHIFT_EXPR:
+ case VEC_RSHIFT_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
case REALIGN_LOAD_EXPR:
+ case REDUC_MAX_EXPR:
+ case REDUC_MIN_EXPR:
+ case REDUC_PLUS_EXPR:
+ case WIDEN_SUM_EXPR:
+ case DOT_PROD_EXPR:
+
+ case WIDEN_MULT_EXPR:
+
case RESX_EXPR:
*count += 1;
break;
*count += PARAM_VALUE (PARAM_INLINE_CALL_COST);
break;
}
+
+ case OMP_PARALLEL:
+ case OMP_FOR:
+ case OMP_SECTIONS:
+ case OMP_SINGLE:
+ case OMP_SECTION:
+ case OMP_MASTER:
+ case OMP_ORDERED:
+ case OMP_CRITICAL:
+ case OMP_ATOMIC:
+ /* OpenMP directives are generally very expensive. */
+ *count += 40;
+ break;
+
default:
gcc_unreachable ();
}
return num;
}
+typedef struct function *function_p;
+
+DEF_VEC_P(function_p);
+DEF_VEC_ALLOC_P(function_p,heap);
+
/* Initialized with NOGC, making this poisonous to the garbage collector. */
-static varray_type cfun_stack;
+static VEC(function_p,heap) *cfun_stack;
void
push_cfun (struct function *new_cfun)
{
- static bool initialized = false;
-
- if (!initialized)
- {
- VARRAY_GENERIC_PTR_NOGC_INIT (cfun_stack, 20, "cfun_stack");
- initialized = true;
- }
- VARRAY_PUSH_GENERIC_PTR (cfun_stack, cfun);
+ VEC_safe_push (function_p, heap, cfun_stack, cfun);
cfun = new_cfun;
}
void
pop_cfun (void)
{
- cfun = (struct function *)VARRAY_TOP_GENERIC_PTR (cfun_stack);
- VARRAY_POP (cfun_stack);
+ cfun = VEC_pop (function_p, cfun_stack);
}
/* Install new lexical TREE_BLOCK underneath 'current_block'. */
;
*blk_p = new_block;
BLOCK_SUPERCONTEXT (new_block) = current_block;
- BLOCK_SUBBLOCKS (new_block) = NULL_TREE;
}
/* If *TP is a CALL_EXPR, replace it with its inline expansion. */
static bool
expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
{
- inline_data *id;
+ copy_body_data *id;
tree t;
tree use_retvar;
tree fn;
bool successfully_inlined = FALSE;
tree t_step;
tree var;
- struct cgraph_node *old_node;
tree decl;
/* See what we've got. */
- id = (inline_data *) data;
+ id = (copy_body_data *) data;
t = *tp;
/* Set input_location here so we get the right instantiation context
/* Objective C and fortran still calls tree_rest_of_compilation directly.
Kill this check once this is fixed. */
- if (!id->current_node->analyzed)
+ if (!id->dst_node->analyzed)
goto egress;
- cg_edge = cgraph_edge (id->current_node, stmt);
+ cg_edge = cgraph_edge (id->dst_node, stmt);
/* Constant propagation on argument done during previous inlining
may create new direct call. Produce an edge for it. */
constant propagating arguments. In all other cases we hit a bug
(incorrect node sharing is most common reason for missing edges. */
gcc_assert (dest->needed || !flag_unit_at_a_time);
- cgraph_create_edge (id->node, dest, stmt,
+ cgraph_create_edge (id->dst_node, dest, stmt,
bb->count, bb->loop_depth)->inline_failed
= N_("originally indirect function call not considered for inlining");
goto egress;
inlining. */
if (!cgraph_inline_p (cg_edge, &reason))
{
- if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
+ if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
+ /* Avoid warnings during early inline pass. */
+ && (!flag_unit_at_a_time || cgraph_global_info_ready))
{
- sorry ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
+ sorry ("inlining failed in call to %q+F: %s", fn, reason);
sorry ("called from here");
}
else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
&& !DECL_IN_SYSTEM_HEADER (fn)
&& strlen (reason)
- && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)))
+ && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn))
+ /* Avoid warnings during early inline pass. */
+ && (!flag_unit_at_a_time || cgraph_global_info_ready))
{
- warning (0, "%Jinlining failed in call to %qF: %s", fn, fn, reason);
- warning (0, "called from here");
+ warning (OPT_Winline, "inlining failed in call to %q+F: %s",
+ fn, reason);
+ warning (OPT_Winline, "called from here");
}
goto egress;
}
+ fn = cg_edge->callee->decl;
#ifdef ENABLE_CHECKING
- if (cg_edge->callee->decl != id->node->decl)
+ if (cg_edge->callee->decl != id->dst_node->decl)
verify_cgraph_node (cg_edge->callee);
#endif
else
{
tree stmt = bsi_stmt (stmt_bsi);
- bsi_remove (&stmt_bsi);
+ bsi_remove (&stmt_bsi, false);
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
}
stmt_bsi = bsi_start (return_block);
statement expression is the return type of the function call. */
id->block = make_node (BLOCK);
BLOCK_ABSTRACT_ORIGIN (id->block) = fn;
+ BLOCK_SOURCE_LOCATION (id->block) = input_location;
add_lexical_block (TREE_BLOCK (stmt), id->block);
-
/* Local declarations will be replaced by their equivalents in this
map. */
st = id->decl_map;
/* Initialize the parameters. */
args = TREE_OPERAND (t, 1);
- if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (t))
- {
- return_slot_addr = TREE_VALUE (args);
- args = TREE_CHAIN (args);
- }
- else
- return_slot_addr = NULL_TREE;
+
+ /* Record the function we are about to inline. */
+ id->src_fn = fn;
+ id->src_node = cg_edge->callee;
initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
- /* Record the function we are about to inline. */
- id->callee = 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
to the RET_LABEL. */
gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
/* Find the lhs to which the result of this call is assigned. */
- modify_dest = stmt;
- if (TREE_CODE (modify_dest) == MODIFY_EXPR)
+ return_slot_addr = NULL;
+ if (TREE_CODE (stmt) == MODIFY_EXPR)
{
- modify_dest = TREE_OPERAND (modify_dest, 0);
+ modify_dest = TREE_OPERAND (stmt, 0);
/* The function which we are inlining might not return a value,
in which case we should issue a warning that the function
uninitialized variable. */
if (DECL_P (modify_dest))
TREE_NO_WARNING (modify_dest) = 1;
+ if (CALL_EXPR_RETURN_SLOT_OPT (t))
+ {
+ return_slot_addr = build_fold_addr_expr (modify_dest);
+ STRIP_USELESS_TYPE_CONVERSION (return_slot_addr);
+ modify_dest = NULL;
+ }
}
else
modify_dest = NULL;
if (decl && !return_slot_addr && decl != modify_dest)
declare_inline_vars (id->block, decl);
- /* After we've initialized the parameters, we insert the body of the
- function itself. */
- old_node = id->current_node;
-
- /* Anoint the callee-to-be-duplicated as the "current_node." When
- CALL_EXPRs within callee are duplicated, the edges from callee to
- callee's callees (caller's grandchildren) will be cloned. */
- id->current_node = cg_edge->callee;
-
/* This is it. Duplicate the callee body. Assume callee is
pre-gimplified. Note that we must not alter the caller
function in any way before this point, as this CALL_EXPR may be
a self-referential call; if we're calling ourselves, we need to
duplicate our body before altering anything. */
copy_body (id, bb->count, bb->frequency, bb, return_block);
- id->current_node = old_node;
+
+ /* Add local vars in this inlined callee to caller. */
+ t_step = id->src_cfun->unexpanded_var_list;
+ for (; t_step; t_step = TREE_CHAIN (t_step))
+ {
+ var = TREE_VALUE (t_step);
+ if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
+ cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
+ cfun->unexpanded_var_list);
+ else
+ cfun->unexpanded_var_list = tree_cons (NULL_TREE, remap_decl (var, id),
+ cfun->unexpanded_var_list);
+ }
/* Clean up. */
splay_tree_delete (id->decl_map);
else
/* We're modifying a TSI owned by gimple_expand_calls_inline();
tsi_delink() will leave the iterator in a sane state. */
- bsi_remove (&stmt_bsi);
+ bsi_remove (&stmt_bsi, true);
bsi_next (&bsi);
if (bsi_end_p (bsi))
/* Update callgraph if needed. */
cgraph_remove_node (cg_edge->callee);
- /* Declare the 'auto' variables added with this inlined body. */
- record_vars (BLOCK_VARS (id->block));
id->block = NULL_TREE;
-
- /* Add local static vars in this inlined callee to caller. */
- for (t_step = id->callee_cfun->unexpanded_var_list;
- t_step;
- t_step = TREE_CHAIN (t_step))
- {
- var = TREE_VALUE (t_step);
- if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
- record_vars (var);
- }
successfully_inlined = TRUE;
egress:
to the CALL_EXPR, not the tree itself. */
static bool
-gimple_expand_calls_inline (basic_block bb, inline_data *id)
+gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
{
block_stmt_iterator bsi;
void
optimize_inline_calls (tree fn)
{
- inline_data id;
+ copy_body_data id;
tree prev_fn;
basic_block bb;
/* There is no point in performing inlining if errors have already
/* Clear out ID. */
memset (&id, 0, sizeof (id));
- id.current_node = id.node = cgraph_node (fn);
- id.caller = fn;
+ id.src_node = id.dst_node = cgraph_node (fn);
+ id.dst_fn = fn;
/* Or any functions that aren't finished yet. */
prev_fn = NULL_TREE;
if (current_function_decl)
{
- id.caller = current_function_decl;
+ id.dst_fn = current_function_decl;
prev_fn = current_function_decl;
}
+
+ id.copy_decl = copy_decl_maybe_to_var;
+ id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ id.transform_new_cfg = false;
+ id.transform_return_to_modify = true;
+ id.transform_lang_insert_block = false;
+
push_gimplify_context ();
/* Reach the trees by walking over the CFG, and note the
FOR_EACH_BB (bb)
gimple_expand_calls_inline (bb, &id);
-
pop_gimplify_context (NULL);
/* Renumber the (code) basic_blocks consecutively. */
compact_blocks ();
{
struct cgraph_edge *e;
- verify_cgraph_node (id.node);
+ verify_cgraph_node (id.dst_node);
/* Double check that we inlined everything we are supposed to inline. */
- for (e = id.node->callees; e; e = e->next_callee)
+ for (e = id.dst_node->callees; e; e = e->next_callee)
gcc_assert (e->inline_failed);
}
#endif
void
clone_body (tree clone, tree fn, void *arg_map)
{
- inline_data id;
+ copy_body_data id;
/* Clone the body, as if we were making an inline call. But, remap the
parameters in the callee to the parameters of caller. */
memset (&id, 0, sizeof (id));
- id.caller = clone;
- id.callee = fn;
- id.callee_cfun = DECL_STRUCT_FUNCTION (fn);
+ id.src_fn = fn;
+ id.dst_fn = clone;
+ id.src_cfun = DECL_STRUCT_FUNCTION (fn);
id.decl_map = (splay_tree)arg_map;
- /* Cloning is treated slightly differently from inlining. Set
- CLONING_P so that it's clear which operation we're performing. */
- id.cloning_p = true;
+ id.copy_decl = copy_decl_no_change;
+ id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ id.transform_new_cfg = true;
+ id.transform_return_to_modify = false;
+ id.transform_lang_insert_block = true;
/* We're not inside any EH region. */
id.eh_region = -1;
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;
-
- 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));
-
- /* 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
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
|| code == TREE_LIST
|| code == TREE_VEC
- || code == TYPE_DECL)
+ || code == TYPE_DECL
+ || code == OMP_CLAUSE)
{
/* Because the chain gets clobbered when we make a copy, we save it
here. */
/* Now, restore the chain, if appropriate. That will cause
walk_tree to walk into the chain as well. */
- if (code == PARM_DECL || code == TREE_LIST)
+ if (code == PARM_DECL
+ || code == TREE_LIST
+ || code == OMP_CLAUSE)
TREE_CHAIN (*tp) = chain;
/* For now, we don't update BLOCKs when we make copies. So, we
if (TREE_CODE (*tp) == BIND_EXPR)
BIND_EXPR_BLOCK (*tp) = NULL_TREE;
}
+ else if (code == CONSTRUCTOR)
+ {
+ /* CONSTRUCTOR nodes need special handling because
+ we need to duplicate the vector of elements. */
+ tree new;
+
+ new = copy_node (*tp);
+
+ /* Propagate mudflap marked-ness. */
+ if (flag_mudflap && mf_marked_p (*tp))
+ mf_mark (new);
+ CONSTRUCTOR_ELTS (new) = VEC_copy (constructor_elt, gc,
+ CONSTRUCTOR_ELTS (*tp));
+ *tp = new;
+ }
else if (TREE_CODE_CLASS (code) == tcc_type)
*walk_subtrees = 0;
else if (TREE_CODE_CLASS (code) == tcc_declaration)
/* Called via walk_tree. If *TP points to a DECL_STMT for a local label,
copies the declaration and enters it in the splay_tree in DATA (which is
- really an `inline_data *'). */
+ really an `copy_body_data *'). */
static tree
mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data)
{
- inline_data *id = (inline_data *) data;
+ copy_body_data *id = (copy_body_data *) data;
/* Don't walk into types. */
if (TYPE_P (*tp))
tree decl = TREE_OPERAND (*tp, 0);
/* Copy the decl and remember the copy. */
- insert_decl_map (id, decl,
- copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
- DECL_CONTEXT (decl)));
+ insert_decl_map (id, decl, id->copy_decl (decl, id));
}
return NULL_TREE;
static tree
unsave_r (tree *tp, int *walk_subtrees, void *data)
{
- inline_data *id = (inline_data *) data;
+ copy_body_data *id = (copy_body_data *) data;
splay_tree st = id->decl_map;
splay_tree_node n;
tree
unsave_expr_now (tree expr)
{
- inline_data id;
+ copy_body_data id;
/* There's nothing to do for NULL_TREE. */
if (expr == 0)
/* Set up ID. */
memset (&id, 0, sizeof (id));
- id.callee = current_function_decl;
- id.caller = current_function_decl;
+ id.src_fn = current_function_decl;
+ id.dst_fn = current_function_decl;
id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+ id.copy_decl = copy_decl_no_change;
+ id.transform_call_graph_edges = CB_CGE_DUPLICATE;
+ id.transform_new_cfg = false;
+ id.transform_return_to_modify = false;
+ id.transform_lang_insert_block = false;
+
/* Walk the tree once to find local labels. */
walk_tree_without_duplicates (&expr, mark_local_for_remap_r, &id);
{
tree t;
for (t = vars; t; t = TREE_CHAIN (t))
- DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
+ {
+ DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
+ gcc_assert (!TREE_STATIC (t) && !TREE_ASM_WRITTEN (t));
+ cfun->unexpanded_var_list =
+ tree_cons (NULL_TREE, t,
+ cfun->unexpanded_var_list);
+ }
if (block)
BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars);
}
-/* Returns true if we're inlining. */
-static inline bool
-inlining_p (inline_data *id)
+
+/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN,
+ but now it will be in the TO_FN. PARM_TO_VAR means enable PARM_DECL to
+ VAR_DECL translation. */
+
+static tree
+copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
+{
+ /* Don't generate debug information for the copy if we wouldn't have
+ generated it for the copy either. */
+ DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
+ DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
+
+ /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what
+ declaration inspired this copy. */
+ DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
+
+ /* The new variable/label has no RTL, yet. */
+ if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL)
+ && !TREE_STATIC (copy) && !DECL_EXTERNAL (copy))
+ SET_DECL_RTL (copy, NULL_RTX);
+
+ /* These args would always appear unused, if not for this. */
+ TREE_USED (copy) = 1;
+
+ /* Set the context for the new declaration. */
+ if (!DECL_CONTEXT (decl))
+ /* Globals stay global. */
+ ;
+ else if (DECL_CONTEXT (decl) != id->src_fn)
+ /* Things that weren't in the scope of the function we're inlining
+ from aren't in the scope we're inlining to, either. */
+ ;
+ else if (TREE_STATIC (decl))
+ /* Function-scoped static variables should stay in the original
+ function. */
+ ;
+ else
+ /* Ordinary automatic local variables are now in the scope of the
+ new function. */
+ DECL_CONTEXT (copy) = id->dst_fn;
+
+ return copy;
+}
+
+static tree
+copy_decl_to_var (tree decl, copy_body_data *id)
+{
+ tree copy, type;
+
+ gcc_assert (TREE_CODE (decl) == PARM_DECL
+ || TREE_CODE (decl) == RESULT_DECL);
+
+ type = TREE_TYPE (decl);
+
+ copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
+ TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
+ TREE_READONLY (copy) = TREE_READONLY (decl);
+ TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
+ DECL_COMPLEX_GIMPLE_REG_P (copy) = DECL_COMPLEX_GIMPLE_REG_P (decl);
+
+ return copy_decl_for_dup_finish (id, decl, copy);
+}
+
+static tree
+copy_decl_no_change (tree decl, copy_body_data *id)
+{
+ tree copy;
+
+ copy = copy_node (decl);
+
+ /* The COPY is not abstract; it will be generated in DST_FN. */
+ DECL_ABSTRACT (copy) = 0;
+ lang_hooks.dup_lang_specific_decl (copy);
+
+ /* TREE_ADDRESSABLE isn't used to indicate that a label's address has
+ been taken; it's for internal bookkeeping in expand_goto_internal. */
+ if (TREE_CODE (copy) == LABEL_DECL)
+ {
+ TREE_ADDRESSABLE (copy) = 0;
+ LABEL_DECL_UID (copy) = -1;
+ }
+
+ return copy_decl_for_dup_finish (id, decl, copy);
+}
+
+static tree
+copy_decl_maybe_to_var (tree decl, copy_body_data *id)
{
- return (!id->saving_p && !id->cloning_p);
+ if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL)
+ return copy_decl_to_var (decl, id);
+ else
+ return copy_decl_no_change (decl, id);
+}
+
+/* Return a copy of the function's argument tree. */
+static tree
+copy_arguments_for_versioning (tree orig_parm, copy_body_data * id)
+{
+ tree *arg_copy, *parg;
+
+ arg_copy = &orig_parm;
+ for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg))
+ {
+ tree new = remap_decl (*parg, id);
+ lang_hooks.dup_lang_specific_decl (new);
+ TREE_CHAIN (new) = TREE_CHAIN (*parg);
+ *parg = new;
+ }
+ return orig_parm;
+}
+
+/* Return a copy of the function's static chain. */
+static tree
+copy_static_chain (tree static_chain, copy_body_data * id)
+{
+ tree *chain_copy, *pvar;
+
+ chain_copy = &static_chain;
+ for (pvar = chain_copy; *pvar; pvar = &TREE_CHAIN (*pvar))
+ {
+ tree new = remap_decl (*pvar, id);
+ lang_hooks.dup_lang_specific_decl (new);
+ TREE_CHAIN (new) = TREE_CHAIN (*pvar);
+ *pvar = new;
+ }
+ return static_chain;
+}
+
+/* Return true if the function is allowed to be versioned.
+ This is a guard for the versioning functionality. */
+bool
+tree_versionable_function_p (tree fndecl)
+{
+ if (fndecl == NULL_TREE)
+ return false;
+ /* ??? There are cases where a function is
+ uninlinable but can be versioned. */
+ if (!tree_inlinable_function_p (fndecl))
+ return false;
+
+ return true;
+}
+
+/* Create a copy of a function's tree.
+ OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes
+ of the original function and the new copied function
+ 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. 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,
+ bool update_clones)
+{
+ struct cgraph_node *old_version_node;
+ struct cgraph_node *new_version_node;
+ copy_body_data id;
+ tree p, new_fndecl;
+ unsigned i;
+ struct ipa_replace_map *replace_info;
+ basic_block old_entry_block;
+ tree t_step;
+
+ gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL
+ && TREE_CODE (new_decl) == FUNCTION_DECL);
+ DECL_POSSIBLY_INLINED (old_decl) = 1;
+
+ old_version_node = cgraph_node (old_decl);
+ new_version_node = cgraph_node (new_decl);
+
+ allocate_struct_function (new_decl);
+ /* Cfun points to the new allocated function struct at this point. */
+ cfun->function_end_locus = DECL_SOURCE_LOCATION (new_decl);
+
+ DECL_ARTIFICIAL (new_decl) = 1;
+ DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
+
+ /* Generate a new name for the new version. */
+ 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)
+ {
+ SET_DECL_RTL (new_decl, copy_rtx (DECL_RTL (old_decl)));
+ XEXP (DECL_RTL (new_decl), 0) =
+ gen_rtx_SYMBOL_REF (GET_MODE (XEXP (DECL_RTL (old_decl), 0)),
+ IDENTIFIER_POINTER (DECL_NAME (new_decl)));
+ }
+
+ /* Prepare the data structures for the tree copy. */
+ memset (&id, 0, sizeof (id));
+
+ id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+ id.src_fn = old_decl;
+ id.dst_fn = new_decl;
+ id.src_node = old_version_node;
+ id.dst_node = new_version_node;
+ id.src_cfun = DECL_STRUCT_FUNCTION (old_decl);
+
+ id.copy_decl = copy_decl_no_change;
+ id.transform_call_graph_edges
+ = update_clones ? CB_CGE_MOVE_CLONES : CB_CGE_MOVE;
+ id.transform_new_cfg = true;
+ id.transform_return_to_modify = false;
+ id.transform_lang_insert_block = false;
+
+ current_function_decl = new_decl;
+
+ /* Copy the function's static chain. */
+ p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl;
+ if (p)
+ DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl =
+ copy_static_chain (DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl,
+ &id);
+ /* Copy the function's arguments. */
+ if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
+ DECL_ARGUMENTS (new_decl) =
+ copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id);
+
+ /* If there's a tree_map, prepare for substitution. */
+ if (tree_map)
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++)
+ {
+ replace_info = VARRAY_GENERIC_PTR (tree_map, i);
+ if (replace_info->replace_p)
+ insert_decl_map (&id, replace_info->old_tree,
+ replace_info->new_tree);
+ }
+
+ DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
+
+ /* Renumber the lexical scoping (non-code) blocks consecutively. */
+ number_blocks (id.dst_fn);
+
+ if (DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list != NULL_TREE)
+ /* Add local vars. */
+ for (t_step = DECL_STRUCT_FUNCTION (old_decl)->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->unexpanded_var_list = tree_cons (NULL_TREE, var,
+ cfun->unexpanded_var_list);
+ else
+ cfun->unexpanded_var_list =
+ tree_cons (NULL_TREE, remap_decl (var, &id),
+ cfun->unexpanded_var_list);
+ }
+
+ /* Copy the Function's body. */
+ old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION
+ (DECL_STRUCT_FUNCTION (old_decl));
+ new_fndecl = copy_body (&id,
+ old_entry_block->count,
+ old_entry_block->frequency, NULL, NULL);
+
+ DECL_SAVED_TREE (new_decl) = DECL_SAVED_TREE (new_fndecl);
+
+ DECL_STRUCT_FUNCTION (new_decl)->cfg =
+ DECL_STRUCT_FUNCTION (new_fndecl)->cfg;
+ DECL_STRUCT_FUNCTION (new_decl)->eh = DECL_STRUCT_FUNCTION (new_fndecl)->eh;
+ DECL_STRUCT_FUNCTION (new_decl)->ib_boundaries_block =
+ DECL_STRUCT_FUNCTION (new_fndecl)->ib_boundaries_block;
+ DECL_STRUCT_FUNCTION (new_decl)->last_label_uid =
+ DECL_STRUCT_FUNCTION (new_fndecl)->last_label_uid;
+
+ if (DECL_RESULT (old_decl) != NULL_TREE)
+ {
+ tree *res_decl = &DECL_RESULT (old_decl);
+ DECL_RESULT (new_decl) = remap_decl (*res_decl, &id);
+ lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl));
+ }
+
+ current_function_decl = NULL;
+ /* Renumber the lexical scoping (non-code) blocks consecutively. */
+ number_blocks (new_decl);
+
+ /* Clean up. */
+ splay_tree_delete (id.decl_map);
+ fold_cond_expr_cond ();
+ return;
+}
+
+/* Duplicate a type, fields and all. */
+
+tree
+build_duplicate_type (tree type)
+{
+ struct copy_body_data id;
+
+ memset (&id, 0, sizeof (id));
+ id.src_fn = current_function_decl;
+ id.dst_fn = current_function_decl;
+ id.src_cfun = cfun;
+ id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
+
+ type = remap_type_1 (type, &id);
+
+ splay_tree_delete (id.decl_map);
+
+ return type;
}