Commit 27dbd3ac by Richard Henderson Committed by Richard Henderson

function.h (struct function): Add cannot_be_copied_reason, and cannot_be_copied_set.

	* function.h (struct function): Add cannot_be_copied_reason,
	and cannot_be_copied_set.
	* tree-inline.c (has_label_address_in_static_1): Rename from
	inline_forbidden_p_2; don't set inline_forbidden_reason here.
	(cannot_copy_type_1): Rename from inline_forbidden_p_op; likewise
	don't set inline_forbidden_reason.
	(copy_forbidden): New function, split out of inline_forbidden_p.
	(inline_forbidden_p_stmt): Don't check for nonlocal labels here.
	(inline_forbidden_p): Use copy_forbidden.
	(tree_versionable_function_p): Likewise.
	(inlinable_function_p): Merge into tree_inlinable_function_p.
	(tree_function_versioning): Remap cfun->nonlocal_goto_save_area.
	* ipa-cp.c (ipcp_versionable_function_p): New function.
	(ipcp_cloning_candidate_p): Use it.
	(ipcp_node_modifiable_p): Likewise.

From-SVN: r148981
parent efb303b1
2009-06-26 Richard Henderson <rth@redhat.com>
* function.h (struct function): Add cannot_be_copied_reason,
and cannot_be_copied_set.
* tree-inline.c (has_label_address_in_static_1): Rename from
inline_forbidden_p_2; don't set inline_forbidden_reason here.
(cannot_copy_type_1): Rename from inline_forbidden_p_op; likewise
don't set inline_forbidden_reason.
(copy_forbidden): New function, split out of inline_forbidden_p.
(inline_forbidden_p_stmt): Don't check for nonlocal labels here.
(inline_forbidden_p): Use copy_forbidden.
(tree_versionable_function_p): Likewise.
(inlinable_function_p): Merge into tree_inlinable_function_p.
(tree_function_versioning): Remap cfun->nonlocal_goto_save_area.
* ipa-cp.c (ipcp_versionable_function_p): New function.
(ipcp_cloning_candidate_p): Use it.
(ipcp_node_modifiable_p): Likewise.
2009-06-26 Olatunji Ruwase <tjruwase@google.com>
* builtins.c (expand_builtin_alloca): Handle builtin alloca
......
......@@ -524,11 +524,17 @@ struct GTY(()) function {
/* Properties used by the pass manager. */
unsigned int curr_properties;
unsigned int last_verified;
/* Interprocedural passes scheduled to have their transform functions
applied next time we execute local pass on them. We maintain it
per-function in order to allow IPA passes to introduce new functions. */
VEC(ipa_opt_pass,heap) * GTY((skip)) ipa_transforms_to_apply;
/* Non-null if the function does something that would prevent it from
being copied; this applies to both versioning and inlining. Set to
a string describing the reason for failure. */
const char * GTY((skip)) cannot_be_copied_reason;
/* Collected bit flags. */
/* Number of units of general registers that need saving in stdarg
......@@ -540,7 +546,6 @@ struct GTY(()) function {
function. */
unsigned int va_list_fpr_size : 8;
/* How commonly executed the function is. Initialized during branch
probabilities pass. */
ENUM_BITFIELD (function_frequency) function_frequency : 2;
......@@ -556,6 +561,11 @@ struct GTY(()) function {
from nested functions. */
unsigned int has_nonlocal_label : 1;
/* Nonzero if we've set cannot_be_copied_reason. I.e. if
(cannot_be_copied_set && !cannot_be_copied_reason), the function
can in fact be copied. */
unsigned int cannot_be_copied_set : 1;
/* Nonzero if current function uses stdarg.h or equivalent. */
unsigned int stdarg : 1;
......
......@@ -351,6 +351,45 @@ ipcp_print_all_lattices (FILE * f)
}
}
/* Return true if ipcp algorithms would allow cloning NODE. */
static bool
ipcp_versionable_function_p (struct cgraph_node *node)
{
tree decl = node->decl;
basic_block bb;
/* There are a number of generic reasons functions cannot be versioned. */
if (!tree_versionable_function_p (decl))
return false;
/* Removing arguments doesn't work if the function takes varargs. */
if (DECL_STRUCT_FUNCTION (decl)->stdarg)
return false;
/* Removing arguments doesn't work if we use __builtin_apply_args. */
FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (decl))
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
const_gimple stmt = gsi_stmt (gsi);
tree t;
if (!is_gimple_call (stmt))
continue;
t = gimple_call_fndecl (stmt);
if (t == NULL_TREE)
continue;
if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (t) == BUILT_IN_APPLY_ARGS)
return false;
}
}
return true;
}
/* Return true if this NODE is viable candidate for cloning. */
static bool
ipcp_cloning_candidate_p (struct cgraph_node *node)
......@@ -374,7 +413,7 @@ ipcp_cloning_candidate_p (struct cgraph_node *node)
cgraph_node_name (node));
return false;
}
if (!tree_versionable_function_p (node->decl))
if (!ipcp_versionable_function_p (node))
{
if (dump_file)
fprintf (dump_file, "Not considering %s for cloning; body is not versionable.\n",
......@@ -677,7 +716,7 @@ ipcp_node_modifiable_p (struct cgraph_node *node)
{
/* Once we will be able to do in-place replacement, we can be more
lax here. */
return tree_versionable_function_p (node->decl);
return ipcp_versionable_function_p (node);
}
/* Print count scale data structures. */
......
......@@ -119,7 +119,6 @@ eni_weights eni_time_weights;
/* Prototypes. */
static tree declare_return_variable (copy_body_data *, tree, tree, tree *);
static bool inlinable_function_p (tree);
static void remap_block (tree *, copy_body_data *);
static void copy_bind_expr (tree *, int *, copy_body_data *);
static tree mark_local_for_remap_r (tree *, int *, void *);
......@@ -2436,26 +2435,32 @@ declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest,
return var;
}
/* Returns nonzero if a function can be inlined as a tree. */
/* Callback through walk_tree. Determine if a DECL_INITIAL makes reference
to a local label. */
bool
tree_inlinable_function_p (tree fn)
static tree
has_label_address_in_static_1 (tree *nodep, int *walk_subtrees, void *fnp)
{
return inlinable_function_p (fn);
}
tree node = *nodep;
tree fn = (tree) fnp;
static const char *inline_forbidden_reason;
if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn)
return node;
if (TYPE_P (node))
*walk_subtrees = 0;
return NULL_TREE;
}
/* A callback for walk_gimple_seq to handle tree operands. Returns
NULL_TREE if a function can be inlined, otherwise sets the reason
why not and returns a tree representing the offending operand. */
/* Callback through walk_tree. Determine if we've got an aggregate
type that we can't support; return non-null if so. */
static tree
inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
void *fnp ATTRIBUTE_UNUSED)
cannot_copy_type_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
tree node = *nodep;
tree t;
tree t, node = *nodep;
if (TREE_CODE (node) == RECORD_TYPE || TREE_CODE (node) == UNION_TYPE)
{
......@@ -2474,21 +2479,78 @@ inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
cycle to try to find out. This should be checked for 4.1. */
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;
}
return node;
}
return NULL_TREE;
}
/* A callback for walk_gimple_seq to handle statements. Returns
non-NULL iff a function can not be inlined. Also sets the reason
why. */
/* Determine if the function can be copied. If so return NULL. If
not return a string describng the reason for failure. */
static const char *
copy_forbidden (struct function *fun, tree fndecl)
{
const char *reason = fun->cannot_be_copied_reason;
tree step;
/* Only examine the function once. */
if (fun->cannot_be_copied_set)
return reason;
/* We cannot copy a function that receives a non-local goto
because we cannot remap the destination label used in the
function that is performing the non-local goto. */
/* ??? Actually, this should be possible, if we work at it.
No doubt there's just a handful of places that simply
assume it doesn't happen and don't substitute properly. */
if (fun->has_nonlocal_label)
{
reason = G_("function %q+F can never be copied "
"because it receives a non-local goto");
goto fail;
}
for (step = fun->local_decls; step; step = TREE_CHAIN (step))
{
tree decl = TREE_VALUE (step);
if (TREE_CODE (decl) == VAR_DECL
&& TREE_STATIC (decl)
&& !DECL_EXTERNAL (decl)
&& DECL_INITIAL (decl)
&& walk_tree_without_duplicates (&DECL_INITIAL (decl),
has_label_address_in_static_1,
fndecl))
{
reason = G_("function %q+F can never be copied because it saves "
"address of local label in a static variable");
goto fail;
}
if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl)
&& variably_modified_type_p (TREE_TYPE (decl), NULL)
&& walk_tree_without_duplicates (&TREE_TYPE (decl),
cannot_copy_type_1, NULL))
{
reason = G_("function %q+F can never be copied "
"because it uses variable sized variables");
goto fail;
}
}
fail:
fun->cannot_be_copied_reason = reason;
fun->cannot_be_copied_set = true;
return reason;
}
static const char *inline_forbidden_reason;
/* A callback for walk_gimple_seq to handle statements. Returns non-null
iff a function can not be inlined. Also sets the reason why. */
static tree
inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
......@@ -2597,21 +2659,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
}
break;
case GIMPLE_LABEL:
t = gimple_label_label (stmt);
if (DECL_NONLOCAL (t))
{
/* We cannot inline a function that receives a non-local goto
because we cannot remap the destination label used in the
function that is performing the non-local goto. */
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it receives a non-local goto");
*handled_ops_p = true;
return t;
}
break;
default:
break;
}
......@@ -2620,28 +2667,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
return NULL_TREE;
}
static tree
inline_forbidden_p_2 (tree *nodep, int *walk_subtrees,
void *fnp)
{
tree node = *nodep;
tree fn = (tree) fnp;
if (TREE_CODE (node) == LABEL_DECL && DECL_CONTEXT (node) == fn)
{
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it saves address of local label in a static variable");
return node;
}
if (TYPE_P (node))
*walk_subtrees = 0;
return NULL_TREE;
}
/* Return true if FNDECL is a function that cannot be inlined into
another one. */
......@@ -2649,12 +2674,18 @@ static bool
inline_forbidden_p (tree fndecl)
{
struct function *fun = DECL_STRUCT_FUNCTION (fndecl);
tree step;
struct walk_stmt_info wi;
struct pointer_set_t *visited_nodes;
basic_block bb;
bool forbidden_p = false;
/* First check for shared reasons not to copy the code. */
inline_forbidden_reason = copy_forbidden (fun, fndecl);
if (inline_forbidden_reason != NULL)
return true;
/* Next, walk the statements of the function looking for
constraucts we can't handle, or are non-optimal for inlining. */
visited_nodes = pointer_set_create ();
memset (&wi, 0, sizeof (wi));
wi.info = (void *) fndecl;
......@@ -2664,31 +2695,12 @@ inline_forbidden_p (tree fndecl)
{
gimple ret;
gimple_seq seq = bb_seq (bb);
ret = walk_gimple_seq (seq, inline_forbidden_p_stmt,
inline_forbidden_p_op, &wi);
ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, NULL, &wi);
forbidden_p = (ret != NULL);
if (forbidden_p)
goto egress;
}
for (step = fun->local_decls; step; step = TREE_CHAIN (step))
{
tree decl = TREE_VALUE (step);
if (TREE_CODE (decl) == VAR_DECL
&& TREE_STATIC (decl)
&& !DECL_EXTERNAL (decl)
&& DECL_INITIAL (decl))
{
tree ret;
ret = walk_tree_without_duplicates (&DECL_INITIAL (decl),
inline_forbidden_p_2, fndecl);
forbidden_p = (ret != NULL);
if (forbidden_p)
goto egress;
}
break;
}
egress:
pointer_set_destroy (visited_nodes);
return forbidden_p;
}
......@@ -2696,8 +2708,8 @@ egress:
/* Returns nonzero if FN is a function that does not have any
fundamental inline blocking properties. */
static bool
inlinable_function_p (tree fn)
bool
tree_inlinable_function_p (tree fn)
{
bool inlinable = true;
bool do_warning;
......@@ -4304,17 +4316,11 @@ copy_static_chain (tree static_chain, copy_body_data * id)
/* Return true if the function is allowed to be versioned.
This is a guard for the versioning functionality. */
bool
tree_versionable_function_p (tree fndecl)
{
if (fndecl == NULL_TREE)
return false;
/* ??? There are cases where a function is
uninlinable but can be versioned. */
if (!tree_inlinable_function_p (fndecl))
return false;
return true;
return copy_forbidden (DECL_STRUCT_FUNCTION (fndecl), fndecl) == NULL;
}
/* Delete all unreachable basic blocks and update callgraph.
......@@ -4420,7 +4426,8 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id)
trees. If UPDATE_CLONES is set, the call_stmt fields
of edges of clones of the function will be updated. */
void
tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc)* tree_map,
tree_function_versioning (tree old_decl, tree new_decl,
VEC(ipa_replace_map_p,gc)* tree_map,
bool update_clones, bitmap args_to_skip)
{
struct cgraph_node *old_version_node;
......@@ -4547,7 +4554,8 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc
}
/* Copy the Function's body. */
copy_body (&id, old_entry_block->count, old_entry_block->frequency, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR);
copy_body (&id, old_entry_block->count, old_entry_block->frequency,
ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR);
if (DECL_RESULT (old_decl) != NULL_TREE)
{
......@@ -4566,6 +4574,16 @@ tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc
insert_init_stmt (bb, VEC_pop (gimple, init_stmts));
}
/* Remap the nonlocal_goto_save_area, if any. */
if (cfun->nonlocal_goto_save_area)
{
struct walk_stmt_info wi;
memset (&wi, 0, sizeof (wi));
wi.info = &id;
walk_tree (&cfun->nonlocal_goto_save_area, remap_gimple_op_r, &wi, NULL);
}
/* Clean up. */
pointer_map_destroy (id.decl_map);
free_dominance_info (CDI_DOMINATORS);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment