aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <rth@redhat.com>2009-06-26 11:23:32 -0700
committerRichard Henderson <rth@gcc.gnu.org>2009-06-26 11:23:32 -0700
commit27dbd3ac30a786c4081b2a1d75c3996bff35a374 (patch)
tree6390d7426655b799c3e583c3c54f543275010601
parentefb303b1e80ea45e0f4595cea31f2934d7af16f8 (diff)
downloadgcc-27dbd3ac30a786c4081b2a1d75c3996bff35a374.zip
gcc-27dbd3ac30a786c4081b2a1d75c3996bff35a374.tar.gz
gcc-27dbd3ac30a786c4081b2a1d75c3996bff35a374.tar.bz2
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
-rw-r--r--gcc/ChangeLog18
-rw-r--r--gcc/function.h12
-rw-r--r--gcc/ipa-cp.c43
-rw-r--r--gcc/tree-inline.c206
4 files changed, 182 insertions, 97 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 744aaf7..b01fbdd 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,21 @@
+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
diff --git a/gcc/function.h b/gcc/function.h
index de3a46a..8f69429 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -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;
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 581a88a..fe335c4 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -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. */
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 052d341..b97b9b2 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -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);