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
This commit is contained in:
parent
c7f9c0b983
commit
2505c5edca
9 changed files with 132 additions and 27 deletions
|
@ -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
|
||||
|
|
|
@ -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: ");
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue