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> 2009-06-26 Olatunji Ruwase <tjruwase@google.com>
* builtins.c (expand_builtin_alloca): Handle builtin alloca * builtins.c (expand_builtin_alloca): Handle builtin alloca
......
...@@ -524,11 +524,17 @@ struct GTY(()) function { ...@@ -524,11 +524,17 @@ struct GTY(()) function {
/* Properties used by the pass manager. */ /* Properties used by the pass manager. */
unsigned int curr_properties; unsigned int curr_properties;
unsigned int last_verified; unsigned int last_verified;
/* Interprocedural passes scheduled to have their transform functions /* Interprocedural passes scheduled to have their transform functions
applied next time we execute local pass on them. We maintain it applied next time we execute local pass on them. We maintain it
per-function in order to allow IPA passes to introduce new functions. */ per-function in order to allow IPA passes to introduce new functions. */
VEC(ipa_opt_pass,heap) * GTY((skip)) ipa_transforms_to_apply; 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. */ /* Collected bit flags. */
/* Number of units of general registers that need saving in stdarg /* Number of units of general registers that need saving in stdarg
...@@ -540,7 +546,6 @@ struct GTY(()) function { ...@@ -540,7 +546,6 @@ struct GTY(()) function {
function. */ function. */
unsigned int va_list_fpr_size : 8; unsigned int va_list_fpr_size : 8;
/* How commonly executed the function is. Initialized during branch /* How commonly executed the function is. Initialized during branch
probabilities pass. */ probabilities pass. */
ENUM_BITFIELD (function_frequency) function_frequency : 2; ENUM_BITFIELD (function_frequency) function_frequency : 2;
...@@ -556,6 +561,11 @@ struct GTY(()) function { ...@@ -556,6 +561,11 @@ struct GTY(()) function {
from nested functions. */ from nested functions. */
unsigned int has_nonlocal_label : 1; 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. */ /* Nonzero if current function uses stdarg.h or equivalent. */
unsigned int stdarg : 1; unsigned int stdarg : 1;
......
...@@ -351,6 +351,45 @@ ipcp_print_all_lattices (FILE * f) ...@@ -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. */ /* Return true if this NODE is viable candidate for cloning. */
static bool static bool
ipcp_cloning_candidate_p (struct cgraph_node *node) ipcp_cloning_candidate_p (struct cgraph_node *node)
...@@ -374,7 +413,7 @@ ipcp_cloning_candidate_p (struct cgraph_node *node) ...@@ -374,7 +413,7 @@ ipcp_cloning_candidate_p (struct cgraph_node *node)
cgraph_node_name (node)); cgraph_node_name (node));
return false; return false;
} }
if (!tree_versionable_function_p (node->decl)) if (!ipcp_versionable_function_p (node))
{ {
if (dump_file) if (dump_file)
fprintf (dump_file, "Not considering %s for cloning; body is not versionable.\n", 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) ...@@ -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 /* Once we will be able to do in-place replacement, we can be more
lax here. */ lax here. */
return tree_versionable_function_p (node->decl); return ipcp_versionable_function_p (node);
} }
/* Print count scale data structures. */ /* Print count scale data structures. */
......
...@@ -119,7 +119,6 @@ eni_weights eni_time_weights; ...@@ -119,7 +119,6 @@ eni_weights eni_time_weights;
/* Prototypes. */ /* Prototypes. */
static tree declare_return_variable (copy_body_data *, tree, tree, tree *); 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 remap_block (tree *, copy_body_data *);
static void copy_bind_expr (tree *, int *, copy_body_data *); static void copy_bind_expr (tree *, int *, copy_body_data *);
static tree mark_local_for_remap_r (tree *, int *, void *); 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, ...@@ -2436,26 +2435,32 @@ declare_return_variable (copy_body_data *id, tree return_slot, tree modify_dest,
return var; 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 static tree
tree_inlinable_function_p (tree fn) 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 /* Callback through walk_tree. Determine if we've got an aggregate
NULL_TREE if a function can be inlined, otherwise sets the reason type that we can't support; return non-null if so. */
why not and returns a tree representing the offending operand. */
static tree static tree
inline_forbidden_p_op (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, cannot_copy_type_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
void *fnp ATTRIBUTE_UNUSED) void *data ATTRIBUTE_UNUSED)
{ {
tree node = *nodep; tree t, node = *nodep;
tree t;
if (TREE_CODE (node) == RECORD_TYPE || TREE_CODE (node) == UNION_TYPE) 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, ...@@ -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. */ cycle to try to find out. This should be checked for 4.1. */
for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t)) for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
if (variably_modified_type_p (TREE_TYPE (t), NULL)) if (variably_modified_type_p (TREE_TYPE (t), NULL))
{ return node;
inline_forbidden_reason
= G_("function %q+F can never be inlined "
"because it uses variable sized variables");
return node;
}
} }
return NULL_TREE; return NULL_TREE;
} }
/* A callback for walk_gimple_seq to handle statements. Returns /* Determine if the function can be copied. If so return NULL. If
non-NULL iff a function can not be inlined. Also sets the reason not return a string describng the reason for failure. */
why. */
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 static tree
inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, 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, ...@@ -2597,21 +2659,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
} }
break; 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: default:
break; break;
} }
...@@ -2620,28 +2667,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p, ...@@ -2620,28 +2667,6 @@ inline_forbidden_p_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
return NULL_TREE; 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 /* Return true if FNDECL is a function that cannot be inlined into
another one. */ another one. */
...@@ -2649,12 +2674,18 @@ static bool ...@@ -2649,12 +2674,18 @@ static bool
inline_forbidden_p (tree fndecl) inline_forbidden_p (tree fndecl)
{ {
struct function *fun = DECL_STRUCT_FUNCTION (fndecl); struct function *fun = DECL_STRUCT_FUNCTION (fndecl);
tree step;
struct walk_stmt_info wi; struct walk_stmt_info wi;
struct pointer_set_t *visited_nodes; struct pointer_set_t *visited_nodes;
basic_block bb; basic_block bb;
bool forbidden_p = false; 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 (); visited_nodes = pointer_set_create ();
memset (&wi, 0, sizeof (wi)); memset (&wi, 0, sizeof (wi));
wi.info = (void *) fndecl; wi.info = (void *) fndecl;
...@@ -2664,31 +2695,12 @@ inline_forbidden_p (tree fndecl) ...@@ -2664,31 +2695,12 @@ inline_forbidden_p (tree fndecl)
{ {
gimple ret; gimple ret;
gimple_seq seq = bb_seq (bb); gimple_seq seq = bb_seq (bb);
ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, ret = walk_gimple_seq (seq, inline_forbidden_p_stmt, NULL, &wi);
inline_forbidden_p_op, &wi);
forbidden_p = (ret != NULL); forbidden_p = (ret != NULL);
if (forbidden_p) if (forbidden_p)
goto egress; break;
}
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;
}
} }
egress:
pointer_set_destroy (visited_nodes); pointer_set_destroy (visited_nodes);
return forbidden_p; return forbidden_p;
} }
...@@ -2696,8 +2708,8 @@ egress: ...@@ -2696,8 +2708,8 @@ egress:
/* Returns nonzero if FN is a function that does not have any /* Returns nonzero if FN is a function that does not have any
fundamental inline blocking properties. */ fundamental inline blocking properties. */
static bool bool
inlinable_function_p (tree fn) tree_inlinable_function_p (tree fn)
{ {
bool inlinable = true; bool inlinable = true;
bool do_warning; bool do_warning;
...@@ -4304,17 +4316,11 @@ copy_static_chain (tree static_chain, copy_body_data * id) ...@@ -4304,17 +4316,11 @@ copy_static_chain (tree static_chain, copy_body_data * id)
/* Return true if the function is allowed to be versioned. /* Return true if the function is allowed to be versioned.
This is a guard for the versioning functionality. */ This is a guard for the versioning functionality. */
bool bool
tree_versionable_function_p (tree fndecl) tree_versionable_function_p (tree fndecl)
{ {
if (fndecl == NULL_TREE) return copy_forbidden (DECL_STRUCT_FUNCTION (fndecl), fndecl) == NULL;
return false;
/* ??? There are cases where a function is
uninlinable but can be versioned. */
if (!tree_inlinable_function_p (fndecl))
return false;
return true;
} }
/* Delete all unreachable basic blocks and update callgraph. /* Delete all unreachable basic blocks and update callgraph.
...@@ -4420,7 +4426,8 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id) ...@@ -4420,7 +4426,8 @@ delete_unreachable_blocks_update_callgraph (copy_body_data *id)
trees. If UPDATE_CLONES is set, the call_stmt fields trees. If UPDATE_CLONES is set, the call_stmt fields
of edges of clones of the function will be updated. */ of edges of clones of the function will be updated. */
void 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) bool update_clones, bitmap args_to_skip)
{ {
struct cgraph_node *old_version_node; 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 ...@@ -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 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) 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 ...@@ -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)); 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. */ /* Clean up. */
pointer_map_destroy (id.decl_map); pointer_map_destroy (id.decl_map);
free_dominance_info (CDI_DOMINATORS); 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