+2004-06-28 Richard Henderson <rth@redhat.com>
+
+ * tree-cfg.c (verify_stmt): Add last_in_block parameter. Verify
+ that eh stmts can throw.
+ (verify_stmts): Update verify_stmt call.
+ (tree_purge_dead_eh_edges, tree_purge_all_dead_eh_edges): New.
+ * tree-eh.c (remove_stmt_from_eh_region): New.
+ (lower_eh_constructs): Fix throw_stmt_table delete routine.
+ (tree_could_trap_p): Match may_trap_p.
+ (maybe_clean_eh_stmt): New.
+ * tree-flow.h: Update decls.
+ * tree-ssa-ccp.c (pass_ccp): Add TODO_verify_stmts.
+ (substitute_and_fold): Clean eh edges.
+ * tree-ssa-dce.c (mark_control_dependent_edges_necessary): Handle
+ empty basic blocks.
+ * tree-ssa-dom.c (need_eh_cleanup): New.
+ (tree_ssa_dominator_optimize): Allocate it. Cleanup eh edges.
+ (optimize_stmt): Cleanup eh stmts; set need_eh_cleanup.
+
2004-06-29 Alan Modra <amodra@bigpond.net.au>
* function.c (assign_parms): Don't abort with zero size stack
TODO: Implement type checking. */
static bool
-verify_stmt (tree stmt)
+verify_stmt (tree stmt, bool last_in_block)
{
tree addr;
if (!is_gimple_stmt (stmt))
{
error ("Is not a valid GIMPLE statement.");
- debug_generic_stmt (stmt);
- return true;
+ goto fail;
}
addr = walk_tree (&stmt, verify_expr, NULL, NULL);
return true;
}
+ /* If the statement is marked as part of an EH region, then it is
+ expected that the statement could throw. Verify that when we
+ have optimizations that simplify statements such that we prove
+ that they cannot throw, that we update other data structures
+ to match. */
+ if (lookup_stmt_eh_region (stmt) >= 0)
+ {
+ if (!tree_could_throw_p (stmt))
+ {
+ error ("Statement marked for throw, but doesn't.");
+ goto fail;
+ }
+ if (!last_in_block && tree_can_throw_internal (stmt))
+ {
+ error ("Statement marked for throw in middle of block.");
+ goto fail;
+ }
+ }
+
return false;
+
+ fail:
+ debug_generic_stmt (stmt);
+ return true;
}
}
}
- for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+ for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
{
tree stmt = bsi_stmt (bsi);
- err |= verify_stmt (stmt);
+ bsi_next (&bsi);
+ err |= verify_stmt (stmt, bsi_end_p (bsi));
addr = walk_tree (&stmt, verify_node_sharing, htab, NULL);
if (addr)
{
return blocks_split;
}
+bool
+tree_purge_dead_eh_edges (basic_block bb)
+{
+ bool changed = false;
+ edge e, next;
+ tree stmt = last_stmt (bb);
+
+ if (stmt && tree_can_throw_internal (stmt))
+ return false;
+
+ for (e = bb->succ; e ; e = next)
+ {
+ next = e->succ_next;
+ if (e->flags & EDGE_EH)
+ {
+ ssa_remove_edge (e);
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+
+bool
+tree_purge_all_dead_eh_edges (bitmap blocks)
+{
+ bool changed = false;
+ size_t i;
+
+ EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i,
+ { changed |= tree_purge_dead_eh_edges (BASIC_BLOCK (i)); });
+
+ return changed;
+}
struct cfg_hooks tree_cfg_hooks = {
"tree",
abort ();
*slot = n;
}
-
+
+bool
+remove_stmt_from_eh_region (tree t)
+{
+ struct throw_stmt_node dummy;
+ void **slot;
+
+ if (!throw_stmt_table)
+ return false;
+
+ dummy.stmt = t;
+ slot = htab_find_slot (throw_stmt_table, &dummy, NO_INSERT);
+ if (slot)
+ {
+ htab_clear_slot (throw_stmt_table, slot);
+ return true;
+ }
+ else
+ return false;
+}
+
int
lookup_stmt_eh_region (tree t)
{
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, free);
+ throw_stmt_table = htab_create_ggc (31, struct_ptr_hash, struct_ptr_eq,
+ ggc_free);
collect_finally_tree (*tp, NULL);
\f
-/* Return true if the expr can trap, as in dereferencing an
- invalid pointer location. */
+/* Return true if the expr can trap, as in dereferencing an invalid pointer
+ location or floating point arithmetic. C.f. the rtl version, may_trap_p.
+ This routine expects only GIMPLE lhs or rhs input. */
bool
tree_could_trap_p (tree expr)
{
enum tree_code code = TREE_CODE (expr);
+ bool honor_nans = false;
+ bool honor_snans = false;
+ bool fp_operation = false;
tree t;
+ if (TREE_CODE_CLASS (code) == '<'
+ || TREE_CODE_CLASS (code) == '1'
+ || TREE_CODE_CLASS (code) == '2')
+ {
+ t = TREE_TYPE (expr);
+ fp_operation = FLOAT_TYPE_P (t);
+ if (fp_operation)
+ {
+ honor_nans = flag_trapping_math && !flag_finite_math_only;
+ honor_snans = flag_signaling_nans != 0;
+ }
+ }
+
switch (code)
{
case ARRAY_REF:
return !t || tree_could_trap_p (t);
case INDIRECT_REF:
- return (TREE_THIS_NOTRAP (expr) == false);
+ return !TREE_THIS_NOTRAP (expr);
+
+ case ASM_EXPR:
+ return TREE_THIS_VOLATILE (expr);
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_MOD_EXPR:
case ROUND_MOD_EXPR:
case TRUNC_MOD_EXPR:
- return true;
+ case RDIV_EXPR:
+ if (honor_snans)
+ return true;
+ if (fp_operation && flag_trapping_math)
+ return true;
+ t = TREE_OPERAND (expr, 1);
+ if (!TREE_CONSTANT (t) || integer_zerop (t))
+ return true;
+ return false;
+
+ case LT_EXPR:
+ case LE_EXPR:
+ case GT_EXPR:
+ case GE_EXPR:
+ case LTGT_EXPR:
+ /* Some floating point comparisons may trap. */
+ return honor_nans;
+
+ case EQ_EXPR:
+ case NE_EXPR:
+ case UNORDERED_EXPR:
+ case ORDERED_EXPR:
+ case UNLT_EXPR:
+ case UNLE_EXPR:
+ case UNGT_EXPR:
+ case UNGE_EXPR:
+ case UNEQ_EXPR:
+ return honor_snans;
+
+ case CONVERT_EXPR:
+ case FIX_TRUNC_EXPR:
+ case FIX_CEIL_EXPR:
+ case FIX_FLOOR_EXPR:
+ case FIX_ROUND_EXPR:
+ /* Conversion of floating point might trap. */
+ return honor_nans;
+
+ case NEGATE_EXPR:
+ case ABS_EXPR:
+ case CONJ_EXPR:
+ /* These operations don't trap even with floating point. */
+ return false;
default:
- break;
+ /* Any floating arithmetic may trap. */
+ if (fp_operation && flag_trapping_math)
+ return true;
+ return false;
}
-
- return false;
}
-
bool
tree_could_throw_p (tree t)
{
return can_throw_external_1 (region_nr);
}
+bool
+maybe_clean_eh_stmt (tree stmt)
+{
+ if (!tree_could_throw_p (stmt))
+ if (remove_stmt_from_eh_region (stmt))
+ return true;
+ return false;
+}
+
#include "gt-tree-eh.h"
extern void verify_stmts (void);
extern tree tree_block_label (basic_block bb);
extern void extract_true_false_edges_from_block (basic_block, edge *, edge *);
+extern bool tree_purge_dead_eh_edges (basic_block);
+extern bool tree_purge_all_dead_eh_edges (bitmap);
/* In tree-pretty-print.c. */
extern void dump_generic_bb (FILE *, basic_block, int, int);
extern bool tree_could_throw_p (tree);
extern bool tree_can_throw_internal (tree);
extern bool tree_can_throw_external (tree);
+extern int lookup_stmt_eh_region (tree);
extern void add_stmt_to_eh_region (tree, int);
+extern bool remove_stmt_from_eh_region (tree);
+extern bool maybe_clean_eh_stmt (tree);
/* In tree-ssa-pre.c */
void add_to_value (tree, tree);
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func | TODO_rename_vars
- | TODO_ggc_collect | TODO_verify_ssa /* todo_flags_finish */
+ | TODO_ggc_collect | TODO_verify_ssa
+ | TODO_verify_stmts /* todo_flags_finish */
};
/* If we folded a builtin function, we'll likely
need to rename VDEFs. */
if (replaced_address || changed)
- mark_new_vars_to_rename (stmt, vars_to_rename);
+ {
+ mark_new_vars_to_rename (stmt, vars_to_rename);
+ if (maybe_clean_eh_stmt (stmt))
+ tree_purge_dead_eh_edges (bb);
+ }
}
if (dump_file && (dump_flags & TDF_DETAILS))
SET_BIT (last_stmt_necessary, cd_bb->index);
t = last_stmt (cd_bb);
- if (is_ctrl_stmt (t))
+ if (t && is_ctrl_stmt (t))
mark_stmt_necessary (t, true);
});
}
/* Track whether or not we have changed the control flow graph. */
static bool cfg_altered;
+/* Bitmap of blocks that have had EH statements cleaned. We should
+ remove thier dead edges eventually. */
+static bitmap need_eh_cleanup;
+
/* Statistics for dominator optimizations. */
struct opt_stats_d
{
nonzero_vars = BITMAP_XMALLOC ();
VARRAY_EDGE_INIT (redirection_edges, 20, "redirection_edges");
VARRAY_GENERIC_PTR_INIT (vrp_data, num_ssa_names, "vrp_data");
+ need_eh_cleanup = BITMAP_XMALLOC ();
/* Setup callbacks for the generic dominator tree walker. */
walk_data.walk_stmts_backward = false;
if (VARRAY_ACTIVE_SIZE (redirection_edges) > 0)
redirect_edges_and_update_ssa_graph (redirection_edges);
+ if (bitmap_first_set_bit (need_eh_cleanup) >= 0)
+ {
+ cfg_altered = tree_purge_all_dead_eh_edges (need_eh_cleanup);
+ bitmap_zero (need_eh_cleanup);
+ }
+
/* We may have made some basic blocks unreachable, remove them. */
cfg_altered |= delete_unreachable_blocks ();
/* If the CFG was altered, then recompute the dominator tree. This
is not strictly needed if we only removed unreachable blocks, but
may produce better results. If we threaded jumps, then rebuilding
- the dominator tree is strictly necessary. */
+ the dominator tree is strictly necessary. Likewise with EH cleanup.
+ Free the dominance info first so that cleanup_tree_cfg doesn't try
+ to verify it. */
if (cfg_altered)
{
+ free_dominance_info (CDI_DOMINATORS);
cleanup_tree_cfg ();
calculate_dominance_info (CDI_DOMINATORS);
}
/* Free nonzero_vars. */
BITMAP_XFREE (nonzero_vars);
+ BITMAP_XFREE (need_eh_cleanup);
}
static bool
the variable in the LHS in the CONST_AND_COPIES table. */
static void
-optimize_stmt (struct dom_walk_data *walk_data,
- basic_block bb ATTRIBUTE_UNUSED,
+optimize_stmt (struct dom_walk_data *walk_data, basic_block bb,
block_stmt_iterator si)
{
stmt_ann_t ann;
else if (TREE_CODE (stmt) == SWITCH_EXPR)
val = SWITCH_COND (stmt);
- if (val && TREE_CODE (val) == INTEGER_CST
- && find_taken_edge (bb_for_stmt (stmt), val))
+ if (val && TREE_CODE (val) == INTEGER_CST && find_taken_edge (bb, val))
cfg_altered = true;
+
+ /* If we simplified a statement in such a way as to be shown that it
+ cannot trap, update the eh information and the cfg to match. */
+ if (maybe_clean_eh_stmt (stmt))
+ {
+ bitmap_set_bit (need_eh_cleanup, bb->index);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, " Flagged to clear EH edges.\n");
+ }
}
-
+
if (may_have_exposed_new_symbols)
{
if (! bd->stmts_to_rescan)