#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "toplev.h"
#include "tree.h"
#include "tree-inline.h"
#include "rtl.h"
#include "expr.h"
#include "flags.h"
#include "params.h"
#include "input.h"
#include "insn-config.h"
#include "varray.h"
#include "hashtab.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"
#include "tree-gimple.h"
int flag_inline_trees = 0;
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 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 void add_lexical_block (tree current_block, tree new_block);
static tree copy_decl_to_var (tree, copy_body_data *);
static tree copy_result_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 *);
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);
if (key != value)
splay_tree_insert (id->decl_map, (splay_tree_key) value,
(splay_tree_value) value);
}
tree
remap_decl (tree decl, copy_body_data *id)
{
splay_tree_node n;
tree fn;
fn = id->src_fn;
n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
if (!n)
{
tree t = id->copy_decl (decl, id);
insert_decl_map (id, decl, t);
if (!DECL_P (t))
return t;
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);
walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL);
walk_tree (&DECL_SIZE_UNIT (t), copy_body_r, id, NULL);
if (TREE_CODE (t) == FIELD_DECL)
{
walk_tree (&DECL_FIELD_OFFSET (t), copy_body_r, id, NULL);
if (TREE_CODE (DECL_CONTEXT (t)) == QUAL_UNION_TYPE)
walk_tree (&DECL_QUALIFIER (t), copy_body_r, id, NULL);
}
return t;
}
return unshare_expr ((tree) n->value);
}
static tree
remap_type_1 (tree type, copy_body_data *id)
{
splay_tree_node node;
tree new, t;
if (type == NULL)
return type;
node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
if (node)
return (tree) node->value;
if (! variably_modified_type_p (type, id->src_fn))
{
insert_decl_map (id, type, type);
return type;
}
if (TREE_CODE (type) == POINTER_TYPE)
{
new = build_pointer_type_for_mode (remap_type (TREE_TYPE (type), id),
TYPE_MODE (type),
TYPE_REF_CAN_ALIAS_ALL (type));
insert_decl_map (id, type, new);
return new;
}
else if (TREE_CODE (type) == REFERENCE_TYPE)
{
new = build_reference_type_for_mode (remap_type (TREE_TYPE (type), id),
TYPE_MODE (type),
TYPE_REF_CAN_ALIAS_ALL (type));
insert_decl_map (id, type, new);
return new;
}
else
new = copy_node (type);
insert_decl_map (id, type, new);
t = TYPE_MAIN_VARIANT (type);
if (type != t)
{
t = remap_type (t, id);
TYPE_MAIN_VARIANT (new) = t;
TYPE_NEXT_VARIANT (new) = TYPE_MAIN_VARIANT (t);
TYPE_NEXT_VARIANT (t) = new;
}
else
{
TYPE_MAIN_VARIANT (new) = new;
TYPE_NEXT_VARIANT (new) = NULL;
}
if (TYPE_STUB_DECL (type))
TYPE_STUB_DECL (new) = remap_decl (TYPE_STUB_DECL (type), id);
TYPE_POINTER_TO (new) = NULL;
TYPE_REFERENCE_TO (new) = NULL;
switch (TREE_CODE (new))
{
case INTEGER_TYPE:
case REAL_TYPE:
case ENUMERAL_TYPE:
case BOOLEAN_TYPE:
t = TYPE_MIN_VALUE (new);
if (t && TREE_CODE (t) != INTEGER_CST)
walk_tree (&TYPE_MIN_VALUE (new), copy_body_r, id, NULL);
t = TYPE_MAX_VALUE (new);
if (t && TREE_CODE (t) != INTEGER_CST)
walk_tree (&TYPE_MAX_VALUE (new), copy_body_r, id, NULL);
return new;
case FUNCTION_TYPE:
TREE_TYPE (new) = remap_type (TREE_TYPE (new), id);
walk_tree (&TYPE_ARG_TYPES (new), copy_body_r, id, NULL);
return new;
case ARRAY_TYPE:
TREE_TYPE (new) = remap_type (TREE_TYPE (new), id);
TYPE_DOMAIN (new) = remap_type (TYPE_DOMAIN (new), id);
break;
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
{
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:
default:
gcc_unreachable ();
}
walk_tree (&TYPE_SIZE (new), copy_body_r, id, NULL);
walk_tree (&TYPE_SIZE_UNIT (new), copy_body_r, id, NULL);
return new;
}
tree
remap_type (tree type, copy_body_data *id)
{
splay_tree_node node;
if (type == NULL)
return type;
node = splay_tree_lookup (id->decl_map, (splay_tree_key) type);
if (node)
return (tree) node->value;
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, copy_body_data *id)
{
tree old_var;
tree new_decls = NULL_TREE;
for (old_var = decls; old_var; old_var = TREE_CHAIN (old_var))
{
tree new_var;
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;
}
new_var = remap_decl (old_var, id);
if (!new_var || new_var == id->retvar)
;
else
{
gcc_assert (DECL_P (new_var));
TREE_CHAIN (new_var) = new_decls;
new_decls = new_var;
}
}
return nreverse (new_decls);
}
static void
remap_block (tree *block, copy_body_data *id)
{
tree old_block;
tree new_block;
tree fn;
old_block = *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;
BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id);
fn = id->dst_fn;
if (id->transform_lang_insert_block)
lang_hooks.decls.insert_block (new_block);
insert_decl_map (id, old_block, new_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)
{
tree_stmt_iterator oi, ni;
tree new;
new = alloc_stmt_list ();
ni = tsi_start (new);
oi = tsi_start (*tp);
*tp = new;
for (; !tsi_end_p (oi); tsi_next (&oi))
tsi_link_after (&ni, tsi_stmt (oi), TSI_NEW_STMT);
}
static void
copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id)
{
tree block = BIND_EXPR_BLOCK (*tp);
copy_tree_r (tp, walk_subtrees, NULL);
if (block)
{
remap_block (&block, id);
BIND_EXPR_BLOCK (*tp) = block;
}
if (BIND_EXPR_VARS (*tp))
BIND_EXPR_VARS (*tp) = remap_decls (BIND_EXPR_VARS (*tp), id);
}
tree
copy_body_r (tree *tp, int *walk_subtrees, void *data)
{
copy_body_data *id = (copy_body_data *) data;
tree fn = id->src_fn;
tree new_block;
if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify)
{
tree assignment = TREE_OPERAND (*tp, 0);
if (assignment && TREE_CODE (assignment) == MODIFY_EXPR)
{
*tp = copy_node (assignment);
}
else
{
*tp = NULL;
return (tree) (void *)1;
}
}
else if (lang_hooks.tree_inlining.auto_var_in_fn_p (*tp, fn))
{
tree new_decl;
new_decl = remap_decl (*tp, id);
gcc_assert (new_decl);
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->src_fn))
*tp = remap_decl (*tp, id);
else if (TREE_CODE (*tp) == BIND_EXPR)
copy_bind_expr (tp, walk_subtrees, id);
else if (TYPE_P (*tp))
*tp = remap_type (*tp, id);
else if (CONSTANT_CLASS_P (*tp))
{
tree new_type = remap_type (TREE_TYPE (*tp), id);
if (new_type == TREE_TYPE (*tp))
*walk_subtrees = 0;
else if (TREE_CODE (*tp) == INTEGER_CST)
*tp = build_int_cst_wide (new_type, TREE_INT_CST_LOW (*tp),
TREE_INT_CST_HIGH (*tp));
else
{
*tp = copy_node (*tp);
TREE_TYPE (*tp) = new_type;
}
}
else
{
if (TREE_CODE (*tp) == MODIFY_EXPR
&& TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1)
&& (lang_hooks.tree_inlining.auto_var_in_fn_p
(TREE_OPERAND (*tp, 0), fn)))
{
tree decl = TREE_OPERAND (*tp, 0), value;
splay_tree_node n;
n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
if (n)
{
value = (tree) n->value;
STRIP_TYPE_NOPS (value);
if (TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value))
{
*tp = build_empty_stmt ();
return copy_body_r (tp, walk_subtrees, data);
}
}
}
else if (TREE_CODE (*tp) == INDIRECT_REF)
{
tree decl = TREE_OPERAND (*tp, 0);
splay_tree_node n;
n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl);
if (n)
{
tree new;
tree old;
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;
}
}
copy_tree_r (tp, walk_subtrees, NULL);
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) =
build_int_cst
(NULL_TREE,
id->eh_region_offset + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0)));
if (TREE_CODE (*tp) != OMP_CLAUSE)
TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
if (TREE_CODE (*tp) == TARGET_EXPR && TREE_OPERAND (*tp, 3))
{
TREE_OPERAND (*tp, 1) = TREE_OPERAND (*tp, 3);
TREE_OPERAND (*tp, 3) = NULL_TREE;
}
else if (TREE_CODE (*tp) == ADDR_EXPR)
{
walk_tree (&TREE_OPERAND (*tp, 0), copy_body_r, id, NULL);
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;
}
if (id->call_location_p && EXPR_P (*tp))
SET_EXPR_LOCATION (*tp, id->call_location);
}
return NULL_TREE;
}
static basic_block
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;
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);
if (stmt)
{
tree call, decl;
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);
if (call && (decl = get_callee_fndecl (call)))
{
struct cgraph_node *node;
struct cgraph_edge *edge;
switch (id->transform_call_graph_edges)
{
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)
{
edge = cgraph_edge (node, orig_stmt);
gcc_assert (edge);
cgraph_set_call_stmt (edge, stmt);
}
case CB_CGE_MOVE:
edge = cgraph_edge (id->dst_node, orig_stmt);
if (edge)
cgraph_set_call_stmt (edge, stmt);
break;
default:
gcc_unreachable ();
}
}
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->src_cfun, orig_stmt);
if (region > 0)
add_stmt_to_eh_region (stmt, region + id->eh_region_offset);
if ((lookup_stmt_eh_region_fn (id->src_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;
}
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;
FOR_EACH_EDGE (old_edge, ei, bb->succs)
if (!(old_edge->flags & EDGE_EH))
{
edge new;
flags = old_edge->flags;
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);
bsi_next (&bsi);
if (tree_can_throw_internal (copy_stmt))
{
if (!bsi_end_p (bsi))
{
edge e = split_block (new_bb, copy_stmt);
new_bb = e->dest;
bsi = bsi_start (new_bb);
}
make_eh_edges (copy_stmt);
}
}
}
static tree
remap_decl_1 (tree decl, void *data)
{
return remap_decl (decl, (copy_body_data *) data);
}
static tree
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->src_fn;
struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl);
struct function *new_cfun;
struct function *cfun_to_copy =
(struct function *) ggc_alloc_cleared (sizeof (struct function));
basic_block bb;
tree new_fndecl = NULL;
int count_scale, frequency_scale;
if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count)
count_scale = (REG_BR_PROB_BASE * count
/ ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count);
else
count_scale = 1;
if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency)
frequency_scale = (REG_BR_PROB_BASE * frequency
/
ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency);
else
frequency_scale = count_scale;
tree_register_cfg_hooks ();
gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION
(DECL_STRUCT_FUNCTION (callee_fndecl)));
*cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl);
id->src_cfun = cfun_to_copy;
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 = 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 (src_cfun)->count * count_scale /
REG_BR_PROB_BASE);
ENTRY_BLOCK_PTR->frequency =
(ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency *
frequency_scale / REG_BR_PROB_BASE);
EXIT_BLOCK_PTR->count =
(EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale /
REG_BR_PROB_BASE);
EXIT_BLOCK_PTR->frequency =
(EXIT_BLOCK_PTR_FOR_FUNCTION (src_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;
if (cfun->eh)
{
if (id->transform_new_cfg)
init_eh_for_function ();
id->eh_region_offset
= duplicate_eh_regions (cfun_to_copy, remap_decl_1, id,
0, id->eh_region);
}
FOR_EACH_BB_FN (bb, cfun_to_copy)
bb->aux = copy_bb (id, bb, frequency_scale, count_scale);
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 (id->transform_new_cfg)
pop_cfun ();
return new_fndecl;
}
static tree
copy_generic_body (copy_body_data *id)
{
tree body;
tree fndecl = id->src_fn;
body = DECL_SAVED_TREE (fndecl);
walk_tree (&body, copy_body_r, id, NULL);
return body;
}
static tree
copy_body (copy_body_data *id, gcov_type count, int frequency,
basic_block entry_block_map, basic_block exit_block_map)
{
tree fndecl = id->src_fn;
tree body;
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;
}
static bool
self_inlining_addr_expr (tree value, tree fn)
{
tree var;
if (TREE_CODE (value) != ADDR_EXPR)
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 (copy_body_data *id, tree p, tree value, tree fn,
basic_block bb, tree *vars)
{
tree init_stmt;
tree var;
tree var_sub;
if (TREE_READONLY (p)
&& !TREE_ADDRESSABLE (p)
&& value && !TREE_SIDE_EFFECTS (value))
{
if (is_gimple_min_invariant (value)
&& lang_hooks.types_compatible_p (TREE_TYPE (value), TREE_TYPE (p))
&& ! self_inlining_addr_expr (value, fn))
{
insert_decl_map (id, p, value);
return;
}
}
var = copy_decl_to_var (p, id);
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;
insert_decl_map (id, p, var_sub);
TREE_CHAIN (var) = *vars;
*vars = var;
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p)))
TREE_READONLY (var) = 0;
if (value)
{
tree rhs = fold_convert (TREE_TYPE (var), value);
block_stmt_iterator bsi = bsi_last (bb);
if (rhs == error_mark_node)
return;
STRIP_USELESS_TYPE_CONVERSION (rhs);
init_stmt = build2 (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
if (!is_gimple_val (rhs)
&& (!is_gimple_cast (rhs)
|| !is_gimple_val (TREE_OPERAND (rhs, 0))))
gimplify_stmt (&init_stmt);
if (init_stmt)
bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT);
}
}
static void
initialize_inlined_parameters (copy_body_data *id, tree args, tree static_chain,
tree fn, basic_block bb)
{
tree parms;
tree a;
tree p;
tree vars = NULL_TREE;
int argnum = 0;
parms = DECL_ARGUMENTS (fn);
for (p = parms, a = args; p;
a = a ? TREE_CHAIN (a) : a, p = TREE_CHAIN (p))
{
tree value;
++argnum;
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, bb, &vars);
}
p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
gcc_assert (fn != current_function_decl);
if (p)
{
gcc_assert (static_chain);
setup_one_parameter (id, p, static_chain, fn, bb, &vars);
}
declare_inline_vars (id->block, vars);
}
static tree
declare_return_variable (copy_body_data *id, tree return_slot_addr,
tree modify_dest, tree *use_p)
{
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));
tree var, use;
if (!result || VOID_TYPE_P (callee_type))
{
*use_p = NULL_TREE;
return NULL_TREE;
}
if (return_slot_addr)
{
gcc_assert (!modify_dest);
if (DECL_BY_REFERENCE (result))
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;
}
gcc_assert (!TREE_ADDRESSABLE (callee_type));
if (modify_dest)
{
bool use_it = false;
if (!lang_hooks.types_compatible_p (caller_type, callee_type))
use_it = false;
else if (TREE_CODE (TYPE_SIZE_UNIT (caller_type)) != INTEGER_CST)
use_it = true;
else if (TREE_ADDRESSABLE (result))
use_it = false;
else
{
tree base_m = get_base_address (modify_dest);
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)
{
var = modify_dest;
use = NULL;
goto done;
}
}
gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
var = copy_result_decl_to_var (result, id);
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
= tree_cons (NULL_TREE, var,
DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list);
TREE_NO_WARNING (var) = 1;
declare_inline_vars (id->block, var);
use = var;
if (!lang_hooks.types_compatible_p (TREE_TYPE (var), caller_type))
use = fold_convert (caller_type, var);
STRIP_USELESS_TYPE_CONVERSION (use);
if (DECL_BY_REFERENCE (result))
var = build_fold_addr_expr (var);
done:
insert_decl_map (id, result, var);
id->retvar = var;
*use_p = use;
return var;
}
bool
tree_inlinable_function_p (tree fn)
{
return inlinable_function_p (fn);
}
static const char *inline_forbidden_reason;
static tree
inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
void *fnp)
{
tree node = *nodep;
tree fn = (tree) fnp;
tree t;
switch (TREE_CODE (node))
{
case CALL_EXPR:
if (alloca_call_p (node)
&& !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
inline_forbidden_reason
= G_("function %q+F can never be inlined because it uses "
"alloca (override using the always_inline attribute)");
return node;
}
t = get_callee_fndecl (node);
if (! t)
break;
if (setjmp_call_p (t))
{
inline_forbidden_reason
= G_("function %q+F can never be inlined because it uses setjmp");
return node;
}
if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (t))
{
case BUILT_IN_VA_START:
case BUILT_IN_STDARG_START:
case BUILT_IN_NEXT_ARG:
case BUILT_IN_VA_END:
inline_forbidden_reason
= G_("function %q+F can never be inlined because it "
"uses variable argument lists");
return node;
case BUILT_IN_LONGJMP:
inline_forbidden_reason
= G_("function %q+F can never be inlined because "
"it uses setjmp-longjmp exception handling");
return node;
case BUILT_IN_NONLOCAL_GOTO:
inline_forbidden_reason
= 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:
inline_forbidden_reason
= G_("function %q+F can never be inlined because "
"it uses __builtin_return or __builtin_apply_args");
return node;
default:
break;
}
break;
case GOTO_EXPR:
t = TREE_OPERAND (node, 0);
if (TREE_CODE (t) != LABEL_DECL)
{
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it contains a computed goto");
return node;
}
break;
case LABEL_EXPR:
t = TREE_OPERAND (node, 0);
if (DECL_NONLOCAL (t))
{
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it receives a non-local goto");
return node;
}
break;
case RECORD_TYPE:
case UNION_TYPE:
for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
if (variably_modified_type_p (TREE_TYPE (t), NULL))
{
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it uses variable sized variables");
return node;
}
default:
break;
}
return NULL_TREE;
}
static tree
inline_forbidden_p (tree fndecl)
{
location_t saved_loc = input_location;
block_stmt_iterator bsi;
basic_block bb;
tree ret = NULL_TREE;
if (DECL_IASM_ASM_FUNCTION (fndecl)
&& ! flag_ms_asms)
{
inline_forbidden_reason
= G_("function %q+F can never be inlined because it was declared with asm");
return fndecl;
}
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;
}
static bool
inlinable_function_p (tree fn)
{
bool inlinable = true;
if (DECL_UNINLINABLE (fn))
return false;
inlinable = !lang_hooks.tree_inlining.cannot_inline_tree_fn (&fn);
if (!DECL_SAVED_TREE (fn))
return false;
else if (!flag_inline_trees)
inlinable = false;
else if (!DECL_INLINE (fn) && !flag_unit_at_a_time)
inlinable = false;
else if (inline_forbidden_p (fn))
{
bool do_warning = (warn_inline
&& DECL_INLINE (fn)
&& DECL_DECLARED_INLINE_P (fn)
&& !DECL_IN_SYSTEM_HEADER (fn));
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
sorry (inline_forbidden_reason, fn);
else if (do_warning)
warning (OPT_Winline, inline_forbidden_reason, fn);
inlinable = false;
}
DECL_UNINLINABLE (fn) = !inlinable;
return inlinable;
}
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)
return 4;
else
return ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
}
static tree
estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
{
int *count = (int *) data;
tree x = *tp;
if (IS_TYPE_OR_DECL_P (x))
{
*walk_subtrees = 0;
return NULL;
}
else if (CONSTANT_CLASS_P (x) || REFERENCE_CLASS_P (x))
return NULL;
switch (TREE_CODE (x))
{
case TREE_LIST:
case TREE_VEC:
case BLOCK:
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:
case EXC_PTR_EXPR:
case FILTER_EXPR:
case COMPOUND_EXPR:
case BIND_EXPR:
case WITH_CLEANUP_EXPR:
case NOP_EXPR:
case VIEW_CONVERT_EXPR:
case SAVE_EXPR:
case ADDR_EXPR:
case COMPLEX_EXPR:
case RANGE_EXPR:
case CASE_LABEL_EXPR:
case SSA_NAME:
case CATCH_EXPR:
case EH_FILTER_EXPR:
case STATEMENT_LIST:
case ERROR_MARK:
case NON_LVALUE_EXPR:
case FDESC_EXPR:
case VA_ARG_EXPR:
case TRY_CATCH_EXPR:
case TRY_FINALLY_EXPR:
case LABEL_EXPR:
case GOTO_EXPR:
case RETURN_EXPR:
case EXIT_EXPR:
case LOOP_EXPR:
case PHI_NODE:
case WITH_SIZE_EXPR:
case OMP_CLAUSE:
case OMP_RETURN:
case OMP_CONTINUE:
break;
case IDENTIFIER_NODE:
case INTEGER_CST:
case REAL_CST:
case COMPLEX_CST:
case VECTOR_CST:
case STRING_CST:
*walk_subtrees = 0;
return NULL;
case INIT_EXPR:
case MODIFY_EXPR:
if (TREE_CODE (TREE_OPERAND (x, 1)) == TARGET_EXPR)
break;
case TARGET_EXPR:
x = TREE_OPERAND (x, 0);
if (is_gimple_reg (x))
break;
case CONSTRUCTOR:
*count += estimate_move_cost (TREE_TYPE (x));
break;
case COND_EXPR:
case VEC_COND_EXPR:
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_CEIL_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case NEGATE_EXPR:
case FLOAT_EXPR:
case MIN_EXPR:
case MAX_EXPR:
case ABS_EXPR:
case LSHIFT_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 BIT_AND_EXPR:
case BIT_NOT_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
case TRUTH_NOT_EXPR:
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
case EQ_EXPR:
case NE_EXPR:
case ORDERED_EXPR:
case UNORDERED_EXPR:
case UNLT_EXPR:
case UNLE_EXPR:
case UNGT_EXPR:
case UNGE_EXPR:
case UNEQ_EXPR:
case LTGT_EXPR:
case CONVERT_EXPR:
case CONJ_EXPR:
case PREDECREMENT_EXPR:
case PREINCREMENT_EXPR:
case POSTDECREMENT_EXPR:
case POSTINCREMENT_EXPR:
case SWITCH_EXPR:
case ASM_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;
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case EXACT_DIV_EXPR:
case TRUNC_MOD_EXPR:
case CEIL_MOD_EXPR:
case FLOOR_MOD_EXPR:
case ROUND_MOD_EXPR:
case RDIV_EXPR:
*count += 10;
break;
case CALL_EXPR:
{
tree decl = get_callee_fndecl (x);
tree arg;
if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (decl))
{
case BUILT_IN_CONSTANT_P:
*walk_subtrees = 0;
return NULL_TREE;
case BUILT_IN_EXPECT:
return NULL_TREE;
default:
break;
}
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;
}
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:
*count += 40;
break;
default:
gcc_unreachable ();
}
return NULL;
}
int
estimate_num_insns (tree expr)
{
int num = 0;
struct pointer_set_t *visited_nodes;
basic_block bb;
block_stmt_iterator bsi;
struct function *my_function;
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);
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);
}
static void
add_lexical_block (tree current_block, tree new_block)
{
tree *blk_p;
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;
}
static bool
expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data)
{
copy_body_data *id;
tree t;
tree use_retvar;
tree fn;
splay_tree st;
tree args;
tree return_slot_addr;
tree modify_dest;
location_t saved_location;
struct cgraph_edge *cg_edge;
const char *reason;
basic_block return_block;
edge e;
block_stmt_iterator bsi, stmt_bsi;
bool successfully_inlined = FALSE;
bool purge_dead_abnormal_edges;
tree t_step;
tree var;
id = (copy_body_data *) data;
t = *tp;
saved_location = input_location;
if (EXPR_HAS_LOCATION (t))
input_location = EXPR_LOCATION (t);
if (TREE_CODE (t) != CALL_EXPR)
goto egress;
fn = get_callee_fndecl (t);
if (!fn)
goto egress;
fn = cgraph_node (fn)->decl;
if (! DECL_INITIAL (fn)
&& DECL_ABSTRACT_ORIGIN (fn)
&& DECL_SAVED_TREE (DECL_ABSTRACT_ORIGIN (fn)))
fn = DECL_ABSTRACT_ORIGIN (fn);
if (!id->dst_node->analyzed)
goto egress;
cg_edge = cgraph_edge (id->dst_node, stmt);
if (!cg_edge)
{
struct cgraph_node *dest = cgraph_node (fn);
gcc_assert (dest->needed || !flag_unit_at_a_time);
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;
}
if (!cgraph_inline_p (cg_edge, &reason))
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))
&& (!flag_unit_at_a_time || cgraph_global_info_ready))
{
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))
&& (!flag_unit_at_a_time || cgraph_global_info_ready))
{
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->dst_node->decl)
verify_cgraph_node (cg_edge->callee);
#endif
if (DECL_STRUCT_FUNCTION (cg_edge->callee->decl)->uses_vector)
DECL_STRUCT_FUNCTION (cg_edge->caller->decl)->uses_vector = 1;
id->eh_region = lookup_stmt_eh_region (stmt);
e = split_block (bb, stmt);
bb = e->src;
return_block = e->dest;
remove_edge (e);
stmt_bsi = bsi_last (bb);
bsi_remove (&stmt_bsi, false);
bsi = bsi_start (return_block);
if (bsi_end_p (bsi))
{
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
purge_dead_abnormal_edges = true;
}
else
{
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
purge_dead_abnormal_edges = false;
}
stmt_bsi = bsi_start (return_block);
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);
st = id->decl_map;
id->decl_map = splay_tree_new (splay_tree_compare_pointers,
NULL, NULL);
if (lookup_attribute ("nodebug", DECL_ATTRIBUTES (fn)) != NULL)
{
id->call_location_p = true;
id->call_location = input_location;
}
args = TREE_OPERAND (t, 1);
id->src_fn = fn;
id->src_node = cg_edge->callee;
initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb);
if (DECL_INITIAL (fn))
add_lexical_block (id->block, remap_blocks (DECL_INITIAL (fn), id));
gcc_assert (DECL_INITIAL (fn));
gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK);
return_slot_addr = NULL;
if (TREE_CODE (stmt) == MODIFY_EXPR)
{
modify_dest = TREE_OPERAND (stmt, 0);
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;
declare_return_variable (id, return_slot_addr,
modify_dest, &use_retvar);
copy_body (id, bb->count, bb->frequency, bb, return_block);
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);
}
splay_tree_delete (id->decl_map);
id->decl_map = st;
if (use_retvar && (TREE_CODE (bsi_stmt (stmt_bsi)) != CALL_EXPR))
{
*tp = use_retvar;
maybe_clean_or_replace_eh_stmt (stmt, stmt);
}
else
bsi_remove (&stmt_bsi, true);
if (purge_dead_abnormal_edges)
tree_purge_dead_abnormal_call_edges (return_block);
TREE_USED (*tp) = 1;
(*debug_hooks->outlining_inline_function) (cg_edge->callee->decl);
cgraph_remove_node (cg_edge->callee);
id->block = NULL_TREE;
successfully_inlined = TRUE;
egress:
input_location = saved_location;
return successfully_inlined;
}
static bool
gimple_expand_calls_inline (basic_block bb, copy_body_data *id)
{
block_stmt_iterator bsi;
tree_register_cfg_hooks ();
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
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;
}
void
optimize_inline_calls (tree fn)
{
copy_body_data id;
tree prev_fn;
basic_block bb;
if (errorcount || sorrycount)
return;
memset (&id, 0, sizeof (id));
id.src_node = id.dst_node = cgraph_node (fn);
id.dst_fn = fn;
prev_fn = NULL_TREE;
if (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 ();
FOR_EACH_BB (bb)
gimple_expand_calls_inline (bb, &id);
pop_gimplify_context (NULL);
compact_blocks ();
number_blocks (fn);
#ifdef ENABLE_CHECKING
{
struct cgraph_edge *e;
verify_cgraph_node (id.dst_node);
for (e = id.dst_node->callees; e; e = e->next_callee)
gcc_assert (e->inline_failed);
}
#endif
if (ENTRY_BLOCK_PTR->count)
counts_to_freqs ();
fold_cond_expr_cond ();
}
void
clone_body (tree clone, tree fn, void *arg_map)
{
copy_body_data id;
memset (&id, 0, sizeof (id));
id.src_fn = fn;
id.dst_fn = clone;
id.src_cfun = DECL_STRUCT_FUNCTION (fn);
id.decl_map = (splay_tree)arg_map;
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;
id.eh_region = -1;
append_to_statement_list_force (copy_generic_body (&id), &DECL_SAVED_TREE (clone));
}
tree
copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
enum tree_code code = TREE_CODE (*tp);
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
|| code == TREE_LIST
|| code == TREE_VEC
|| code == TYPE_DECL
|| code == OMP_CLAUSE)
{
tree chain = TREE_CHAIN (*tp);
tree new;
new = copy_node (*tp);
if (flag_mudflap && mf_marked_p (*tp))
mf_mark (new);
*tp = new;
if (code == PARM_DECL
|| code == TREE_LIST
|| code == OMP_CLAUSE)
TREE_CHAIN (*tp) = chain;
if (TREE_CODE (*tp) == BIND_EXPR)
BIND_EXPR_BLOCK (*tp) = NULL_TREE;
}
else if (code == CONSTRUCTOR)
{
tree new;
new = copy_node (*tp);
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)
*walk_subtrees = 0;
else if (TREE_CODE_CLASS (code) == tcc_constant)
*walk_subtrees = 0;
else
gcc_assert (code != STATEMENT_LIST);
return NULL_TREE;
}
static void
remap_save_expr (tree *tp, void *st_, int *walk_subtrees)
{
splay_tree st = (splay_tree) st_;
splay_tree_node n;
tree t;
n = splay_tree_lookup (st, (splay_tree_key) *tp);
if (!n)
{
t = copy_node (*tp);
splay_tree_insert (st, (splay_tree_key) *tp, (splay_tree_value) t);
splay_tree_insert (st, (splay_tree_key) t, (splay_tree_value) t);
}
else
{
*walk_subtrees = 0;
t = (tree) n->value;
}
*tp = t;
}
static tree
mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data)
{
copy_body_data *id = (copy_body_data *) data;
if (TYPE_P (*tp))
*walk_subtrees = 0;
else if (TREE_CODE (*tp) == LABEL_EXPR)
{
tree decl = TREE_OPERAND (*tp, 0);
insert_decl_map (id, decl, id->copy_decl (decl, id));
}
return NULL_TREE;
}
static void
unsave_expr_1 (tree expr)
{
switch (TREE_CODE (expr))
{
case TARGET_EXPR:
if (TREE_OPERAND (expr, 1))
break;
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 3);
TREE_OPERAND (expr, 3) = NULL_TREE;
break;
default:
break;
}
}
static tree
unsave_r (tree *tp, int *walk_subtrees, void *data)
{
copy_body_data *id = (copy_body_data *) data;
splay_tree st = id->decl_map;
splay_tree_node n;
if ((TREE_CODE (*tp) == VAR_DECL && !TREE_STATIC (*tp))
|| TREE_CODE (*tp) == LABEL_DECL)
{
n = splay_tree_lookup (st, (splay_tree_key) *tp);
if (n)
*tp = (tree) n->value;
}
else if (TREE_CODE (*tp) == STATEMENT_LIST)
copy_statement_list (tp);
else if (TREE_CODE (*tp) == BIND_EXPR)
copy_bind_expr (tp, walk_subtrees, id);
else if (TREE_CODE (*tp) == SAVE_EXPR)
remap_save_expr (tp, st, walk_subtrees);
else
{
copy_tree_r (tp, walk_subtrees, NULL);
unsave_expr_1 (*tp);
}
return NULL_TREE;
}
tree
unsave_expr_now (tree expr)
{
copy_body_data id;
if (expr == 0)
return expr;
memset (&id, 0, sizeof (id));
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_tree_without_duplicates (&expr, mark_local_for_remap_r, &id);
walk_tree (&expr, unsave_r, &id, NULL);
splay_tree_delete (id.decl_map);
return expr;
}
static tree
debug_find_tree_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data)
{
if (*tp == data)
return (tree) data;
else
return NULL;
}
bool
debug_find_tree (tree top, tree search)
{
return walk_tree_without_duplicates (&top, debug_find_tree_1, search) != 0;
}
static void
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;
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);
}
static tree
copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
{
DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL)
&& !TREE_STATIC (copy) && !DECL_EXTERNAL (copy))
SET_DECL_RTL (copy, NULL_RTX);
TREE_USED (copy) = 1;
if (!DECL_CONTEXT (decl))
;
else if (DECL_CONTEXT (decl) != id->src_fn)
;
else if (TREE_STATIC (decl))
;
else
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_result_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);
if (DECL_BY_REFERENCE (decl))
type = TREE_TYPE (type);
copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
TREE_READONLY (copy) = TREE_READONLY (decl);
TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
if (!DECL_BY_REFERENCE (decl))
{
TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (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);
DECL_ABSTRACT (copy) = 0;
lang_hooks.dup_lang_specific_decl (copy);
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)
{
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);
}
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;
}
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;
}
bool
tree_versionable_function_p (tree fndecl)
{
if (fndecl == NULL_TREE)
return false;
if (!tree_inlinable_function_p (fndecl))
return false;
return true;
}
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->function_end_locus = DECL_SOURCE_LOCATION (new_decl);
DECL_ARTIFICIAL (new_decl) = 1;
DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
if (!update_clones)
DECL_NAME (new_decl) = create_tmp_var_name (NULL);
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)));
}
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;
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);
if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
DECL_ARGUMENTS (new_decl) =
copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id);
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);
number_blocks (id.dst_fn);
if (DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list != NULL_TREE)
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);
}
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;
number_blocks (new_decl);
splay_tree_delete (id.decl_map);
fold_cond_expr_cond ();
return;
}
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;
}