decisions about when a function is too big to inline. */
#define INSNS_PER_STMT (10)
-static tree declare_return_variable (inline_data *, tree, tree *);
static tree copy_body_r (tree *, int *, void *);
static tree copy_body (inline_data *);
static tree expand_call_inline (tree *, int *, void *);
(splay_tree_value) value);
}
-/* Remap DECL during the copying of the BLOCK tree for the function.
+/* Remap DECL during the copying of the BLOCK tree for the function.
We are only called to remap local variables in the current function. */
static tree
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. */
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);
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) == UNSAVE_EXPR)
- /* UNSAVE_EXPRs should not be generated until expansion time. */
- abort ();
else if (TREE_CODE (*tp) == BIND_EXPR)
copy_bind_expr (tp, walk_subtrees, id);
else if (TREE_CODE (*tp) == LABELED_BLOCK_EXPR)
}
}
}
- else if (TREE_CODE (*tp) == ADDR_EXPR
- && (lang_hooks.tree_inlining.auto_var_in_fn_p
- (TREE_OPERAND (*tp, 0), fn)))
- {
- /* Get rid of &* from inline substitutions. It can occur when
- someone takes the address of a parm or return slot passed by
- invisible reference. */
- 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;
- if (TREE_CODE (value) == INDIRECT_REF)
- {
- if (!lang_hooks.types_compatible_p
- (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0))))
- *tp = fold_convert (TREE_TYPE (*tp),
- TREE_OPERAND (value, 0));
- else
- *tp = TREE_OPERAND (value, 0);
-
- return copy_body_r (tp, walk_subtrees, data);
- }
- }
- }
else if (TREE_CODE (*tp) == INDIRECT_REF)
{
/* Get rid of *& from inline substitutions that can happen when a
{
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
function. */
var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0));
- /* 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 = build1 (INDIRECT_REF, TREE_TYPE (p), 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_sub);
+ insert_decl_map (id, p, var);
/* Declare this new variable. */
TREE_CHAIN (var) = *vars;
return init_stmts;
}
-/* Declare a return variable to replace the RESULT_DECL for the
- function we are calling. An appropriate decl is returned.
-
- ??? Needs documentation of parameters. */
+/* 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
+ 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.
+
+ The return value is a (possibly null) value that is the result of the
+ function as seen by the callee. *USE_P is a (possibly null) value that
+ holds the result as seen by the caller. */
static tree
-declare_return_variable (inline_data *id, tree return_slot_addr, tree *use_p)
+declare_return_variable (inline_data *id, tree return_slot_addr,
+ tree modify_dest, tree *use_p)
{
- tree fn = VARRAY_TOP_TREE (id->fns);
- tree result = DECL_RESULT (fn);
- int need_return_decl = 1;
- tree var;
+ 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;
/* We don't need to do anything for functions that don't return
anything. */
- if (!result || VOID_TYPE_P (TREE_TYPE (result)))
+ if (!result || VOID_TYPE_P (callee_type))
{
*use_p = NULL_TREE;
return NULL_TREE;
}
- var = (lang_hooks.tree_inlining.copy_res_decl_for_inlining
- (result, fn, VARRAY_TREE (id->fns, 0), id->decl_map,
- &need_return_decl, return_slot_addr));
-
+ /* If there was a return slot, then the return value is the
+ dereferenced address of that object. */
+ if (return_slot_addr)
+ {
+ /* The front end shouldn't have used both return_slot_addr and
+ a modify expression. */
+ if (modify_dest)
+ abort ();
+ if (DECL_BY_REFERENCE (result))
+ var = return_slot_addr;
+ else
+ var = build_fold_indirect_ref (return_slot_addr);
+ use = NULL;
+ goto done;
+ }
+
+ /* All types requiring non-trivial constructors should have been handled. */
+ if (TREE_ADDRESSABLE (callee_type))
+ abort ();
+
+ /* Attempt to avoid creating a new temporary variable. */
+ if (modify_dest)
+ {
+ bool use_it = false;
+
+ /* We can't use MODIFY_DEST if there's type promotion involved. */
+ if (!lang_hooks.types_compatible_p (caller_type, callee_type))
+ use_it = false;
+
+ /* ??? If we're assigning to a variable sized type, then we must
+ reuse the destination variable, because we've no good way to
+ create variable sized temporaries at this point. */
+ else if (TREE_CODE (TYPE_SIZE_UNIT (caller_type)) != INTEGER_CST)
+ use_it = true;
+
+ /* 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;
+
+ if (use_it)
+ {
+ var = modify_dest;
+ use = NULL;
+ goto done;
+ }
+ }
+
+ if (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) != INTEGER_CST)
+ abort ();
+
+ 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);
+
/* Do not have the rest of GCC warn about this variable as it should
not be visible to the user. */
TREE_NO_WARNING (var) = 1;
+ /* Build the use expr. If the return type of the function was
+ promoted, convert it back to the expected type. */
+ use = var;
+ if (!lang_hooks.types_compatible_p (TREE_TYPE (var), caller_type))
+ use = fold_convert (caller_type, var);
+
+ done:
/* Register the VAR_DECL as the equivalent for the RESULT_DECL; that
way, when the RESULT_DECL is encountered, it will be
automatically replaced by the VAR_DECL. */
/* Remember this so we can ignore it in remap_decls. */
id->retvar = var;
- /* Build the use expr. If the return type of the function was
- promoted, convert it back to the expected type. */
- if (return_slot_addr)
- /* The function returns through an explicit return slot, not a normal
- return value. */
- *use_p = NULL_TREE;
- else if (TREE_TYPE (var) == TREE_TYPE (TREE_TYPE (fn)))
- *use_p = var;
- else if (TREE_CODE (var) == INDIRECT_REF)
- *use_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (fn)),
- TREE_OPERAND (var, 0));
- else if (TREE_ADDRESSABLE (TREE_TYPE (var)))
- abort ();
- else
- *use_p = build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)), var);
-
- /* Build the declaration statement if FN does not return an
- aggregate. */
- if (need_return_decl)
- return var;
- /* If FN does return an aggregate, there's no need to declare the
- return variable; we're using a variable in our caller's frame. */
- else
- return NULL_TREE;
+ *use_p = use;
+ return var;
}
/* Returns nonzero if a function can be inlined as a tree. */
return NULL;
switch (TREE_CODE (x))
- {
+ {
/* Containers have no cost. */
case TREE_LIST:
case TREE_VEC:
case NOP_EXPR:
case VIEW_CONVERT_EXPR:
case SAVE_EXPR:
- case UNSAVE_EXPR:
case ADDR_EXPR:
case COMPLEX_EXPR:
case EXIT_BLOCK_EXPR:
case STATEMENT_LIST:
case ERROR_MARK:
case NON_LVALUE_EXPR:
- case ENTRY_VALUE_EXPR:
case FDESC_EXPR:
case VA_ARG_EXPR:
case TRY_CATCH_EXPR:
case EXIT_EXPR:
case LOOP_EXPR:
case PHI_NODE:
+ case WITH_SIZE_EXPR:
break;
/* We don't account constants for now. Assume that the cost is amortized
case ASM_EXPR:
case RESX_EXPR:
- *count++;
+ *count += 1;
break;
/* Few special cases of expensive operations. This is useful
tree fn;
tree arg_inits;
tree *inlined_body;
- tree inline_result;
splay_tree st;
tree args;
tree return_slot_addr;
+ tree modify_dest;
location_t saved_location;
struct cgraph_edge *edge;
const char *reason;
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, TREE_TYPE (TREE_TYPE (fn)), NULL_TREE,
+ expr = build (BIND_EXPR, void_type_node, NULL_TREE,
stmt, make_node (BLOCK));
BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn;
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;
-
+
save_tsi = id->tsi;
expand_calls_inline (&arg_inits, id);
id->tsi = save_tsi;
|| TREE_CODE (DECL_INITIAL (fn)) != BLOCK)
abort ();
+ /* 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);
+ else
+ modify_dest = NULL;
+
/* Declare the return variable for the function. */
- decl = declare_return_variable (id, return_slot_addr, &use_retvar);
- if (decl)
- declare_inline_vars (expr, decl);
+ decl = declare_return_variable (id, return_slot_addr,
+ modify_dest, &use_retvar);
/* After we've initialized the parameters, we insert the body of the
function itself. */
append_to_statement_list (label, &BIND_EXPR_BODY (expr));
}
- /* Finally, mention the returned value so that the value of the
- statement-expression is the returned value of the function. */
- if (use_retvar)
- /* Set TREE_TYPE on BIND_EXPR? */
- append_to_statement_list_force (use_retvar, &BIND_EXPR_BODY (expr));
-
/* Clean up. */
splay_tree_delete (id->decl_map);
id->decl_map = st;
/* The new expression has side-effects if the old one did. */
TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t);
- /* We want to create a new variable to hold the result of the inlined
- body. This new variable needs to be added to the function which we
- are inlining into, thus the saving and restoring of
- current_function_decl. */
- {
- tree save_decl = current_function_decl;
- current_function_decl = id->node->decl;
- inline_result = voidify_wrapper_expr (expr, NULL);
- current_function_decl = save_decl;
- }
+ 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 (TREE_CODE (tsi_stmt (id->tsi)) != CALL_EXPR)
- {
- tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
- *tp = inline_result;
- }
+ if (!use_retvar || !modify_dest)
+ *tsi_stmt_ptr (id->tsi) = build_empty_stmt ();
else
- *tp = expr;
+ *tp = use_retvar;
/* 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
the toplevel expression. */
recalculate_side_effects (expr);
- /* 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;
-
/* Update callgraph if needed. */
cgraph_remove_node (edge->callee);
expand_calls_inline (tree *stmt_p, inline_data *id)
{
tree stmt = *stmt_p;
- enum tree_code code = TREE_CODE (stmt);
+ enum tree_code code = TREE_CODE (stmt);
int dummy;
switch (code)
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;
{
inline_data id;
tree prev_fn;
+ tree ifn;
/* There is no point in performing inlining if errors have already
occurred -- and we might crash if we try to inline invalid
/* Clean up. */
htab_delete (id.tree_pruner);
- if (DECL_LANG_SPECIFIC (fn))
- {
- tree 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;
- }
+ 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
{
append_to_statement_list_force (copy_body (&id), &DECL_SAVED_TREE (clone));
}
-/* Save duplicate of body in FN. MAP is used to pass around splay tree
- used to update arguments in restore_body. */
+/* Make and return duplicate of body in FN. Put copies of DECL_ARGUMENTS
+ in *arg_copy and of the static chain, if any, in *sc_copy. */
+
tree
-save_body (tree fn, tree *arg_copy)
+save_body (tree fn, tree *arg_copy, tree *sc_copy)
{
inline_data id;
tree body, *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));
/* Actually copy the body. */
if (TREE_CODE (*tp) == BIND_EXPR)
BIND_EXPR_BLOCK (*tp) = NULL_TREE;
}
-
+
else if (TREE_CODE_CLASS (code) == 't')
*walk_subtrees = 0;
else if (TREE_CODE_CLASS (code) == 'd')
/* Copy the decl and remember the copy. */
insert_decl_map (id, decl,
- copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
+ copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
DECL_CONTEXT (decl)));
}
{
/* Lookup the declaration. */
n = splay_tree_lookup (st, (splay_tree_key) *tp);
-
+
/* If it's there, remap it. */
if (n)
*tp = (tree) n->value;