/* CFG cleanup for trees.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
-#include "rtl.h"
#include "tm_p.h"
-#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "flags.h"
#include "function.h"
-#include "expr.h"
#include "ggc.h"
#include "langhooks.h"
-#include "diagnostic.h"
#include "tree-flow.h"
#include "timevar.h"
#include "tree-dump.h"
#include "tree-pass.h"
-#include "toplev.h"
#include "except.h"
#include "cfgloop.h"
#include "cfglayout.h"
edge e;
edge_iterator ei;
bool warned;
+ location_t loc;
fold_defer_overflow_warnings ();
- val = gimple_fold (stmt);
+ loc = gimple_location (stmt);
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_COND:
+ {
+ tree lhs = gimple_cond_lhs (stmt);
+ tree rhs = gimple_cond_rhs (stmt);
+ /* For conditions try harder and lookup single-argument
+ PHI nodes. Only do so from the same basic-block though
+ as other basic-blocks may be dead already. */
+ if (TREE_CODE (lhs) == SSA_NAME
+ && !name_registered_for_update_p (lhs))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (lhs);
+ if (gimple_code (def_stmt) == GIMPLE_PHI
+ && gimple_phi_num_args (def_stmt) == 1
+ && gimple_bb (def_stmt) == gimple_bb (stmt)
+ && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
+ || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
+ 0))))
+ lhs = PHI_ARG_DEF (def_stmt, 0);
+ }
+ if (TREE_CODE (rhs) == SSA_NAME
+ && !name_registered_for_update_p (rhs))
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (rhs);
+ if (gimple_code (def_stmt) == GIMPLE_PHI
+ && gimple_phi_num_args (def_stmt) == 1
+ && gimple_bb (def_stmt) == gimple_bb (stmt)
+ && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
+ || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
+ 0))))
+ rhs = PHI_ARG_DEF (def_stmt, 0);
+ }
+ val = fold_binary_loc (loc, gimple_cond_code (stmt),
+ boolean_type_node, lhs, rhs);
+ break;
+ }
+
+ case GIMPLE_SWITCH:
+ val = gimple_switch_index (stmt);
+ break;
+
+ default:
+ val = NULL_TREE;
+ }
taken_edge = find_taken_edge (bb, val);
if (!taken_edge)
{
tree_forwarder_block_p (basic_block bb, bool phi_wanted)
{
gimple_stmt_iterator gsi;
+ location_t locus;
/* BB must have a single outgoing edge. */
if (single_succ_p (bb) != 1
|| (single_succ_edge (bb)->flags & EDGE_ABNORMAL))
return false;
-#if ENABLE_CHECKING
- gcc_assert (bb != ENTRY_BLOCK_PTR);
-#endif
+ gcc_checking_assert (bb != ENTRY_BLOCK_PTR);
+
+ locus = single_succ_edge (bb)->goto_locus;
/* There should not be an edge coming from entry, or an EH edge. */
{
FOR_EACH_EDGE (e, ei, bb->preds)
if (e->src == ENTRY_BLOCK_PTR || (e->flags & EDGE_EH))
return false;
+ /* If goto_locus of any of the edges differs, prevent removing
+ the forwarder block for -O0. */
+ else if (optimize == 0 && e->goto_locus != locus)
+ return false;
}
/* Now walk through the statements backward. We can ignore labels,
case GIMPLE_LABEL:
if (DECL_NONLOCAL (gimple_label_label (stmt)))
return false;
+ if (optimize == 0 && gimple_location (stmt) != locus)
+ return false;
break;
/* ??? For now, hope there's a corresponding debug
return true;
}
-/* Return true if BB has at least one abnormal incoming edge. */
-
-static inline bool
-has_abnormal_incoming_edge_p (basic_block bb)
-{
- edge e;
- edge_iterator ei;
-
- FOR_EACH_EDGE (e, ei, bb->preds)
- if (e->flags & EDGE_ABNORMAL)
- return true;
-
- return false;
-}
-
/* If all the PHI nodes in DEST have alternatives for E1 and E2 and
those alternatives are equal in each of the PHI nodes, then return
true, else return false. */
gimple label;
edge_iterator ei;
gimple_stmt_iterator gsi, gsi_to;
+ bool can_move_debug_stmts;
/* We check for infinite loops already in tree_forwarder_block_p.
However it may happen that the infinite loop is created
if (dest == bb)
return false;
- /* If the destination block consists of a nonlocal label, do not merge
- it. */
+ /* If the destination block consists of a nonlocal label or is a
+ EH landing pad, do not merge it. */
label = first_stmt (dest);
if (label
&& gimple_code (label) == GIMPLE_LABEL
- && DECL_NONLOCAL (gimple_label_label (label)))
+ && (DECL_NONLOCAL (gimple_label_label (label))
+ || EH_LANDING_PAD_NR (gimple_label_label (label)) != 0))
return false;
/* If there is an abnormal edge to basic block BB, but not into
So if there is an abnormal edge to BB, proceed only if there is
no abnormal edge to DEST and there are no phi nodes in DEST. */
- if (has_abnormal_incoming_edge_p (bb)
- && (has_abnormal_incoming_edge_p (dest)
+ if (bb_has_abnormal_pred (bb)
+ && (bb_has_abnormal_pred (dest)
|| !gimple_seq_empty_p (phi_nodes (dest))))
return false;
}
}
+ can_move_debug_stmts = MAY_HAVE_DEBUG_STMTS && single_pred_p (dest);
+
/* Redirect the edges. */
for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
gsi_next (&gsi);
}
- /* Move debug statements if the destination has just a single
- predecessor. */
- if (single_pred_p (dest))
+ /* Move debug statements if the destination has a single predecessor. */
+ if (can_move_debug_stmts)
{
gsi_to = gsi_after_labels (dest);
for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); )
{
- if (!is_gimple_debug (gsi_stmt (gsi)))
+ gimple debug = gsi_stmt (gsi);
+ if (!is_gimple_debug (debug))
break;
gsi_remove (&gsi, false);
- gsi_insert_before (&gsi_to, label, GSI_SAME_STMT);
+ gsi_insert_before (&gsi_to, debug, GSI_SAME_STMT);
}
}
return true;
}
+/* STMT is a call that has been discovered noreturn. Fixup the CFG
+ and remove LHS. Return true if something changed. */
+
+bool
+fixup_noreturn_call (gimple stmt)
+{
+ basic_block bb = gimple_bb (stmt);
+ bool changed = false;
+
+ if (gimple_call_builtin_p (stmt, BUILT_IN_RETURN))
+ return false;
+
+ /* First split basic block if stmt is not last. */
+ if (stmt != gsi_stmt (gsi_last_bb (bb)))
+ split_block (bb, stmt);
+
+ changed |= remove_fallthru_edge (bb->succs);
+
+ /* If there is LHS, remove it. */
+ if (gimple_call_lhs (stmt))
+ {
+ tree op = gimple_call_lhs (stmt);
+ gimple_call_set_lhs (stmt, NULL_TREE);
+
+ /* We need to remove SSA name to avoid checking errors.
+ All uses are dominated by the noreturn and thus will
+ be removed afterwards.
+ We proactively remove affected non-PHI statements to avoid
+ fixup_cfg from trying to update them and crashing. */
+ if (TREE_CODE (op) == SSA_NAME)
+ {
+ use_operand_p use_p;
+ imm_use_iterator iter;
+ gimple use_stmt;
+ bitmap_iterator bi;
+ unsigned int bb_index;
+
+ bitmap blocks = BITMAP_ALLOC (NULL);
+
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, op)
+ {
+ if (gimple_code (use_stmt) != GIMPLE_PHI)
+ bitmap_set_bit (blocks, gimple_bb (use_stmt)->index);
+ else
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ SET_USE (use_p, error_mark_node);
+ }
+ EXECUTE_IF_SET_IN_BITMAP (blocks, 0, bb_index, bi)
+ delete_basic_block (BASIC_BLOCK (bb_index));
+ BITMAP_FREE (blocks);
+ release_ssa_name (op);
+ }
+ update_stmt (stmt);
+ changed = true;
+ }
+ /* Similarly remove VDEF if there is any. */
+ else if (gimple_vdef (stmt))
+ update_stmt (stmt);
+ return changed;
+}
+
+
/* Split basic blocks on calls in the middle of a basic block that are now
known not to return, and remove the unreachable code. */
BB is present in the cfg. */
if (bb == NULL
|| bb->index < NUM_FIXED_BLOCKS
- || bb->index >= n_basic_blocks
+ || bb->index >= last_basic_block
|| BASIC_BLOCK (bb->index) != bb
- || last_stmt (bb) == stmt
|| !gimple_call_noreturn_p (stmt))
continue;
- changed = true;
- split_block (bb, stmt);
- remove_fallthru_edge (bb->succs);
+ changed |= fixup_noreturn_call (stmt);
}
return changed;
retval = cleanup_control_flow_bb (bb);
- /* Forwarder blocks can carry line number information which is
- useful when debugging, so we only clean them up when
- optimizing. */
- if (optimize > 0
- && tree_forwarder_block_p (bb, false)
+ if (tree_forwarder_block_p (bb, false)
&& remove_forwarder_block (bb))
return true;
static void
repair_loop_structures (void)
{
- bitmap changed_bbs = BITMAP_ALLOC (NULL);
+ bitmap changed_bbs;
+
+ timevar_push (TV_REPAIR_LOOPS);
+ changed_bbs = BITMAP_ALLOC (NULL);
fix_loop_structure (changed_bbs);
/* This usually does nothing. But sometimes parts of cfg that originally
scev_reset ();
loops_state_clear (LOOPS_NEED_FIXUP);
+ timevar_pop (TV_REPAIR_LOOPS);
}
/* Cleanup cfg and repair loop structures. */
redirection, replace it with the PHI argument that used
to be on E. */
head = redirect_edge_var_map_vector (e);
- for (i = 0; VEC_iterate (edge_var_map, head, i, vm); ++i)
+ FOR_EACH_VEC_ELT (edge_var_map, head, i, vm)
{
tree old_arg = redirect_edge_var_map_result (vm);
tree new_arg = redirect_edge_var_map_def (vm);
if (gimple_seq_empty_p (phi_nodes (dest))
/* We don't want to deal with a basic block with
abnormal edges. */
- || has_abnormal_incoming_edge_p (bb))
+ || bb_has_abnormal_pred (bb))
continue;
if (!dominated_by_p (CDI_DOMINATORS, dest, bb))
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_ggc_collect /* todo_flags_finish */
+ TODO_ggc_collect /* todo_flags_finish */
| TODO_verify_ssa
}
};