summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog17
-rw-r--r--gcc/cgraph.c4
-rw-r--r--gcc/cgraph.h4
-rw-r--r--gcc/except.c4
-rw-r--r--gcc/ipa-pure-const.c105
-rw-r--r--gcc/ipa-reference.c4
-rw-r--r--gcc/ipa-utils.c13
-rw-r--r--gcc/ipa-utils.h3
-rw-r--r--gcc/tree-cfg.c5
9 files changed, 132 insertions, 27 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 19806f55446..33cea052eff 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,20 @@
+2009-04-18 Jan Hubicka <jh@suse.cz>
+
+ * cgraph.c (cgraph_make_edge, dump_cgraph_node, cgraph_set_call_stmt):
+ Set nothrow flag.
+ * cgraph.h (struct function): Reduce loop_nest to 30 bits; add
+ can_throw_external flag.
+ * ipa-reference.c (ipa_utils_reduced_inorder): Update call.
+ * ipa-pure-const.c (ignore_edge): New function.
+ (propagate): Compute order for NOTHROW computation; set NOTHROWs
+ only over can_throw_external edges.
+ (local_pure_const): Add nothrow flag.
+ * ipa-utils.c (searchc): Add ignore_edge callback.
+ (ipa_utils_reduced_inorder): Add ignore_edge callback.
+ * ipa-utils.h (ipa_utils_reduced_inorder): Update prototype.
+ (set_nothrow_function_flags): Update cgraph.
+ * tree-cfg.c (verify_stmt): Relax nothrow checking when in IPA mode.
+
2009-04-18 Richard Guenther <rguenther@suse.de>
PR middle-end/39804
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 86642ab605b..f7189b92eca 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -640,6 +640,7 @@ cgraph_set_call_stmt (struct cgraph_edge *e, gimple new_stmt)
htab_hash_pointer (e->call_stmt));
}
e->call_stmt = new_stmt;
+ e->can_throw_external = stmt_can_throw_external (new_stmt);
if (e->caller->call_site_hash)
{
void **slot;
@@ -704,6 +705,7 @@ cgraph_create_edge (struct cgraph_node *caller, struct cgraph_node *callee,
edge->caller = caller;
edge->callee = callee;
edge->call_stmt = call_stmt;
+ edge->can_throw_external = stmt_can_throw_external (call_stmt);
edge->prev_caller = NULL;
edge->next_caller = callee->callers;
if (callee->callers)
@@ -1215,6 +1217,8 @@ dump_cgraph_node (FILE *f, struct cgraph_node *node)
fprintf(f, "(inlined) ");
if (edge->indirect_call)
fprintf(f, "(indirect) ");
+ if (edge->can_throw_external)
+ fprintf(f, "(can throw external) ");
}
fprintf (f, "\n calls: ");
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index aba0e5927a3..347653fc79e 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -216,9 +216,11 @@ struct cgraph_edge GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_call
per function call. The range is 0 to CGRAPH_FREQ_MAX. */
int frequency;
/* Depth of loop nest, 1 means no loop nest. */
- unsigned int loop_nest : 31;
+ unsigned int loop_nest : 30;
/* Whether this edge describes a call that was originally indirect. */
unsigned int indirect_call : 1;
+ /* Can this call throw externally? */
+ unsigned int can_throw_external : 1;
/* Unique id of the edge. */
int uid;
};
diff --git a/gcc/except.c b/gcc/except.c
index 9874b595d83..bc3b8c495a2 100644
--- a/gcc/except.c
+++ b/gcc/except.c
@@ -2846,6 +2846,10 @@ set_nothrow_function_flags (void)
(current_function_decl))
>= AVAIL_AVAILABLE))
{
+ struct cgraph_node *node = cgraph_node (current_function_decl);
+ struct cgraph_edge *e;
+ for (e = node->callers; e; e = e->next_caller)
+ e->can_throw_external = false;
TREE_NOTHROW (current_function_decl) = 1;
if (dump_file)
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index ca4da1c6a6c..1aef09f5221 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -637,6 +637,12 @@ generate_summary (void)
visited_nodes = NULL;
}
+static bool
+ignore_edge (struct cgraph_edge *e)
+{
+ return (!e->can_throw_external);
+}
+
/* Produce the global information by preforming a transitive closure
on the local information that was produced by generate_summary.
Note that there is no function_transform pass since this only
@@ -656,7 +662,7 @@ propagate (void)
cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
cgraph_remove_node_duplication_hook (node_duplication_hook_holder);
cgraph_remove_node_removal_hook (node_removal_hook_holder);
- order_pos = ipa_utils_reduced_inorder (order, true, false);
+ order_pos = ipa_utils_reduced_inorder (order, true, false, NULL);
if (dump_file)
{
dump_cgraph (dump_file);
@@ -671,7 +677,6 @@ propagate (void)
{
enum pure_const_state_e pure_const_state = IPA_CONST;
bool looping = false;
- bool can_throw = false;
int count = 0;
node = order[i];
@@ -684,13 +689,10 @@ propagate (void)
if (pure_const_state < w_l->pure_const_state)
pure_const_state = w_l->pure_const_state;
- if (w_l->can_throw)
- can_throw = true;
if (w_l->looping)
looping = true;
- if (pure_const_state == IPA_NEITHER
- && can_throw)
+ if (pure_const_state == IPA_NEITHER)
break;
count++;
@@ -707,16 +709,10 @@ propagate (void)
funct_state y_l = get_function_state (y);
if (pure_const_state < y_l->pure_const_state)
pure_const_state = y_l->pure_const_state;
- if (pure_const_state == IPA_NEITHER
- && can_throw)
+ if (pure_const_state == IPA_NEITHER)
break;
if (y_l->looping)
looping = true;
- if (y_l->can_throw && !TREE_NOTHROW (w->decl)
- /* FIXME: We should check that the throw can get external.
- We also should handle only loops formed by can throw external
- edges. */)
- can_throw = true;
}
}
w_info = (struct ipa_dfs_info *) w->aux;
@@ -766,12 +762,80 @@ propagate (void)
default:
break;
}
+ w_info = (struct ipa_dfs_info *) w->aux;
+ w = w_info->next_cycle;
+ }
+ }
+
+ /* Cleanup. */
+ for (node = cgraph_nodes; node; node = node->next)
+ {
+ /* Get rid of the aux information. */
+ if (node->aux)
+ {
+ w_info = (struct ipa_dfs_info *) node->aux;
+ free (node->aux);
+ node->aux = NULL;
+ }
+ }
+ order_pos = ipa_utils_reduced_inorder (order, true, false, ignore_edge);
+ if (dump_file)
+ {
+ dump_cgraph (dump_file);
+ ipa_utils_print_order(dump_file, "reduced for nothrow", order, order_pos);
+ }
+ /* Propagate the local information thru the call graph to produce
+ the global information. All the nodes within a cycle will have
+ the same info so we collapse cycles first. Then we can do the
+ propagation in one pass from the leaves to the roots. */
+ for (i = 0; i < order_pos; i++ )
+ {
+ bool can_throw = false;
+ node = order[i];
+
+ /* Find the worst state for any node in the cycle. */
+ w = node;
+ while (w)
+ {
+ struct cgraph_edge *e;
+ funct_state w_l = get_function_state (w);
+
+ if (w_l->can_throw)
+ can_throw = true;
+
+ if (can_throw)
+ break;
+
+ for (e = w->callees; e; e = e->next_callee)
+ {
+ struct cgraph_node *y = e->callee;
+
+ if (cgraph_function_body_availability (y) > AVAIL_OVERWRITABLE)
+ {
+ funct_state y_l = get_function_state (y);
+
+ if (can_throw)
+ break;
+ if (y_l->can_throw && !TREE_NOTHROW (w->decl)
+ && e->can_throw_external)
+ can_throw = true;
+ }
+ }
+ w_info = (struct ipa_dfs_info *) w->aux;
+ w = w_info->next_cycle;
+ }
+
+ /* Copy back the region's pure_const_state which is shared by
+ all nodes in the region. */
+ w = node;
+ while (w)
+ {
if (!can_throw && !TREE_NOTHROW (w->decl))
{
- /* FIXME: TREE_NOTHROW is not set because passmanager will execute
- verify_ssa and verify_cfg on every function. Before fixup_cfg is done,
- those functions are going to have NOTHROW calls in EH regions reulting
- in ICE. */
+ struct cgraph_edge *e;
+ TREE_NOTHROW (w->decl) = true;
+ for (e = w->callers; e; e = e->next_caller)
+ e->can_throw_external = false;
if (dump_file)
fprintf (dump_file, "Function found to be nothrow: %s\n",
cgraph_node_name (w));
@@ -918,7 +982,12 @@ local_pure_const (void)
}
if (!l->can_throw && !TREE_NOTHROW (current_function_decl))
{
- TREE_NOTHROW (current_function_decl) = 1;
+ struct cgraph_edge *e;
+
+ TREE_NOTHROW (current_function_decl) = true;
+ for (e = cgraph_node (current_function_decl)->callers;
+ e; e = e->next_caller)
+ e->can_throw_external = false;
changed = true;
if (dump_file)
fprintf (dump_file, "Function found to be nothrow: %s\n",
diff --git a/gcc/ipa-reference.c b/gcc/ipa-reference.c
index 6f5c751e05b..36cb5f3f5f9 100644
--- a/gcc/ipa-reference.c
+++ b/gcc/ipa-reference.c
@@ -995,7 +995,7 @@ propagate (void)
struct cgraph_node *w;
struct cgraph_node **order =
XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
- int order_pos = ipa_utils_reduced_inorder (order, false, true);
+ int order_pos = ipa_utils_reduced_inorder (order, false, true, NULL);
int i;
cgraph_remove_function_insertion_hook (function_insertion_hook_holder);
@@ -1006,7 +1006,7 @@ propagate (void)
the global information. All the nodes within a cycle will have
the same info so we collapse cycles first. Then we can do the
propagation in one pass from the leaves to the roots. */
- order_pos = ipa_utils_reduced_inorder (order, true, true);
+ order_pos = ipa_utils_reduced_inorder (order, true, true, NULL);
if (dump_file)
ipa_utils_print_order(dump_file, "reduced", order, order_pos);
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index 3b5d128f60d..2fe0396b496 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -81,7 +81,8 @@ struct searchc_env {
searching from. */
static void
-searchc (struct searchc_env* env, struct cgraph_node *v)
+searchc (struct searchc_env* env, struct cgraph_node *v,
+ bool (*ignore_edge) (struct cgraph_edge *))
{
struct cgraph_edge *edge;
struct ipa_dfs_info *v_info = (struct ipa_dfs_info *) v->aux;
@@ -101,12 +102,15 @@ searchc (struct searchc_env* env, struct cgraph_node *v)
struct ipa_dfs_info * w_info;
struct cgraph_node *w = edge->callee;
+ if (ignore_edge && ignore_edge (edge))
+ continue;
+
if (w->aux && cgraph_function_body_availability (edge->callee) > AVAIL_OVERWRITABLE)
{
w_info = (struct ipa_dfs_info *) w->aux;
if (w_info->new_node)
{
- searchc (env, w);
+ searchc (env, w, ignore_edge);
v_info->low_link =
(v_info->low_link < w_info->low_link) ?
v_info->low_link : w_info->low_link;
@@ -152,7 +156,8 @@ searchc (struct searchc_env* env, struct cgraph_node *v)
int
ipa_utils_reduced_inorder (struct cgraph_node **order,
- bool reduce, bool allow_overwritable)
+ bool reduce, bool allow_overwritable,
+ bool (*ignore_edge) (struct cgraph_edge *))
{
struct cgraph_node *node;
struct searchc_env env;
@@ -193,7 +198,7 @@ ipa_utils_reduced_inorder (struct cgraph_node **order,
while (result)
{
node = (struct cgraph_node *)result->value;
- searchc (&env, node);
+ searchc (&env, node, ignore_edge);
result = splay_tree_min (env.nodes_marked_new);
}
splay_tree_delete (env.nodes_marked_new);
diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h
index 3ccdbafe909..31d78374ff6 100644
--- a/gcc/ipa-utils.h
+++ b/gcc/ipa-utils.h
@@ -39,7 +39,8 @@ struct ipa_dfs_info {
/* In ipa-utils.c */
void ipa_utils_print_order (FILE*, const char *, struct cgraph_node**, int);
-int ipa_utils_reduced_inorder (struct cgraph_node **, bool, bool);
+int ipa_utils_reduced_inorder (struct cgraph_node **, bool, bool,
+ bool (*ignore_edge) (struct cgraph_edge *));
tree get_base_var (tree);
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index d4aa935e1f6..9c701463784 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -4156,7 +4156,10 @@ verify_stmt (gimple_stmt_iterator *gsi)
to match. */
if (lookup_stmt_eh_region (stmt) >= 0)
{
- if (!stmt_could_throw_p (stmt))
+ /* During IPA passes, ipa-pure-const sets nothrow flags on calls
+ and they are updated on statements only after fixup_cfg
+ is executed at beggining of expansion stage. */
+ if (!stmt_could_throw_p (stmt) && cgraph_state != CGRAPH_STATE_IPA_SSA)
{
error ("statement marked for throw, but doesn%'t");
goto fail;