From 2505c5edcac3fbc0a39ad74c63bab1534ee6e491 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Sat, 18 Apr 2009 20:39:52 +0200 Subject: [PATCH] cgraph.c (cgraph_make_edge, [...]): Set nothrow flag. * 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. From-SVN: r146322 --- gcc/ChangeLog | 17 +++++++++ gcc/cgraph.c | 4 ++ gcc/cgraph.h | 4 +- gcc/except.c | 4 ++ gcc/ipa-pure-const.c | 105 ++++++++++++++++++++++++++++++++++++++++++--------- gcc/ipa-reference.c | 4 +- gcc/ipa-utils.c | 13 +++++-- gcc/ipa-utils.h | 3 +- gcc/tree-cfg.c | 5 ++- 9 files changed, 132 insertions(+), 27 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 19806f5..33cea05 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2009-04-18 Jan Hubicka + + * 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 PR middle-end/39804 diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 86642ab..f7189b9 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 aba0e59..347653f 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 9874b59..bc3b8c4 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 ca4da1c..1aef09f 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 6f5c751..36cb5f3 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 3b5d128..2fe0396 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 3ccdbaf..31d7837 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 d4aa935..9c70146 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; -- 2.7.4