#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 "integrate.h"
#include "varray.h"
#include "hashtab.h"
#include "splay-tree.h"
#include "langhooks.h"
#include "cgraph.h"
#include "intl.h"
#include "tree-mudflap.h"
#include "function.h"
#include "diagnostic.h"
#include "tree-iterator.h"
#include "tree-gimple.h"
int flag_inline_trees = 0;
typedef struct inline_data
{
varray_type fns;
unsigned first_inlined_fn;
tree ret_label;
tree retvar;
splay_tree decl_map;
int in_target_cleanup_p;
varray_type inlined_fns;
bool cloning_p;
bool saving_p;
htab_t tree_pruner;
struct cgraph_node *node;
struct cgraph_node *current_node;
tree_stmt_iterator tsi;
} inline_data;
#define INSNS_PER_STMT (10)
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 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_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
insert_decl_map (inline_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);
}
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);
if (!n)
{
tree t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0));
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);
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);
}
#if 0
if (! DECL_NAME (t) && TREE_TYPE (t)
&& lang_hooks.tree_inlining.anon_aggr_type_p (TREE_TYPE (t)))
{
tree members = NULL;
tree src;
for (src = DECL_ANON_UNION_ELEMS (t); src;
src = TREE_CHAIN (src))
{
tree member = remap_decl (TREE_VALUE (src), id);
gcc_assert (!TREE_PURPOSE (src));
members = tree_cons (NULL, member, members);
}
DECL_ANON_UNION_ELEMS (t) = nreverse (members);
}
#endif
insert_decl_map (id, decl, t);
return t;
}
return unshare_expr ((tree) n->value);
}
static tree
remap_type (tree type, inline_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, VARRAY_TOP_TREE (id->fns)))
{
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;
}
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:
case CHAR_TYPE:
t = TYPE_MIN_VALUE (new);
if (t && TREE_CODE (t) != INTEGER_CST)
walk_tree (&TYPE_MIN_VALUE (new), copy_body_r, id, NULL);
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:
walk_tree (&TYPE_FIELDS (new), copy_body_r, id, NULL);
break;
case FILE_TYPE:
case SET_TYPE:
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;
}
static tree
remap_decls (tree decls, inline_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;
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, inline_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 = new_block;
BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id);
fn = VARRAY_TREE (id->fns, 0);
#if 1
if (id->cloning_p)
lang_hooks.decls.insert_block (new_block);
else
{
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
insert_decl_map (id, old_block, new_block);
}
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, inline_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);
}
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);
#if 0
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
if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label)
{
tree return_stmt = *tp;
tree goto_stmt;
tree assignment = TREE_OPERAND (return_stmt, 0);
goto_stmt = build1 (GOTO_EXPR, void_type_node, id->ret_label);
TREE_USED (id->ret_label) = 1;
if (assignment)
{
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));
}
else
*tp = goto_stmt;
}
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;
}
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) == BIND_EXPR)
copy_bind_expr (tp, walk_subtrees, id);
else if (TREE_CODE (*tp) == LABELED_BLOCK_EXPR)
{
tree new_block = copy_node (*tp);
insert_decl_map (id, *tp, new_block);
*tp = new_block;
}
else if (TREE_CODE (*tp) == EXIT_BLOCK_EXPR)
{
splay_tree_node n
= splay_tree_lookup (id->decl_map,
(splay_tree_key) TREE_OPERAND (*tp, 0));
gcc_assert (n);
*tp = copy_node (*tp);
TREE_OPERAND (*tp, 0) = (tree) n->value;
}
else if (TYPE_P (*tp))
*tp = remap_type (*tp, id);
else
{
tree old_node = *tp;
if (TREE_CODE (*tp) == MODIFY_EXPR
&& TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1)
&& (lang_hooks.tree_inlining.auto_var_in_fn_p
(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 = value;
return copy_body_r (tp, walk_subtrees, data);
}
}
}
else if (TREE_CODE (*tp) == INDIRECT_REF)
{
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_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);
}
}
}
copy_tree_r (tp, walk_subtrees, NULL);
if (TREE_CODE (*tp) == CALL_EXPR && id->node && get_callee_fndecl (*tp))
{
if (id->saving_p)
{
struct cgraph_node *node;
struct cgraph_edge *edge;
for (node = id->node->next_clone; node; node = node->next_clone)
{
edge = cgraph_edge (node, old_node);
gcc_assert (edge);
edge->call_expr = *tp;
}
}
else
{
struct cgraph_edge *edge
= cgraph_edge (id->current_node, old_node);
if (edge)
cgraph_clone_edge (edge, id->node, *tp);
}
}
TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id);
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);
recompute_tree_invarant_for_addr_expr (*tp);
*walk_subtrees = 0;
}
}
return NULL_TREE;
}
static tree
copy_body (inline_data *id)
{
tree body;
tree fndecl = VARRAY_TOP_TREE (id->fns);
if (fndecl == current_function_decl
&& cfun->saved_tree)
body = cfun->saved_tree;
else
body = DECL_SAVED_TREE (fndecl);
walk_tree (&body, copy_body_r, id, NULL);
return body;
}
static void
setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
tree *init_stmts, tree *vars, bool *gimplify_init_stmts_p)
{
tree init_stmt;
tree var;
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)))
{
insert_decl_map (id, p, value);
return;
}
}
var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0));
insert_decl_map (id, p, var);
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);
if (rhs == error_mark_node)
return;
init_stmt = build (MODIFY_EXPR, TREE_TYPE (var), var, rhs);
append_to_statement_list (init_stmt, init_stmts);
if (!is_gimple_val (rhs)
&& (!is_gimple_cast (rhs)
|| !is_gimple_val (TREE_OPERAND (rhs, 0))))
*gimplify_init_stmts_p = true;
}
}
static tree
initialize_inlined_parameters (inline_data *id, tree args, tree static_chain,
tree fn, tree bind_expr)
{
tree init_stmts = NULL_TREE;
tree parms;
tree a;
tree p;
tree vars = NULL_TREE;
bool gimplify_init_stmts_p = false;
int argnum = 0;
parms = DECL_ARGUMENTS (fn);
if (fn == current_function_decl)
parms = cfun->saved_args;
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, &init_stmts, &vars,
&gimplify_init_stmts_p);
}
for (; a; a = TREE_CHAIN (a))
{
tree value = TREE_VALUE (a);
append_to_statement_list (value, &init_stmts);
}
p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
if (p)
{
gcc_assert (static_chain);
setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars,
&gimplify_init_stmts_p);
}
if (gimplify_init_stmts_p)
gimplify_body (&init_stmts, current_function_decl);
declare_inline_vars (bind_expr, vars);
return init_stmts;
}
static tree
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 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);
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_STATIC (modify_dest)
&& !TREE_ADDRESSABLE (modify_dest)
&& !TREE_ADDRESSABLE (result))
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_decl_for_inlining (result, callee, caller);
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;
use = var;
if (!lang_hooks.types_compatible_p (TREE_TYPE (var), caller_type))
use = fold_convert (caller_type, 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
= N_("%Jfunction '%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
= N_("%Jfunction '%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
= N_("%Jfunction '%F' can never be inlined because it "
"uses variable argument lists");
return node;
case BUILT_IN_LONGJMP:
inline_forbidden_reason
= N_("%Jfunction '%F' can never be inlined because "
"it uses setjmp-longjmp exception handling");
return node;
case BUILT_IN_NONLOCAL_GOTO:
inline_forbidden_reason
= N_("%Jfunction '%F' can never be inlined because "
"it uses non-local goto");
return node;
default:
break;
}
break;
case GOTO_EXPR:
t = TREE_OPERAND (node, 0);
if (TREE_CODE (t) != LABEL_DECL)
{
inline_forbidden_reason
= N_("%Jfunction '%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
= N_("%Jfunction '%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
= N_("%Jfunction '%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;
tree ret = walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
inline_forbidden_p_1, fndecl);
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, fn);
else if (do_warning)
warning (inline_forbidden_reason, fn, fn);
inlinable = false;
}
DECL_UNINLINABLE (fn) = !inlinable;
return inlinable;
}
static tree
estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data)
{
int *count = 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 ARRAY_REF:
case ARRAY_RANGE_REF:
case OBJ_TYPE_REF:
case EXC_PTR_EXPR:
case FILTER_EXPR:
case COMPOUND_EXPR:
case BIND_EXPR:
case LABELED_BLOCK_EXPR:
case WITH_CLEANUP_EXPR:
case NOP_EXPR:
case VIEW_CONVERT_EXPR:
case SAVE_EXPR:
case ADDR_EXPR:
case COMPLEX_EXPR:
case EXIT_BLOCK_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:
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:
x = TREE_OPERAND (x, 0);
case TARGET_EXPR:
case CONSTRUCTOR:
{
HOST_WIDE_INT size;
size = int_size_in_bytes (TREE_TYPE (x));
if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO)
*count += 10;
else
*count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
}
break;
case 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 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 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);
if (decl && DECL_BUILT_IN (decl))
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;
}
*count += 10;
break;
}
default:
gcc_unreachable ();
}
return NULL;
}
int
estimate_num_insns (tree expr)
{
int num = 0;
walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num);
return num;
}
static tree
expand_call_inline (tree *tp, int *walk_subtrees, 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;
const char *reason;
id = (inline_data *) data;
t = *tp;
saved_location = input_location;
if (EXPR_HAS_LOCATION (t))
input_location = EXPR_LOCATION (t);
if (TREE_CODE (*tp) == TARGET_EXPR)
{
#if 0
int i, len = first_rtl_op (TARGET_EXPR);
*walk_subtrees = 0;
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))
*walk_subtrees = 0;
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->current_node->analyzed)
goto egress;
edge = cgraph_edge (id->current_node, t);
if (!edge)
{
struct cgraph_node *dest = cgraph_node (fn);
gcc_assert (dest->needed || !flag_unit_at_a_time);
cgraph_create_edge (id->node, dest, t)->inline_failed
= N_("originally indirect function call not considered for inlining");
goto egress;
}
if (!cgraph_inline_p (edge, &reason))
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
{
sorry ("%Jinlining failed in call to '%F': %s", fn, fn, reason);
sorry ("called from here");
}
else if (warn_inline && DECL_DECLARED_INLINE_P (fn)
&& !DECL_IN_SYSTEM_HEADER (fn)
&& strlen (reason))
{
warning ("%Jinlining failed in call to '%F': %s", fn, fn, reason);
warning ("called from here");
}
goto egress;
}
#ifdef ENABLE_CHECKING
if (edge->callee->decl != id->node->decl)
verify_cgraph_node (edge->callee);
#endif
if (! lang_hooks.tree_inlining.start_inlining (fn))
goto egress;
stmt = NULL;
expr = build (BIND_EXPR, void_type_node, NULL_TREE,
stmt, make_node (BLOCK));
BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn;
st = id->decl_map;
id->decl_map = splay_tree_new (splay_tree_compare_pointers,
NULL, NULL);
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)
{
tree_stmt_iterator save_tsi;
save_tsi = id->tsi;
expand_calls_inline (&arg_inits, id);
id->tsi = save_tsi;
append_to_statement_list (arg_inits, &BIND_EXPR_BODY (expr));
}
VARRAY_PUSH_TREE (id->fns, fn);
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);
}
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);
modify_dest = tsi_stmt (id->tsi);
if (TREE_CODE (modify_dest) == MODIFY_EXPR)
modify_dest = TREE_OPERAND (modify_dest, 0);
else
modify_dest = NULL;
decl = declare_return_variable (id, return_slot_addr,
modify_dest, &use_retvar);
{
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);
if (TREE_USED (id->ret_label))
{
tree label = build1 (LABEL_EXPR, void_type_node, id->ret_label);
append_to_statement_list (label, &BIND_EXPR_BODY (expr));
}
splay_tree_delete (id->decl_map);
id->decl_map = st;
TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t);
tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
if (!use_retvar || !modify_dest)
*tsi_stmt_ptr (id->tsi) = build_empty_stmt ();
else
*tp = use_retvar;
recalculate_side_effects (expr);
cgraph_remove_node (edge->callee);
expand_calls_inline (inlined_body, id);
VARRAY_POP (id->fns);
*walk_subtrees = 0;
lang_hooks.tree_inlining.end_inlining (fn);
egress:
input_location = saved_location;
return NULL_TREE;
}
static void
expand_calls_inline (tree *stmt_p, inline_data *id)
{
tree stmt = *stmt_p;
enum tree_code code = TREE_CODE (stmt);
int dummy;
switch (code)
{
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:
gcc_unreachable ();
case RETURN_EXPR:
stmt_p = &TREE_OPERAND (stmt, 0);
stmt = *stmt_p;
if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
break;
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;
case CALL_EXPR:
expand_call_inline (stmt_p, &dummy, id);
break;
default:
break;
}
}
void
optimize_inline_calls (tree fn)
{
inline_data id;
tree prev_fn;
tree ifn;
if (errorcount || sorrycount)
return;
memset (&id, 0, sizeof (id));
id.current_node = id.node = cgraph_node (fn);
VARRAY_TREE_INIT (id.fns, 32, "fns");
VARRAY_PUSH_TREE (id.fns, fn);
prev_fn = NULL_TREE;
if (current_function_decl)
{
VARRAY_PUSH_TREE (id.fns, current_function_decl);
prev_fn = current_function_decl;
}
prev_fn = lang_hooks.tree_inlining.add_pending_fn_decls (&id.fns, prev_fn);
VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");
id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns);
id.tree_pruner = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
expand_calls_inline (&DECL_SAVED_TREE (fn), &id);
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;
#ifdef ENABLE_CHECKING
{
struct cgraph_edge *e;
verify_cgraph_node (id.node);
for (e = id.node->callees; e; e = e->next_callee)
gcc_assert (e->inline_failed);
}
#endif
}
void
clone_body (tree clone, tree fn, void *arg_map)
{
inline_data id;
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.decl_map = (splay_tree)arg_map;
id.cloning_p = true;
append_to_statement_list_force (copy_body (&id), &DECL_SAVED_TREE (clone));
}
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));
body = copy_body (&id);
splay_tree_delete (id.decl_map);
return body;
}
#define WALK_SUBTREE(NODE) \
do \
{ \
result = walk_tree (&(NODE), func, data, htab); \
if (result) \
return result; \
} \
while (0)
static tree
walk_type_fields (tree type, walk_tree_fn func, void *data, void *htab)
{
tree result = NULL_TREE;
switch (TREE_CODE (type))
{
case POINTER_TYPE:
case REFERENCE_TYPE:
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))))
&& !htab)
{
result = walk_tree_without_duplicates (&TREE_TYPE (type),
func, data);
if (result)
return result;
break;
}
case COMPLEX_TYPE:
WALK_SUBTREE (TREE_TYPE (type));
break;
case METHOD_TYPE:
WALK_SUBTREE (TYPE_METHOD_BASETYPE (type));
case FUNCTION_TYPE:
WALK_SUBTREE (TREE_TYPE (type));
{
tree arg;
for (arg = TYPE_ARG_TYPES (type); arg; arg = TREE_CHAIN (arg))
WALK_SUBTREE (TREE_VALUE (arg));
}
break;
case ARRAY_TYPE:
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;
}
tree
walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_)
{
htab_t htab = (htab_t) htab_;
enum tree_code code;
int walk_subtrees;
tree result;
#define WALK_SUBTREE_TAIL(NODE) \
do \
{ \
tp = & (NODE); \
goto tail_recurse; \
} \
while (0)
tail_recurse:
if (!*tp)
return NULL_TREE;
if (htab)
{
void **slot;
slot = htab_find_slot (htab, *tp, INSERT);
if (*slot)
return NULL_TREE;
*slot = *tp;
}
walk_subtrees = 1;
result = (*func) (tp, &walk_subtrees, data);
if (result)
return result;
code = TREE_CODE (*tp);
if (!walk_subtrees)
{
if (code == TREE_LIST)
WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
else
return NULL_TREE;
}
result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func,
data, htab);
if (result || ! walk_subtrees)
return result;
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));
result = (*func) (type_p, &walk_subtrees, data);
if (result || !walk_subtrees)
return NULL_TREE;
result = walk_type_fields (*type_p, func, data, htab_);
if (result)
return result;
WALK_SUBTREE (TYPE_SIZE (*type_p));
WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p));
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))
{
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 != EXIT_BLOCK_EXPR
&& code != SAVE_EXPR
&& code != BIND_EXPR
&& IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
{
int i, len;
len = first_rtl_op (code);
if (code == TARGET_EXPR
&& TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1))
--len;
#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)
{
if (code != BIND_EXPR
&& !TREE_CHAIN (*tp))
WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1));
else
WALK_SUBTREE (TREE_OPERAND (*tp, len - 1));
}
#endif
}
else if (TYPE_P (*tp))
{
result = walk_type_fields (*tp, func, data, htab_);
if (result)
return result;
}
else
{
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:
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;
while (--len)
WALK_SUBTREE (TREE_VEC_ELT (*tp, len));
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 EXIT_BLOCK_EXPR:
WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 1));
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_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:
break;
}
}
return NULL_TREE;
#undef WALK_SUBTREE
#undef WALK_SUBTREE_TAIL
}
tree
walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data)
{
tree result;
htab_t htab;
htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
result = walk_tree (tp, func, data, htab);
htab_delete (htab);
return result;
}
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)
{
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)
TREE_CHAIN (*tp) = chain;
if (TREE_CODE (*tp) == BIND_EXPR)
BIND_EXPR_BLOCK (*tp) = NULL_TREE;
}
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;
}
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)
{
inline_data *id = (inline_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,
copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
DECL_CONTEXT (decl)));
}
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)
{
inline_data *id = (inline_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)
{
inline_data id;
if (expr == 0)
return expr;
memset (&id, 0, sizeof (id));
VARRAY_TREE_INIT (id.fns, 1, "fns");
VARRAY_PUSH_TREE (id.fns, current_function_decl);
id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
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 bind_expr, 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);
}