/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005 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 "params.h"
#include "input.h"
#include "insn-config.h"
-#include "integrate.h"
#include "varray.h"
#include "hashtab.h"
-#include "pointer-set.h"
#include "splay-tree.h"
#include "langhooks.h"
+#include "basic-block.h"
+#include "tree-iterator.h"
#include "cgraph.h"
#include "intl.h"
#include "tree-mudflap.h"
+#include "tree-flow.h"
#include "function.h"
+#include "ggc.h"
+#include "tree-flow.h"
#include "diagnostic.h"
+#include "except.h"
+#include "debug.h"
+#include "pointer-set.h"
+#include "ipa-prop.h"
/* I'm not real happy about this, but we need to handle gimple and
non-gimple trees. */
-#include "tree-iterator.h"
#include "tree-gimple.h"
+/* Inlining, Saving, Cloning
+
+ Inlining: a function body is duplicated, but the PARM_DECLs are
+ remapped into VAR_DECLs, and non-void RETURN_EXPRs become
+ MODIFY_EXPRs that store to a dedicated returned-value variable.
+ The duplicated eh_region info of the copy will later be appended
+ 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).
+
+ 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
+ updated to point into the new body. (Note that the original
+ callgraph node and edge list will not be altered.)
+
+ See the CALL_EXPR handling case in copy_body_r (). */
+
/* 0 if we should not perform inlining.
1 if we should expand functions calls inline at the tree level.
2 if we should consider *all* functions to be inline
typedef struct inline_data
{
- /* A stack of the functions we are inlining. For example, if we are
- compiling `f', which calls `g', which calls `h', and we are
- inlining the body of `h', the stack will contain, `h', followed
- by `g', followed by `f'. The first few elements of the stack may
- contain other functions that we know we should not recurse into,
- even though they are not directly being inlined. */
- varray_type fns;
- /* The index of the first element of FNS that really represents an
- inlined function. */
- unsigned first_inlined_fn;
- /* The label to jump to when a return statement is encountered. If
- this value is NULL, then return statements will simply be
- remapped as return statements, rather than as jumps. */
- tree ret_label;
+ /* FUNCTION_DECL for function being inlined. */
+ tree callee;
+ /* FUNCTION_DECL for function being inlined into. */
+ tree caller;
+ /* struct function for function being inlined. */
+ 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;
- /* Nonzero if we are currently within the cleanup for a
- TARGET_EXPR. */
- int in_target_cleanup_p;
- /* A list of the functions current function has inlined. */
- varray_type inlined_fns;
/* 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;
- /* Hash table used to prevent walk_tree from visiting the same node
- umpteen million times. */
- htab_t tree_pruner;
+ /* 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. */
struct cgraph_node *current_node;
- /* Statement iterator. We need this so we can keep the tree in
- gimple form when we insert the inlined function. It is not
- used when we are not dealing with gimple trees. */
- tree_stmt_iterator tsi;
+ /* Current BLOCK. */
+ tree block;
+ varray_type ipa_info;
+ /* 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. */
-/* The approximate number of instructions per statement. This number
- need not be particularly accurate; it is used only to make
- decisions about when a function is too big to inline. */
-#define INSNS_PER_STMT (10)
-
+static tree declare_return_variable (inline_data *, tree, tree, tree *);
static tree copy_body_r (tree *, int *, void *);
-static tree copy_body (inline_data *);
-static tree expand_call_inline (tree *, int *, void *);
-static void expand_calls_inline (tree *, inline_data *);
+static tree copy_generic_body (inline_data *);
static bool inlinable_function_p (tree);
static tree remap_decl (tree, inline_data *);
static tree remap_type (tree, inline_data *);
-static tree initialize_inlined_parameters (inline_data *, tree,
- tree, tree, tree);
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 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 bind_expr, tree vars);
+static void declare_inline_vars (tree, tree);
static void remap_save_expr (tree *, void *, int *);
+static bool replace_ref_tree (inline_data *, tree *);
+static inline bool inlining_p (inline_data *);
+static void add_lexical_block (tree current_block, tree new_block);
/* Insert a tree->tree mapping for ID. Despite the name suggests
that the trees should be variables, it is used for more than that. */
(splay_tree_value) value);
}
-/* Remap DECL during the copying of the BLOCK tree for the function.
- We are only called to remap local variables in the current function. */
+/* Remap DECL during the copying of the BLOCK tree for the function. */
static tree
remap_decl (tree decl, inline_data *id)
{
- splay_tree_node n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
- tree fn = VARRAY_TOP_TREE (id->fns);
+ splay_tree_node n;
+ tree fn;
+
+ /* We only remap local variables in the current function. */
+ fn = id->callee;
+
+ /* See if we have remapped this declaration. */
- /* See if we have remapped this declaration. If we didn't already have an
- equivalent for this declaration, create one now. */
+ n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
+
+ /* If we didn't already have an equivalent for this declaration,
+ create one now. */
if (!n)
{
/* Make a copy of the variable or label. */
- tree t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0));
+ tree t;
+ t = copy_decl_for_dup (decl, fn, id->caller, id->versioning_p);
+
+ /* 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);
/* 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);
}
static tree
-remap_type (tree type, inline_data *id)
+remap_type_1 (tree type, inline_data *id)
{
- splay_tree_node node;
tree new, t;
- 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 by a variable
- in the function we are inlining. */
- if (! variably_modified_type_p (type, VARRAY_TOP_TREE (id->fns)))
- {
- insert_decl_map (id, type, type);
- return type;
- }
-
/* We do need a copy. build and register it now. If this is a pointer or
reference type, remap the designated type and make a new pointer or
reference type. */
TYPE_NEXT_VARIANT (new) = NULL;
}
+ if (TYPE_STUB_DECL (type))
+ TYPE_STUB_DECL (new) = remap_decl (TYPE_STUB_DECL (type), id);
+
/* Lazily create pointer and reference types. */
TYPE_POINTER_TO (new) = NULL;
TYPE_REFERENCE_TO (new) = 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 FILE_TYPE:
case OFFSET_TYPE:
default:
/* Shouldn't have been thought variable sized. */
}
static tree
+remap_type (tree type, inline_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->callee))
+ {
+ insert_decl_map (id, type, type);
+ return type;
+ }
+
+ return remap_type_1 (type, id);
+}
+
+static tree
remap_decls (tree decls, inline_data *id)
{
tree old_var;
{
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->callee)
+ && !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);
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 = VARRAY_TREE (id->fns, 0);
-#if 1
- /* FIXME! It shouldn't be so hard to manage blocks. Rebuilding them in
- rest_of_compilation is a good start. */
+ fn = id->caller;
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. */
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, inline_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)
{
BIND_EXPR_VARS (*tp) = remap_decls (BIND_EXPR_VARS (*tp), id);
}
-/* Called from copy_body via walk_tree. DATA is really an `inline_data *'. */
+/* Called from copy_body_id via walk_tree. DATA is really an
+ `inline_data *'. */
static tree
copy_body_r (tree *tp, int *walk_subtrees, void *data)
{
inline_data *id = (inline_data *) data;
- tree fn = VARRAY_TOP_TREE (id->fns);
+ tree fn = id->callee;
+ tree new_block;
-#if 0
- /* All automatic variables should have a DECL_CONTEXT indicating
- what function they come from. */
- if ((TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == LABEL_DECL)
- && DECL_NAMESPACE_SCOPE_P (*tp))
- gcc_assert (DECL_EXTERNAL (*tp) || TREE_STATIC (*tp));
-#endif
+ /* Begin by recognizing trees that we'll completely rewrite for the
+ inlining context. Our output for these trees is completely
+ different from out input (e.g. RETURN_EXPR is deleted, and morphs
+ into an edge). Further down, we'll handle trees that get
+ duplicated and/or tweaked. */
- /* If this is a RETURN_EXPR, change it into a MODIFY_EXPR and a
- GOTO_EXPR with the RET_LABEL as its target. */
- if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label)
+ /* 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))
{
- tree return_stmt = *tp;
- tree goto_stmt;
-
- /* Build the GOTO_EXPR. */
- tree assignment = TREE_OPERAND (return_stmt, 0);
- goto_stmt = build1 (GOTO_EXPR, void_type_node, id->ret_label);
- TREE_USED (id->ret_label) = 1;
+ tree assignment = TREE_OPERAND (*tp, 0);
/* If we're returning something, just turn that into an
- assignment into the equivalent of the original
- RESULT_DECL. */
- if (assignment)
- {
- /* Do not create a statement containing a naked RESULT_DECL. */
- if (TREE_CODE (assignment) == RESULT_DECL)
- gimplify_stmt (&assignment);
-
- *tp = build (BIND_EXPR, void_type_node, NULL, NULL, NULL);
- append_to_statement_list (assignment, &BIND_EXPR_BODY (*tp));
- append_to_statement_list (goto_stmt, &BIND_EXPR_BODY (*tp));
- }
- /* If we're not returning anything just do the jump. */
- else
- *tp = goto_stmt;
+ assignment into the equivalent of the original RESULT_DECL.
+ If the "assignment" is just the result decl, the result
+ decl has already been set (e.g. a recent "foo (&result_decl,
+ ...)"); just toss the entire RETURN_EXPR. */
+ if (assignment && TREE_CODE (assignment) == MODIFY_EXPR)
+ {
+ /* Replace the RETURN_EXPR with (a copy of) the
+ MODIFY_EXPR hanging underneath. */
+ *tp = copy_node (assignment);
+ }
+ else /* Else the RETURN_EXPR returns no value. */
+ {
+ *tp = NULL;
+ return (tree) (void *)1;
+ }
}
+
/* Local variables and labels need to be replaced by equivalent
variables. We don't want to copy static variables; there's only
one of those, no matter how many times we inline the containing
/* Replace this variable with the copy. */
STRIP_TYPE_NOPS (new_decl);
*tp = new_decl;
+ *walk_subtrees = 0;
}
else if (TREE_CODE (*tp) == STATEMENT_LIST)
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
+ && (! DECL_CONTEXT (*tp)
+ || decl_function_context (*tp) == id->callee))
+ /* These may need to be remapped for EH handling. */
+ *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. */
/* If this is a constant, we have to copy the node iff the type will be
remapped. copy_tree_r will not copy a constant. */
- else if (TREE_CODE_CLASS (TREE_CODE (*tp)) == tcc_constant)
+ else if (CONSTANT_CLASS_P (*tp))
{
tree new_type = remap_type (TREE_TYPE (*tp), id);
knows not to copy VAR_DECLs, etc., so this is safe. */
else
{
- tree old_node = *tp;
-
+ /* Here we handle trees that are not completely rewritten.
+ First we detect some inlining-induced bogosities for
+ discarding. */
if (TREE_CODE (*tp) == MODIFY_EXPR
&& TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1)
&& (lang_hooks.tree_inlining.auto_var_in_fn_p
}
}
}
- else if (TREE_CODE (*tp) == INDIRECT_REF)
+ else if (TREE_CODE (*tp) == INDIRECT_REF
+ && !id->versioning_p)
{
/* Get rid of *& from inline substitutions that can happen when a
pointer argument is an ADDR_EXPR. */
- tree decl = TREE_OPERAND (*tp, 0), value;
+ tree decl = TREE_OPERAND (*tp, 0);
splay_tree_node n;
n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
if (n)
{
- value = (tree) n->value;
- STRIP_NOPS (value);
- if (TREE_CODE (value) == ADDR_EXPR
- && (lang_hooks.types_compatible_p
- (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0)))))
- {
- *tp = TREE_OPERAND (value, 0);
- return copy_body_r (tp, walk_subtrees, data);
+ tree new;
+ /* 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. 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);
+ *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);
}
+ *walk_subtrees = 0;
+ return NULL;
}
}
- copy_tree_r (tp, walk_subtrees, NULL);
-
- if (TREE_CODE (*tp) == CALL_EXPR && id->node && get_callee_fndecl (*tp))
+ /* Here is the "usual case". Copy this tree node, and then
+ tweak some special cases. */
+ copy_tree_r (tp, walk_subtrees, id->versioning_p ? data : NULL);
+
+ /* 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))))
{
- 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);
- gcc_assert (edge);
- edge->call_expr = *tp;
- }
- }
- else
+ new_block = id->block;
+ if (TREE_BLOCK (*tp))
{
- struct cgraph_edge *edge
- = cgraph_edge (id->current_node, old_node);
-
- if (edge)
- cgraph_clone_edge (edge, id->node, *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) =
+ build_int_cst
+ (NULL_TREE,
+ id->eh_region_offset + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0)));
+
TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
/* The copied TARGET_EXPR has never been expanded, even if the
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);
+ recompute_tree_invariant_for_addr_expr (*tp);
*walk_subtrees = 0;
}
}
return NULL_TREE;
}
+/* Copy basic block, scale profile accordingly. Edges will be taken care of
+ later */
+
+static basic_block
+copy_bb (inline_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,
+ (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);
+ copy_bsi = bsi_start (copy_basic_block);
+
+ for (bsi = bsi_start (bb);
+ !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ tree stmt = bsi_stmt (bsi);
+ tree orig_stmt = stmt;
+
+ walk_tree (&stmt, copy_body_r, id, NULL);
+
+ /* RETURN_EXPR might be removed,
+ this is signalled by making stmt pointer NULL. */
+ if (stmt)
+ {
+ tree call, decl;
+ 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->versioning_p)
+ {
+ struct cgraph_edge *edge;
+
+ /* We're cloning or inlining this body; duplicate the
+ associate callgraph nodes. */
+ edge = cgraph_edge (id->current_node, orig_stmt);
+ if (edge)
+ cgraph_clone_edge (edge, id->node, stmt,
+ REG_BR_PROB_BASE, 1, true);
+ }
+ 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;
+ 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.
+ There is no region 0 in tree land. */
+ gcc_assert (lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt)
+ != 0);
+
+ if (tree_could_throw_p (stmt))
+ {
+ int region = lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt);
+ /* Add an entry for the copied tree in the EH hashtable.
+ When saving or 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)
+ add_stmt_to_eh_region (stmt, region + id->eh_region_offset);
+
+ /* If this tree doesn't have a region associated with it,
+ 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,
+ orig_stmt) <= 0
+ && id->eh_region > 0)
+ && tree_could_throw_p (stmt))
+ add_stmt_to_eh_region (stmt, id->eh_region);
+ }
+ }
+ }
+ return copy_basic_block;
+}
+
+/* Copy edges from BB into its copy constructed earlier, scale profile
+ accordingly. Edges will be taken care of later. Assume aux
+ pointers to point to the copies of each BB. */
+static void
+copy_edges_for_bb (basic_block bb, int count_scale)
+{
+ basic_block new_bb = (basic_block) bb->aux;
+ edge_iterator ei;
+ edge old_edge;
+ block_stmt_iterator bsi;
+ int flags;
+
+ /* Use the indices from the original blocks to create edges for the
+ new ones. */
+ FOR_EACH_EDGE (old_edge, ei, bb->succs)
+ if (!(old_edge->flags & EDGE_EH))
+ {
+ edge new;
+
+ flags = old_edge->flags;
+
+ /* Return edges do get a FALLTHRU flag when the get inlined. */
+ 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, (basic_block) old_edge->dest->aux, flags);
+ new->count = old_edge->count * count_scale / REG_BR_PROB_BASE;
+ new->probability = old_edge->probability;
+ }
+
+ if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK)
+ return;
+
+ for (bsi = bsi_start (new_bb); !bsi_end_p (bsi);)
+ {
+ tree copy_stmt;
+
+ copy_stmt = bsi_stmt (bsi);
+ update_stmt (copy_stmt);
+ /* Do this before the possible split_block. */
+ bsi_next (&bsi);
+
+ /* If this tree could throw an exception, there are two
+ cases where we need to add abnormal edge(s): the
+ tree wasn't in a region and there is a "current
+ region" in the caller; or the original tree had
+ EH edges. In both cases split the block after the tree,
+ and add abnormal edge(s) as needed; we need both
+ those from the callee and the caller.
+ We check whether the copy can throw, because the const
+ propagation can change an INDIRECT_REF which throws
+ into a COMPONENT_REF which doesn't. If the copy
+ can throw, the original could also throw. */
+
+ if (tree_can_throw_internal (copy_stmt))
+ {
+ if (!bsi_end_p (bsi))
+ /* Note that bb's predecessor edges aren't necessarily
+ right at this point; split_block doesn't care. */
+ {
+ edge e = split_block (new_bb, copy_stmt);
+ new_bb = e->dest;
+ bsi = bsi_start (new_bb);
+ }
+
+ make_eh_edges (copy_stmt);
+ }
+ }
+}
+
+/* Wrapper for remap_decl so it can be used as a callback. */
+static tree
+remap_decl_1 (tree decl, void *data)
+{
+ return remap_decl (decl, (inline_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,
+ basic_block entry_block_map, basic_block exit_block_map)
+{
+ tree callee_fndecl = id->callee;
+ /* Original cfun for the callee, doesn't change. */
+ struct function *callee_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,
+ use that instead of the main copy. */
+ struct function *cfun_to_copy =
+ (struct function *) ggc_alloc_cleared (sizeof (struct function));
+ basic_block bb;
+ tree new_fndecl = NULL;
+ bool versioning_or_cloning;
+ int count_scale, frequency_scale;
+
+ if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count)
+ count_scale = (REG_BR_PROB_BASE * count
+ / ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count);
+ else
+ count_scale = 1;
+
+ if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency)
+ frequency_scale = (REG_BR_PROB_BASE * frequency
+ /
+ ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency);
+ else
+ frequency_scale = count_scale;
+
+ /* Register specific tree functions. */
+ tree_register_cfg_hooks ();
+
+ /* Must have a CFG here at this point. */
+ gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION
+ (DECL_STRUCT_FUNCTION (callee_fndecl)));
+
+ *cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl);
+
+ 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. */
+ versioning_or_cloning = (id->cloning_p || id->versioning_p);
+ if (versioning_or_cloning)
+ {
+ 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;
+ 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 /
+ REG_BR_PROB_BASE);
+ ENTRY_BLOCK_PTR->frequency =
+ (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+ frequency_scale / REG_BR_PROB_BASE);
+ EXIT_BLOCK_PTR->count =
+ (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale /
+ REG_BR_PROB_BASE);
+ EXIT_BLOCK_PTR->frequency =
+ (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency *
+ frequency_scale / REG_BR_PROB_BASE);
+
+ entry_block_map = ENTRY_BLOCK_PTR;
+ exit_block_map = EXIT_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 (versioning_or_cloning)
+ 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);
+ }
+ /* Use aux pointers to map the original blocks to copy. */
+ FOR_EACH_BB_FN (bb, cfun_to_copy)
+ bb->aux = copy_bb (id, bb, frequency_scale, count_scale);
+ /* Now that we've duplicated the blocks, duplicate their edges. */
+ FOR_ALL_BB_FN (bb, cfun_to_copy)
+ copy_edges_for_bb (bb, count_scale);
+ FOR_ALL_BB_FN (bb, cfun_to_copy)
+ bb->aux = NULL;
+
+ if (versioning_or_cloning)
+ pop_cfun ();
+
+ return new_fndecl;
+}
+
/* Make a copy of the body of FN so that it can be inserted inline in
another function. */
static tree
-copy_body (inline_data *id)
+copy_generic_body (inline_data *id)
{
tree body;
- tree fndecl = VARRAY_TOP_TREE (id->fns);
+ tree fndecl = id->callee;
- if (fndecl == current_function_decl
- && cfun->saved_tree)
- body = cfun->saved_tree;
- else
- body = DECL_SAVED_TREE (fndecl);
+ body = DECL_SAVED_TREE (fndecl);
walk_tree (&body, copy_body_r, id, NULL);
return body;
}
+static tree
+copy_body (inline_data *id, gcov_type count, int frequency,
+ basic_block entry_block_map, basic_block exit_block_map)
+{
+ tree fndecl = id->callee;
+ tree body;
+
+ /* If this body has a CFG, walk CFG and copy. */
+ gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fndecl)));
+ body = copy_cfg_body (id, count, frequency, entry_block_map, exit_block_map);
+
+ return body;
+}
+
/* Return true if VALUE is an ADDR_EXPR of an automatic variable
defined in function FN, or of a data member thereof. */
return false;
var = get_base_address (TREE_OPERAND (value, 0));
-
+
return var && lang_hooks.tree_inlining.auto_var_in_fn_p (var, fn);
}
static void
setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
- tree *init_stmts, tree *vars, bool *gimplify_init_stmts_p)
+ basic_block bb, tree *vars)
{
tree init_stmt;
tree var;
+ tree var_sub;
/* If the parameter is never assigned to, we may not need to
create a new variable here at all. Instead, we may be able
&& !TREE_ADDRESSABLE (p)
&& value && !TREE_SIDE_EFFECTS (value))
{
- /* We can't risk substituting complex expressions. They
- might contain variables that will be assigned to later.
- Theoretically, we could check the expression to see if
- all of the variables that determine its value are
- read-only, but we don't bother. */
/* We may produce non-gimple trees by adding NOPs or introduce
invalid sharing when operand is not really constant.
It is not big deal to prohibit constant propagation here as
/* 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, VARRAY_TREE (id->fns, 0));
+ var = copy_decl_for_dup (p, fn, id->caller, /*versioning=*/false);
+
+ /* 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
+ replace uses of the PARM_DECL with dereferences. */
+ if (TREE_TYPE (var) != TREE_TYPE (p)
+ && POINTER_TYPE_P (TREE_TYPE (var))
+ && TREE_TYPE (TREE_TYPE (var)) == TREE_TYPE (p))
+ {
+ insert_decl_map (id, var, var);
+ var_sub = build_fold_indirect_ref (var);
+ }
+ else
+ var_sub = var;
/* Register the VAR_DECL as the equivalent for the PARM_DECL;
that way, when the PARM_DECL is encountered, it will be
automatically replaced by the VAR_DECL. */
- insert_decl_map (id, p, var);
+ insert_decl_map (id, p, var_sub);
/* Declare this new variable. */
TREE_CHAIN (var) = *vars;
if (value)
{
tree rhs = fold_convert (TREE_TYPE (var), value);
+ block_stmt_iterator bsi = bsi_last (bb);
if (rhs == error_mark_node)
return;
/* 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);
- append_to_statement_list (init_stmt, init_stmts);
+ 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
at the end. Note that is_gimple_cast only checks the outer
- tree code, not its operand. Thus the explicit check that it's
+ tree code, not its operand. Thus the explicit check that its
operand is a gimple value. */
if (!is_gimple_val (rhs)
&& (!is_gimple_cast (rhs)
|| !is_gimple_val (TREE_OPERAND (rhs, 0))))
- *gimplify_init_stmts_p = true;
+ gimplify_stmt (&init_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);
}
}
/* Generate code to initialize the parameters of the function at the
top of the stack in ID from the ARGS (presented as a TREE_LIST). */
-static tree
+static void
initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
- tree fn, tree bind_expr)
+ tree fn, basic_block bb)
{
- tree init_stmts = NULL_TREE;
tree parms;
tree a;
tree p;
tree vars = NULL_TREE;
- bool gimplify_init_stmts_p = false;
int argnum = 0;
/* 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. */
value = lang_hooks.tree_inlining.convert_parm_for_inlining
(p, a ? TREE_VALUE (a) : NULL_TREE, fn, argnum);
- setup_one_parameter (id, p, value, fn, &init_stmts, &vars,
- &gimplify_init_stmts_p);
- }
-
- /* Evaluate trailing arguments. */
- for (; a; a = TREE_CHAIN (a))
- {
- tree value = TREE_VALUE (a);
- append_to_statement_list (value, &init_stmts);
+ setup_one_parameter (id, p, value, fn, bb, &vars);
}
/* Initialize the static chain. */
p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
+ gcc_assert (fn != current_function_decl);
if (p)
{
/* No static chain? Seems like a bug in tree-nested.c. */
gcc_assert (static_chain);
- setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars,
- &gimplify_init_stmts_p);
+ setup_one_parameter (id, p, static_chain, fn, bb, &vars);
}
- if (gimplify_init_stmts_p)
- gimplify_body (&init_stmts, current_function_decl, false);
-
- declare_inline_vars (bind_expr, vars);
- return init_stmts;
+ declare_inline_vars (id->block, vars);
}
-/* Declare a return variable to replace the RESULT_DECL for the function we
- are calling. RETURN_SLOT_ADDR, if non-null, was a fake parameter that
+/* Declare a return variable to replace the RESULT_DECL for the
+ function we are calling. An appropriate DECL_STMT is returned.
+ The USE_STMT is filled to contain a use of the declaration to
+ indicate the return value of the function.
+
+ RETURN_SLOT_ADDR, if non-null, was a fake parameter that
took the address of the result. MODIFY_DEST, if non-null, was the LHS of
the MODIFY_EXPR to which this call is the RHS.
declare_return_variable (inline_data *id, tree return_slot_addr,
tree modify_dest, tree *use_p)
{
- tree callee = VARRAY_TOP_TREE (id->fns);
- tree caller = VARRAY_TREE (id->fns, 0);
+ tree callee = id->callee;
+ tree caller = id->caller;
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_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_for_dup (result, callee, caller, /*versioning=*/false);
+
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
= tree_cons (NULL_TREE, var,
&& !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;
+ case BUILT_IN_RETURN:
+ case BUILT_IN_APPLY_ARGS:
+ /* If a __builtin_apply_args caller would be inlined,
+ it would be saving arguments of the function it has
+ been inlined into. Similarly __builtin_return would
+ return from the function the inline has been inlined into. */
+ inline_forbidden_reason
+ = G_("function %q+F can never be inlined because "
+ "it uses __builtin_return or __builtin_apply_args");
+ return node;
+
default:
break;
}
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;
}
UNION_TYPE nodes, then it goes into infinite recursion on a
structure containing a pointer to its own type. If it doesn't,
then the type node for S doesn't get adjusted properly when
- F is inlined, and we abort in find_function_data.
+ F is inlined.
??? This is likely no longer true, but it's too late in the 4.0
cycle to try to find out. This should be checked for 4.1. */
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;
}
inline_forbidden_p (tree fndecl)
{
location_t saved_loc = input_location;
- tree ret = walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
- inline_forbidden_p_1, fndecl);
+ block_stmt_iterator bsi;
+ basic_block bb;
+ tree ret = NULL_TREE;
+
+ FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (fndecl))
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ {
+ ret = walk_tree_without_duplicates (bsi_stmt_ptr (bsi),
+ inline_forbidden_p_1, fndecl);
+ if (ret)
+ goto egress;
+ }
+egress:
input_location = saved_loc;
return ret;
}
&& !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 (inline_forbidden_reason, fn, fn);
+ warning (OPT_Winline, inline_forbidden_reason, fn);
inlinable = false;
}
return inlinable;
}
+/* Estimate the cost of a memory move. Use machine dependent
+ word size and take possible memcpy call into account. */
+
+int
+estimate_move_cost (tree type)
+{
+ HOST_WIDE_INT size;
+
+ size = int_size_in_bytes (type);
+
+ if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO)
+ /* Cost of a memcpy call, 3 arguments and the call. */
+ return 4;
+ else
+ return ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
+}
+
/* Used by estimate_num_insns. Estimate number of instructions seen
by given statement. */
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 COMPONENT_REF:
case BIT_FIELD_REF:
case INDIRECT_REF:
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
case OBJ_TYPE_REF:
/* We don't account constants for now. Assume that the cost is amortized
by operations that do use them. We may re-consider this decision once
- we are able to optimize the tree before estimating it's size and break
+ we are able to optimize the tree before estimating its size and break
out static initializers. */
case IDENTIFIER_NODE:
case INTEGER_CST:
*walk_subtrees = 0;
return NULL;
- /* Recognize assignments of large structures and constructors of
- big arrays. */
+ /* Try to estimate the cost of assignments. We have three cases to
+ deal with:
+ 1) Simple assignments to registers;
+ 2) Stores to things that must live in memory. This includes
+ "normal" stores to scalars, but also assignments of large
+ structures, or constructors of big arrays;
+ 3) TARGET_EXPRs.
+
+ Let us look at the first two cases, assuming we have "a = b + C":
+ <modify_expr <var_decl "a"> <plus_expr <var_decl "b"> <constant C>>
+ If "a" is a GIMPLE register, the assignment to it is free on almost
+ any target, because "a" usually ends up in a real register. Hence
+ the only cost of this expression comes from the PLUS_EXPR, and we
+ can ignore the MODIFY_EXPR.
+ If "a" is not a GIMPLE register, the assignment to "a" will most
+ likely be a real store, so the cost of the MODIFY_EXPR is the cost
+ of moving something into "a", which we compute using the function
+ estimate_move_cost.
+
+ The third case deals with TARGET_EXPRs, for which the semantics are
+ that a temporary is assigned, unless the TARGET_EXPR itself is being
+ assigned to something else. In the latter case we do not need the
+ temporary. E.g. in <modify_expr <var_decl "a"> <target_expr>>, the
+ MODIFY_EXPR is free. */
case INIT_EXPR:
case MODIFY_EXPR:
- x = TREE_OPERAND (x, 0);
- /* FALLTHRU */
- case TARGET_EXPR:
- case CONSTRUCTOR:
- {
- HOST_WIDE_INT size;
+ /* Is the right and side a TARGET_EXPR? */
+ if (TREE_CODE (TREE_OPERAND (x, 1)) == TARGET_EXPR)
+ break;
+ /* ... fall through ... */
- size = int_size_in_bytes (TREE_TYPE (x));
+ case TARGET_EXPR:
+ x = TREE_OPERAND (x, 0);
+ /* Is this an assignments to a register? */
+ if (is_gimple_reg (x))
+ break;
+ /* Otherwise it's a store, so fall through to compute the move cost. */
- if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO)
- *count += 10;
- else
- *count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
- }
+ case CONSTRUCTOR:
+ *count += estimate_move_cost (TREE_TYPE (x));
break;
- /* Assign cost of 1 to usual operations.
- ??? We may consider mapping RTL costs to this. */
+ /* Assign cost of 1 to usual operations.
+ ??? We may consider mapping RTL costs to this. */
case COND_EXPR:
+ case VEC_COND_EXPR:
case PLUS_EXPR:
case MINUS_EXPR:
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 ASM_EXPR:
+ case REALIGN_LOAD_EXPR:
+
+ case REDUC_MAX_EXPR:
+ case REDUC_MIN_EXPR:
+ case REDUC_PLUS_EXPR:
+
case RESX_EXPR:
*count += 1;
break;
case CALL_EXPR:
{
tree decl = get_callee_fndecl (x);
+ tree arg;
- if (decl && DECL_BUILT_IN (decl))
+ if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (decl))
{
case BUILT_IN_CONSTANT_P:
default:
break;
}
- *count += 10;
+
+ /* Our cost must be kept in sync with cgraph_estimate_size_after_inlining
+ that does use function declaration to figure out the arguments. */
+ if (!decl)
+ {
+ for (arg = TREE_OPERAND (x, 1); arg; arg = TREE_CHAIN (arg))
+ *count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg)));
+ }
+ else
+ {
+ for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
+ *count += estimate_move_cost (TREE_TYPE (arg));
+ }
+
+ *count += PARAM_VALUE (PARAM_INLINE_CALL_COST);
break;
}
default:
- /* Abort here se we know we don't miss any nodes. */
gcc_unreachable ();
}
return NULL;
estimate_num_insns (tree expr)
{
int num = 0;
- walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num);
+ struct pointer_set_t *visited_nodes;
+ basic_block bb;
+ block_stmt_iterator bsi;
+ struct function *my_function;
+
+ /* If we're given an entire function, walk the CFG. */
+ if (TREE_CODE (expr) == FUNCTION_DECL)
+ {
+ my_function = DECL_STRUCT_FUNCTION (expr);
+ gcc_assert (my_function && my_function->cfg);
+ visited_nodes = pointer_set_create ();
+ FOR_EACH_BB_FN (bb, my_function)
+ {
+ for (bsi = bsi_start (bb);
+ !bsi_end_p (bsi);
+ bsi_next (&bsi))
+ {
+ walk_tree (bsi_stmt_ptr (bsi), estimate_num_insns_1,
+ &num, visited_nodes);
+ }
+ }
+ pointer_set_destroy (visited_nodes);
+ }
+ else
+ walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num);
+
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 VEC(function_p,heap) *cfun_stack;
+
+void
+push_cfun (struct function *new_cfun)
+{
+ VEC_safe_push (function_p, heap, cfun_stack, cfun);
+ cfun = new_cfun;
+}
+
+void
+pop_cfun (void)
+{
+ cfun = VEC_pop (function_p, cfun_stack);
+}
+
+/* Install new lexical TREE_BLOCK underneath 'current_block'. */
+static void
+add_lexical_block (tree current_block, tree new_block)
+{
+ tree *blk_p;
+
+ /* Walk to the last sub-block. */
+ for (blk_p = &BLOCK_SUBBLOCKS (current_block);
+ *blk_p;
+ blk_p = &TREE_CHAIN (*blk_p))
+ ;
+ *blk_p = new_block;
+ BLOCK_SUPERCONTEXT (new_block) = current_block;
+}
+
/* If *TP is a CALL_EXPR, replace it with its inline expansion. */
-static tree
-expand_call_inline (tree *tp, int *walk_subtrees, void *data)
+static bool
+expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
{
inline_data *id;
tree t;
- tree expr;
- tree stmt;
tree use_retvar;
- tree decl;
tree fn;
- tree arg_inits;
- tree *inlined_body;
splay_tree st;
tree args;
tree return_slot_addr;
tree modify_dest;
location_t saved_location;
- struct cgraph_edge *edge;
+ struct cgraph_edge *cg_edge;
const char *reason;
+ basic_block return_block;
+ edge e;
+ block_stmt_iterator bsi, stmt_bsi;
+ 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;
if (EXPR_HAS_LOCATION (t))
input_location = EXPR_LOCATION (t);
- /* Recurse, but letting recursive invocations know that we are
- inside the body of a TARGET_EXPR. */
- if (TREE_CODE (*tp) == TARGET_EXPR)
- {
-#if 0
- int i, len = TREE_CODE_LENGTH (TARGET_EXPR);
-
- /* We're walking our own subtrees. */
- *walk_subtrees = 0;
-
- /* Actually walk over them. This loop is the body of
- walk_trees, omitting the case where the TARGET_EXPR
- itself is handled. */
- for (i = 0; i < len; ++i)
- {
- if (i == 2)
- ++id->in_target_cleanup_p;
- walk_tree (&TREE_OPERAND (*tp, i), expand_call_inline, data,
- id->tree_pruner);
- if (i == 2)
- --id->in_target_cleanup_p;
- }
-
- goto egress;
-#endif
- }
-
- if (TYPE_P (t))
- /* Because types were not copied in copy_body, CALL_EXPRs beneath
- them should not be expanded. This can happen if the type is a
- dynamic array type, for example. */
- *walk_subtrees = 0;
-
/* From here on, we're only interested in CALL_EXPRs. */
if (TREE_CODE (t) != CALL_EXPR)
goto egress;
if (!id->current_node->analyzed)
goto egress;
- edge = cgraph_edge (id->current_node, t);
+ cg_edge = cgraph_edge (id->current_node, stmt);
/* Constant propagation on argument done during previous inlining
may create new direct call. Produce an edge for it. */
- if (!edge)
+ if (!cg_edge)
{
struct cgraph_node *dest = cgraph_node (fn);
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, t)->inline_failed
+ cgraph_create_edge (id->node, dest, stmt,
+ bb->count, bb->loop_depth)->inline_failed
= N_("originally indirect function call not considered for inlining");
goto egress;
}
/* Don't try to inline functions that are not well-suited to
inlining. */
- if (!cgraph_inline_p (edge, &reason))
+ 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 ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
- warning ("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 (edge->callee->decl != id->node->decl)
- verify_cgraph_node (edge->callee);
+ if (cg_edge->callee->decl != id->node->decl)
+ verify_cgraph_node (cg_edge->callee);
#endif
- if (! lang_hooks.tree_inlining.start_inlining (fn))
- goto egress;
+ /* We will be inlining this callee. */
+
+ id->eh_region = lookup_stmt_eh_region (stmt);
+
+ /* Split the block holding the CALL_EXPR. */
+
+ e = split_block (bb, stmt);
+ bb = e->src;
+ return_block = e->dest;
+ remove_edge (e);
+
+ /* split_block splits before the statement, work around this by moving
+ the call into the first half_bb. Not pretty, but seems easier than
+ doing the CFG manipulation by hand when the CALL_EXPR is in the last
+ statement in BB. */
+ stmt_bsi = bsi_last (bb);
+ bsi = bsi_start (return_block);
+ if (!bsi_end_p (bsi))
+ bsi_move_before (&stmt_bsi, &bsi);
+ else
+ {
+ tree stmt = bsi_stmt (stmt_bsi);
+ bsi_remove (&stmt_bsi, false);
+ bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
+ }
+ stmt_bsi = bsi_start (return_block);
/* Build a block containing code to initialize the arguments, the
actual inline expansion of the body, and a label for the return
statements within the function to jump to. The type of the
statement expression is the return type of the function call. */
- stmt = NULL;
- expr = build (BIND_EXPR, void_type_node, NULL_TREE,
- stmt, make_node (BLOCK));
- BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn;
+ 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. */
/* Initialize the parameters. */
args = TREE_OPERAND (t, 1);
- return_slot_addr = NULL_TREE;
- if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (t))
- {
- return_slot_addr = TREE_VALUE (args);
- args = TREE_CHAIN (args);
- TREE_TYPE (expr) = void_type_node;
- }
- arg_inits = initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2),
- fn, expr);
- if (arg_inits)
- {
- /* Expand any inlined calls in the initializers. Do this before we
- push FN on the stack of functions we are inlining; we want to
- inline calls to FN that appear in the initializers for the
- parameters.
-
- Note we need to save and restore the saved tree statement iterator
- to avoid having it clobbered by expand_calls_inline. */
- tree_stmt_iterator save_tsi;
+ initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
- save_tsi = id->tsi;
- expand_calls_inline (&arg_inits, id);
- id->tsi = save_tsi;
+ /* Record the function we are about to inline. */
+ id->callee = fn;
- /* And add them to the tree. */
- append_to_statement_list (arg_inits, &BIND_EXPR_BODY (expr));
- }
-
- /* Record the function we are about to inline so that we can avoid
- recursing into it. */
- VARRAY_PUSH_TREE (id->fns, fn);
-
- /* Record the function we are about to inline if optimize_function
- has not been called on it yet and we don't have it in the list. */
- if (! DECL_INLINED_FNS (fn))
- {
- int i;
-
- for (i = VARRAY_ACTIVE_SIZE (id->inlined_fns) - 1; i >= 0; i--)
- if (VARRAY_TREE (id->inlined_fns, i) == fn)
- break;
- if (i < 0)
- VARRAY_PUSH_TREE (id->inlined_fns, 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. */
- id->ret_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
- DECL_ARTIFICIAL (id->ret_label) = 1;
- DECL_CONTEXT (id->ret_label) = VARRAY_TREE (id->fns, 0);
- insert_decl_map (id, id->ret_label, id->ret_label);
gcc_assert (DECL_INITIAL (fn));
gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
/* Find the lhs to which the result of this call is assigned. */
- modify_dest = tsi_stmt (id->tsi);
- if (TREE_CODE (modify_dest) == MODIFY_EXPR)
- modify_dest = TREE_OPERAND (modify_dest, 0);
+ return_slot_addr = NULL;
+ if (TREE_CODE (stmt) == MODIFY_EXPR)
+ {
+ 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
+ does not return a value. In that case the optimizers will
+ see that the variable to which the value is assigned was not
+ initialized. We do not want to issue a warning about that
+ 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);
+ modify_dest = NULL;
+ }
+ }
else
modify_dest = NULL;
/* Declare the return variable for the function. */
decl = declare_return_variable (id, return_slot_addr,
- modify_dest, &use_retvar);
+ modify_dest, &use_retvar);
+ /* Do this only if declare_return_variable created a new one. */
+ 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. */
- {
- struct cgraph_node *old_node = id->current_node;
-
- id->current_node = edge->callee;
- append_to_statement_list (copy_body (id), &BIND_EXPR_BODY (expr));
- id->current_node = old_node;
- }
- inlined_body = &BIND_EXPR_BODY (expr);
-
- /* After the body of the function comes the RET_LABEL. This must come
- before we evaluate the returned value below, because that evaluation
- may cause RTL to be generated. */
- if (TREE_USED (id->ret_label))
+ 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->callee_cfun->unexpanded_var_list;
+ for (; t_step; t_step = TREE_CHAIN (t_step))
{
- tree label = build1 (LABEL_EXPR, void_type_node, id->ret_label);
- append_to_statement_list (label, &BIND_EXPR_BODY (expr));
+ 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);
id->decl_map = st;
- /* Although, from the semantic viewpoint, the new expression has
- side-effects only if the old one did, it is not possible, from
- the technical viewpoint, to evaluate the body of a function
- multiple times without serious havoc. */
- TREE_SIDE_EFFECTS (expr) = 1;
-
- tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
-
/* If the inlined function returns a result that we care about,
- then we're going to need to splice in a MODIFY_EXPR. Otherwise
- the call was a standalone statement and we can just replace it
- with the BIND_EXPR inline representation of the called function. */
- if (!use_retvar || !modify_dest)
- *tsi_stmt_ptr (id->tsi) = build_empty_stmt ();
+ clobber the CALL_EXPR with a reference to the return variable. */
+ if (use_retvar && (TREE_CODE (bsi_stmt (stmt_bsi)) != CALL_EXPR))
+ {
+ *tp = use_retvar;
+ maybe_clean_or_replace_eh_stmt (stmt, stmt);
+ }
else
- *tp = use_retvar;
+ /* 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, true);
- /* When we gimplify a function call, we may clear TREE_SIDE_EFFECTS on
- the call if it is to a "const" function. Thus the copy of
- TREE_SIDE_EFFECTS from the CALL_EXPR to the BIND_EXPR above with
- result in TREE_SIDE_EFFECTS not being set for the inlined copy of a
- "const" function.
+ bsi_next (&bsi);
+ if (bsi_end_p (bsi))
+ tree_purge_dead_eh_edges (return_block);
- Unfortunately, that is wrong as inlining the function can create/expose
- interesting side effects (such as setting of a return value).
+ /* If the value of the new expression is ignored, that's OK. We
+ don't warn about this for CALL_EXPRs, so we shouldn't warn about
+ the equivalent inlined version either. */
+ TREE_USED (*tp) = 1;
- The easiest solution is to simply recalculate TREE_SIDE_EFFECTS for
- the toplevel expression. */
- recalculate_side_effects (expr);
+ /* Output the inlining info for this abstract function, since it has been
+ inlined. If we don't do this now, we can lose the information about the
+ variables in the function when the blocks get blown away as soon as we
+ remove the cgraph node. */
+ (*debug_hooks->outlining_inline_function) (cg_edge->callee->decl);
/* Update callgraph if needed. */
- cgraph_remove_node (edge->callee);
-
- /* Recurse into the body of the just inlined function. */
- expand_calls_inline (inlined_body, id);
- VARRAY_POP (id->fns);
+ cgraph_remove_node (cg_edge->callee);
- /* Don't walk into subtrees. We've already handled them above. */
- *walk_subtrees = 0;
+ /* Declare the 'auto' variables added with this inlined body. */
+ record_vars (BLOCK_VARS (id->block));
+ id->block = NULL_TREE;
+ successfully_inlined = TRUE;
- lang_hooks.tree_inlining.end_inlining (fn);
-
- /* Keep iterating. */
egress:
input_location = saved_location;
- return NULL_TREE;
+ return successfully_inlined;
}
-static void
-expand_calls_inline (tree *stmt_p, inline_data *id)
+/* Expand call statements reachable from STMT_P.
+ We can only have CALL_EXPRs as the "toplevel" tree code or nested
+ in a MODIFY_EXPR. See tree-gimple.c:get_call_expr_in(). We can
+ unfortunately not use that function here because we need a pointer
+ to the CALL_EXPR, not the tree itself. */
+
+static bool
+gimple_expand_calls_inline (basic_block bb, inline_data *id)
{
- tree stmt = *stmt_p;
- enum tree_code code = TREE_CODE (stmt);
- int dummy;
+ block_stmt_iterator bsi;
- switch (code)
+ /* Register specific tree functions. */
+ tree_register_cfg_hooks ();
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i;
- tree new;
-
- for (i = tsi_start (stmt); !tsi_end_p (i); )
- {
- id->tsi = i;
- expand_calls_inline (tsi_stmt_ptr (i), id);
-
- new = tsi_stmt (i);
- if (TREE_CODE (new) == STATEMENT_LIST)
- {
- tsi_link_before (&i, new, TSI_SAME_STMT);
- tsi_delink (&i);
- }
- else
- tsi_next (&i);
- }
- }
- break;
-
- case COND_EXPR:
- expand_calls_inline (&COND_EXPR_THEN (stmt), id);
- expand_calls_inline (&COND_EXPR_ELSE (stmt), id);
- break;
-
- case CATCH_EXPR:
- expand_calls_inline (&CATCH_BODY (stmt), id);
- break;
-
- case EH_FILTER_EXPR:
- expand_calls_inline (&EH_FILTER_FAILURE (stmt), id);
- break;
-
- case TRY_CATCH_EXPR:
- case TRY_FINALLY_EXPR:
- expand_calls_inline (&TREE_OPERAND (stmt, 0), id);
- expand_calls_inline (&TREE_OPERAND (stmt, 1), id);
- break;
-
- case BIND_EXPR:
- expand_calls_inline (&BIND_EXPR_BODY (stmt), id);
- break;
-
- case COMPOUND_EXPR:
- /* We're gimple. We should have gotten rid of all these. */
- gcc_unreachable ();
-
- case RETURN_EXPR:
- stmt_p = &TREE_OPERAND (stmt, 0);
- stmt = *stmt_p;
- if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
- break;
-
- /* FALLTHRU */
-
- case MODIFY_EXPR:
- stmt_p = &TREE_OPERAND (stmt, 1);
- stmt = *stmt_p;
- if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
- {
- stmt_p = &TREE_OPERAND (stmt, 0);
- stmt = *stmt_p;
- }
- if (TREE_CODE (stmt) != CALL_EXPR)
- break;
-
- /* FALLTHRU */
-
- case CALL_EXPR:
- expand_call_inline (stmt_p, &dummy, id);
- break;
-
- default:
- break;
+ tree *expr_p = bsi_stmt_ptr (bsi);
+ tree stmt = *expr_p;
+
+ if (TREE_CODE (*expr_p) == MODIFY_EXPR)
+ expr_p = &TREE_OPERAND (*expr_p, 1);
+ if (TREE_CODE (*expr_p) == WITH_SIZE_EXPR)
+ expr_p = &TREE_OPERAND (*expr_p, 0);
+ if (TREE_CODE (*expr_p) == CALL_EXPR)
+ if (expand_call_inline (bb, stmt, expr_p, id))
+ return true;
}
+ return false;
}
/* Expand calls to inline functions in the body of FN. */
{
inline_data id;
tree prev_fn;
- tree ifn;
-
+ basic_block bb;
/* There is no point in performing inlining if errors have already
occurred -- and we might crash if we try to inline invalid
code. */
memset (&id, 0, sizeof (id));
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);
+ id.caller = fn;
/* Or any functions that aren't finished yet. */
prev_fn = NULL_TREE;
if (current_function_decl)
{
- VARRAY_PUSH_TREE (id.fns, current_function_decl);
+ id.caller = current_function_decl;
prev_fn = current_function_decl;
}
+ push_gimplify_context ();
- prev_fn = lang_hooks.tree_inlining.add_pending_fn_decls (&id.fns, prev_fn);
-
- /* Create the list of functions this call will inline. */
- VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");
+ /* Reach the trees by walking over the CFG, and note the
+ enclosing basic-blocks in the call edges. */
+ /* We walk the blocks going forward, because inlined function bodies
+ will split id->current_basic_block, and the new blocks will
+ follow it; we'll trudge through them, processing their CALL_EXPRs
+ along the way. */
+ FOR_EACH_BB (bb)
+ gimple_expand_calls_inline (bb, &id);
- /* Keep track of the low-water mark, i.e., the point where the first
- real inlining is represented in ID.FNS. */
- id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns);
- /* Replace all calls to inline functions with the bodies of those
- functions. */
- id.tree_pruner = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
- expand_calls_inline (&DECL_SAVED_TREE (fn), &id);
-
- /* Clean up. */
- htab_delete (id.tree_pruner);
- ifn = make_tree_vec (VARRAY_ACTIVE_SIZE (id.inlined_fns));
- if (VARRAY_ACTIVE_SIZE (id.inlined_fns))
- memcpy (&TREE_VEC_ELT (ifn, 0), &VARRAY_TREE (id.inlined_fns, 0),
- VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree));
- DECL_INLINED_FNS (fn) = ifn;
+ pop_gimplify_context (NULL);
+ /* Renumber the (code) basic_blocks consecutively. */
+ compact_blocks ();
+ /* Renumber the lexical scoping (non-code) blocks consecutively. */
+ number_blocks (fn);
#ifdef ENABLE_CHECKING
{
gcc_assert (e->inline_failed);
}
#endif
+ /* We need to rescale frequencies again to peak at REG_BR_PROB_BASE
+ as inlining loops might increase the maximum. */
+ if (ENTRY_BLOCK_PTR->count)
+ counts_to_freqs ();
+ fold_cond_expr_cond ();
}
/* FN is a function that has a complete body, and CLONE is a function whose
inline_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. If there's an
- in-charge parameter, map it to an appropriate constant. */
+ parameters in the callee to the parameters of caller. */
memset (&id, 0, sizeof (id));
- VARRAY_TREE_INIT (id.fns, 2, "fns");
- VARRAY_PUSH_TREE (id.fns, clone);
- VARRAY_PUSH_TREE (id.fns, fn);
+ id.caller = clone;
+ id.callee = fn;
+ id.callee_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;
- /* Actually copy the body. */
- append_to_statement_list_force (copy_body (&id), &DECL_SAVED_TREE (clone));
-}
-
-/* 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. */
-
-tree
-save_body (tree fn, tree *arg_copy, tree *sc_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;
- }
-
- *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;
- }
-
- insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn));
+ /* We're not inside any EH region. */
+ id.eh_region = -1;
/* Actually copy the body. */
- body = copy_body (&id);
-
- /* Clean up. */
- splay_tree_delete (id.decl_map);
- return body;
-}
-
-#define WALK_SUBTREE(NODE) \
- do \
- { \
- result = walk_tree (&(NODE), func, data, pset); \
- if (result) \
- return result; \
- } \
- while (0)
-
-/* This is a subroutine of walk_tree that walks field of TYPE that are to
- be walked whenever a type is seen in the tree. Rest of operands and return
- value are as for walk_tree. */
-
-static tree
-walk_type_fields (tree type, walk_tree_fn func, void *data,
- struct pointer_set_t *pset)
-{
- tree result = NULL_TREE;
-
- switch (TREE_CODE (type))
- {
- case POINTER_TYPE:
- case REFERENCE_TYPE:
- /* We have to worry about mutually recursive pointers. These can't
- be written in C. They can in Ada. It's pathological, but
- there's an ACATS test (c38102a) that checks it. Deal with this
- by checking if we're pointing to another pointer, that one
- points to another pointer, that one does too, and we have no htab.
- If so, get a hash table. We check three levels deep to avoid
- the cost of the hash table if we don't need one. */
- if (POINTER_TYPE_P (TREE_TYPE (type))
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (type)))
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (TREE_TYPE (type))))
- && !pset)
- {
- result = walk_tree_without_duplicates (&TREE_TYPE (type),
- func, data);
- if (result)
- return result;
-
- break;
- }
-
- /* ... fall through ... */
-
- case COMPLEX_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- break;
-
- case METHOD_TYPE:
- WALK_SUBTREE (TYPE_METHOD_BASETYPE (type));
-
- /* Fall through. */
-
- case FUNCTION_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- {
- tree arg;
-
- /* We never want to walk into default arguments. */
- for (arg = TYPE_ARG_TYPES (type); arg; arg = TREE_CHAIN (arg))
- WALK_SUBTREE (TREE_VALUE (arg));
- }
- break;
-
- case ARRAY_TYPE:
- /* Don't follow this nodes's type if a pointer for fear that we'll
- have infinite recursion. Those types are uninteresting anyway. */
- if (!POINTER_TYPE_P (TREE_TYPE (type))
- && TREE_CODE (TREE_TYPE (type)) != OFFSET_TYPE)
- WALK_SUBTREE (TREE_TYPE (type));
- WALK_SUBTREE (TYPE_DOMAIN (type));
- break;
-
- case BOOLEAN_TYPE:
- case ENUMERAL_TYPE:
- case INTEGER_TYPE:
- case CHAR_TYPE:
- case REAL_TYPE:
- WALK_SUBTREE (TYPE_MIN_VALUE (type));
- WALK_SUBTREE (TYPE_MAX_VALUE (type));
- break;
-
- case OFFSET_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- WALK_SUBTREE (TYPE_OFFSET_BASETYPE (type));
- break;
-
- default:
- break;
- }
-
- return NULL_TREE;
-}
-
-/* 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 value returned by FUNC
- is returned. If PSET is non-NULL it is used to record the nodes visited,
- and to avoid visiting a node more than once. */
-
-tree
-walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset)
-{
- enum tree_code code;
- int walk_subtrees;
- tree result;
-
-#define WALK_SUBTREE_TAIL(NODE) \
- do \
- { \
- tp = & (NODE); \
- goto tail_recurse; \
- } \
- while (0)
-
- tail_recurse:
- /* Skip empty subtrees. */
- if (!*tp)
- return NULL_TREE;
-
- /* Don't walk the same tree twice, if the user has requested
- that we avoid doing so. */
- if (pset && pointer_set_insert (pset, *tp))
- return NULL_TREE;
-
- /* Call the function. */
- walk_subtrees = 1;
- result = (*func) (tp, &walk_subtrees, data);
-
- /* If we found something, return it. */
- if (result)
- return result;
-
- code = TREE_CODE (*tp);
-
- /* Even if we didn't, FUNC may have decided that there was nothing
- interesting below this point in the tree. */
- if (!walk_subtrees)
- {
- if (code == TREE_LIST)
- /* But we still need to check our siblings. */
- WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
- else
- return NULL_TREE;
- }
-
- result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func,
- data, pset);
- if (result || ! walk_subtrees)
- return result;
-
- /* If this is a DECL_EXPR, walk into various fields of the type that it's
- defining. We only want to walk into these fields of a type in this
- case. Note that decls get walked as part of the processing of a
- BIND_EXPR.
-
- ??? Precisely which fields of types that we are supposed to walk in
- this case vs. the normal case aren't well defined. */
- if (code == DECL_EXPR
- && TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL
- && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK)
- {
- tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp));
-
- /* Call the function for the type. See if it returns anything or
- doesn't want us to continue. If we are to continue, walk both
- the normal fields and those for the declaration case. */
- result = (*func) (type_p, &walk_subtrees, data);
- if (result || !walk_subtrees)
- return NULL_TREE;
-
- result = walk_type_fields (*type_p, func, data, pset);
- if (result)
- return result;
-
- WALK_SUBTREE (TYPE_SIZE (*type_p));
- WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p));
-
- /* If this is a record type, also walk the fields. */
- if (TREE_CODE (*type_p) == RECORD_TYPE
- || TREE_CODE (*type_p) == UNION_TYPE
- || TREE_CODE (*type_p) == QUAL_UNION_TYPE)
- {
- tree field;
-
- for (field = TYPE_FIELDS (*type_p); field;
- field = TREE_CHAIN (field))
- {
- /* We'd like to look at the type of the field, but we can easily
- get infinite recursion. So assume it's pointed to elsewhere
- in the tree. Also, ignore things that aren't fields. */
- if (TREE_CODE (field) != FIELD_DECL)
- continue;
-
- WALK_SUBTREE (DECL_FIELD_OFFSET (field));
- WALK_SUBTREE (DECL_SIZE (field));
- WALK_SUBTREE (DECL_SIZE_UNIT (field));
- if (TREE_CODE (*type_p) == QUAL_UNION_TYPE)
- WALK_SUBTREE (DECL_QUALIFIER (field));
- }
- }
- }
-
- else if (code != SAVE_EXPR
- && code != BIND_EXPR
- && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
- {
- int i, len;
-
- /* Walk over all the sub-trees of this operand. */
- len = TREE_CODE_LENGTH (code);
- /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same.
- But, we only want to walk once. */
- if (code == TARGET_EXPR
- && TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1))
- --len;
-
- /* Go through the subtrees. We need to do this in forward order so
- that the scope of a FOR_EXPR is handled properly. */
-#ifdef DEBUG_WALK_TREE
- for (i = 0; i < len; ++i)
- WALK_SUBTREE (TREE_OPERAND (*tp, i));
-#else
- for (i = 0; i < len - 1; ++i)
- WALK_SUBTREE (TREE_OPERAND (*tp, i));
-
- if (len)
- {
- /* The common case is that we may tail recurse here. */
- if (code != BIND_EXPR
- && !TREE_CHAIN (*tp))
- WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1));
- else
- WALK_SUBTREE (TREE_OPERAND (*tp, len - 1));
- }
-#endif
- }
-
- /* If this is a type, walk the needed fields in the type. */
- else if (TYPE_P (*tp))
- {
- result = walk_type_fields (*tp, func, data, pset);
- if (result)
- return result;
- }
- else
- {
- /* Not one of the easy cases. We must explicitly go through the
- children. */
- switch (code)
- {
- case ERROR_MARK:
- case IDENTIFIER_NODE:
- case INTEGER_CST:
- case REAL_CST:
- case VECTOR_CST:
- case STRING_CST:
- case BLOCK:
- case PLACEHOLDER_EXPR:
- case SSA_NAME:
- case FIELD_DECL:
- case RESULT_DECL:
- /* None of thse have subtrees other than those already walked
- above. */
- break;
-
- case TREE_LIST:
- WALK_SUBTREE (TREE_VALUE (*tp));
- WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
- break;
-
- case TREE_VEC:
- {
- int len = TREE_VEC_LENGTH (*tp);
-
- if (len == 0)
- break;
-
- /* Walk all elements but the first. */
- while (--len)
- WALK_SUBTREE (TREE_VEC_ELT (*tp, len));
-
- /* Now walk the first one as a tail call. */
- WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0));
- }
-
- case COMPLEX_CST:
- WALK_SUBTREE (TREE_REALPART (*tp));
- WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp));
-
- case CONSTRUCTOR:
- WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp));
-
- case SAVE_EXPR:
- WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0));
-
- case BIND_EXPR:
- {
- tree decl;
- for (decl = BIND_EXPR_VARS (*tp); decl; decl = TREE_CHAIN (decl))
- {
- /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk
- into declarations that are just mentioned, rather than
- declared; they don't really belong to this part of the tree.
- And, we can see cycles: the initializer for a declaration
- can refer to the declaration itself. */
- WALK_SUBTREE (DECL_INITIAL (decl));
- WALK_SUBTREE (DECL_SIZE (decl));
- WALK_SUBTREE (DECL_SIZE_UNIT (decl));
- }
- WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp));
- }
-
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i;
- for (i = tsi_start (*tp); !tsi_end_p (i); tsi_next (&i))
- WALK_SUBTREE (*tsi_stmt_ptr (i));
- }
- break;
-
- default:
- /* ??? This could be a language-defined node. We really should make
- a hook for it, but right now just ignore it. */
- break;
- }
- }
-
- /* We didn't find what we were looking for. */
- return NULL_TREE;
-
-#undef WALK_SUBTREE
-#undef WALK_SUBTREE_TAIL
-}
-
-/* Like walk_tree, but does not walk duplicate nodes more than once. */
-
-tree
-walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data)
-{
- tree result;
- struct pointer_set_t *pset;
-
- pset = pointer_set_create ();
- result = walk_tree (tp, func, data, pset);
- pointer_set_destroy (pset);
- return result;
+ append_to_statement_list_force (copy_generic_body (&id), &DECL_SAVED_TREE (clone));
}
/* Passed to walk_tree. Copies the node pointed to, if appropriate. */
copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
enum tree_code code = TREE_CODE (*tp);
+ inline_data *id = (inline_data *) data;
/* We make copies of most nodes. */
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
tree chain = TREE_CHAIN (*tp);
tree new;
+ if (id && id->versioning_p && replace_ref_tree (id, tp))
+ {
+ *walk_subtrees = 0;
+ return NULL_TREE;
+ }
/* Copy the node. */
new = copy_node (*tp);
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)
/* The SAVE_EXPR pointed to by TP is being copied. If ST contains
information indicating to what new SAVE_EXPR this one should be mapped,
- use that one. Otherwise, create a new node and enter it in ST. */
+ use that one. Otherwise, create a new node and enter it in ST. FN is
+ the function into which the copy will be placed. */
static void
remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
/* Copy the decl and remember the copy. */
insert_decl_map (id, decl,
- copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
- DECL_CONTEXT (decl)));
+ copy_decl_for_dup (decl, DECL_CONTEXT (decl),
+ DECL_CONTEXT (decl), /*versioning=*/false));
}
return NULL_TREE;
/* Set up ID. */
memset (&id, 0, sizeof (id));
- VARRAY_TREE_INIT (id.fns, 1, "fns");
- VARRAY_PUSH_TREE (id.fns, current_function_decl);
+ id.callee = current_function_decl;
+ id.caller = current_function_decl;
id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
/* Walk the tree once to find local labels. */
return walk_tree_without_duplicates (&top, debug_find_tree_1, search) != 0;
}
+
/* Declare the variables created by the inliner. Add all the variables in
VARS to BIND_EXPR. */
static void
-declare_inline_vars (tree bind_expr, tree vars)
+declare_inline_vars (tree block, tree vars)
{
tree t;
for (t = vars; t; t = TREE_CHAIN (t))
DECL_SEEN_IN_BIND_EXPR_P (t) = 1;
- add_var_to_bind_expr (bind_expr, vars);
+ if (block)
+ BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars);
+}
+
+
+/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN,
+ but now it will be in the TO_FN. VERSIONING means that this function
+ is used by the versioning utility (not inlining or cloning). */
+
+tree
+copy_decl_for_dup (tree decl, tree from_fn, tree to_fn, bool versioning)
+{
+ tree copy;
+
+ gcc_assert (DECL_P (decl));
+ /* Copy the declaration. */
+ if (!versioning
+ && (TREE_CODE (decl) == PARM_DECL
+ || TREE_CODE (decl) == RESULT_DECL))
+ {
+ tree type = TREE_TYPE (decl);
+
+ /* For a parameter or result, we must make an equivalent VAR_DECL,
+ not a new PARM_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);
+ }
+ else
+ {
+ copy = copy_node (decl);
+ /* The COPY is not abstract; it will be generated in TO_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;
+ }
+ }
+
+ /* 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) != from_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) = to_fn;
+
+ return copy;
+}
+
+/* Return a copy of the function's argument tree. */
+static tree
+copy_arguments_for_versioning (tree orig_parm, inline_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, inline_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;
+ inline_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));
+
+ /* The new version. */
+ id.node = new_version_node;
+
+ /* The old version. */
+ 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;
+ id.callee_cfun = DECL_STRUCT_FUNCTION (old_decl);
+
+ 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 && !replace_info->ref_p)
+ insert_decl_map (&id, replace_info->old_tree,
+ replace_info->new_tree);
+ else if (replace_info->replace_p && replace_info->ref_p)
+ id.ipa_info = tree_map;
+ }
+
+ DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.callee), &id);
+
+ /* Renumber the lexical scoping (non-code) blocks consecutively. */
+ number_blocks (id.caller);
+
+ 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;
+}
+
+/* Replace an INDIRECT_REF tree of a given DECL tree with a new
+ given tree.
+ ID->ipa_info keeps the old tree and the new tree.
+ TP points to the INDIRECT REF tree. Return true if
+ the trees were replaced. */
+static bool
+replace_ref_tree (inline_data * id, tree * tp)
+{
+ bool replaced = false;
+ tree new;
+
+ if (id->ipa_info && VARRAY_ACTIVE_SIZE (id->ipa_info) > 0)
+ {
+ unsigned i;
+
+ for (i = 0; i < VARRAY_ACTIVE_SIZE (id->ipa_info); i++)
+ {
+ struct ipa_replace_map *replace_info;
+ replace_info = VARRAY_GENERIC_PTR (id->ipa_info, i);
+
+ if (replace_info->replace_p && replace_info->ref_p)
+ {
+ tree old_tree = replace_info->old_tree;
+ tree new_tree = replace_info->new_tree;
+
+ if (TREE_CODE (*tp) == INDIRECT_REF
+ && TREE_OPERAND (*tp, 0) == old_tree)
+ {
+ new = copy_node (new_tree);
+ *tp = new;
+ replaced = true;
+ }
+ }
+ }
+ }
+ return replaced;
+}
+
+/* Return true if we are inlining. */
+static inline bool
+inlining_p (inline_data * id)
+{
+ return (!id->cloning_p && !id->versioning_p);
+}
+
+/* Duplicate a type, fields and all. */
+
+tree
+build_duplicate_type (tree type)
+{
+ inline_data id;
+
+ memset (&id, 0, sizeof (id));
+ id.callee = current_function_decl;
+ id.caller = current_function_decl;
+ id.callee_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;
}