/* CFG cleanup for trees.
- Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Copyright (C) 2001-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
+#include "fold-const.h"
#include "tm_p.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "cfgcleanup.h"
#include "basic-block.h"
#include "diagnostic-core.h"
#include "flags.h"
-#include "function.h"
#include "langhooks.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "stringpool.h"
#include "tree-ssanames.h"
#include "tree-ssa-loop-manip.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "tree-dfa.h"
#include "tree-ssa.h"
#include "tree-pass.h"
#include "except.h"
#include "cfgloop.h"
-#include "hashtab.h"
#include "tree-ssa-propagate.h"
#include "tree-scalar-evolution.h"
break;
case GIMPLE_SWITCH:
- val = gimple_switch_index (stmt);
+ val = gimple_switch_index (as_a <gswitch *> (stmt));
break;
default:
return retval;
}
+/* Cleanup the GF_CALL_CTRL_ALTERING flag according to
+ to updated gimple_call_flags. */
+
+static void
+cleanup_call_ctrl_altering_flag (gimple bb_end)
+{
+ if (!is_gimple_call (bb_end)
+ || !gimple_call_ctrl_altering_p (bb_end))
+ return;
+
+ int flags = gimple_call_flags (bb_end);
+ if (((flags & (ECF_CONST | ECF_PURE))
+ && !(flags & ECF_LOOPING_CONST_OR_PURE))
+ || (flags & ECF_LEAF))
+ gimple_call_set_ctrl_altering (bb_end, false);
+}
+
/* Try to remove superfluous control structures in basic block BB. Returns
true if anything changes. */
stmt = gsi_stmt (gsi);
+ /* Try to cleanup ctrl altering flag for call which ends bb. */
+ cleanup_call_ctrl_altering_flag (stmt);
+
if (gimple_code (stmt) == GIMPLE_COND
|| gimple_code (stmt) == GIMPLE_SWITCH)
retval |= cleanup_control_expr_graph (bb, gsi);
switch (gimple_code (stmt))
{
case GIMPLE_LABEL:
- if (DECL_NONLOCAL (gimple_label_label (stmt)))
+ if (DECL_NONLOCAL (gimple_label_label (as_a <glabel *> (stmt))))
return false;
if (optimize == 0 && gimple_location (stmt) != locus)
return false;
if (current_loops)
{
basic_block dest;
- /* Protect loop latches, headers and preheaders. */
+ /* Protect loop headers. */
if (bb->loop_father->header == bb)
return false;
- dest = EDGE_SUCC (bb, 0)->dest;
+ dest = EDGE_SUCC (bb, 0)->dest;
+ /* Protect loop preheaders and latches if requested. */
if (dest->loop_father->header == dest)
- return false;
+ {
+ if (bb->loop_father == dest->loop_father)
+ {
+ if (loops_state_satisfies_p (LOOPS_HAVE_SIMPLE_LATCHES))
+ return false;
+ /* If bb doesn't have a single predecessor we'd make this
+ loop have multiple latches. Don't do that if that
+ would in turn require disambiguating them. */
+ return (single_pred_p (bb)
+ || loops_state_satisfies_p
+ (LOOPS_MAY_HAVE_MULTIPLE_LATCHES));
+ }
+ else if (bb->loop_father == loop_outer (dest->loop_father))
+ return !loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS);
+ /* Always preserve other edges into loop headers that are
+ not simple latches or preheaders. */
+ return false;
+ }
}
+
return true;
}
{
int n1 = e1->dest_idx;
int n2 = e2->dest_idx;
- gimple_stmt_iterator gsi;
+ gphi_iterator gsi;
for (gsi = gsi_start_phis (dest); !gsi_end_p (gsi); gsi_next (&gsi))
{
- gimple phi = gsi_stmt (gsi);
+ gphi *phi = gsi.phi ();
tree val1 = gimple_phi_arg_def (phi, n1);
tree val2 = gimple_phi_arg_def (phi, n2);
/* 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))
- || EH_LANDING_PAD_NR (gimple_label_label (label)) != 0))
- return false;
+ if (label)
+ if (glabel *label_stmt = dyn_cast <glabel *> (label))
+ if (DECL_NONLOCAL (gimple_label_label (label_stmt))
+ || EH_LANDING_PAD_NR (gimple_label_label (label_stmt)) != 0)
+ return false;
/* If there is an abnormal edge to basic block BB, but not into
dest, problems might occur during removal of the phi node at out
can_move_debug_stmts = MAY_HAVE_DEBUG_STMTS && single_pred_p (dest);
+ basic_block pred = NULL;
+ if (single_pred_p (bb))
+ pred = single_pred (bb);
+
/* Redirect the edges. */
for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
{
/* Create arguments for the phi nodes, since the edge was not
here before. */
- for (gsi = gsi_start_phis (dest);
- !gsi_end_p (gsi);
- gsi_next (&gsi))
+ for (gphi_iterator psi = gsi_start_phis (dest);
+ !gsi_end_p (psi);
+ gsi_next (&psi))
{
- gimple phi = gsi_stmt (gsi);
+ gphi *phi = psi.phi ();
source_location l = gimple_phi_arg_location_from_edge (phi, succ);
tree def = gimple_phi_arg_def (phi, succ->dest_idx);
add_phi_arg (phi, unshare_expr (def), s, l);
label = gsi_stmt (gsi);
if (is_gimple_debug (label))
break;
- decl = gimple_label_label (label);
+ decl = gimple_label_label (as_a <glabel *> (label));
if (EH_LANDING_PAD_NR (decl) != 0
|| DECL_NONLOCAL (decl)
|| FORCED_LABEL (decl)
set_immediate_dominator (CDI_DOMINATORS, dest, dom);
}
+ /* Adjust latch infomation of BB's parent loop as otherwise
+ the cfg hook has a hard time not to kill the loop. */
+ if (current_loops && bb->loop_father->latch == bb)
+ bb->loop_father->latch = pred;
+
/* And kill the forwarder block. */
delete_basic_block (bb);
return true;
}
-/* STMT is a call that has been discovered noreturn. Fixup the CFG
- and remove LHS. Return true if something changed. */
+/* STMT is a call that has been discovered noreturn. Split the
+ block to prepare fixing up the CFG and remove LHS.
+ Return true if cleanup-cfg needs to run. */
bool
fixup_noreturn_call (gimple stmt)
/* 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 (stmt == gsi_stmt (gsi_last_nondebug_bb (bb)))
+ {
+ /* Don't split if there are only debug stmts
+ after stmt, that can result in -fcompare-debug
+ failures. Remove the debug stmts instead,
+ they should be all unreachable anyway. */
+ gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+ for (gsi_next (&gsi); !gsi_end_p (gsi); )
+ gsi_remove (&gsi, true);
+ }
+ else
+ {
+ split_block (bb, stmt);
+ changed = true;
+ }
+ }
- /* If there is LHS, remove it. */
- if (gimple_call_lhs (stmt))
+ /* If there is an LHS, remove it, but only if its type has fixed size.
+ The LHS will need to be recreated during RTL expansion and creating
+ temporaries of variable-sized types is not supported. */
+ tree lhs = gimple_call_lhs (stmt);
+ if (lhs && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST)
{
- 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)
+ /* We need to fix up the SSA name to avoid checking errors. */
+ if (TREE_CODE (lhs) == 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_FOR_FN (cfun, bb_index));
- BITMAP_FREE (blocks);
- release_ssa_name (op);
+ tree new_var = create_tmp_reg (TREE_TYPE (lhs));
+ SET_SSA_NAME_VAR_OR_IDENTIFIER (lhs, new_var);
+ SSA_NAME_DEF_STMT (lhs) = gimple_build_nop ();
+ set_ssa_default_def (cfun, new_var, lhs);
}
+
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. */
-
-static bool
-split_bbs_on_noreturn_calls (void)
-{
- bool changed = false;
- gimple stmt;
- basic_block bb;
-
- /* Detect cases where a mid-block call is now known not to return. */
- if (cfun->gimple_df)
- while (vec_safe_length (MODIFIED_NORETURN_CALLS (cfun)))
- {
- stmt = MODIFIED_NORETURN_CALLS (cfun)->pop ();
- 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 >= last_basic_block_for_fn (cfun)
- || BASIC_BLOCK_FOR_FN (cfun, bb->index) != bb
- || !gimple_call_noreturn_p (stmt))
- continue;
-
- changed |= fixup_noreturn_call (stmt);
- }
+ /* Mark the call as altering control flow. */
+ if (!gimple_call_ctrl_altering_p (stmt))
+ {
+ gimple_call_set_ctrl_altering (stmt, true);
+ changed = true;
+ }
return changed;
}
+
/* Tries to cleanup cfg in basic block BB. Returns true if anything
changes. */
if (single_succ_p (bb)
&& can_merge_blocks_p (bb, single_succ (bb)))
{
- merge_blocks (bb, single_succ (bb));
- return true;
+ /* If there is a merge opportunity with the predecessor
+ do nothing now but wait until we process the predecessor.
+ This happens when we visit BBs in a non-optimal order and
+ avoids quadratic behavior with adjusting stmts BB pointer. */
+ if (single_pred_p (bb)
+ && can_merge_blocks_p (single_pred (bb), bb))
+ ;
+ else
+ {
+ merge_blocks (bb, single_succ (bb));
+ return true;
+ }
}
return retval;
basic_block bb;
unsigned i, n;
- retval |= split_bbs_on_noreturn_calls ();
-
/* Prepare the worklists of altered blocks. */
cfgcleanup_altered_bbs = BITMAP_ALLOC (NULL);
continue;
retval |= cleanup_tree_cfg_bb (bb);
-
- /* Rerun split_bbs_on_noreturn_calls, in case we have altered any noreturn
- calls. */
- retval |= split_bbs_on_noreturn_calls ();
}
end_recording_case_labels ();
/* If the destination block consists of a nonlocal label, do not
merge it. */
label = first_stmt (dest);
- if (label
- && gimple_code (label) == GIMPLE_LABEL
- && DECL_NONLOCAL (gimple_label_label (label)))
- return false;
+ if (label)
+ if (glabel *label_stmt = dyn_cast <glabel *> (label))
+ if (DECL_NONLOCAL (gimple_label_label (label_stmt)))
+ return false;
+
+ /* Record BB's single pred in case we need to update the father
+ loop's latch information later. */
+ basic_block pred = NULL;
+ if (single_pred_p (bb))
+ pred = single_pred (bb);
/* Redirect each incoming edge to BB to DEST. */
while (EDGE_COUNT (bb->preds) > 0)
{
edge e = EDGE_PRED (bb, 0), s;
- gimple_stmt_iterator gsi;
+ gphi_iterator gsi;
s = find_edge (e->src, dest);
if (s)
!gsi_end_p (gsi);
gsi_next (&gsi))
{
- gimple phi = gsi_stmt (gsi);
+ gphi *phi = gsi.phi ();
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)
{
- edge_var_map_vector *head;
- edge_var_map *vm;
- size_t i;
-
/* If DEF is one of the results of PHI nodes removed during
redirection, replace it with the PHI argument that used
to be on E. */
- head = redirect_edge_var_map_vector (e);
- FOR_EACH_VEC_SAFE_ELT (head, i, vm)
+ vec<edge_var_map> *head = redirect_edge_var_map_vector (e);
+ size_t length = head ? head->length () : 0;
+ for (size_t i = 0; i < length; i++)
{
+ edge_var_map *vm = &(*head)[i];
tree old_arg = redirect_edge_var_map_result (vm);
tree new_arg = redirect_edge_var_map_def (vm);
set_immediate_dominator (CDI_DOMINATORS, dest, dom);
+ /* Adjust latch infomation of BB's parent loop as otherwise
+ the cfg hook has a hard time not to kill the loop. */
+ if (current_loops && bb->loop_father->latch == bb)
+ bb->loop_father->latch = pred;
+
/* Remove BB since all of BB's incoming edges have been redirected
to DEST. */
delete_basic_block (bb);
<L10>:;
*/
-static unsigned int
-merge_phi_nodes (void)
+namespace {
+
+const pass_data pass_data_merge_phi =
+{
+ GIMPLE_PASS, /* type */
+ "mergephi", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_TREE_MERGE_PHI, /* tv_id */
+ ( PROP_cfg | PROP_ssa ), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_merge_phi : public gimple_opt_pass
{
- basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (cfun));
+public:
+ pass_merge_phi (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_merge_phi, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ opt_pass * clone () { return new pass_merge_phi (m_ctxt); }
+ virtual unsigned int execute (function *);
+
+}; // class pass_merge_phi
+
+unsigned int
+pass_merge_phi::execute (function *fun)
+{
+ basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (fun));
basic_block *current = worklist;
basic_block bb;
calculate_dominance_info (CDI_DOMINATORS);
/* Find all PHI nodes that we may be able to merge. */
- FOR_EACH_BB_FN (bb, cfun)
+ FOR_EACH_BB_FN (bb, fun)
{
basic_block dest;
}
else
{
- gimple_stmt_iterator gsi;
+ gphi_iterator gsi;
unsigned int dest_idx = single_succ_edge (bb)->dest_idx;
/* BB dominates DEST. There may be many users of the PHI
for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
gsi_next (&gsi))
{
- gimple phi = gsi_stmt (gsi);
+ gphi *phi = gsi.phi ();
tree result = gimple_phi_result (phi);
use_operand_p imm_use;
gimple use_stmt;
return 0;
}
-static bool
-gate_merge_phi (void)
-{
- return 1;
-}
-
-namespace {
-
-const pass_data pass_data_merge_phi =
-{
- GIMPLE_PASS, /* type */
- "mergephi", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_gate */
- true, /* has_execute */
- TV_TREE_MERGE_PHI, /* tv_id */
- ( PROP_cfg | PROP_ssa ), /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_ssa, /* todo_flags_finish */
-};
-
-class pass_merge_phi : public gimple_opt_pass
-{
-public:
- pass_merge_phi (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_merge_phi, ctxt)
- {}
-
- /* opt_pass methods: */
- opt_pass * clone () { return new pass_merge_phi (m_ctxt); }
- bool gate () { return gate_merge_phi (); }
- unsigned int execute () { return merge_phi_nodes (); }
-
-}; // class pass_merge_phi
-
} // anon namespace
gimple_opt_pass *
static unsigned int
execute_cleanup_cfg_post_optimizing (void)
{
- unsigned int todo = 0;
+ unsigned int todo = execute_fixup_cfg ();
if (cleanup_tree_cfg ())
- todo |= TODO_update_ssa;
+ {
+ todo &= ~TODO_cleanup_cfg;
+ todo |= TODO_update_ssa;
+ }
maybe_remove_unreachable_handlers ();
cleanup_dead_labels ();
group_case_labels ();
GIMPLE_PASS, /* type */
"optimized", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- false, /* has_gate */
- true, /* has_execute */
TV_TREE_CLEANUP_CFG, /* tv_id */
PROP_cfg, /* properties_required */
0, /* properties_provided */
{}
/* opt_pass methods: */
- unsigned int execute () {
- return execute_cleanup_cfg_post_optimizing ();
- }
+ virtual unsigned int execute (function *)
+ {
+ return execute_cleanup_cfg_post_optimizing ();
+ }
}; // class pass_cleanup_cfg_post_optimizing