/* CFG cleanup for trees.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 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"
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;
- edge_iterator ei;
- edge e, succ;
- basic_block dest;
+ location_t locus;
/* BB must have a single outgoing edge. */
if (single_succ_p (bb) != 1
gcc_assert (bb != ENTRY_BLOCK_PTR);
#endif
+ locus = single_succ_edge (bb)->goto_locus;
+
+ /* There should not be an edge coming from entry, or an EH edge. */
+ {
+ edge_iterator ei;
+ edge e;
+
+ 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,
anything else means this is not a forwarder block. */
for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
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
+ assignment at the destination. */
+ case GIMPLE_DEBUG:
break;
default:
}
}
- if (find_edge (ENTRY_BLOCK_PTR, bb))
- return false;
-
if (current_loops)
{
basic_block dest;
if (dest->loop_father->header == dest)
return false;
}
-
- /* If we have an EH edge leaving this block, make sure that the
- destination of this block has only one predecessor. This ensures
- that we don't get into the situation where we try to remove two
- forwarders that go to the same basic block but are handlers for
- different EH regions. */
- succ = single_succ_edge (bb);
- dest = succ->dest;
- FOR_EACH_EDGE (e, ei, bb->preds)
- {
- if (e->flags & EDGE_EH)
- {
- if (!single_pred_p (dest))
- return false;
- }
- }
-
return true;
}
gimple label;
edge_iterator ei;
gimple_stmt_iterator gsi, gsi_to;
- bool seen_abnormal_edge = false;
+ 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))
- {
- seen_abnormal_edge = true;
-
- if (has_abnormal_incoming_edge_p (dest)
- || !gimple_seq_empty_p (phi_nodes (dest)))
- return false;
- }
+ if (has_abnormal_incoming_edge_p (bb)
+ && (has_abnormal_incoming_edge_p (dest)
+ || !gimple_seq_empty_p (phi_nodes (dest))))
+ return false;
/* If there are phi nodes in DEST, and some of the blocks that are
predecessors of BB are also predecessors of DEST, check that the
}
}
+ can_move_debug_stmts = single_pred_p (dest);
+
/* Redirect the edges. */
for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
gsi_next (&gsi))
{
gimple phi = gsi_stmt (gsi);
- add_phi_arg (phi, gimple_phi_arg_def (phi, succ->dest_idx), s);
+ source_location l = gimple_phi_arg_location_from_edge (phi, succ);
+ add_phi_arg (phi, gimple_phi_arg_def (phi, succ->dest_idx), s, l);
}
}
}
- if (seen_abnormal_edge)
+ /* Move nonlocal labels and computed goto targets as well as user
+ defined labels and labels with an EH landing pad number to the
+ new block, so that the redirection of the abnormal edges works,
+ jump targets end up in a sane place and debug information for
+ labels is retained. */
+ gsi_to = gsi_start_bb (dest);
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+ {
+ tree decl;
+ label = gsi_stmt (gsi);
+ if (is_gimple_debug (label))
+ break;
+ decl = gimple_label_label (label);
+ if (EH_LANDING_PAD_NR (decl) != 0
+ || DECL_NONLOCAL (decl)
+ || FORCED_LABEL (decl)
+ || !DECL_ARTIFICIAL (decl))
+ {
+ gsi_remove (&gsi, false);
+ gsi_insert_before (&gsi_to, label, GSI_SAME_STMT);
+ }
+ else
+ gsi_next (&gsi);
+ }
+
+ /* Move debug statements if the destination has just a single
+ predecessor. */
+ if (can_move_debug_stmts)
{
- /* Move the labels to the new block, so that the redirection of
- the abnormal edges works. */
- gsi_to = gsi_start_bb (dest);
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
+ gsi_to = gsi_after_labels (dest);
+ for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); )
{
- label = gsi_stmt (gsi);
- gcc_assert (gimple_code (label) == GIMPLE_LABEL);
+ gimple debug = gsi_stmt (gsi);
+ if (!is_gimple_debug (debug))
+ break;
gsi_remove (&gsi, false);
- gsi_insert_before (&gsi_to, label, GSI_CONTINUE_LINKING);
+ gsi_insert_before (&gsi_to, debug, GSI_SAME_STMT);
}
}
{
stmt = VEC_pop (gimple, MODIFIED_NORETURN_CALLS (cfun));
bb = gimple_bb (stmt);
+ /* BB might be deleted at this point, so verify first
+ BB is present in the cfg. */
if (bb == NULL
+ || bb->index < NUM_FIXED_BLOCKS
+ || bb->index >= n_basic_blocks
+ || BASIC_BLOCK (bb->index) != bb
|| last_stmt (bb) == stmt
|| !gimple_call_noreturn_p (stmt))
continue;
control_bb = single_pred (bb);
stmt = last_stmt (control_bb);
- if (gimple_code (stmt) != GIMPLE_OMP_SECTIONS_SWITCH)
+ if (stmt == NULL || gimple_code (stmt) != GIMPLE_OMP_SECTIONS_SWITCH)
return false;
/* The block with the control statement normally has two entry edges -- one
return true;
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;
calls. */
retval |= split_bbs_on_noreturn_calls ();
}
-
+
end_recording_case_labels ();
BITMAP_FREE (cfgcleanup_altered_bbs);
return retval;
{
gimple phi = gsi_stmt (gsi);
tree def = gimple_phi_arg_def (phi, succ->dest_idx);
+ source_location locus = gimple_phi_arg_location_from_edge (phi, succ);
if (TREE_CODE (def) == SSA_NAME)
{
if (def == old_arg)
{
def = new_arg;
+ locus = redirect_edge_var_map_location (vm);
break;
}
}
}
- add_phi_arg (phi, def, s);
+ add_phi_arg (phi, def, s, locus);
}
redirect_edge_var_map_clear (e);
/* We have to feed into another basic block with PHI
nodes. */
- if (!phi_nodes (dest)
+ 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))
return 1;
}
-struct gimple_opt_pass pass_merge_phi =
+struct gimple_opt_pass pass_merge_phi =
{
{
GIMPLE_PASS,