You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "timevar.h"
#include "langhooks.h"
#include "ggc.h"
+#include "toplev.h"
\f
/* Nonzero if we are using EH to handle cleanups. */
compared to those that can. We should be saving some amount
of space by only allocating memory for those that can throw. */
-struct throw_stmt_node GTY(())
-{
- tree stmt;
- int region_nr;
-};
-
-static GTY((param_is (struct throw_stmt_node))) htab_t throw_stmt_table;
-
static void
record_stmt_eh_region (struct eh_region *region, tree t)
{
- struct throw_stmt_node *n;
- void **slot;
-
if (!region)
return;
- n = ggc_alloc (sizeof (*n));
- n->stmt = t;
- n->region_nr = get_eh_region_number (region);
-
- slot = htab_find_slot (throw_stmt_table, n, INSERT);
- gcc_assert (!*slot);
- *slot = n;
+ add_stmt_to_eh_region (t, get_eh_region_number (region));
}
void
-add_stmt_to_eh_region (tree t, int num)
+add_stmt_to_eh_region_fn (struct function *ifun, tree t, int num)
{
struct throw_stmt_node *n;
void **slot;
gcc_assert (num >= 0);
+ gcc_assert (TREE_CODE (t) != RESX_EXPR);
n = ggc_alloc (sizeof (*n));
n->stmt = t;
n->region_nr = num;
- slot = htab_find_slot (throw_stmt_table, n, INSERT);
+ if (!get_eh_throw_stmt_table (ifun))
+ set_eh_throw_stmt_table (ifun, htab_create_ggc (31, struct_ptr_hash,
+ struct_ptr_eq,
+ ggc_free));
+
+ slot = htab_find_slot (get_eh_throw_stmt_table (ifun), n, INSERT);
gcc_assert (!*slot);
*slot = n;
+ /* ??? For the benefit of calls.c, converting all this to rtl,
+ we need to record the call expression, not just the outer
+ modify statement. */
+ if (TREE_CODE (t) == MODIFY_EXPR
+ && (t = get_call_expr_in (t)))
+ add_stmt_to_eh_region_fn (ifun, t, num);
+}
+
+void
+add_stmt_to_eh_region (tree t, int num)
+{
+ add_stmt_to_eh_region_fn (cfun, t, num);
}
bool
-remove_stmt_from_eh_region (tree t)
+remove_stmt_from_eh_region_fn (struct function *ifun, tree t)
{
struct throw_stmt_node dummy;
void **slot;
- if (!throw_stmt_table)
+ if (!get_eh_throw_stmt_table (ifun))
return false;
dummy.stmt = t;
- slot = htab_find_slot (throw_stmt_table, &dummy, NO_INSERT);
+ slot = htab_find_slot (get_eh_throw_stmt_table (ifun), &dummy,
+ NO_INSERT);
if (slot)
{
- htab_clear_slot (throw_stmt_table, slot);
+ htab_clear_slot (get_eh_throw_stmt_table (ifun), slot);
+ /* ??? For the benefit of calls.c, converting all this to rtl,
+ we need to record the call expression, not just the outer
+ modify statement. */
+ if (TREE_CODE (t) == MODIFY_EXPR
+ && (t = get_call_expr_in (t)))
+ remove_stmt_from_eh_region_fn (ifun, t);
return true;
}
else
return false;
}
+bool
+remove_stmt_from_eh_region (tree t)
+{
+ return remove_stmt_from_eh_region_fn (cfun, t);
+}
+
int
-lookup_stmt_eh_region (tree t)
+lookup_stmt_eh_region_fn (struct function *ifun, tree t)
{
struct throw_stmt_node *p, n;
- if (!throw_stmt_table)
+ if (!get_eh_throw_stmt_table (ifun))
return -2;
n.stmt = t;
- p = htab_find (throw_stmt_table, &n);
+ p = htab_find (get_eh_throw_stmt_table (ifun), &n);
return (p ? p->region_nr : -1);
}
+int
+lookup_stmt_eh_region (tree t)
+{
+ /* We can get called from initialized data when -fnon-call-exceptions
+ is on; prevent crash. */
+ if (!cfun)
+ return -1;
+ return lookup_stmt_eh_region_fn (cfun, t);
+}
\f
/* First pass of EH node decomposition. Build up a tree of TRY_FINALLY_EXPR
size_t goto_queue_active;
/* The set of unique labels seen as entries in the goto queue. */
- varray_type dest_array;
+ VEC(tree,heap) *dest_array;
/* A label to be added at the end of the completed transformed
sequence. It will be set if may_fallthru was true *at one time*,
if (! tf->dest_array)
{
- VARRAY_TREE_INIT (tf->dest_array, 10, "dest_array");
- VARRAY_PUSH_TREE (tf->dest_array, lab);
+ tf->dest_array = VEC_alloc (tree, heap, 10);
+ VEC_quick_push (tree, tf->dest_array, lab);
index = 0;
}
else
{
- int n = VARRAY_ACTIVE_SIZE (tf->dest_array);
+ int n = VEC_length (tree, tf->dest_array);
for (index = 0; index < n; ++index)
- if (VARRAY_TREE (tf->dest_array, index) == lab)
+ if (VEC_index (tree, tf->dest_array, index) == lab)
break;
if (index == n)
- VARRAY_PUSH_TREE (tf->dest_array, lab);
+ VEC_safe_push (tree, heap, tf->dest_array, lab);
}
}
break;
x = build (MODIFY_EXPR, void_type_node, x, save_filt);
tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
- x = build1 (RESX_EXPR, void_type_node,
- build_int_cst (NULL_TREE,
- get_eh_region_number (tf->region)));
+ x = build_resx (get_eh_region_number (tf->region));
tsi_link_after (&i, x, TSI_CONTINUE_LINKING);
}
append_to_statement_list (finally, tf->top_p);
- x = build1 (RESX_EXPR, void_type_node,
- build_int_cst (NULL_TREE,
- get_eh_region_number (tf->region)));
+ x = build_resx (get_eh_region_number (tf->region));
+
append_to_statement_list (x, tf->top_p);
return;
do_goto_redirection (q, finally_label, NULL);
replace_goto_queue (tf);
- if (VARRAY_TREE (tf->dest_array, 0) == tf->fallthru_label)
+ if (VEC_index (tree, tf->dest_array, 0) == tf->fallthru_label)
{
/* Reachable by goto to fallthru label only. Redirect it
to the new label (already created, sadly), and do not
lower_eh_constructs_1 (state, &x);
append_to_statement_list (x, &new_stmt);
- x = build1 (RESX_EXPR, void_type_node,
- build_int_cst (NULL_TREE,
- get_eh_region_number (tf->region)));
+ x = build_resx (get_eh_region_number (tf->region));
append_to_statement_list (x, &new_stmt);
}
tree label;
} *labels;
- if (tf->dest_array)
- return_index = VARRAY_ACTIVE_SIZE (tf->dest_array);
- else
- return_index = 0;
+ return_index = VEC_length (tree, tf->dest_array);
labels = xcalloc (sizeof (*labels), return_index + 1);
q = tf->goto_queue;
lower_eh_constructs_1 (state, &finally);
/* Prepare for switch statement generation. */
- if (tf->dest_array)
- nlabels = VARRAY_ACTIVE_SIZE (tf->dest_array);
- else
- nlabels = 0;
+ nlabels = VEC_length (tree, tf->dest_array);
return_index = nlabels;
eh_index = return_index + tf->may_return;
fallthru_index = eh_index + tf->may_throw;
x = build (LABEL_EXPR, void_type_node, CASE_LABEL (last_case));
append_to_statement_list (x, &switch_body);
- x = build1 (RESX_EXPR, void_type_node,
- build_int_cst (NULL_TREE,
- get_eh_region_number (tf->region)));
+ x = build_resx (get_eh_region_number (tf->region));
append_to_statement_list (x, &switch_body);
}
how many destinations are reached by the finally block. Use this to
determine how we process the finally block itself. */
- if (this_tf.dest_array)
- ndests = VARRAY_ACTIVE_SIZE (this_tf.dest_array);
- else
- ndests = 0;
+ ndests = VEC_length (tree, this_tf.dest_array);
ndests += this_tf.may_fallthru;
ndests += this_tf.may_return;
ndests += this_tf.may_throw;
append_to_statement_list (x, tp);
}
+ VEC_free (tree, heap, this_tf.dest_array);
if (this_tf.goto_queue)
free (this_tf.goto_queue);
}
/* Look for things that can throw exceptions, and record them. */
if (state->cur_region && tree_could_throw_p (t))
{
- tree op;
-
record_stmt_eh_region (state->cur_region, t);
note_eh_region_may_contain_throw (state->cur_region);
-
- /* ??? For the benefit of calls.c, converting all this to rtl,
- we need to record the call expression, not just the outer
- modify statement. */
- op = get_call_expr_in (t);
- if (op)
- record_stmt_eh_region (state->cur_region, op);
}
break;
tree *tp = &DECL_SAVED_TREE (current_function_decl);
finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free);
- throw_stmt_table = htab_create_ggc (31, struct_ptr_hash, struct_ptr_eq,
- ggc_free);
collect_finally_tree (*tp, NULL);
foreach_reachable_handler (region_nr, is_resx, make_eh_edge, stmt);
}
+static bool mark_eh_edge_found_error;
+
+/* Mark edge make_eh_edge would create for given region by setting it aux
+ field, output error if something goes wrong. */
+static void
+mark_eh_edge (struct eh_region *region, void *data)
+{
+ tree stmt, lab;
+ basic_block src, dst;
+ edge e;
+
+ stmt = data;
+ lab = get_eh_region_tree_label (region);
+
+ src = bb_for_stmt (stmt);
+ dst = label_to_block (lab);
+
+ e = find_edge (src, dst);
+ if (!e)
+ {
+ error ("EH edge %i->%i is missing %i %i.", src->index, dst->index, src, dst);
+ mark_eh_edge_found_error = true;
+ }
+ else if (!(e->flags & EDGE_EH))
+ {
+ error ("EH edge %i->%i miss EH flag.", src->index, dst->index);
+ mark_eh_edge_found_error = true;
+ }
+ else if (e->aux)
+ {
+ /* ??? might not be mistake. */
+ error ("EH edge %i->%i has duplicated regions.", src->index, dst->index);
+ mark_eh_edge_found_error = true;
+ }
+ else
+ e->aux = (void *)1;
+}
+
+/* Verify that BB containing stmt as last stmt has precisely the edges
+ make_eh_edges would create. */
+bool
+verify_eh_edges (tree stmt)
+{
+ int region_nr;
+ bool is_resx;
+ basic_block bb = bb_for_stmt (stmt);
+ edge_iterator ei;
+ edge e;
+
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ gcc_assert (!e->aux);
+ mark_eh_edge_found_error = false;
+ if (TREE_CODE (stmt) == RESX_EXPR)
+ {
+ region_nr = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0));
+ is_resx = true;
+ }
+ else
+ {
+ region_nr = lookup_stmt_eh_region (stmt);
+ if (region_nr < 0)
+ {
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (e->flags & EDGE_EH)
+ {
+ error ("BB %i can not throw but has EH edges", bb->index);
+ return true;
+ }
+ return false;
+ }
+ if (!tree_could_throw_p (stmt))
+ {
+ error ("BB %i last statement has incorrectly set region", bb->index);
+ return true;
+ }
+ is_resx = false;
+ }
+
+ foreach_reachable_handler (region_nr, is_resx, mark_eh_edge, stmt);
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ if ((e->flags & EDGE_EH) && !e->aux)
+ {
+ error ("Unnecessary EH edge %i->%i", bb->index, e->dest->index);
+ mark_eh_edge_found_error = true;
+ return true;
+ }
+ e->aux = NULL;
+ }
+ return mark_eh_edge_found_error;
+}
\f
/* Return true if the expr can trap, as in dereferencing an invalid pointer
restart:
switch (code)
{
+ case TARGET_MEM_REF:
+ /* For TARGET_MEM_REFs use the information based on the original
+ reference. */
+ expr = TMR_ORIGINAL (expr);
+ code = TREE_CODE (expr);
+ goto restart;
+
case COMPONENT_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
tree_can_throw_internal (tree stmt)
{
int region_nr;
+ bool is_resx = false;
if (TREE_CODE (stmt) == RESX_EXPR)
- region_nr = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0));
+ region_nr = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0)), is_resx = true;
else
region_nr = lookup_stmt_eh_region (stmt);
if (region_nr < 0)
return false;
- return can_throw_internal_1 (region_nr);
+ return can_throw_internal_1 (region_nr, is_resx);
}
bool
tree_can_throw_external (tree stmt)
{
- int region_nr = lookup_stmt_eh_region (stmt);
+ int region_nr;
+ bool is_resx = false;
+
+ if (TREE_CODE (stmt) == RESX_EXPR)
+ region_nr = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0)), is_resx = true;
+ else
+ region_nr = lookup_stmt_eh_region (stmt);
if (region_nr < 0)
return tree_could_throw_p (stmt);
else
- return can_throw_external_1 (region_nr);
+ return can_throw_external_1 (region_nr, is_resx);
}
-bool
-maybe_clean_eh_stmt (tree stmt)
+/* Given a statement OLD_STMT and a new statement NEW_STMT that has replaced
+ OLD_STMT in the function, remove OLD_STMT from the EH table and put NEW_STMT
+ in the table if it should be in there. Return TRUE if a replacement was
+ done that my require an EH edge purge. */
+
+bool
+maybe_clean_or_replace_eh_stmt (tree old_stmt, tree new_stmt)
{
- if (!tree_could_throw_p (stmt))
- if (remove_stmt_from_eh_region (stmt))
- return true;
+ int region_nr = lookup_stmt_eh_region (old_stmt);
+
+ if (region_nr >= 0)
+ {
+ bool new_stmt_could_throw = tree_could_throw_p (new_stmt);
+
+ if (new_stmt == old_stmt && new_stmt_could_throw)
+ return false;
+
+ remove_stmt_from_eh_region (old_stmt);
+ if (new_stmt_could_throw)
+ {
+ add_stmt_to_eh_region (new_stmt, region_nr);
+ return false;
+ }
+ else
+ return true;
+ }
+
return false;
}
-
-#include "gt-tree-eh.h"